/*
Theme Name: Kadence Child — Northshore Parent
Template: kadence
Description: Northshore Parent customizations. All NP theme changes live here.
Version: 3.6.0
*/

/* NP brand tokens */
:root {
  --np-dark-blue:#165272; --np-grey-blue:#5894b4; --np-aqua:#7ebcc7;
  --np-aqua-2:#b1e1ea; --np-aqua-3:#d8eff3; --np-aqua-4:#ebf7fc;
  --np-orange:#e5892a; --np-gold:#e5b02c; --np-cream:#faf1db;
  --np-ink:#2f4858; --np-muted:#5f7480;
  /* The one keyboard-focus ring colour, sitewide -- see "Focus states (sitewide)" below. */
  --np-focus-ring: var(--np-dark-blue);
  /* Every heading's colour, sitewide -- see "Typography: headlines" below. Reads through
     --global-palette3 (a Kadence Customizer navy slot) rather than a literal, since that's
     the exact value .entry-title was already computing by inheriting Kadence's own default
     h1 colour -- naming it here makes that the deliberate site-wide value instead of an
     unstyled coincidence a future Customizer change could quietly break apart again. */
  --np-heading-color: var(--global-palette3, #10344a);
  /* A light, neutral blue-grey -- deliberately not from the --np-aqua family, which reads
     as this site's own vivid accent rather than a quiet ground. The blog post hero's
     category chips' own background; see "Blog post hero" below. */
  --np-hero-chip-bg: #dde6ec;

  /* A true neutral grey, not blue-tinted like the tokens above -- the category/similar-posts
     carousel arrows' own hover ground (a plain light grey, as asked, not the listicle
     carousel's own translucent-navy hover tint). See ".np-carousel-btn" below. */
  --np-carousel-btn-hover-bg: #f0f0f0;
  /* One step darker than the hover ground above, so the same stroke reads against either
     background it sits on (idle white, or the grey the circle turns to on hover) -- a
     belt-and-suspenders outline for the white circle against a white page, independent of
     the drop shadow already doing that job. */
  --np-carousel-btn-border: #dcdcdc;

  /*
  The display face, in one place. It was written out fifteen times across this file, which
  meant changing the headline font was fifteen edits and any one of them could be missed.
  */
  --np-font-condensed: "acumin-pro-condensed", "Arial Narrow", "Helvetica Neue Condensed", sans-serif;
  /*
  The body face, tokenised for the same reason as the condensed one above -- so anything
  that needs "the site's regular text font" (the listicle template, most immediately) has
  one name to ask for rather than a literal to keep in step with the `body` rule below.
  */
  --np-font-body: "acumin-pro", "Helvetica Neue", Arial, sans-serif;
  /*
  The site's card-image corner, promoted here from a local declaration on the homepage
  card rule (below) once the listicle template became a second consumer that needed the
  same value -- one token rather than two 8px literals that could drift apart.
  */
  --np-img-radius: 8px;

  /*
  Family Favorites colours live here, at :root, and the sub-brand block below aliases them.

  They were declared only on `.np-nsff`, which meant anything outside that scope -- the
  Family Favorites button in the site navbar, most obviously -- had to repeat the hex. Two
  sources for one colour is how a palette drifts. Declared here they are available to the
  whole document, and `.np-nsff` still exposes them under the style guide's own names so the
  sub-brand rules read the way the guide does.
  */
  --np-bloom-pink: #F2799B;
  --np-midnight-navy: #1E295B;
  --np-ff-gold: #E8C845;
  --np-ff-band-blue: #3495D2;
  /*
  Deep Harbor. The style guide names it in its own comments -- "Deep Harbor rather than Navy
  on a dark ground" -- where Navy is #0C4F73. White on it is about 11:1.
  */
  --np-harbor-blue: #053952;

  /*
  The hero-image shape (ratio, radius) is still shared -- see --np-hero-ratio/--np-hero-radius
  below -- but this specific width value is not any more: it's now only the fallback
  single-post/page hero's own width (".single .post-thumbnail.article-post-thumbnail",
  further down), for a page that shows its featured image above the content without going
  through the Family Favorites page hero (.np-page-hero) or the blog post hero
  (.np-post-hero), neither of which reads this token now -- .np-page-hero uses a plain
  1178px literal instead, matching the nav and footer.

  The 20% step here is measured against the **content column** (1178px), not Kadence's
  `--global-content-width` (1290px): 1290 x 1.2 is 1548px, which exceeds a 1440 viewport,
  clamps to the available width and produces a full-bleed hero instead of an inset one.
  */
  --np-hero-w: calc(1178px * 1.2);
  --np-hero-ratio: 3 / 1;
  --np-hero-radius: 14px;

  /*
  Site chrome -- header, social strip, mobile drawer, footer -- in light mode.

  These exist so the chrome has one place to change. Every colour in those four regions
  reads a token from this block rather than naming a brand colour directly, so returning to
  the dark treatment means editing eleven values here instead of fifty-four declarations
  spread over three thousand lines. The brand tokens above are untouched: cards, ribbons,
  hero strokes and the NSFF sub-brand still use them directly.

  Three inks rather than one, because the nav distinguishes three states -- the current page
  (strong), everything else (muted), and whatever the cursor is on (accent). Collapsing
  accent into strong would mean hovering the active link did nothing visible.
  */
  --np-chrome-nav-bg: #ffffff;
  --np-chrome-drawer-bg: #ffffff;
  --np-chrome-strip-bg: #f2f6f9;          /* thin strip above the navbar */
  --np-chrome-footer-bg: #eef3f7;         /* light blue grey; same value as --np-band */
  --np-chrome-footer-bottom-bg: #dfe8ee;  /* copyright strip, one step deeper */
  --np-chrome-ink: #2f4858;               /* body-weight chrome text */
  --np-chrome-ink-strong: #0e3a52;        /* current page, footer headings */
  --np-chrome-ink-muted: #5f7480;         /* inactive nav links, social glyphs */
  --np-chrome-accent: #165272;            /* hover and focus text */
  --np-chrome-hover-bg: #ebf7fc;          /* hover ground; was --np-navy-shade */
  --np-chrome-rule: #d7dee4;              /* dividers; was --np-navy-shade */

  /* Placeholder text, site wide. Same value as the NSFF `--on-surface-dis` the nomination
     form already used -- that form was the reference, so the two are deliberately equal
     rather than coincidentally close. */
  --np-placeholder: #94a3b8;

  /* How long a content/post image takes to fade in once loaded -- see "Image fade-in on
     load" below. */
  --np-img-fade-duration: .35s;

  /* Space above the body content, sitewide -- see "Content top spacing (sitewide)"
     further down. */
  --np-content-top-spacing: 1rem;
}

body { background: #fff; }

/* Header: dark blue, white nav */
.site-header, #masthead, .site-header .site-header-upper-wrap,
.site-header .site-main-header-wrap { background: var(--np-chrome-nav-bg); }
.site-header .main-navigation a, .site-header .menu > li > a { color: var(--np-chrome-ink-muted); }
.site-header .main-navigation a:hover { color: var(--np-chrome-accent); }
.mobile-toggle-open, .menu-toggle-open { color: var(--np-chrome-ink); }
.site-header .site-title, .site-header .site-tagline { display: none; }
/*
The logo is a wide mark (1440x263 natural), and Kadence's dynamic CSS caps it with
`.site-branding a.brand img { max-width: 200px }` from its "Logo max width" setting.
That width cap is what governs the rendered height -- a `max-height` alone never bound,
which is why raising it changed nothing (37px rendered against a 68px max-height).

Width is therefore the lever: 340px at this aspect ratio comes out ~62px tall, inside
the 80px header row. The `#masthead` id outranks Kadence's two-class-two-element rule.
*/
#masthead .site-branding a.brand img,
#masthead .custom-logo,
.site-header .brand img {
  max-width: 250px;
  max-height: 56px;
  width: auto;
  height: auto;
}
/* On mobile the bar also has to hold the hamburger toggle; at desktop width the mark
   overlapped it. */
@media (max-width: 767px) {
  #masthead .site-branding a.brand img,
  #masthead .custom-logo,
  .site-header .brand img {
    max-width: 200px;
    max-height: 46px;
  }
}

/* Section titles. Border, padding and margin are set in the section-band block near
   the bottom of this file -- the band owns that spacing, because a margin here would
   fall outside the painted background and show as a seam.

   The heading is a flex row: its text is an anonymous flex item and the ::after rule is
   a second item with `flex: 1`, so the rule simply absorbs whatever width the title
   leaves. That is what makes it shrink and grow with the title length, with no
   per-section values to maintain. */
.np-section-title {
  display: flex;
  align-items: center;
  gap: 1rem;
  letter-spacing: .02em;
  /* All caps, at every width -- the desktop-only clamp further down only touches size. */
  text-transform: uppercase;
}
.np-section-title::after {
  content: "";
  flex: 1 1 auto;
  min-width: 1.5rem;   /* never vanishes entirely on a very long title */
  height: 2px;
  background: var(--np-rule);
}

/* ---------- Homepage ---------- */
.home .entry-content > .wp-block-group { max-width: 1200px; margin-left: auto; margin-right: auto; }

/* Section headings: serif, dark blue, orange kicker bar */
.home .entry-content h2:not(.wp-block-post-title):not(.np-section-title) {
  color: var(--np-dark-blue);
  font-size: 1.5rem;
  letter-spacing: .01em;
  margin: 2.75rem 0 1.25rem;
  padding-top: 1rem;
  border-top: 3px solid var(--np-orange);
  display: block;
}

/* Post grids: breathing room */
:is(.home, .np-related, .np-archive-loop) .wp-block-post-template { gap: 1.75rem 1.5rem; }
.home .wp-block-query { margin-bottom: 1rem; }

/* Cards */
:is(.home, .np-related, .np-archive-loop) .wp-block-post-template .wp-block-post {
  display: flex; flex-direction: column;
}
:is(.home, .np-related, .np-archive-loop) .wp-block-post-featured-image { margin-bottom: .7rem; }
:is(.home, .np-related, .np-archive-loop) .wp-block-post-featured-image img {
  border-radius: 8px;
  transition: transform .25s ease, box-shadow .25s ease;
}
/* Card hover intentionally empty -- the previous lift/shadow "pop" was removed;
   a different hover treatment is planned. The img transition above is left in place
   so the replacement has something to animate. */
:is(.home, .np-related, .np-archive-loop) .wp-block-post-title {
  font-size: 1.02rem; line-height: 1.35; margin: 0 0 .3rem;
}
/* Was --np-ink -- moved to the same --np-heading-color every other heading on the site
   now shares (see "Typography: headlines"), since this anchor's own colour is more
   specific than that shared rule and would otherwise keep winning over it, leaving the
   card title looking like a different colour from the section heading above it for no
   deliberate reason. The hover-to-gold effect right below is untouched -- that's a real,
   separate, wanted treatment, not part of this. */
:is(.home, .np-related, .np-archive-loop) .wp-block-post-title a { color: var(--np-heading-color); text-decoration: none; }

/* Title colour is driven by hovering the whole card, not just the title itself, so it
   changes in step with the image stroke and the Read More reveal -- all three now share
   the same `.wp-block-post:hover` trigger. Scoped to `.wp-block-post-template` rather
   than `.home` so archive and category listings behave identically.
   `:focus-within` keeps the keyboard path in sync. */
.wp-block-post-template .wp-block-post-title a {
  transition: color .24s ease;
}
.wp-block-post-template .wp-block-post-title a:hover,
.wp-block-post-template .wp-block-post:hover .wp-block-post-title a,
.wp-block-post-template .wp-block-post:focus-within .wp-block-post-title a {
  color: var(--np-gold);
}
.home .wp-block-post-date {
  color: var(--np-muted); font-size: .8rem;
  text-transform: uppercase; letter-spacing: .06em;
}

/* Hero: first section, first story goes big */
@media (min-width: 768px) {
  .home .wp-block-query:first-of-type .wp-block-post-template {
    grid-template-columns: repeat(4, minmax(0, 1fr));
  }
  .home .wp-block-query:first-of-type .wp-block-post:first-child {
    grid-column: span 2; grid-row: span 2;
  }
  .home .wp-block-query:first-of-type .wp-block-post:first-child .wp-block-post-featured-image img {
    height: 100%; object-fit: cover;
  }
  /* Hero headline. clamp() rather than a fixed size so it scales between the
     768px breakpoint and wide desktops without overflowing the 2-column tile. */
  .home .wp-block-query:first-of-type .wp-block-post:first-child .wp-block-post-title {
    /* !important: the h2 carries has-medium-font-size, and WordPress emits preset
       font-size classes with !important, which beats specificity alone. */
    font-size: clamp(1.9rem, 3.1vw, 3.1rem) !important;
    line-height: 1.1;
    letter-spacing: -0.015em;
    /* No font-weight here: this rule is more specific than the headline rule, so
       declaring 700 held the hero at Bold and kept it off the 800 the kit provides. */
    margin: 0 0 .45rem;
    text-wrap: balance;
  }
}
.home .wp-block-query:first-of-type {
  margin-top: 1.5rem;
  /* Extra separation between the hero and the first banded section. */
  padding-bottom: 3rem;
}

/* ---------- Post thumbnails: hard 4:3 ---------- */
/*
Global on purpose: applies to every core Post Featured Image, not just .home, so
archives and category pages match without a second rule.

Two things are being fought here. (1) The block editor's "Aspect ratio" control
writes aspect-ratio inline on the <figure> and width/height/object-fit inline on
the <img>; inline styles can only be beaten with !important. (2) More subtly, the
card above is `display:flex; flex-direction:column`, which makes the figure a flex
item whose `min-height:auto` resolves to the image's min-content height -- that is
what let portrait sources stretch past the declared ratio and produce mismatched
heights even though the inline 3/2 was present on all 18. `min-height:0` is the
part that actually fixes it; the ratio alone did not.

Consequence for the editor: the block's Aspect ratio control no longer has any
visible effect. Ratio is set here, in code, on purpose.
*/
.wp-block-post-featured-image {
  aspect-ratio: 4 / 3 !important;
  min-height: 0;
  /* No overflow:hidden -- object-fit already crops, and hiding overflow would clip
     the Read More CTA that deliberately hangs past the image's bottom edge. */
}
.wp-block-post-featured-image a {
  display: block;
  height: 100%;
}
.wp-block-post-featured-image img {
  display: block;
  width: 100% !important;
  height: 100% !important;
  object-fit: cover !important;
  object-position: center;
}

/* The homepage hero tile keeps the same 4:3 ratio and gets its prominence from
   spanning 2 grid columns instead: ~577px wide against ~277px for a normal tile,
   so it is larger in both dimensions at an identical ratio. Nothing to override
   here -- it inherits the rule above by design. The 2x2 span lives in the Hero
   block below. */

/* ---------- Card meta line: author + date ---------- */
/*
Rendered as a single `.np-post-meta` element by the render_block_core/post-title filter,
with core's Post Date block suppressed for cards. Done in PHP rather than CSS because
the card is a flex column: the author and date were separate sibling blocks, and flex
blockifies its items, so no amount of `display:inline` would put them on one line.

Replaces the earlier absolutely-positioned chip on the image -- the date is back to
plain text.
*/
.wp-block-post-template .np-post-meta {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: .45em;
  margin: 0 0 .4rem;
  color: var(--np-muted);
  font-size: .82rem;
  letter-spacing: .04em;
  text-transform: uppercase;
}

/* ---------- "Read More" CTA ---------- */
/*
There is no Read More block in the Post Template markup, so this is generated on the
featured image's own <a>. Because the pseudo-element lives inside that anchor it is
genuinely clickable and lands on the post -- no new markup and nothing to add in the
editor, so the whole treatment stays in this file and in git.

Tradeoff: a real core/read-more block would be semantically cleaner. It would mean
editing all five Post Templates, which is page content rather than code and therefore
invisible to git. Chosen the CSS route to match "layout in CSS, as global as possible".

`bottom: 0` + `translateY(50%)` puts exactly half the button below the image edge
regardless of the button's own height -- no magic pixel offset to maintain.
*/
.wp-block-post-template .wp-block-post-featured-image a:not(.np-read-more) {
  position: relative;
  /* Single knob for CTA scale. Both pseudo-elements size their geometry in `em`, so
     the ribbon, its notch and its fold all scale together from this one value -- the
     hero override below only has to change this, not re-declare the shape. Needed
     because ::before does NOT inherit ::after's font-size; they are siblings. */
  /* Base (small card) ribbon scale. Every ribbon dimension -- padding, notch depth,
     overhang, fold, and the 3em lift off the image edge -- is expressed in `em`, so
     this one value resizes the whole assembly coherently. */
  --np-cta-fs: .95rem;
  --np-cta-pad-y: .85em;
  /* How far the ribbon pokes past the image's left edge. The overhang plus the darker
     fold beneath it is what reads as 3D -- the reference does the same thing with
     negative margins. Supersedes the earlier flat 2em inset. */
  --np-cta-out: 1.15em;
  /* Distance from the image's bottom edge up to the ribbon's bottom. The ribbon now
     sits fully inside the image rather than straddling the edge. In `em` of the CTA
     font-size, so it scales with the ribbon on the hero tile. */
  --np-cta-bottom: 3em;
  --np-gold-shade: #a8761a;
}

/* Ribbon body. The trailing chevron is cut with clip-path rather than drawn with
   extra elements, since an ::after cannot have children of its own. */
.wp-block-post-template .wp-block-post-featured-image a:not(.np-read-more)::after {
  content: "Read More";
  position: absolute;
  left: calc(-1 * var(--np-cta-out));
  bottom: var(--np-cta-bottom);
  z-index: 3;
  font-size: var(--np-cta-fs);
  /* Left padding absorbs the overhang so the label stays optically inside the image. */
  padding: var(--np-cta-pad-y) 2.1em var(--np-cta-pad-y) calc(var(--np-cta-out) + 1.15em);
  background: var(--np-gold);
  color: var(--np-dark-blue);
  font-family: var(--np-font-condensed);
  font-weight: 600;
  line-height: 1;
  letter-spacing: .07em;
  text-transform: uppercase;
  white-space: nowrap;
  clip-path: polygon(0 0, 100% 0, calc(100% - .9em) 50%, 100% 100%, 0 100%);
  transition: background-color .28s ease, color .28s ease;
  /* drop-shadow, not box-shadow: box-shadow would trace the unclipped rectangle and
     draw a shadow across the notch that clip-path just cut out. */
  filter: drop-shadow(0 3px 8px rgba(22, 82, 114, .28));
}

/* The fold: a darker wedge tucked beneath the ribbon's leading end, which is what
   reads as "wrapped around the edge" rather than "button sitting on top". Offset down
   by half the ribbon's own height (pad*2 + 1em line, halved) so it meets the body. */
.wp-block-post-template .wp-block-post-featured-image a:not(.np-read-more)::before {
  content: "";
  position: absolute;
  left: calc(-1 * var(--np-cta-out));
  bottom: var(--np-cta-bottom);
  z-index: 2;
  font-size: var(--np-cta-fs);
  width: var(--np-cta-out);
  height: .62em;
  /* Both pseudo-elements share the same `bottom`, so translateY(100%) of the fold's
     own height puts its top edge exactly on the ribbon's bottom edge. This no longer
     depends on the ribbon's padding at all, so the two cannot drift apart when the
     ribbon is retuned. */
  transform: translateY(100%);
  background: var(--np-gold-shade);
  clip-path: polygon(0 0, 100% 0, 100% 100%);
  transition: background-color .28s ease;
}

/* Ribbon hover: invert to navy-on-gold, cross-fading both the body and its fold.
   The trigger is the image's own <a>, which is also the ribbon's generating element --
   so hovering anywhere on the photo or on the ribbon itself drives the same state, with
   no JS and no duplicated selectors. :focus-visible is included so keyboard users get
   the same affordance. */
.wp-block-post-template .wp-block-post-featured-image a:not(.np-read-more):hover::after,
.wp-block-post-template .wp-block-post-featured-image a:not(.np-read-more):focus-visible::after {
  background: var(--np-dark-blue);
  color: var(--np-gold);
}
.wp-block-post-template .wp-block-post-featured-image a:not(.np-read-more):hover::before,
.wp-block-post-template .wp-block-post-featured-image a:not(.np-read-more):focus-visible::before {
  background: var(--np-navy-shade);
}

@media (prefers-reduced-motion: reduce) {
  .wp-block-post-template .wp-block-post-featured-image a:not(.np-read-more)::after,
  .wp-block-post-template .wp-block-post-featured-image a:not(.np-read-more)::before {
    transition: none;
  }
}

/* Clearance so the overhanging CTA does not land on the title. */
.wp-block-post-template .wp-block-post-featured-image {
  margin-bottom: 1.7rem;
}
:is(.home, .np-related, .np-archive-loop) .wp-block-post-featured-image {
  margin-bottom: 1.7rem;
}

/* Hero CTA scales with the hero tile -- a button sized for a 277px card looks lost
   under a 44px headline. Hero only, so it lives in the 768px breakpoint where the
   hero treatment exists at all. */
@media (min-width: 768px) {
  .home .wp-block-query:first-of-type .wp-block-post:first-child
    .wp-block-post-featured-image a {
    /* Drives the label and every em-based dimension of the ribbon, so the shape scales
       with the text instead of the text outgrowing it. */
    --np-cta-fs: 1.32rem;
  }
}

/* Card titles on the smaller tiles. `!important` for the same reason as the hero:
   `has-medium-font-size` is emitted by WordPress with !important and otherwise pins
   every title to the 20px preset. The hero rule is more specific, so it still wins. */
.wp-block-post-template .wp-block-post-title {
  /* The vw term has to be large enough to actually clear the 20px preset at desktop
     widths -- 1.45vw resolved to 20.88px at 1440, i.e. no visible change at all.
     1.75vw gives ~25px at 1440 and still degrades sensibly on narrow screens. */
  font-size: clamp(1.36rem, 1.75vw, 1.72rem) !important;
  line-height: 1.2;
}

/* ---------- Homepage hero: responsive layout ---------- */
/*
Three steps, narrowest last so each overrides the one above it (all carry equal
specificity, so source order decides):

  >= 990px   the 4-column grid with a 2x2 hero, defined further up
  601-989px  hero spans the full row, the remaining four sit two-up beneath it
  <= 600px   everything stacks in a single column

`grid-column: 1 / -1` is what makes the hero full width regardless of how many columns the
track has, and `grid-row: auto` undoes the 2-row span it carries at desktop -- leaving that
in place would open an empty second row.
*/
@media (max-width: 989px) and (min-width: 601px) {
  .home .wp-block-query:first-of-type .wp-block-post-template {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
  .home .wp-block-query:first-of-type .wp-block-post:first-child {
    grid-column: 1 / -1;
    grid-row: auto;
  }
  /* The featured article owns the full row here, so its headline scales up to match.
     `!important` for the `has-medium-font-size` preset, as everywhere else. */
  .home .wp-block-query:first-of-type .wp-block-post:first-child .wp-block-post-title {
    font-size: clamp(2.2rem, 5.5vw, 3.4rem) !important;
    line-height: 1.06;
    letter-spacing: -0.015em;
  }
}
@media (max-width: 600px) {
  .home .wp-block-query:first-of-type .wp-block-post-template {
    grid-template-columns: minmax(0, 1fr);
  }
  .home .wp-block-query:first-of-type .wp-block-post:first-child {
    grid-column: auto;
    grid-row: auto;
  }

  /*
  Featured article goes full-bleed at this size. Only the IMAGE breaks out -- the title and
  meta stay in the content column, since running body text to the screen edges would be
  worse, not bolder.

  Same viewport-based breakout used by the single-post hero: 100vw with a half-width
  translate, which does not care how wide the column it sits in happens to be. Corners are
  squared because a rounded edge-to-edge image shows the page through its corners, and the
  scrim's own radius has to be cleared with it.
  */
  .home .wp-block-query:first-of-type .wp-block-post:first-child .wp-block-post-featured-image {
    width: 100vw;
    max-width: none;
    margin-left: 50%;
    transform: translateX(-50%);
    border-radius: 0;
  }
  .home .wp-block-query:first-of-type .wp-block-post:first-child .wp-block-post-featured-image img,
  .home .wp-block-query:first-of-type .wp-block-post:first-child .wp-block-post-featured-image::after {
    border-radius: 0;
  }

  /* No hover stroke on the featured image here: it is drawn 9px OUTSIDE the image, which
     has nowhere to go once the image spans the viewport -- and a hover affordance earns
     little on a touch screen anyway. */
  .home .wp-block-query:first-of-type .wp-block-post:first-child .wp-block-post-featured-image::before {
    display: none;
  }

  /* Larger headline for the featured article. `!important` for the same reason as
     elsewhere: the h2 carries `has-medium-font-size`, which WordPress emits with
     !important. */
  .home .wp-block-query:first-of-type .wp-block-post:first-child .wp-block-post-title {
    font-size: clamp(1.8rem, 7.5vw, 2.5rem) !important;
    line-height: 1.1;
    letter-spacing: -0.015em;
  }

  /*
  Sit the featured image flush under the navbar. The 80px gap was three separate values
  stacking -- `.content-area` margin-top 32px, `.entry-content-wrap` padding-top 24px and
  the query block's own margin-top 24px -- so all three have to go, not just the nearest
  one.
  */
  .home .content-area {
    margin-top: 0;
  }
  .home .entry-content-wrap {
    padding-top: 0;
  }
  .home .wp-block-query:first-of-type {
    margin-top: 0;
  }
  /* Breathing room under the featured card, now that it runs edge to edge. */
  .home .wp-block-query:first-of-type .wp-block-post:first-child {
    margin-bottom: 1.6rem;
  }
}

/*
The same seam at the *bottom* of the page, at every width -- not just mobile.

56px of plain white sat between the last band and the footer: Kadence's
`.entry-content-wrap` carries a 24px bottom padding and `.content-area` a 32px bottom
margin, neither zeroed here because the top-of-page fix above only touched the matching
top values. Both are Kadence defaults, not anything this theme set on purpose.
*/
.home .entry-content-wrap {
  padding-bottom: 0;
}
.home .content-area {
  margin-bottom: 0;
}

/* ---------- Bottom gradient scrim on card images ---------- */
/*
Uses the *figure's* ::after. The ribbon occupies the anchor's ::after/::before, and the
anchor does not create a stacking context (position:relative with z-index:auto), so the
ribbon's z-index:3 still paints above this scrim's z-index:1 inside the figure.
*/
.wp-block-post-template .wp-block-post-featured-image::after {
  content: "";
  position: absolute;
  inset: auto 0 0 0;
  height: 38%;
  z-index: 1;
  background: linear-gradient(to top, rgba(0, 0, 0, .42), rgba(0, 0, 0, 0));
  border-radius: 0 0 8px 8px;   /* follow the img's own 8px radius */
  pointer-events: none;         /* clicks continue through to the image link */
}

/* ---------- Small-card Read More button ---------- */
/*
A real anchor emitted by the render_block_core/post-title filter, sitting below the
author line -- not generated content, so it is focusable and has its own accessible
name. Square corners as requested.
*/
/*
Selector is `.wp-block-post-featured-image .np-read-more`, not a bare `.np-read-more`.
WordPress core ships `.wp-block-post-featured-image a { display:block; height:100% }`,
which matches this button because it is an <a> inside the figure. At one class plus one
element that outranks a bare single-class selector, so core's `height:100%` won and the
button rendered as a full-height block (106x195px) instead of a button. Two classes
beats one-class-plus-element, and height/width are reset explicitly.
*/
.wp-block-post-featured-image .np-read-more {
  height: auto;
  width: auto;
  /* Centred on the image. Absolute against the figure (the PHP filter injects this
     inside it), so it does not depend on the image's rendered height. */
  position: absolute;
  left: 50%;
  top: 50%;
  z-index: 4;
  opacity: 0;
  transform: translate(-50%, -50%) scale(.94);
  /* Hidden state must not be clickable, or an invisible target sits over the photo. */
  pointer-events: none;
  padding: .82em 1.7em;
  /* Same 8px as the image itself, so the button echoes its container's corner. */
  border-radius: var(--np-img-radius, 8px);
  background: var(--np-gold);
  color: var(--np-dark-blue);
  font-family: var(--np-font-condensed);
  font-size: 1.02rem;
  font-weight: 600;
  line-height: 1;
  letter-spacing: .07em;
  text-transform: uppercase;
  text-decoration: none;
  /* Required, not cosmetic. The button is absolutely positioned with `left: 50%` and no
     `right`, so its available width is only from the midpoint to the figure's right edge
     (~138px on a 277px card). With the larger padding the label needs ~151px and wrapped
     onto two lines. nowrap lets it size to content; translate(-50%) still centres it. */
  white-space: nowrap;
  transition: opacity .26s ease, transform .26s ease,
              background-color .26s ease, color .26s ease;
}
/* Revealed by hovering anywhere on the card. `focus-within` is the keyboard
   equivalent -- without it this button would be unreachable without a mouse. */
.wp-block-post:hover .wp-block-post-featured-image .np-read-more,
.wp-block-post:focus-within .wp-block-post-featured-image .np-read-more {
  opacity: 1;
  transform: translate(-50%, -50%) scale(1);
  pointer-events: auto;
}
/* Colour inversion on the button itself, for direct feedback once it is visible. */
.wp-block-post-featured-image .np-read-more:hover,
.wp-block-post-featured-image .np-read-more:focus-visible {
  background: var(--np-dark-blue);
  color: var(--np-gold);
}
@media (prefers-reduced-motion: reduce) {
  .wp-block-post-featured-image .np-read-more { transition: none; }
}

/* ---------- Ribbon retired ---------- */
/*
Every card in a post template, the homepage featured article included, now uses the
`.np-read-more` hover button. The ribbon was the hero's alone, and it is off.

Suppressed with `content: none` rather than by deleting the ribbon block above: those
rules still describe the only ribbon this site has, and nothing on a post template
enables them any more. They are dead code as far as the current design goes -- say the
word and they come out, along with the scrim comment that refers to them.

Two things that were tied to the ribbon and are now gone with it: the extra vertical
padding on the hero's ribbon, and the larger hero label. The featured article takes the
same button as its neighbours, which is the point.
*/
.wp-block-post-template .wp-block-post-featured-image a:not(.np-read-more)::after,
.wp-block-post-template .wp-block-post-featured-image a:not(.np-read-more)::before {
  content: none;
}

/* ---------- Hover: yellow stroke drawn from the top centre ---------- */
/*
`--np-sweep` is registered with @property so it can be *transitioned* -- a plain custom
property is not animatable, and this whole effect depends on animating an angle.

The mask is a conic gradient starting at 12 o'clock. Two symmetric opaque wedges grow
from 0deg: one clockwise to +sweep, one anticlockwise to (360 - sweep). They meet at the
bottom when sweep reaches 180deg, so the stroke draws from the top centre around both
sides simultaneously.

Why not the previous approaches:
  - four straight background-gradient lines: no corner geometry, cannot be rounded.
  - a radial mask expanding from the top edge: mathematically "worked" and reached
    300%, but a circle centred on the top edge already covers the entire border by
    ~100%, so the reveal completed in the first third of the duration and read as a
    pop-in rather than a draw.

Still a transition, so it unwinds on mouse-out with no reverse keyframes.

Degradation: without @property support the angle is not animatable, so the stroke snaps
in and out instead of drawing. Acceptable -- the hover affordance still exists.
*/
@property --np-sweep {
  syntax: "<angle>";
  inherits: false;
  initial-value: 0deg;
}

.wp-block-post-template .wp-block-post-featured-image {
  /* 3px was chosen to match the old .np-section-title divider, which has since been
     removed in favour of the alternating section bands. Kept at 3px deliberately. */
  --np-stroke: 3px;
  --np-stroke-gap: 9px;
  /* --np-img-radius itself now lives at :root -- see the token block near the top of this
     file -- so the listicle template's images can share the exact same corner as every
     card image on the site instead of a second 8px written out to match it by hand. */
}
.wp-block-post-template .wp-block-post-featured-image::before {
  content: "";
  position: absolute;
  inset: calc(-1 * var(--np-stroke-gap));
  z-index: 1;
  pointer-events: none;
  border: var(--np-stroke) solid var(--np-gold);
  /* Image radius plus the gap keeps the curve concentric with the image's corner. */
  border-radius: calc(var(--np-img-radius) + var(--np-stroke-gap));
  --np-sweep: 0deg;
  -webkit-mask-image: conic-gradient(from 0deg at 50% 50%,
      #000 0deg, #000 var(--np-sweep),
      transparent var(--np-sweep), transparent calc(360deg - var(--np-sweep)),
      #000 calc(360deg - var(--np-sweep)), #000 360deg);
          mask-image: conic-gradient(from 0deg at 50% 50%,
      #000 0deg, #000 var(--np-sweep),
      transparent var(--np-sweep), transparent calc(360deg - var(--np-sweep)),
      #000 calc(360deg - var(--np-sweep)), #000 360deg);
  transition: --np-sweep .52s cubic-bezier(.4, 0, .2, 1);
}
.wp-block-post-template .wp-block-post:hover .wp-block-post-featured-image::before,
.wp-block-post-template .wp-block-post-featured-image:hover::before {
  --np-sweep: 180deg;
}
@media (prefers-reduced-motion: reduce) {
  .wp-block-post-template .wp-block-post-featured-image::before {
    transition: none;
  }
}

/* ---------- Typography: headlines ---------- */
/*
Adobe Fonts kit udr2iav, enqueued in functions.php. The kit ships
acumin-pro-condensed at 400/600/700/800 -- there is no 900, so 800 is the heaviest
available and stands in for Black. Fallbacks are condensed-ish system faces so a
failed kit load does not jump to a wide default.

Colour added here too, not just font: `.entry-title` and `.np-section-title` (the
homepage's category headings, reached through the bare `h2` above) never had a colour of
their own in this theme -- each fell through to whatever Kadence core's own default
heading colour happens to be, which is how the two ended up reading as different colours
in the first place despite looking like they should match. `--np-heading-color` points at
`--global-palette3`, the same Kadence Customizer navy `.entry-title` was already computing
(confirmed live, and independently the value `.nspl-navy`/the carousel arrows' own icon
colour already stand in for), so this makes that the deliberate, named value every heading
here shares instead of an unstyled coincidence. `.wp-block-post-title` itself picks it up
this way; its own `a` still needs pointing at the same token directly, see that rule's own
comment further down -- an anchor's colour doesn't inherit past a more specific rule
already setting it.
*/
h1, h2, h3, h4, h5, h6,
.wp-block-post-title,
.entry-title,
.site-title,
.widget-title {
  font-family: var(--np-font-condensed);
  font-weight: 800;
  color: var(--np-heading-color);
}

/* Everything else on Acumin Pro (the non-condensed family from the same kit). Set on
   `body` so it inherits; form controls are listed explicitly because they do not
   inherit font-family from an ancestor by default. The heading rule above is more
   specific, so headlines stay condensed. */
body,
input, select, textarea, button,
.entry-content, .site-footer {
  font-family: var(--np-font-body);
}

/* ---------- Tags / chips (sitewide) ---------- */
/*
One shared rule for exactly the three properties asked to change on a tag pill -- no
stroke, the site's regular body face rather than the condensed display one, and label
text that is *the same hue as the ground it sits on*, just at full opacity rather than the
ground's own washed-out tint of that colour. A tonal chip, not an outlined one.

Everything else about a tag -- its size, its padding, its corner radius, which colour it
actually uses -- stays declared at the point of use, because those genuinely differ by
context (the listicle chips below keep their own .74rem/letter-spacing/padding exactly as
authored). Only the three shared properties live here, driven by two custom properties
each context sets: `--np-tag-bg` is the pale ground, `--np-tag-ink` is that same colour at
full strength. The defaults exist so `.np-tag` alone -- with no colours set -- still
renders something sane if it is ever used bare.

`.nspl-chips li:not(.nspl-chips-lbl) a` and `.nspl-price` are listed directly in this
selector rather than inheriting `.np-tag` via markup, because that markup is generated by
nsp_listicle_render_item() in inc/listicles.php, which has its own reasons to name its own
classes rather than reuse this one (see that file's own comments). This is the sitewide
source for their border, font and colour regardless.
*/
.np-tag,
.nspl-chips li:not(.nspl-chips-lbl) a,
.nspl-price {
  font-family: var(--np-font-body);
  border: 0;
  color: var(--np-tag-ink, var(--np-dark-blue));
  background: var(--np-tag-bg, var(--np-aqua-4));
}

/* ---------- Homepage section spacing ---------- */
/*
A "section" is an `h2.np-section-title` plus the `.wp-block-query` immediately after it.
They are separate sibling blocks with no wrapper in the page content, so the vertical
rhythm below is set on both and they abut to read as one continuous run. That is why
their vertical margins are zeroed and replaced with padding -- a margin here would fall
*outside* whatever this block paints, and show as a seam between the heading and its posts
if a background were ever reintroduced.

There is no coloured band any more -- see the rule below for why -- but the mechanism for
painting one is left in place, set to `none`/`transparent` rather than deleted. If a band
comes back, it is three values in that one rule, not rebuilding the full-bleed technique:
`box-shadow` spread + `clip-path`, not negative margins, so the shadow paints past the
container on both sides while the element's own box -- and therefore the content width --
stays untouched and aligned with the hero. `clip-path: inset(...)` would confine that
spread vertically to the section; the vertical inset was -14px rather than 0 because the
card hover stroke sits 9px outside each image plus its own 3px width, and clipping at
exactly 0 sheared the top of the stroke on the first row.
*/
:root {
  --np-band: #eef3f7;   /* light blue-grey; swap for --np-aqua-4 (#ebf7fc) if too neutral */
  --np-rule: #d7dee4;   /* light grey rule beside section titles */
  /* Was originally declared on the ribbon's anchor rule, which meant it did not exist
     anywhere else -- the nav's hover `background: var(--np-navy-shade)` silently
     resolved to an invalid value and no fill appeared. Root scope now. */
  --np-navy-shade: #0e3a52;
  /* Search submit: a light medium blue-grey that still separates from the panel's
     lighter --np-band behind it. */
  --np-search-btn-bg: #cfdae3;
  --np-search-btn-bg-hover: #b9c9d6;
}

/*
Margins must be zero, not merely small: they fall OUTSIDE the painted band, so any
margin here appears as an unbanded white gap. Kadence's content.min.css sets
`.single-content h2 { margin-top: 1.5em }` at (0,1,1), which beat a bare
`.np-section-title`, leaving a 69px white strip above every grey band -- and because
that value is in `em`, enlarging the section titles to 46px inflated it from ~36px to
69px. White sections merged with that gap while grey ones did not, which is why they
looked like they had different padding. The selector below is (0,3,1) to win.
*/
.home .entry-content h2.np-section-title,
.home .entry-content h2.np-section-title + .wp-block-query,
.np-related h2.np-section-title,
.np-related h2.np-section-title + .wp-block-query,
.np-section-title,
.np-section-title + .wp-block-query {
  border-top: 0;
  margin-top: 0;
  margin-bottom: 0;
}
.np-section-title {
  padding: 2.4rem 0 1.1rem;
}
.np-section-title + .wp-block-query {
  padding: .9rem 0 2.6rem;
}

/*
No band colour on the homepage sections at all -- white, like the page around them.

This went through two other states first: an odd/even alternation, then a single grey
`--np-band` on every section (matching `.np-related`'s own fixed colour), then grey on all
but the first section. Each was a reasonable reading of "get rid of the evenodd effect...
just keep the background the same for related posts", and each still left grey showing on
the homepage, which is what the section stayed unwanted. Landing here instead: nothing
about "category sections" was ever asked to be grey, only to stop *alternating* -- so the
resolution is no colour at all, not a different fixed one.

`.np-related` is untouched throughout this: its grey comes from a background declared
directly on the `.np-related` wrapper in the "Similar Posts" block further down, a rule
this selector has never touched. "Keep the background the same for related posts" has
therefore been true by construction the whole time, on every one of these attempts.
*/
.np-section-title,
.np-section-title + .wp-block-query {
  background: transparent;
  box-shadow: none;
  clip-path: none;
}

/* Section headings, tablet and up. Matches the specificity of the (now excluded)
   divider rule so it reliably beats the 24px coming from the theme's h2 sizing.
   Trimmed slightly from clamp(2rem, 3.2vw, 3rem) -- same shape, about 12% smaller at
   both ends of the range, now that the all-caps transform above adds its own weight. */
@media (min-width: 768px) {
  .home .entry-content h2.np-section-title,
  .np-related h2.np-section-title {
    font-size: clamp(1.75rem, 2.8vw, 2.65rem);
    line-height: 1.08;
  }
}

/* ---------- Advertisement slots ---------- */
/*
Advanced Ads wraps each slot in a div whose id is randomised per render but always
prefixed `nspar-`, so the attribute prefix selector is the only stable hook -- there is
no class to target.

No colour here either, for the same reason as the sections above and to the same end: a
grey ad slot between two white sections is itself a two-tone alternation, which is exactly
what "get rid of the evenodd effect... for the ad sections" asked to stop. `--np-band-ad`,
the shade this used to carry, was retired when the sections first went to one colour; the
plain `--np-band` that replaced it here is retired now too. Padding and margin stay --
that spacing is independent of colour, and it is what still separates an ad visually from
the editorial content around it.
*/
.home .entry-content div[id^="nspar-"] {
  background: transparent;
  box-shadow: none;
  clip-path: none;
  padding: 2.6rem 1rem;
  margin: 0;
}

/* Shadow is on the ad creative only, never the band. Two reasons: it is the intended
   effect, and the band could not carry one anyway -- its full bleed depends on
   `clip-path: inset(0 -100vmax)`, which confines all painting to its own box vertically,
   so a shadow on the band would be clipped away.
   Navy-tinted rather than black, and much softer than the first pass, which was tuned
   for a navy backdrop and reads far too heavy on a light band. */
.home .entry-content div[id^="nspar-"] > div {
  box-shadow: 0 6px 16px rgba(22, 82, 114, .16), 0 1px 3px rgba(22, 82, 114, .1);
}

/* ---------- Category section carousels ---------- */
/*
The track is a native scroll-snap container, so touch, trackpad and keyboard scrolling
all work with no JavaScript; carousel.js only drives the arrows and their disabled state.

`overflow-x: auto` forces `overflow-y` to compute as auto too, which would clip the card
hover stroke where it sits 9px above the first row -- hence the padding-top, since
overflow clips at the padding box, not the content box.
*/
.np-carousel {
  position: relative;
}
.np-carousel .wp-block-post-template.is-layout-grid {
  display: flex;
  /* Required. Depending on how a loop's displayLayout is authored, core may add
     `is-flex-container`, whose own rule sets `flex-wrap: wrap` -- which turned the
     Similar Posts track into two stacked rows of three instead of one scrolling row. */
  flex-wrap: nowrap;
  grid-template-columns: none;
  gap: 1.5rem;
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  scroll-behavior: smooth;
  overscroll-behavior-x: contain;
  /*
  The scroll container clips at its padding box on BOTH axes (overflow-x:auto forces
  overflow-y to auto), which was shearing the card hover stroke -- it sits 9px outside
  each image plus 3px of its own width. Extending the box 14px past the content on the
  inline axis and padding it straight back gives the stroke room while leaving the cards
  exactly aligned with the section's content width. `scroll-padding-inline` moves the
  snap edge back to the content start so cards still snap flush.
  */
  /* `width: auto` is essential, not tidying. Core sets `width: 100%` on
     .wp-block-post-template, so the negative margin merely SHIFTED the box 14px left
     instead of widening it: the card area came out 1150px against the hero's 1178px and
     the right arrow ended up 28px past the last card. With width auto the box grows to
     content + 28px, the padding puts the cards back on the content edges, and the
     scrollport still extends far enough for the hover stroke. */
  width: auto;
  /* Core's inline block CSS also sets `.wp-block-post-template { max-width: 100% }`,
     which caps the box at the containing block's width -- so `width: auto` alone still
     could not grow past 1178px and the cards stayed 28px narrow. Both are required. */
  max-width: none;
  margin-inline: -14px;
  padding: 14px 14px .6rem;
  scroll-padding-inline: 14px;
  scrollbar-width: none;            /* arrows are the affordance */
  -ms-overflow-style: none;
}
.np-carousel .wp-block-post-template.is-layout-grid::-webkit-scrollbar {
  display: none;
}
.np-carousel .wp-block-post-template.is-layout-grid > .wp-block-post {
  flex: 0 0 calc((100% - 3rem) / 3);   /* three across, matching the old grid */
  scroll-snap-align: start;
}

/*
`display: contents` so the wrapper generates no box of its own: each button then
positions against `.np-carousel` directly, letting one sit on the left edge and one on
the right rather than the pair sharing a corner.
*/
.np-carousel-nav {
  display: contents;
}
.np-carousel--static .np-carousel-btn {
  display: none;   /* nothing to scroll */
}
.np-carousel-btn {
  position: absolute;
  /* Set by carousel.js to the image row's vertical centre. The fallback only matters
     for the instant before the script runs -- `top: 50%` of the carousel lands ~87px
     low, because the box also contains each card's title, meta and the band's padding. */
  top: var(--np-arrow-top, 50%);
  z-index: 6;
  width: 3rem;
  height: 3rem;
  display: grid;
  place-items: center;
  padding: 0;
  /* A visible stroke, not just the drop shadow, so the white circle still reads against a
     white background even if something (a future change, a print stylesheet, a browser
     that suppresses box-shadow) ever drops the shadow again -- box-sizing is border-box
     sitewide, so this doesn't grow the button past its 3rem diameter. */
  border: 1px solid var(--np-carousel-btn-border);
  border-radius: 50%;
  /*
  White circle with a navy chevron, matching the listicle carousel's own arrow icon and
  container (inc/listicles.php's .nspl-caro-nav pill) -- solid, not transparent, since these
  buttons overlap photographs and a see-through control would be unreadable over a busy
  image. Colour reads through the same token chain --nspl-navy itself resolves through
  (--global-palette3, falling back to --np-dark-blue) rather than a second, separately-
  maintained navy, since .nspl-navy is scoped to `.nspl` and cannot be read outside it.
  */
  background: #fff;
  color: var(--global-palette3, var(--np-dark-blue));
  font-size: 1.6rem;
  line-height: 1;
  cursor: pointer;
  /* Neutral, not the old navy-tinted shadow (rgba(22,82,114,...) made sense against a navy
     fill; it doesn't against white) -- matches the listicle pill's own plain black shadow. */
  box-shadow: 0 4px 12px rgba(0, 0, 0, .18);
  transition: background-color .2s ease, box-shadow .2s ease, opacity .2s ease;
}
/* `display: block` so the SVG is not treated as inline text, where its baseline would
   reintroduce the very off-centring the glyphs caused. */
.np-carousel-btn svg {
  display: block;
}
/* Each button's centre lands exactly on the track's edge, so it is half over the cards
   and half outside them. The section band is full-bleed and the content is inset ~131px
   from the viewport, so the outer half has room and is never clipped. */
.np-carousel-btn[data-np-dir="prev"] {
  left: 0;
  transform: translate(-50%, -50%);
}
.np-carousel-btn[data-np-dir="next"] {
  right: 0;
  transform: translate(50%, -50%);
}
/*
Hover and press share one appearance -- the white circle turns a light, plain grey, per
the ask, rather than the icon itself changing colour. `:active` and `:focus` are pinned
explicitly because Kadence's own generic button rule (see "Neutralise Kadence's orange
button hover/focus/active" further down) would otherwise repaint these on click; `:focus`
resets to the plain idle colours rather than a distinct appearance of its own -- the
sitewide `:focus-visible` outline (see "Focus states (sitewide)") is the actual keyboard-
focus indicator, so this only needs to stop Kadence's own default from showing through,
not draw a second one. The base rule's own box-shadow needs no repeating here any more --
the sitewide focus rule that used to strip it on every click/tab no longer touches
box-shadow at all (see that section's own comment).
*/
.np-carousel-btn:hover:not(:disabled),
.np-carousel-btn:active:not(:disabled) {
  background: var(--np-carousel-btn-hover-bg);
  color: var(--global-palette3, var(--np-dark-blue));
}
.np-carousel-btn:focus:not(:hover):not(:disabled) {
  background: #fff;
  color: var(--global-palette3, var(--np-dark-blue));
}
/* At either end of the track the arrow disappears rather than sitting there greyed out.
   `pointer-events: none` matters: without it the invisible button still takes hover and
   clicks, and Kadence's own button hover styling would repaint it. */
.np-carousel-btn:disabled {
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
}

/* Fewer cards in view as the viewport narrows. */
@media (max-width: 1024px) {
  .np-carousel .wp-block-post-template.is-layout-grid > .wp-block-post {
    flex-basis: calc((100% - 1.5rem) / 2);
  }
}
@media (max-width: 640px) {
  .np-carousel .wp-block-post-template.is-layout-grid > .wp-block-post {
    flex-basis: 82%;   /* a sliver of the next card hints that it scrolls */
  }
  .np-carousel-btn {
    width: 2.5rem;
    height: 2.5rem;
    font-size: 1.3rem;
  }
}

@media (prefers-reduced-motion: reduce) {
  .np-carousel .wp-block-post-template.is-layout-grid {
    scroll-behavior: auto;
  }
}

/* Kadence gives content containers (`.content-bg`) a card shadow
   (`rgba(0,0,0,.05) 0 15px 15px -10px`). On the homepage it read as a stray grey smudge
   spanning the width just above the footer, once the full-bleed section bands made the
   article's bottom edge visible. Removed sitewide, not just on the homepage.
   Both selectors are listed because Kadence's own rule is `.entry.content-bg` (two
   classes); a bare `.content-bg` would lose to it on the article wrapper, while the
   bare form is still needed for the other containers that carry the class. */
.content-bg,
.entry.content-bg {
  box-shadow: none;
}

/* ---------- Similar Posts (single post) ---------- */
/*
Rendered by template-parts/content/entry_related.php as a core Query Loop with
queryId 900, which is in np_carousel_query_ids() -- so the `.np-carousel` class, the
arrows, the 4:3 ratio, the hover stroke, the Read More reveal and the meta line all come
from the rules above with nothing duplicated here. This block only supplies the band and
the content width.

`width: min(1178px, 100% - 3rem)` reproduces the homepage's measured 1178px content
width exactly on wide screens while still leaving gutters on narrow ones.
*/
.np-related {
  /*
  The section is rendered inside the single-post content column, which is only 794px
  wide -- so `min(1178px, ...)` on the inner element resolved against that and the
  carousel came out far narrower than the homepage. These negative margins break the
  section out to the full viewport width first; the inner element then re-centres at the
  homepage's 1178px. Safe here because that content column is itself centred in the
  viewport (measured: 323px of gutter on both sides at 1440px).

  The box-shadow spread is kept as a backstop: if a future template ever places this
  section in an off-centre column, the fill still reaches both edges even though the box
  would not.
  */
  margin-inline: calc(50% - 50vw);
  background: var(--np-band);
  box-shadow: 0 0 0 100vmax var(--np-band);
  clip-path: inset(0 -100vmax);
  margin-top: 3rem;
  padding: .5rem 0 3rem;
}
.np-related-inner {
  width: min(1178px, 100% - 3rem);
  margin-inline: auto;
}
/* The band lives on .np-related, so the odd/even rules above must not also paint the
   heading and loop -- they would stack a second background inside the first. */
.np-related .np-section-title,
.np-related .np-section-title + .wp-block-query {
  background: transparent;
  box-shadow: none;
  clip-path: none;
}

/* ---------- Primary navigation ---------- */
/*
Top-level links computed to --np-muted (#5f7480) on the navy header, which is what made
them illegible. Kadence sets that through its own palette rules, so these use `#masthead`
(an id) to outrank them cleanly rather than piling on !important.

Items are made full-height so the hover fill and the underline read as bar-height blocks
rather than floating pills: the bar is 80px while the links were only 48px.
*/
#masthead .site-header-item .main-navigation {
  /* The parent is a flex row with align-items:center, so without this the nav is only
     as tall as its text (27px) and `height: 100%` further down has nothing to fill --
     which is why the underline sat directly under the link instead of at the bar's edge. */
  align-self: stretch;
}
#masthead .main-navigation,
#masthead .main-navigation .primary-menu-container,
#masthead .main-navigation .menu {
  height: 100%;
}
#masthead .main-navigation .menu,
#masthead .main-navigation .menu > li {
  align-items: stretch;
}
#masthead .main-navigation .menu > li {
  display: flex;
}
#masthead .main-navigation .menu > li > a {
  position: relative;
  display: flex;
  align-items: center;
  gap: .4rem;
  height: 100%;
  /* Wider gutters, since the hover fill now spans the item. */
  /* Slightly tighter than before, to absorb the extra "Home" item without the bar
     wrapping on mid-size desktops. */
  padding: 0 1.2rem;
  /*
  Condensed semibold caps. 600 exists in the kit (400/600/700/800 for
  acumin-pro-condensed), so this is a real weight rather than a synthesised one.
  Letter-spacing is added deliberately: condensed uppercase sets very tight, and without
  tracking the labels read as a solid block. Size is nudged up because condensed caps
  occupy less width per character than the previous mixed-case setting.
  */
  font-family: var(--np-font-condensed);
  font-weight: 600;
  font-size: 1.02rem;
  letter-spacing: .05em;
  text-transform: uppercase;
  /* Inactive links sit a step darker than the footer's light blue, so the white active
     item reads as clearly selected. */
  color: var(--np-chrome-ink-muted);
  transition: background-color .2s ease, color .2s ease;
}

/*
Active page. WordPress marks the current item with several classes depending on whether
it is the page itself, an ancestor, or the parent of the current item -- all are listed
so a child page still highlights its top-level parent in the bar.

These sit BEFORE the hover rules on purpose: the two carry equal specificity, so source
order is what lets hover still win over the active state.
*/
#masthead .main-navigation .menu > li.current-menu-item > a,
#masthead .main-navigation .menu > li.current-menu-ancestor > a,
#masthead .main-navigation .menu > li.current-menu-parent > a,
#masthead .main-navigation .menu > li.current_page_item > a,
#masthead .main-navigation .menu > li.current_page_ancestor > a,
#masthead .main-navigation .menu > li.current_page_parent > a {
  color: var(--np-chrome-ink-strong);
}

/* Chevrons: Kadence colours these separately and ships fill:#000, so both properties
   need setting or the arrow stays dark against the navy. */
#masthead .main-navigation .menu > li > a .dropdown-nav-toggle,
#masthead .main-navigation .menu > li > .dropdown-nav-toggle,
#masthead .main-navigation .menu > li .dropdown-nav-toggle svg,
#masthead .main-navigation .menu > li > a svg {
  color: inherit;   /* follow the label: light blue, or white when active */
  fill: currentColor;
}

#masthead .main-navigation .menu > li > a:hover,
#masthead .main-navigation .menu > li:hover > a,
#masthead .main-navigation .menu > li:focus-within > a {
  color: var(--np-chrome-accent);
  /* Colour only, no weight change: bolding re-measured the text and visibly widened the
     item (and shifted its neighbours) on every hover. */
  background: var(--np-chrome-hover-bg);
}
#masthead .main-navigation .menu > li > a:hover .dropdown-nav-toggle,
#masthead .main-navigation .menu > li:hover > a .dropdown-nav-toggle,
#masthead .main-navigation .menu > li:hover .dropdown-nav-toggle svg,
#masthead .main-navigation .menu > li:hover > a svg {
  color: var(--np-chrome-accent);
  fill: currentColor;
}

/*
Underline swipe, in three colours that arrive one after another: orange, then gold, then
aqua.

**Three background layers on one pseudo-element, not three stacked elements.** Each layer has
its own animated width, so the three draw across independently and the stagger comes from
giving each its own `transition-delay`. A single `transition-delay` on `background-size` could
not do this: the property takes a list of sizes but one delay, so all three layers would move
together.

**The layers are flat colour, not ramps.** `linear-gradient(<colour> 0 0)` is the idiom for a
solid background *layer* -- there is only one `background-color`, so a stack of solids has to
be expressed as images. Every colour boundary here is therefore a hard edge: what you see is
each layer's `background-size` edge, not a blend. Written in the two-position form rather than
as `linear-gradient(c, c)` precisely so it does not read as a gradient to the next person.

Why not three elements: only `a::after` is genuinely free here. `.sub-menu::before` is
already the bridge that keeps dropdowns reachable, and the `<li>` is **not** a positioned
element -- adding `position: relative` to it to host a pseudo-element would change the
containing block the dropdowns resolve against, which is not a thing worth risking for an
underline.

`@property` is what makes the widths animatable at all: a plain custom property is not
interpolated, so the layers would jump rather than draw. Same mechanism as the card hover
stroke further up this file. Without `@property` support the stroke snaps in instead of
sweeping -- the affordance survives, the flourish does not.

Layer order paints first-listed on top, so aqua ends up covering the other two: during the
sweep you see an orange leading edge trailed by gold and then aqua, and at rest the stroke
is solid aqua.
*/
@property --np-stroke-1 {
  syntax: "<percentage>";
  inherits: false;
  initial-value: 0%;
}
@property --np-stroke-2 {
  syntax: "<percentage>";
  inherits: false;
  initial-value: 0%;
}
@property --np-stroke-3 {
  syntax: "<percentage>";
  inherits: false;
  initial-value: 0%;
}

#masthead .main-navigation .menu > li > a::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: 4px;
  background:
    linear-gradient(var(--np-aqua) 0 0) left bottom / var(--np-stroke-3) 100% no-repeat,
    linear-gradient(var(--np-gold) 0 0) left bottom / var(--np-stroke-2) 100% no-repeat,
    linear-gradient(var(--np-orange) 0 0) left bottom / var(--np-stroke-1) 100% no-repeat;
  /*
  The delays are only on the way in. Declared here without them, so on mouse-out all three
  retract together -- staggered on the way out reads as the stroke coming apart.
  */
  transition:
    --np-stroke-1 .5s cubic-bezier(.4, 0, .2, 1),
    --np-stroke-2 .5s cubic-bezier(.4, 0, .2, 1),
    --np-stroke-3 .5s cubic-bezier(.4, 0, .2, 1);
}
/*
Hover only. The gold stroke used to draw itself on `:focus-within` as well, which meant it
appeared under an item whenever anything inside it took focus -- including the moment a
dropdown was opened from the keyboard, and on the parent of a focused sub-menu link.

Keyboard users are not left without feedback: `li:focus-within > a` still changes both the
text colour and the ground, so a focused item is still clearly marked.
*/
#masthead .main-navigation .menu > li > a:hover::after,
#masthead .main-navigation .menu > li:hover > a::after {
  --np-stroke-1: 100%;
  --np-stroke-2: 100%;
  --np-stroke-3: 100%;
  /*
  200ms apart, against a 500ms draw.

  How wide each colour band looks is the **ratio** of the stagger to the duration, not the
  duration on its own: a layer's leading edge sits `delay / duration` of the way behind the
  one before it. The old pair was 90ms against 280ms, so each band was about a third of the
  item's width; slowing everything proportionally would have looked exactly the same, only
  lazier. Raising the stagger faster than the duration is what actually separates the
  colours -- 200/500 puts each band at roughly 40% of the width, so orange is nearly all the
  way across before aqua sets off.

  The whole cascade now finishes in 900ms. If that reads as sluggish on hover, bring the
  duration down before the delays: shortening the delays first would narrow the bands again
  and undo the point of the change.
  */
  transition-delay: 0ms, 200ms, 400ms;
}
@media (prefers-reduced-motion: reduce) {
  #masthead .main-navigation .menu > li > a::after {
    transition: none;
  }
  /* Straight to the final colour, with no cascade. */
  #masthead .main-navigation .menu > li > a:hover::after,
  #masthead .main-navigation .menu > li:hover > a::after {
    transition-delay: 0ms;
  }
}

/* Sub-menus sit below the underline rather than covering it. The ::before bridges the
   gap back up to the nav item: without it the pointer leaves the <li> while crossing
   the offset and the dropdown closes before it can be reached. Hovering a pseudo-element
   counts as hovering its element, so the bridge keeps `li:hover` true. */
/*
The dropdown panel was the one part of the header light mode missed, because its ground is
Kadence's (#10344a, from the Customizer) rather than this stylesheet's -- so there was no
declaration here to remap. It stayed a dark panel hanging off a white bar.

The hairline earns its place now: against a white navbar a shadow alone leaves the panel
edge indistinct.
*/
#masthead .main-navigation .menu > li > .sub-menu {
  top: calc(100% + 6px);
  min-width: 17.5rem;
  background: var(--np-chrome-nav-bg);
  border: 1px solid var(--np-chrome-rule);
  box-shadow: 0 10px 24px rgba(88, 148, 180, .28);
}
#masthead .main-navigation .sub-menu li > a {
  color: var(--np-chrome-ink);
  /*
  Kadence sets a fixed `width: 200px` on dropdown anchors, both in header.min.css and
  again in its dynamic customiser CSS (its "dropdown width" setting). Widening the <ul>
  therefore left the anchors -- and so the hover fill -- covering only part of the row.
  The `#masthead` id in the prefix is what lets this outrank the dynamic rule's four
  classes without !important.
  */
  width: 100%;
  padding: .85rem 1.4rem;
  font-size: 1rem;
  transition: background-color .18s ease, color .18s ease;
}
#masthead .main-navigation .sub-menu li > a:hover,
#masthead .main-navigation .sub-menu li:hover > a,
#masthead .main-navigation .sub-menu li:focus-within > a {
  color: var(--np-chrome-accent);
  background: var(--np-chrome-hover-bg);
}
/*
The "you are here" state, neutralised.

Kadence's own dynamic CSS carries `...li.menu-item.current-menu-item > a { color:
var(--global-palette9); background: var(--global-palette4) }`. This site's palette-4 slot
resolves to the exact same ink as `--np-chrome-ink` -- and my rule above already wins the
*text* colour on the `#masthead` id (an id beats any number of classes), so the current
item's text was already this ink colour. Kadence's rule was never contested on
*background*, though, so its palette-4 fill -- the same value -- painted straight under it.
Text and background identical: invisible, not just wrong.

Reset explicitly rather than only fixing the collision, since a highlighted current item
in a dropdown was reported as unwanted, not merely broken.
*/
#masthead .main-navigation .sub-menu li.current-menu-item > a,
#masthead .main-navigation .sub-menu li.current_page_item > a,
#masthead .main-navigation .sub-menu li.current-menu-ancestor > a,
#masthead .main-navigation .sub-menu li.current_page_ancestor > a {
  background: transparent;
  color: var(--np-chrome-ink);
}
#masthead .main-navigation .menu > li > .sub-menu::before {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  top: -6px;
  height: 6px;
}

/* Soft blue-grey shadow under the bar. */
#masthead {
  /* Lighter than it was (.3): with a white bar the shadow is the only thing separating the
     header from the page, so it reads far stronger than it did against dark blue. */
  box-shadow: 0 6px 18px rgba(88, 148, 180, .16);
}

/* ---------- Family Favorites as a nav button ---------- */
/*
The one item in the navbar that is a call to action rather than navigation, so it drops the
shared nav treatment entirely -- the full-height hover ground, the gold underline that
sweeps in, the inactive/current colour pair -- and becomes a pill.

Bloom pink with white text, reversing to midnight navy with white text on hover, both read
from the `:root` tokens. They used to be literals here: the guide-named tokens are declared
on `.np-nsff`, and the navbar sits outside that scope on every page but the sub-brand's, so
referencing them would have resolved to nothing across the rest of the site.

**This block must stay after the current-page colour rules above.** `li.np-nav-cta > a` and
`li.current_page_parent > a` are both (1,3,2), so the tie is settled on source order, and
this item is a current-page ancestor on every Family Favorites page -- precisely where the
button has to keep its white text.

The class comes from a `nav_menu_css_class` filter keyed on the target page id, so it does
not depend on the menu label or the URL.
*/
#masthead .main-navigation .menu > li.np-nav-cta {
  display: flex;
  align-items: center;
}
#masthead .main-navigation .menu > li.np-nav-cta > a {
  /* The other items are full-bleed vertically; this one is a pill inside the bar. */
  height: auto;
  min-height: 40px;
  margin: 0 .3rem;
  padding: 0 20px;
  border-radius: 999px;
  background: var(--np-bloom-pink);
  color: #fff;
}
/* The sweeping gold underline belongs to the nav items, not to a button. */
#masthead .main-navigation .menu > li.np-nav-cta > a::after {
  content: none;
}
#masthead .main-navigation .menu > li.np-nav-cta:hover > a,
#masthead .main-navigation .menu > li.np-nav-cta:focus-within > a {
  background: var(--np-midnight-navy);
  color: #fff;
}


/* ---------- Family Favorites section navigation ---------- */
/*
The site nav's treatment, applied to a second bar under the header on Family Favorites
pages. Same type, same interaction: uppercase condensed labels, a muted resting colour, the
current page picked out, and a stroke that sweeps in under the item on hover.

Two deliberate differences from the main bar. The stroke is bloom pink rather than gold,
because this bar belongs to the sub-brand and the colour is what says so. And the items are
not full-bleed vertically -- this is a slimmer secondary bar, so the hover ground is a
rounded chip rather than a full-height block.

Sub-brand tokens are safe to use here: the bar only ever renders inside `body.np-nsff`, and
a `var()` in a normal property resolves on the element, not where the rule is written.
*/
.np-ff-nav {
  background: var(--np-chrome-nav-bg);
  border-bottom: 1px solid var(--np-chrome-rule);
}
/*
Also carries Kadence's own `site-container` class in the markup (functions.php), the same
as the site header's own row did before that was overridden to 1178px -- without a width
rule of its own, this sub-nav would still be reading Kadence's separate, wider
`--global-content-width` (1290px) and sit out of step with the header immediately above it.
*/
.np-ff-nav__inner {
  width: min(1178px, 100% - 3rem);
  display: flex;
  align-items: center;
  gap: .25rem;
  /*
  Six items do not fit a phone. Scrolling sideways keeps them all reachable and in one row,
  which a wrap would not -- a second row here would push the page content down on every
  Family Favorites page.
  */
  overflow-x: auto;
  scrollbar-width: none;
}
.np-ff-nav__inner::-webkit-scrollbar {
  display: none;
}
.np-ff-nav__link {
  position: relative;
  flex: none;
  padding: .85rem .9rem;
  color: var(--np-chrome-ink-muted);
  font-family: var(--np-font-condensed);
  font-size: .95rem;
  font-weight: 600;
  letter-spacing: .05em;
  line-height: 1;
  text-transform: uppercase;
  text-decoration: none;
  white-space: nowrap;
  border-radius: var(--shape-sm);
  transition: color var(--dur-short, .15s) ease, background-color var(--dur-short, .15s) ease;
}
/* The current page, and the section a deeper page belongs to (Winners, from /winners/2027/). */
.np-ff-nav__link.is-current,
.np-ff-nav__link.is-section {
  color: var(--np-chrome-ink-strong);
}
/*
`:focus` and `:active` are here for a reason, not for completeness.

Kadence's `global.min.css` carries `a:hover, a:focus, a:active { color:
var(--global-palette-highlight-alt) }`, and that variable is NP orange. An element plus a
pseudo-class is (0,1,1), which outranks the single class of `.np-ff-nav__link` at (0,1,0) --
so the resting rule never had a say, and clicking a link left it orange. Naming the states
here puts them at (0,2,0) and settles it.

The tint is deliberately not applied to `:focus`/`:active`: it would stay behind a link the
reader has merely clicked. Hover and keyboard focus only.
*/
.np-ff-nav__link:hover,
.np-ff-nav__link:focus,
.np-ff-nav__link:active,
.np-ff-nav__link:focus-visible {
  color: var(--midnight-navy);
}
.np-ff-nav__link:hover,
.np-ff-nav__link:focus-visible {
  background: var(--np-chrome-hover-bg);
}
/*
The sweeping stroke, as on the main nav: scaled to nothing from the left, and drawn across
on hover. `transform` rather than `width` so it animates on the compositor.
*/
.np-ff-nav__link::after {
  content: "";
  position: absolute;
  left: .9rem;
  right: .9rem;
  bottom: .35rem;
  height: 3px;
  border-radius: 2px;
  background: var(--bloom-pink);
  transform: scaleX(0);
  transform-origin: left center;
  transition: transform .28s cubic-bezier(.4, 0, .2, 1);
}
.np-ff-nav__link:hover::after,
.np-ff-nav__link:focus-visible::after,
.np-ff-nav__link.is-current::after {
  transform: scaleX(1);
}
@media (prefers-reduced-motion: reduce) {
  .np-ff-nav__link,
  .np-ff-nav__link::after {
    transition: none;
  }
}

/* ---------- Header social strip ---------- */
/*
Rendered by np_render_social_strip() on kadence_top_header, so it sits directly above the
navbar. It lives INSIDE the header wrapper that this stylesheet paints navy, hence the
explicit background -- otherwise the strip would inherit the navy behind it.

Inner width matches the 1178px used by the homepage grids so the icons line up with the
content column rather than the viewport edge.
*/
.np-social-strip {
  /* Same darker navy as the footer copyright band. No divider needed: it sits directly
     above the navbar's lighter navy, so the tonal step already separates them -- a light
     rule would read as a seam. */
  background: var(--np-chrome-strip-bg);
}
/*
The inner element carries Kadence's `site-container` class, so its width and gutters are
identical to the header row's by construction -- no second set of numbers to keep in sync.
*/
.np-social-strip-inner {
  display: flex;
  justify-content: flex-end;
}
/*
Matches the footer's own content width (.np-footer-inner, further down) rather than
Kadence's own `--global-content-width` (1290px) that this row used by default -- the two
never actually agreed before, despite reading as "the site's content width" either place.
1178px is the value this theme uses consistently everywhere else for that measure (the
footer, single-post content, the homepage/category grids), so the nav now matches those
instead of Kadence's own separate Customizer value.

Scoped to `.site-header`/`#masthead` specifically, not a bare `.site-container` override:
that class is also the footer's own utility-links row, the sitewide announcement bar, the
Family Favorites sub-nav, and (with its own more specific `.single` rule already) the main
content wrapper -- a bare override would have resized all of those along with the header.
*/
.site-header .site-container,
#masthead .site-container {
  width: min(1178px, 100% - 3rem);
}
.np-social-strip-list {
  display: flex;
  align-items: center;
  gap: .15rem;
  /* Zeroed explicitly: this is a <ul>, and Kadence's global `ul, ol` rule adds a 1.5em
     left margin that would push the strip out of alignment. */
  margin: 0;
  padding: 0;
  list-style: none;
}
/*
Aligns the last strip glyph with the navbar's rightmost glyph, which is now the search
icon rather than a menu label. Both are glyphs centred in round-ish boxes flush to the
same container edge, so the offset is just the difference between how far each glyph sits
inside its own box. Written out from the actual box and glyph sizes so it follows any
change to either, instead of hard-coding the resulting pixel value.
*/
.np-social-strip-list {
  margin-right: calc((2.1rem - 1.05rem) / 2 - (1.85rem - .95rem) / 2);
}
/*
Only one copy may show at a time. This boundary used to track Kadence's own header switch
exactly (1240px, its `header_mobile_switch` Customizer option) -- it no longer does, on
purpose: 1350px is 110px wider, a deliberate safety margin now that the header row itself
is 112px narrower than it was (1178px, matching the footer, instead of Kadence's own
`--global-content-width` at 1290px -- see ".site-header .site-container" further up).
Measured live at the old 1240px boundary with the new narrower row: the logo and nav still
had 148px of slack, no actual overflow -- this margin is headroom for a future longer menu
label or larger logo, not a fix for a measured problem. Kadence's own native switch still
fires at its own unmoved 1240px underneath this; the "Force the header switch earlier"
rule right below is what actually makes the row swap to mobile in the 1240-1350px gap this
creates, so the two never show a mismatched half-desktop, half-mobile header. If Kadence's
own setting ever changes, this 1350px and that override both need reconsidering, and so
does the fallback in js/nav-fit.js.
*/
@media (min-width: 1350px) {
  .np-social-strip--mobile {
    display: none;
  }
}
@media (max-width: 1349px) {
  .np-social-strip--desktop {
    display: none;
  }
  /* The desktop offset lines the icons up with the search circle, which the mobile bar
     does not have. */
  .np-social-strip-list {
    margin-right: 0;
  }
}
/*
Force the header switch earlier, matching the social strip's own new 1350px boundary above:
Kadence's own native switch (its `header_mobile_switch` Customizer option) is unmoved at
1240px and generates its own media queries for `.site-header-wrap`/`.site-mobile-header-wrap`
that this repo cannot see or edit directly. `!important` because this competes with that
generated CSS at equal specificity (a bare class selector each) with no source-order
guarantee -- the "Nav fit guard" rule further down gets away without `!important` only
because its `body.np-nav-overflow` prefix is a genuine specificity win, which a plain media
query here has no equivalent of.
*/
@media (max-width: 1349px) {
  .site-header-wrap {
    display: none !important;
  }
  .site-mobile-header-wrap {
    display: block !important;
  }
}

.np-social-strip-link {
  display: grid;
  place-items: center;
  width: 1.85rem;
  height: 1.85rem;
  color: var(--np-chrome-ink-muted);
  text-decoration: none;
  transition: color .18s ease;
}
.np-social-strip-link svg {
  display: block;
  width: .95rem;
  height: .95rem;
  fill: currentColor;
}
.np-social-strip-link:hover,
.np-social-strip-link:focus-visible {
  /* Gold on hover -- navy-on-navy would vanish now the strip is dark. */
  color: var(--np-chrome-accent);
}

/* ---------- Footer utility menu ---------- */
/*
Rendered on kadence_middle_footer, which fires between our main footer content and the
copyright strip. Centred, with gold bullets drawn as ::after on every item except the last
-- a pseudo-element rather than a border so it can sit on the text baseline and inherit the
list's own gap.
*/
.np-footer-utility {
  padding: 1.4rem 0 1.6rem;
}
.np-footer-utility-list {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  align-items: center;
  gap: .35rem 1rem;
  /* Kadence's global `ul, ol` margin would knock the centring off. */
  margin: 0;
  padding: 0;
  list-style: none;
}
.np-footer-utility-list li {
  display: flex;
  align-items: center;
  gap: 1rem;
}
.np-footer-utility-list li:not(:last-child)::after {
  content: "\2022";
  color: var(--np-gold);
  font-size: 1.1em;
  line-height: 1;
}
.np-footer-utility-list a {
  color: var(--np-chrome-ink);
  text-decoration: none;
  font-size: .95rem;
  transition: color .2s ease;
}
.np-footer-utility-list a:hover,
.np-footer-utility-list a:focus-visible {
  color: var(--np-chrome-accent);
}

/* ---------- Header search ---------- */
/*
The toggle is the last item in the primary menu, so it inherits the bar's full height and
sits level with the links. Its chrome is reset because it is a <button> inside a menu that
otherwise contains anchors -- and Kadence paints bare buttons orange on hover/focus/active,
so all three states are pinned here as they are for the carousel arrows.
*/
/*
`align-items: center` rather than `stretch`, and a fixed size rather than height:100%:
that is what stops the hover state filling the whole bar-height column.

The selector must carry the same `#masthead .main-navigation .menu > li` prefix used
above. As a bare `.np-search-item` it lost to that rule's `align-items: stretch`, and the
button -- having a fixed height -- was pinned to the TOP of its cell rather than centred
in the bar.
*/
#masthead .main-navigation .menu > li.np-search-item {
  display: flex;
  align-items: center;
}
.np-search-toggle {
  display: grid;
  place-items: center;
  width: var(--np-search-btn, 2.1rem);
  height: var(--np-search-btn, 2.1rem);
  margin-left: .55rem;
  padding: 0;
  border: 0;
  border-radius: 50%;
  /* Faint disc so the circle reads against the white bar even at rest. */
  background: var(--np-chrome-hover-bg);
  color: var(--np-chrome-ink);
  cursor: pointer;
  transition: background-color .2s ease, color .2s ease;
}
.np-search-toggle svg {
  display: block;
  width: var(--np-search-icon, 1.05rem);
  height: var(--np-search-icon, 1.05rem);
  fill: currentColor;
}
/* Glyph swap driven entirely by the button's own state, so it cannot desync from
   aria-expanded the way a JS-swapped icon could. */
.np-search-toggle .np-icon-search,
.np-search-toggle .np-icon-close {
  display: grid;
  place-items: center;
}
.np-search-toggle .np-icon-close,
.np-search-toggle[aria-expanded="true"] .np-icon-search {
  display: none;
}
.np-search-toggle[aria-expanded="true"] .np-icon-close {
  display: grid;
}
/*
Plain `:focus` is pinned back to the resting look. Kadence paints bare buttons orange on
focus, and the button keeps focus after a click -- search.js returns focus to it on close
deliberately -- so closing the panel left an orange disc behind. `:focus-visible` is NOT
included here: it only matches keyboard focus, which should still be highlighted.

The `#masthead` prefix is there so these outrank Kadence's own button-state rules.
*/
#masthead .np-search-toggle:focus {
  background: var(--np-chrome-hover-bg);
  color: var(--np-chrome-ink);
}
#masthead .np-search-toggle:hover,
#masthead .np-search-toggle:focus-visible,
#masthead .np-search-toggle:active,
#masthead .np-search-toggle[aria-expanded="true"] {
  background: var(--np-gold);
  color: var(--np-dark-blue);
}

/*
The panel slides down from behind the navbar. `max-height` rather than `height` because a
content-derived height cannot be transitioned; the value only needs to exceed the panel's
real height, and `overflow: hidden` does the clipping. Opacity is deliberately a shorter,
delayed transition so the field does not appear before there is room for it.
*/
.np-search-panel {
  overflow: hidden;
  max-height: 0;
  opacity: 0;
  background: var(--np-band);
  transition: max-height .34s cubic-bezier(.4, 0, .2, 1), opacity .2s ease;
}
.np-search-panel.np-search-open {
  max-height: 9rem;
  opacity: 1;
}
.np-search-panel-inner {
  padding-block: 1.1rem 1.3rem;
}
/* The submit sits INSIDE the field, so the form is a positioning context rather than a
   flex row. `--np-search-inset` is the gap between the button and the field's edge; it
   drives both the button's inset and how much it is shorter than the field. */
.np-search-form {
  position: relative;
  --np-search-inset: 6px;
}
/*
Prefixed with the panel's id: Kadence styles form controls with enough specificity to beat
a lone class, and it silently won both properties -- the radius came out at its 3px and
the box-shadow resolved to a transparent one, so the field had neither the rounded corners
nor the shadow that were asked for.
*/
#np-search-panel .np-search-field,
#mobile-drawer .np-search-field {
  flex: 1 1 auto;
  min-width: 0;
  padding: .8rem 1.1rem;
  border-radius: 8px;   /* matches the cards' corner */
  background: #fff;
  color: var(--np-ink);
  font-size: 1rem;
  width: 100%;
  /* Room for the inset button so typed text never runs under it. */
  padding-right: 9rem;
  /* A hairline rather than a drop shadow. Kept on `border` (not an inset shadow) so the
     field still reads as an input; box-sizing is border-box sitewide, so adding it does
     not change the field's height. */
  border: 1px solid #dfe5ea;
  box-shadow: none;
}
#np-search-panel .np-search-field:focus,
#mobile-drawer .np-search-field:focus {
  outline: 2px solid var(--np-chrome-accent);
  outline-offset: 2px;
}
#np-search-panel .np-search-submit,
#mobile-drawer .np-search-submit {
  position: absolute;
  top: var(--np-search-inset);
  bottom: var(--np-search-inset);
  right: var(--np-search-inset);
  padding: 0 1.05rem;
  font-size: 1rem;
  border: 0;
  /* Slightly tighter than the field's 8px so the inset corners look concentric. */
  border-radius: 6px;
  background: var(--np-search-btn-bg);
  color: var(--np-ink);
  font-family: var(--np-font-condensed);
  font-weight: 600;
  letter-spacing: .06em;
  text-transform: uppercase;
  cursor: pointer;
  box-shadow: none;
  transition: background-color .2s ease, color .2s ease;
}
#np-search-panel .np-search-submit:hover,
#np-search-panel .np-search-submit:focus-visible,
#np-search-panel .np-search-submit:active,
#mobile-drawer .np-search-submit:hover,
#mobile-drawer .np-search-submit:focus-visible,
#mobile-drawer .np-search-submit:active {
  /* A step darker rather than a colour jump -- Kadence would otherwise paint these
     states orange, so they have to be declared regardless. */
  background: var(--np-search-btn-bg-hover);
  color: var(--np-dark-blue);
}

@media (prefers-reduced-motion: reduce) {
  .np-search-panel {
    transition: none;
  }
}

/*
Replace the browser's native search-clear control. WebKit draws a small filled X that does
not match anything else here; `-webkit-appearance: none` strips it and the background
supplies a Feather-style stroked X in --np-muted instead. The button keeps its clear
behaviour when restyled this way, so the affordance is preserved rather than removed.
Edge/IE draw their own via ::-ms-clear, which has no styling hooks worth using -- hidden.
*/
#np-search-panel .np-search-field::-webkit-search-cancel-button,
#mobile-drawer .np-search-field::-webkit-search-cancel-button {
  -webkit-appearance: none;
  appearance: none;
  width: .95rem;
  height: .95rem;
  margin-left: .5rem;
  cursor: pointer;
  opacity: .8;
  background: center / contain no-repeat
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%235f7480' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cline x1='18' y1='6' x2='6' y2='18'/%3E%3Cline x1='6' y1='6' x2='18' y2='18'/%3E%3C/svg%3E");
}
#np-search-panel .np-search-field::-webkit-search-cancel-button:hover,
#mobile-drawer .np-search-field::-webkit-search-cancel-button:hover {
  opacity: 1;
}
#np-search-panel .np-search-field::-ms-clear,
#mobile-drawer .np-search-field::-ms-clear {
  display: none;
  width: 0;
  height: 0;
}

/*
Align the drawer's close X with the hamburger it replaces. The drawer overlays from the
very top of the viewport, while the mobile bar now starts below the 31px social strip, so
the X rendered 43px higher than the burger glyph (centres at 26.5 vs 70) and 3px to its
right.

The offset is a custom property because it is the sum of the strip's height and the
difference between the two buttons' box heights -- CSS cannot read the header row's height,
so if the strip's height or the mobile bar's padding changes, this is the one value to
adjust.
*/
#mobile-drawer {
  --np-drawer-close-offset: 43px;
}
#mobile-drawer .menu-toggle-close {
  position: relative;
  top: var(--np-drawer-close-offset);
  right: 3px;
}
/* The menu drops by the same amount, so the gap below the X is unchanged -- one property
   moves both rather than two values that could drift apart. */
#mobile-drawer .mobile-menu-container {
  padding-top: var(--np-drawer-close-offset);
}

/* ---------- Placeholder text ---------- */
/*
One placeholder colour for every field on the site, taken from the nomination form.

`opacity: 1` is not decoration: Firefox applies its own opacity to placeholder text, so
without it the colour renders lighter there than everywhere else.

The search field needs the id selector. Kadence sets `::placeholder { color: currentcolor }`
on it from an inline stylesheet at plain class specificity, so a class-level rule here ties
and loses on source order.
*/
input::placeholder,
textarea::placeholder {
  color: var(--np-placeholder);
  opacity: 1;
}
#masthead .np-search-field::placeholder,
#mobile-drawer .np-search-field::placeholder {
  color: var(--np-placeholder);
  opacity: 1;
}

/* ---------- Mobile drawer accordions ---------- */
/*
Matches the footer accordion treatment. Kadence already provides the collapse behaviour
here (`.drawer-nav-drop-wrap` wraps the label and a `.drawer-sub-toggle` button carrying
aria-expanded), so this is styling only -- the structure happens to mirror the footer's
injected `.np-acc-head` almost exactly.

Colours are the footer's exactly -- --np-chrome-rule strokes on the --np-dark-blue panel.
(An earlier pass had to invert these because the drawer's background was itself
navy-shade, which would have made every divider invisible; that no longer applies.)
*/
#mobile-drawer #mobile-menu > li:not(.np-mobile-search-item) {
  border-bottom: 2px solid var(--np-chrome-rule);
}
/*
The search form is appended as the final <li>, so `:last-of-type` resolves to IT -- and
combined with the :not() exclusion the rule matched nothing, leaving a stroke under the
last real menu item. `:has(+ ...)` targets the item immediately before the search instead,
which is the actual last row of the accordion list.
*/
#mobile-drawer #mobile-menu > li:not(.np-mobile-search-item):last-of-type,
#mobile-drawer #mobile-menu > li:has(+ .np-mobile-search-item) {
  border-bottom: 0;
}
/* And no trailing stroke at the end of an expanded sub-list either. */
#mobile-drawer #mobile-menu .sub-menu > li:last-child {
  border-bottom: 0;
}

#mobile-drawer .drawer-nav-drop-wrap {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: .75rem;
  padding: .1rem 0;
}
/*
Kadence styles the drawer menu ANCHORS as well as leaving the list items to us: it gives
them generous vertical padding and -- importantly -- their own
`border-bottom: 1px rgba(255,255,255,.1)`. That link border is a second, independent stroke
system, and it is why lines kept appearing under items whose <li> border had been zeroed.
It is removed here so the <li> borders are the only dividers.

Padding lands between the two extremes tried: Kadence's default gave ~100px rows, and
.45rem cut them to ~44px, which read as too thin.
*/
/*
Kadence puts that same translucent-white border on `.drawer-nav-drop-wrap` too, which is
the third stroke system here -- and because the wrap only exists on items that have
children, it showed up on exactly the accordion title rows. Both are cleared so the list
item borders are the only dividers in the drawer.
*/
#mobile-drawer #mobile-menu a,
#mobile-drawer .drawer-nav-drop-wrap {
  border-bottom: 0;
}
#mobile-drawer #mobile-menu > li > a,
#mobile-drawer .drawer-nav-drop-wrap > a {
  padding: 1.15rem 0;
}
#mobile-drawer #mobile-menu > li > a,
#mobile-drawer .drawer-nav-drop-wrap > a {
  /* Matches the nav bar and the footer accordions. */
  font-family: var(--np-font-condensed);
  font-weight: 600;
  font-size: 1.12rem;
  letter-spacing: .05em;
  text-transform: uppercase;
  color: var(--np-chrome-ink);
  text-decoration: none;
}
/* Hover only, for the same reason as the footer: focus stays on the chevron after a
   collapse, so :focus-within left the title highlighted once it was shut. */
#mobile-drawer .drawer-nav-drop-wrap:hover > a,
#mobile-drawer .drawer-nav-drop-wrap:hover .drawer-sub-toggle {
  color: var(--np-chrome-accent);
}

#mobile-drawer .drawer-sub-toggle {
  flex: 0 0 auto;
  display: grid;
  place-items: center;
  width: 2rem;
  height: 2rem;
  padding: 0;
  border: 0;
  border-radius: 50%;
  background: transparent;
  color: var(--np-chrome-ink);
  cursor: pointer;
  transition: transform .24s ease, color .2s ease, background-color .2s ease;
}
#mobile-drawer .drawer-sub-toggle svg {
  display: block;
  width: 1rem;
  height: 1rem;
  fill: currentColor;
}
/* Hover and press only, matching the footer chevrons. `:focus` is pinned back to the
   resting look because Kadence would otherwise paint it orange. */
#mobile-drawer .drawer-sub-toggle:hover,
#mobile-drawer .drawer-sub-toggle:active {
  background: var(--np-chrome-hover-bg);
  color: var(--np-chrome-accent);
}
#mobile-drawer .drawer-sub-toggle:focus:not(:hover):not(:active) {
  background: transparent;
  color: var(--np-chrome-ink);
}
#mobile-drawer .drawer-sub-toggle[aria-expanded="true"] {
  transform: rotate(180deg);
}

/* Children: flush with the parent labels, divided, as in the footer. */
#mobile-drawer #mobile-menu .sub-menu {
  padding: 0 0 .35rem;
  margin: 0;
  list-style: none;
}
#mobile-drawer #mobile-menu .sub-menu > li {
  border-top: 1px solid var(--np-chrome-rule);
}
#mobile-drawer #mobile-menu .sub-menu a {
  display: block;
  padding: .8rem 0;
  font-size: .98rem;
  font-weight: 400;
  color: var(--np-chrome-ink-muted);
  text-decoration: none;
}
#mobile-drawer #mobile-menu .sub-menu a:hover,
#mobile-drawer #mobile-menu .sub-menu a:focus-visible {
  color: var(--np-chrome-accent);
}

/* ---------- Mobile drawer search ---------- */
/*
The form is the last item in the drawer's menu list (see the wp_nav_menu_items filter), so
it inherits the menu's full width instead of becoming its own cramped column. No toggle is
rendered on mobile at all -- it would target the desktop panel, which is hidden there.
*/
.np-mobile-search-item {
  display: block;
  padding: 1.1rem 0 .3rem;
}
.np-mobile-search-item .np-search-form {
  position: relative;
  --np-search-inset: 5px;
}
#mobile-drawer .np-search-field {
  /* Narrower gutter than desktop, and less room reserved for the button, since the
     drawer is much tighter. */
  padding: .82rem .9rem;
  padding-right: 7.25rem;
}

/* Roomier field on desktop. */
@media (min-width: 768px) {
  #np-search-panel .np-search-form {
    /* Larger inset than mobile, which is what makes the button noticeably shorter than
       the field it sits in rather than nearly filling it. */
    --np-search-inset: 10px;
  }
  #np-search-panel .np-search-field {
    /* Taller than the drawer's on request; the side gutter stays roomier too. */
    padding: 1.05rem 1.3rem;
    padding-right: 8.5rem;
    font-size: 1rem;
  }
  #np-search-panel .np-search-submit {
    padding: 0 1.3rem;
    font-size: 1.1rem;
  }
  .np-search-panel.np-search-open {
    /* Must clear the taller field, or the slide would clip it. */
    max-height: 11rem;
  }
  .np-search-panel-inner {
    padding-block: 1.35rem 1.55rem;
  }
}

/* ---------- Nav fit guard ---------- */
/*
Set by js/nav-fit.js when the logo and nav genuinely do not fit, regardless of viewport
width -- the case a fixed pixel breakpoint cannot see (an added menu item, a longer
translation, a bigger logo).

These two declarations are the whole of Kadence's own header swap: it shows
`.site-header-wrap` and hides `.site-mobile-header-wrap` above the breakpoint. Forcing the
same pair is why the guard does not have to reimplement anything else -- the drawer, its
JS and the mobile rows all behave exactly as they do below the breakpoint.
*/
body.np-nav-overflow .site-header-wrap {
  display: none;
}
body.np-nav-overflow .site-mobile-header-wrap {
  display: block;
}
/* The strip copies key off media queries, which know nothing about the guard -- so swap
   them here too, or both would show while the guard is active. */
body.np-nav-overflow .np-social-strip--desktop {
  display: none;
}
body.np-nav-overflow .np-social-strip--mobile {
  display: block;
}

/* ---------- Sticky header (reveal on scroll up) ---------- */
/*
Driven by js/sticky-header.js, which pins the header only after ~240px of scrolling and
slides it away while scrolling down. The spacer it injects holds the header's height
while it is fixed -- without it the page jumps up the moment the header leaves flow.
`top` follows the admin bar so it is not hidden behind it for logged-in users.
*/
.np-header-spacer {
  height: 0;
}
#masthead.np-header-stuck {
  position: fixed;
  top: var(--wp-admin--admin-bar--height, 0px);
  left: 0;
  right: 0;
  z-index: 999;
  transition: transform .32s cubic-bezier(.4, 0, .2, 1);
}
#masthead.np-header-hidden {
  transform: translateY(-105%);
}
/*
The Family Favorites bar rides with the header, pinned and revealed by the same script.

Its `top` and its hide distance are both written by that script rather than set here: `top`
depends on the header's measured height (and the admin bar, for logged-in users), and the
hide distance cannot be a percentage -- an element can only translate by a percentage of its
own height, and this one sits a header's height down the page, so 105% of its own 46px would
leave most of it on screen. It has to travel its height plus the header's.

A step below the header's z-index, so the header passes over it rather than under.
*/
.np-ff-nav.np-header-stuck {
  position: fixed;
  left: 0;
  right: 0;
  z-index: 998;
  transition: transform .32s cubic-bezier(.4, 0, .2, 1);
}
.np-ff-nav.np-header-hidden {
  transform: translateY(var(--np-ff-nav-hide, -200%));
}

@media (prefers-reduced-motion: reduce) {
  #masthead.np-header-stuck,
  .np-ff-nav.np-header-stuck {
    transition: none;
  }
}

/* ---------- Archive / category pages ---------- */
/*
The post loop is rendered as a core Query Loop by this child theme's
template-parts/content/archive.php, so the card rules above apply to it via
`.np-archive-loop` -- there is no separate set of archive card styles. It is deliberately
NOT a carousel: archives stay a paginated grid.

The hero title treatment that used to live here is now site-wide -- see
"Hero title sections" below.
*/

/* Grid spacing to match the homepage sections.
   `margin-left: 0` matters: the post-template is a <ul>, and Kadence's global
   `ul, ol { margin-left: 1.5em }` pushed the whole grid 25.5px right, leaving the cards
   visibly off-centre (125px of gutter on the left against 99px on the right). */
.np-archive-loop .wp-block-post-template {
  gap: 1.75rem 1.5rem;
  margin-left: 0;
  padding-left: 0;
  list-style: none;
}
/* Three across on desktop. Stated explicitly rather than trusting the block's
   columnCount: the archive sits beside a sidebar, and core's generated container rule
   was resolving to fewer, wider columns. */
/*
`display: grid` is the load-bearing part. The block is authored with
`displayLayout: flex`, so core adds `is-flex-container` and sets `display: flex;
flex-wrap: wrap` -- which made `grid-template-columns` inert. Three 391px cards plus
gaps came to 1221px against a 1217px track, overflowing by 4px and wrapping to two per
row even though the grid template said three.
*/
@media (min-width: 1024px) {
  .np-archive-loop .wp-block-post-template {
    grid-template-columns: repeat(3, minmax(0, 1fr));
  }
}
@media (min-width: 600px) and (max-width: 1023px) {
  .np-archive-loop .wp-block-post-template {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}

/* ---------- Page hero image slot ---------- */
/*
Rendered by np_render_page_hero() for any page that has a Featured Image. The theme's own
title band is hidden on those pages only -- pages without a hero image keep it, so this is
additive rather than a site-wide replacement.

Inset and centred rather than full-bleed, matching the same 1178px content-column width
the nav and footer now both use (see ".site-header .site-container" and ".np-footer-inner")
rather than the wider, 20%-larger-than-that-column value this used to sit at -- a value
that was, at the time, deliberately chosen to stay clear of Kadence's own separate
`--global-content-width` (1290px), back when nav and footer disagreed with each other too.
Now that all three -- nav, footer, and this hero -- read the same number, that's a plain
literal here rather than the shared `--np-hero-w` token: that token is also the single-
post/page fallback hero's own width (".single .post-thumbnail.article-post-thumbnail",
further down), and changing it would have resized that too, unasked.

3:1 is enforced with `aspect-ratio` and `object-fit: cover`, so the slot holds its shape
whatever is dropped into it -- the current artwork is 1201x401, already 3:1, so nothing is
cropped.
*/
.np-has-page-hero .entry-hero {
  display: none;
}

.np-page-hero {
  position: relative;
  width: min(1178px, calc(100% - 3rem));
  margin: 1.75rem auto 0;
  aspect-ratio: var(--np-hero-ratio);
  border-radius: var(--np-hero-radius);
  /* Required, not decorative: without it the image's square corners paint over the
     radius, and the scrim would square them off again too. */
  overflow: hidden;
}
.np-page-hero .np-page-hero-img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center;
}
/* Same scrim as the article images: 38% tall, black fading up to transparent. */
.np-page-hero::after {
  content: "";
  position: absolute;
  inset: auto 0 0 0;
  height: 38%;
  background: linear-gradient(to top, rgba(0, 0, 0, .42), rgba(0, 0, 0, 0));
  pointer-events: none;
}

/*
On phones the hero goes edge to edge and sits flush under the navbar. The inset and the
rounded corners are desktop refinements -- at this width they just waste horizontal space
and leave a gap under the header.

`width: 100%` is enough for full bleed because `kadence_hero_header` renders this inside
`main.wrap`, which already spans the document; no viewport breakout needed (and a `100vw`
one would overhang by the scrollbar's width).

Applies to any page hero, not just NSFF -- only Family Favorites has a featured image today,
so nothing else changes.
*/
@media (max-width: 767px) {
  .np-page-hero {
    width: 100%;
    max-width: none;
    margin-top: 0;
    margin-inline: 0;
    border-radius: 0;
  }
}

/* ---------- Hero title sections (site-wide) ---------- */
/*
One treatment for every hero title section, keyed on `.entry-hero` -- the wrapper Kadence
emits for pages (`page-hero-section`), post archives (`post-archive-hero-section`) and its
other variants (events, products, courses). Previously this was written against the archive
classes only, so pages like Family Favorites missed it entirely.

`.entry-hero .entry-header > h1` is the reliable hook, because the class names are
SWAPPED between contexts: on a page the header carries `page-title` and the h1 is
`entry-title`; on an archive the header is `post-archive-title` and the h1 is
`page-title archive-title`. Targeting the h1 by position sidesteps that entirely.

Single posts are untouched by design -- they have no `.entry-hero` at all; their title sits
in the content area under the full-bleed featured image.
*/
.entry-hero,
.entry-hero .entry-hero-container-inner {
  background: var(--np-band);
}

/*
Category archives are the one hero title exception: white, not the shared grey band.

`.category` is WordPress's own body class for a category taxonomy archive -- not `.archive`,
which this install also puts on tag, date and the several GeoDirectory post-type archives,
and turning all of those white was never asked for. The extra class also settles this on
specificity (0,2,0) rather than needing to rely on coming after the shared rule.
*/
.category .entry-hero,
.category .entry-hero .entry-hero-container-inner {
  background: #fff;
}

/*
Kadence centres hero headers (its `title-align-*` classes). Left-aligned here, and the
header stays a flex row so `align-items` keeps the title vertically centred in the band --
switching it to `display: block` left-aligns but drops that centring, and the title then
touches the navbar.
*/
.entry-hero .entry-header {
  display: flex;
  justify-content: flex-start;
  align-items: center;
  text-align: left;
}
.entry-hero .entry-header > h1 {
  flex: 1 1 auto;
  width: 100%;
  display: flex;
  align-items: center;
  gap: 1rem;
  text-align: left;
  letter-spacing: .02em;
  color: var(--np-dark-blue);
}
/* The flexing grey rule: absorbs whatever width the title leaves, so it tracks the title's
   length with no per-page values. */
.entry-hero .entry-header > h1::after {
  content: "";
  flex: 1 1 auto;
  min-width: 1.5rem;
  height: 2px;
  background: var(--np-rule);
}
@media (min-width: 768px) {
  .entry-hero .entry-header > h1 {
    font-size: clamp(2rem, 3.2vw, 3rem);
    line-height: 1.04;
  }
}
/* Archive-specific alignment and colour rules removed: the `.entry-hero` block above now
   covers them for every hero, archives included. */

/* Placeholder for posts with no featured image. Keeps the card's shape and ratio -- the
   figure still carries .wp-block-post-featured-image, so the 4:3 rule, hover stroke and
   centred button all apply unchanged; only the picture is missing. */
.np-thumb-placeholder {
  background: var(--np-band);
  border-radius: 8px;
}
.np-thumb-placeholder > a {
  display: block;
  width: 100%;
  height: 100%;
}

/* ---------- Single post width & footer gap ---------- */
/*
Kadence caps single posts with `.content-container { max-width: 842px }`. Raised to the
1178px the homepage and category grids use, so the page's structural width is consistent
across templates.

NOTE: this widens the body copy too -- prose will now run to ~1150px, which is a long
measure to read. If that reads poorly, cap `.single .entry-content` on its own rather
than narrowing the container again, so the related carousel keeps the full width.
*/
/*
The single-post width, as one named value rather than a literal repeated wherever a
template needs it.

Raised from 860px to make room for the listicle template's two-column spots (Split Rows,
Guide Cards) without it also widening every other single-post layout on its own -- this one
token is what both now share, so retuning it once retunes both. A regular post is still
prose in one column at this width; a listicle spot splits it into two.
*/
:root {
  --np-single-content-w: 940px;
}
.single .content-container.site-container {
  /* Narrowed for readability. 1178px matched the grids but gave a ~1150px measure, which
     is far too long for body copy. The Similar Posts band and the hero both break out to
     the viewport independently, so neither loses width because of this. */
  max-width: var(--np-single-content-w);
}

/*
`.content-area` carries `margin-bottom: 80px`. On a post that ends with the Similar Posts
band, that margin sits outside the band and reads as dead space between it and the
footer. Removed only where the band is actually present -- posts without related posts
still need that breathing room before the footer.
*/
.single .content-area:has(.np-related) {
  margin-bottom: 0;
}

/* The same 80px margin at the top pushed the full-bleed hero away from the header.
   Removed only where that hero is present, so posts without a featured image keep the
   breathing room between the nav and their title. */
.single .content-area:has(.post-thumbnail.article-post-thumbnail) {
  margin-top: 0;
}

/* ---------- Single post hero image ---------- */
/*
Widening the content container to 1178px also blew up the post's featured image (it was
rendering ~1390x753). It is now full-bleed at a fixed 16:9, cropped with object-fit so
tall source images no longer dictate the height.

Same negative-margin breakout as the Similar Posts band, and safe for the same reason:
the content column is centred in the viewport. The scrim reuses the card treatment
exactly -- the figure's ::after, 38% tall, black fading to transparent.
*/
.single .post-thumbnail.article-post-thumbnail {
  position: relative;
  /*
  Viewport-based breakout rather than `margin-inline: calc(50% - 50vw)`. That form
  depends on the parent being centred, and Kadence puts `alignwide` on this element with
  its own negative margins -- the result landed at left:25px instead of 0, so the image
  never actually reached the edges and its height barely changed.

  This form does not care what the parent is doing. `.site` has `overflow: clip`, so the
  scrollbar's share of 100vw cannot introduce a horizontal scrollbar.
  */
  /*
  Kadence gives this element `alignwide` plus `kadence-thumbnail-position-behind`, which
  between them set width, margins and a height. Fighting one property at a time did not
  work: forcing only the width produced a 1440px-wide box that was still 541px tall
  (ratio 2.66) and sat 536px off-screen, because its margins and height were still
  Kadence's. So the geometry is reset wholesale first, then rebuilt. Those `!important`
  flags are load-bearing, not defensive.

  It was a full-bleed 45vh band; it is now the same shape of inset 3:1 slot the Family
  Favorites page hero uses (same ratio/radius tokens), independently sized rather than
  reading the exact same width any more -- this one is 20% wider than the content column,
  .np-page-hero is now the content column's own 1178px, matching the nav and footer.

  `left: 50%` plus `translateX(-50%)` still does the centring, and it has to: this element
  sits inside the 1178px content column, so a percentage width would resolve against the
  column and could never exceed it -- `margin-inline: auto` cannot produce a negative margin.
  The pair moves it half the column right and half its own width left, which lands it centred
  in the viewport for any width, so the 20% step out is possible from inside a narrower
  parent. `100vw` is the bound rather than `100%` for the same reason.
  */
  width: min(var(--np-hero-w), calc(100vw - 3rem)) !important;
  max-width: none !important;
  height: auto !important;
  min-height: 0 !important;
  padding: 0 !important;
  margin: 1.75rem 0 2rem !important;
  left: 50%;
  transform: translateX(-50%);
  aspect-ratio: var(--np-hero-ratio);
  border-radius: var(--np-hero-radius);
  /* Required, not decorative: without it the image's square corners paint over the radius,
     and the scrim would square them off again too. */
  overflow: hidden;
}
.single .post-thumbnail.article-post-thumbnail .post-thumbnail-inner {
  height: 100%;
}
.single .post-thumbnail.article-post-thumbnail img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center;
}
.single .post-thumbnail.article-post-thumbnail::after {
  content: "";
  position: absolute;
  inset: auto 0 0 0;
  height: 38%;
  z-index: 1;
  background: linear-gradient(to top, rgba(0, 0, 0, .42), rgba(0, 0, 0, 0));
  pointer-events: none;
}

/*
On phones the post header goes edge to edge and loses its corner, matching what the page
hero already does -- the inset and the radius are desktop refinements that only waste
horizontal space at this width.

`100vw` with the centring pair, not `width: 100%`: this element's parent is the content
column, so 100% would stop at the column's edges. The page hero can use `100%` because it
renders inside `main.wrap`, which already spans the document.
*/
@media (max-width: 767px) {
  .single .post-thumbnail.article-post-thumbnail {
    width: 100vw !important;
    margin: 0 0 1.5rem !important;
    border-radius: 0;
  }
}

/* Content images pick up the cards' 8px corner. Scoped to .entry-content so the
   hero above is untouched -- rounding it separately would fight the shared radius. */
.single .entry-content img,
.single .entry-content .wp-block-image img,
.single .entry-content figure img {
  border-radius: 8px;
}
/*
Full width of the content column, always -- not `max-width: 100%`, which is what a
regular blog post image had before this (Kadence/core's own responsive default) and which
let a naturally-narrower source photo render at less than the column's own width. Two
images of different native sizes in the same post were landing at two different rendered
widths as a result. `width: 100%` forces every one to the same width regardless of its own
pixel dimensions; `height: auto` keeps each at its own aspect ratio rather than stretching
it. Excludes `.nspl-img` -- the listicle template's own photos are deliberately a fixed
4:3 (or 3:2, on Guide Cards) crop via `object-fit`, not a free-standing width/height pair,
and already carry their own sizing rules that this would otherwise fight.
*/
.single .entry-content img:not(.nspl-img) {
  width: 100%;
  height: auto;
}
/*
Quick, scoped fix, not the general answer: this post's inline image is the school's own
small logo (a ~100px-wide graphic), not a photo, and the rule above stretched it to the
full 828px column -- visibly pixelated. Excluding it by post ID rather than reworking the
general rule, which is doing exactly what was asked everywhere else. The real, sitewide
fix is a size-aware version of the rule above (skip anything under some pixel threshold,
the way this session's own image-loading work already did elsewhere) -- planned, not done
here, so this is a stopgap for the one post that's visibly broken right now.
*/
.postid-30907 .entry-content img:not(.nspl-img) {
  width: auto !important;
  max-width: 100%;
  height: auto;
}
/*
The ancestor-scoped rule above matched, was confirmed present with !important on the live
site, and still had no visible effect -- so this targets the exact image directly by its
own WordPress-assigned class instead, with an explicit pixel width rather than `auto`, to
remove every remaining variable from the fix at once.
*/
img.wp-image-30908 {
  width: 100px !important;
  height: auto !important;
  max-width: 100% !important;
}

/* Post title: large on desktop, unchanged on mobile where the current size already
   fills the column.

   .nspl .nspl-h1 shares this rule rather than a copy of these three values -- the
   listicle's own title text is meant to match the plain-post title exactly (per the ask),
   so it reads from the same declaration a future change to one would need to reach both
   anyway. Its mobile base size is set to match separately, in listicle.css, since below
   this breakpoint .entry-title is left at Kadence's own unstyled h1 default (2em, i.e.
   32px against this site's 16px root) rather than anything this theme sets itself -- see
   that rule's own comment for why .nspl-h1 needs an explicit value instead of relying on
   the same unset default. */
@media (min-width: 768px) {
  .single .entry-title,
  .single .entry-header .entry-title,
  .nspl .nspl-h1 {
    font-size: clamp(2.5rem, 4.2vw, 4rem);
    line-height: 1.04;
    letter-spacing: -0.015em;
  }
}

/* ---------- Mobile menu ---------- */
/*
The hamburger rendered in --np-muted (#5f7480) against the navy bar. Both `color` and
`fill` are set: Kadence colours the icon through the SVG's own fill, so colour alone
leaves the glyph grey.

The drawer panel ships near-black (#090c10); it now uses the same darker navy as the
desktop dropdowns and nav hover fill, so the mobile menu belongs to the same palette.
*/
#masthead .menu-toggle-open,
#masthead .drawer-toggle,
.menu-toggle-open.drawer-toggle,
.menu-toggle-open .kadence-svg-icon,
.menu-toggle-open svg,
.drawer-toggle svg,
/*
The close control inside the drawer, added later and for a plain reason: the drawer panel is
white now, and Kadence still colours this button white for the dark panel it used to be, so
the X was white on white and simply not there. Only its *position* had ever been set here,
which is why the light-mode pass missed it -- there was no colour declaration to remap.

`color` on the button plus `fill`/`stroke` on anything inside it, because the glyph is not in
the DOM until the drawer opens and Kadence draws these either as an injected SVG or as a
character. Setting all three covers both without needing to know which.
*/
#mobile-drawer .menu-toggle-close,
#mobile-drawer .menu-toggle-close .kadence-svg-icon,
#mobile-drawer .menu-toggle-close svg,
#mobile-drawer .menu-toggle-close svg * {
  color: var(--np-chrome-ink);
  fill: currentColor;
}
/* Stroke-drawn variants of the same glyph follow the colour too. */
#mobile-drawer .menu-toggle-close svg[stroke],
#mobile-drawer .menu-toggle-close svg[stroke] * {
  stroke: currentColor;
}

/*
The drawer now matches the navbar: a --np-chrome-drawer-bg panel with --np-navy-shade
strokes. It previously used navy-shade as its background, which is why the accordion
dividers below had to be inverted to the lighter tone -- with the footer's pairing
restored, they use the footer's stroke colour directly.
*/
#mobile-drawer .drawer-inner,
.popup-drawer .drawer-inner {
  background: var(--np-chrome-drawer-bg);
}

/* Buttons share the nav's type style: condensed semibold caps with tracking. Covers the
   block editor's and Kadence's own button classes as well as the custom controls, so a
   button added later in the editor matches rather than falling back to the body face. */
.wp-block-button__link,
.wp-element-button,
.kb-button,
button.button,
input[type="submit"] {
  font-family: var(--np-font-condensed);
  font-weight: 600;
  letter-spacing: .05em;
  text-transform: uppercase;
}

/* ============================================================================
 * NSFF sub-brand — Family Favorites
 * Ported from the NSFF style guide: https://hyrizon-group.web.app/nsff/style-guide/
 * ==========================================================================*/
/*
Everything here is scoped to `.np-nsff` (set in functions.php for the Family Favorites
page and its children), so Family Favorites reads as its own sub-brand and the rest of the
site is untouched.

Values are taken from the guide's own `core.css` rather than sampled from screenshots, so
they are exact. Names are kept as the guide names them (`--btn-ground`, `--shape-full`,
`--e2`) so the two can be compared line by line later.

Deliberate divergences from the main site, all of them the guide's own choices:
  - UI font is Poppins, not Acumin. The guide loads the Acumin kit but never references it.
  - Buttons are pills (`--shape-full`), not the site's 8px.
  - Navy is #0C4F73 and yellow #E9CF46, against the site's #165272 / #E5B02C.
*/
.np-nsff {
  /* Brand */
  --brand-navy: #0C4F73;
  --brand-deep: #053952;
  --brand-teal: #5AA9B8;
  --brand-yellow: #E9CF46;
  --brand-orange: #E5892A;
  --brand-orange-ui: #E46A27;

  /* Roles */
  /* Midnight navy is the sub-brand's blue, confirmed 2026-08-31. This replaced #0C4F73,
     which came from the style guide's `--primary` -- the two are different blues and
     having both made the forms read as two palettes. Same hex as --midnight-navy. */
  --primary: #1E295B;
  --primary-variant: #141C40;
  --secondary: #5AA9B8;
  --highlight: #B5179E;

  /* Surfaces and ink */
  --background: #F1F5F9;
  --surface: #FFFFFF;
  --surface-2: #F8FAFC;
  --surface-3: #E2E8F0;
  --on-surface: #0F172A;
  --on-surface-med: #475569;
  --on-surface-dis: #94A3B8;
  --outline: #E2E8F0;
  --outline-strong: #CBD5E1;
  --error: #B23A34;
  --success: #2E7D46;

  /* Buttons.
     NOTE: the shadow values and --btn2-* come from the LIVE style guide, not its
     `core.css`. The stylesheet's checked-in defaults are stale -- `core.css` has
     `--btn2-ground` falling back to the old yellow #E9CF46, while the guide actually
     renders the secondary button in bloom pink. Read values off the live page. */
  /* Was #132A4A, the guide's own button token. Moved onto midnight navy with --primary:
     the two navies were a shade apart, which reads as a mistake rather than a system.
     Revert this one line if the guide's value should hold. */
  --btn-ground: #1E295B;
  --btn-ink: #FFFFFF;
  --btn2-ground: var(--np-bloom-pink);   /* bloom pink */
  --btn2-ink: var(--np-midnight-navy);
  --bloom-pink: var(--np-bloom-pink);
  --rally-red: #EF4760;

  /*
  The white card skin, shared by every white card in the sub-brand -- the hero card and each
  nomination entry. They had drifted apart: the entries carried the gradient ring, the hero
  card a plain --surface-2 ground and a shadow. One definition now, four values to tune.

  Only the *skin* is shared. Padding, margin and max-width stay with each card, because a
  hero card straddling a band and an entry inside a form genuinely want different geometry.

  **These have to live here, not in `:root`.** A custom property's own `var()` references are
  resolved on the element where the property is *declared*, not where it is used. Declared in
  `:root`, `var(--shape-lg)` and `var(--bloom-pink)` are undefined -- both are sub-brand
  tokens -- so the whole property became invalid and computed to an empty string. That took
  out `border-radius` and the entire `background` shorthand that reads it, leaving every card
  a transparent box with a 3px transparent border.
  */
  --np-ff-card-ground: #fff;
  --np-ff-card-radius: var(--shape-lg);
  --np-ff-card-ring: linear-gradient(45deg, var(--bloom-pink) 0%, var(--np-ff-gold) 100%);
  --np-ff-card-shadow: 0 18px 44px rgba(15, 23, 42, .18);
  --midnight-navy: var(--np-midnight-navy);   /* same colour as --btn2-ink, used here as a surface */
  --btn-shadow: 0 1px 2px rgba(5, 43, 60, .45), 0 2px 6px -1px rgba(5, 43, 60, .40);
  --btn-shadow-lift: 0 2px 4px rgba(5, 43, 60, .50), 0 10px 20px -4px rgba(5, 43, 60, .46);

  /* Shape, motion, type */
  --shape-sm: 4px;
  --shape-lg: 8px;
  --shape-full: 999px;
  --ease-standard: cubic-bezier(0.4, 0, 0.2, 1);
  --dur-short: 150ms;
  --dur-med: 250ms;
  --font-ui: "Poppins", "Helvetica Neue", Arial, sans-serif;

  --state-hover: rgba(15, 23, 42, .04);
}

/* ---------- NSFF buttons ---------- */
/*
Two sets of selectors on purpose. `.btn` is the guide's own class, ready for any markup we
write by hand later; `.wp-block-button__link` is what the page ACTUALLY renders today --
its buttons are core button blocks, and the `[ff_phase_button]` shortcode outputs one too.
Without that second selector none of this would show up on the page.
*/
.np-nsff .btn,
.np-nsff .wp-block-button__link,
.np-nsff .wp-element-button {
  appearance: none;
  border: 0;
  cursor: pointer;
  position: relative;
  overflow: hidden;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  min-height: 36px;
  padding: .45rem 16px;
  border-radius: var(--shape-full);
  font-family: var(--font-ui);
  font-size: .875rem;
  font-weight: 500;
  letter-spacing: 1.25px;
  text-transform: uppercase;
  text-decoration: none;
  transition: box-shadow var(--dur-med) var(--ease-standard),
              background var(--dur-short) var(--ease-standard),
              transform var(--dur-short) var(--ease-standard);
}
.np-nsff .btn svg {
  width: 18px;
  height: 18px;
  fill: currentColor;
}

/* Contained is the default, since that is what the page's existing buttons should read as. */
.np-nsff .btn--contained,
.np-nsff .wp-block-button__link,
.np-nsff .wp-element-button {
  background: var(--btn-ground);
  color: var(--btn-ink);
  box-shadow: var(--btn-shadow);
}
.np-nsff .btn--contained:hover:not(:disabled),
.np-nsff .wp-block-button__link:hover,
.np-nsff .wp-element-button:hover,
.np-nsff .btn--accent:hover:not(:disabled),
.np-nsff .btn--secondary:hover:not(:disabled) {
  transform: translateY(-2px);
  box-shadow: var(--btn-shadow-lift);
}
.np-nsff .btn--contained:active:not(:disabled),
.np-nsff .wp-block-button__link:active,
.np-nsff .wp-element-button:active,
.np-nsff .btn--accent:active:not(:disabled),
.np-nsff .btn--secondary:active:not(:disabled) {
  transform: translateY(0);
  box-shadow: var(--btn-shadow);
}

.np-nsff .btn--accent {
  background: var(--brand-yellow);
  color: #1C2B33;
  box-shadow: var(--btn-shadow);
}
.np-nsff .btn--secondary {
  background: var(--btn2-ground);
  color: var(--btn2-ink);
  box-shadow: var(--btn-shadow);
}
.np-nsff .btn--outlined {
  background: transparent;
  color: var(--primary);
  box-shadow: none;
  border: 1px solid var(--outline-strong);
}
.np-nsff .btn--outlined:hover {
  background: var(--state-hover);
}
.np-nsff .btn--text {
  background: transparent;
  color: var(--primary);
  box-shadow: none;
  padding: 0 8px;
}
.np-nsff .btn--text:hover {
  background: var(--state-hover);
}
.np-nsff .btn--sm {
  height: 30px;
  font-size: .6875rem;
  letter-spacing: 1px;
  padding: 0 12px;
}

/* Kadence paints bare buttons orange on hover/focus/active -- pinned here as everywhere
   else in this stylesheet, or that orange overrides the brand fills above. */
.np-nsff button.btn:focus,
.np-nsff .wp-block-button__link:focus,
.np-nsff .wp-element-button:focus {
  background: var(--btn-ground);
  color: var(--btn-ink);
}

@media (prefers-reduced-motion: reduce) {
  .np-nsff .btn,
  .np-nsff .wp-block-button__link,
  .np-nsff .wp-element-button {
    transition: none;
  }
  .np-nsff .btn:hover,
  .np-nsff .wp-block-button__link:hover,
  .np-nsff .wp-element-button:hover {
    transform: none;
  }
}

/* ---------- NSFF form fields ---------- */
/*
The guide's filled and outlined field components, floating label included. Nothing on the
Family Favorites page uses these yet -- the nominate and vote pages will -- so these are
the component definitions rather than overrides of existing markup.
*/
.np-nsff .field {
  display: flex;
  flex-direction: column;
  gap: 4px;
}
.np-nsff .field input,
.np-nsff .field textarea,
.np-nsff .field select {
  width: 100%;
  border: 0;
  background: transparent;
  outline: none;
  color: var(--on-surface);
  font-family: var(--font-ui);
  font-size: 1rem;
  letter-spacing: .5px;
  padding: 0;
}
.np-nsff .field__filled {
  position: relative;
  background: var(--surface-3);
  border-radius: var(--shape-sm) var(--shape-sm) 0 0;
  border-bottom: 1px solid var(--on-surface-dis);
  padding: 18px 12px 6px;
  transition: background var(--dur-short) var(--ease-standard),
              border-color var(--dur-short) var(--ease-standard);
}
/* Focus thickens the underline; padding drops 1px so the field does not grow by the extra
   border width and shift the content below it. */
.np-nsff .field__filled:focus-within {
  border-bottom: 2px solid var(--primary);
  padding-bottom: 5px;
}
.np-nsff .field__outlined {
  position: relative;
  border: 1px solid var(--outline-strong);
  border-radius: var(--shape-sm);
  padding: 18px 12px 6px;
  transition: border-color var(--dur-short) var(--ease-standard);
}
/* Same trick on all four sides for the outlined variant. */
.np-nsff .field__outlined:focus-within {
  border: 2px solid var(--primary);
  padding: 17px 11px 5px;
}
.np-nsff .field__filled label,
.np-nsff .field__outlined label {
  position: absolute;
  top: 6px;
  left: 12px;
  font-size: .6875rem;
  letter-spacing: .4px;
  color: var(--on-surface-med);
  pointer-events: none;
}
.np-nsff .field__filled:focus-within label,
.np-nsff .field__outlined:focus-within label {
  color: var(--primary);
}
.np-nsff .field__help {
  font-size: .75rem;
  letter-spacing: .4px;
  color: var(--on-surface-med);
  padding: 0 12px;
}

/* ---------- NSFF: gradient hero and overlapping title card ---------- */
/*
Rendered by PHP on `kadence_hero_header` (empty), then filled by ff-hero.js, which moves
the mark, eyebrow, title and deck in. Landing page, /nominate/ and /vote/ only.

The band is full-bleed on `width: 100%` alone, because that hook outputs inside `main.wrap`
which already spans the document. No `100vw` breakout, and so no scrollbar overhang.
*/
.np-nsff .np-ff-hero-wrap {
  /*
  Half of this is how far the card is pulled up onto the band. ff-hero.js overwrites it
  with the card's measured height and re-measures on resize and after the webfont lands;
  the value here is only what holds the layout together until then, so a slow script shows
  a near-right hero rather than a broken one.
  */
  --np-ff-card-h: 260px;
  /*
  The band's effective height: its nominal height, or enough to hold the card with 2rem to
  spare, whichever is larger. Centring is only meaningful if the band actually contains the
  card -- at 40vh the band is 360px against a 422px card, so a true centre would have lifted
  the card 31px over the navbar and the clamp below pinned it flush to the top instead.

  **Declared here, on the wrap, and not on `.np-nsff` with the other tokens.** A custom
  property's own `var()` references resolve on the element where it is declared, and
  `--np-ff-card-h` is set by ff-hero.js on this element -- at body level it would be
  undefined, the whole property would compute to nothing, and the band would collapse. That
  exact mistake took every card on the site out earlier today.
  */
  --np-ff-band-eff: max(var(--np-ff-band-h), calc(var(--np-ff-card-h) + 4rem));
  /*
  How far the card's edges sit inside the band's -- the centring offset, named so both
  margins can use it.
  */
  --np-ff-card-offset: max(0px, (var(--np-ff-band-eff) - var(--np-ff-card-h)) / 2);
  /*
  Clear space between the *band's* bottom edge and whatever follows.

  It has to be measured from the band, not the card. The card is centred, so its lower edge
  sits `--np-ff-card-offset` above the band's -- a plain 3rem margin on the card left only
  16px of daylight below the banner while reading as 48px in the stylesheet. Adding the
  offset back makes this value mean what it says whatever the centring works out to.
  */
  --np-ff-band-gap: 3.5rem;
  /*
  The card is centred on the band rather than offset from its top.

  `max(0px, ...)` is the part that matters. Centring only makes sense while the band is
  taller than the card; when the card is the taller of the two, a true centre would lift it
  up over the navbar. The clamp means it centres whenever it can and otherwise sits flush
  with the band's top edge, which is the closest thing to centred that does not collide with
  the header.
  */
  /*
  The band's height, in one place.

  It used to be written out four times -- `height` and `min-height` on the band, then
  repeated inside the card's offset calc, twice over for desktop and phone -- with a comment
  telling the next person to keep all four in step. This is that comment made unnecessary.

  It also makes "resize the band without moving the card" a one-value change: the card is
  positioned at `band top + inset`, so as long as the offset reads the same property, the
  band grows downward and the card stays exactly where it is.
  */
  --np-ff-band-h: max(40vh, 320px);
  position: relative;
}
.np-nsff .np-ff-hero-band {
  width: 100%;
  height: var(--np-ff-band-eff);
  /* Blue into bloom pink. In CSS 45deg points to the top right, so the blue sits at the
     bottom left and the pink at the top right. */
  background: linear-gradient(45deg, var(--np-ff-band-blue) 0%, var(--bloom-pink) 100%);
}
.np-nsff .np-ff-hero-card {
  position: relative;
  /* Above the band, whose stacking context is otherwise a sibling's. */
  z-index: 1;
  /*
  The overlap. A percentage margin would resolve against the containing block's *width*,
  not its height, and a transform would leave the card's original space empty below it --
  hence the measured height in a custom property.
  */
  /*
  Centred on the band: pulled up by the band's height, then pushed back down by half the
  difference between the two. Both terms are properties, so the band can be resized without
  this needing to change.
  */
  margin:
    calc(var(--np-ff-card-offset) - var(--np-ff-band-eff))
    auto
    calc(var(--np-ff-card-offset) + var(--np-ff-band-gap));
  /*
  The page's content-column width, measured by ff-hero.js -- the card sits in `main.wrap`,
  which spans the document, so it cannot inherit the column's width. The fallback only holds
  the layout until the script runs.
  */
  max-width: min(var(--np-ff-content-w, 64rem), 100%);
  /*
  One value top and bottom. They were different clamps, which read as a heavier top --
  and the mark makes it worse: the animation's artwork does not fill its 600x600 viewBox,
  so it carries its own transparent margin on top of whatever padding is set here.
  */
  padding: clamp(1.75rem, 3.5vw, 2.5rem) clamp(1.5rem, 4vw, 3.25rem);
  /* Ground, ring, radius and shadow come from the shared card skin below. */
  text-align: center;
}

/*
The four elements were styled for the contexts they came from -- a flex hero header, or the
content column -- so their positioning properties are reset here and their type restated.
*/
.np-nsff .np-ff-hero-card .np-ff-mark,
.np-nsff .np-ff-hero-card .np-ff-eyebrow {
  /* `order` was how they stacked inside the flex header. The card is a plain block, so
     document order does the job and these would be inert at best. */
  order: 0;
}
/*
The card's padding measured 40px top and bottom, but the top *read* as more than twice the
bottom -- 88px against 40px to the first visible pixel. The cause is the artwork, not the
padding: the animation's ink fills only 68px of its 600x600 viewBox, leaving 48px of
transparent space above it and 34px below at the 150px size.

So the box is cropped back to its ink. The insets are fractions of the mark's own width
rather than pixels, because that width is a clamp -- a fixed -48px would over-pull at the
84px end of the range.
*/
.np-nsff .np-ff-hero-card .np-ff-mark {
  margin: calc(var(--np-ff-mark-w) * -0.32) auto calc(var(--np-ff-mark-w) * -0.15);
}
/* The same values as the hero titles on the other Family Favorites pages, so the two
   treatments read as one design rather than two. */
.np-nsff .np-ff-hero-card > h1 {
  display: block;
  margin: 0;
  font-size: clamp(2.4rem, 5.2vw, 4.25rem);
  line-height: 1.02;
  letter-spacing: -0.02em;
  text-align: center;
  color: var(--on-surface);
}
/* The rule under the title, as before -- restated because the selector it lived on
   (`.entry-hero .entry-header > h1`) no longer matches once the title is in the card. */
.np-nsff .np-ff-hero-card > h1::after {
  content: "";
  display: block;
  width: 88px;
  height: 3px;
  margin: var(--np-ff-rule-gap) auto 0;
  background: var(--outline);
}
.np-nsff .np-ff-hero-card > p.np-ff-lead {
  margin: var(--np-ff-rule-gap) auto 0;
}

/*
What the band replaces.

The theme's title band is now an empty box on these three pages -- its header has been
emptied into the card -- so it is taken out of the flow rather than left holding padding.

The landing page's 3:1 featured image is hidden here too: the band occupies the same slot,
and the two stacked read as two heros. **One line to restore** if the photograph should
come back, in which case it wants to become the band's background rather than sit above it.
*/
.np-ff-hero-page .entry-hero,
.np-ff-hero-page .np-page-hero {
  display: none;
}

@media (max-width: 767px) {
  /*
  The mobile-only wrapper-card removal that used to live here is gone: the wrapper is not a
  card at any width now, so there is nothing left to switch off.

  It never worked in any case, and the reason is worth keeping. This media block sits with
  the hero rules, several hundred lines *above* the form rule it was overriding -- equal
  specificity, earlier source order, so the base rule won and the phone kept its wrapper
  card. A media query buys no specificity.
  */
  .np-nsff {
    /* Phones get a shorter band; the card's offset follows it automatically. */
    --np-ff-band-h: max(34vh, 240px);
  }
  .np-nsff .np-ff-hero-card {
    /* The measured column already accounts for the page's own gutters on a phone. */
    max-width: var(--np-ff-content-w, calc(100% - 1.75rem));
    /* Nothing to restate: the offset reads --np-ff-band-h and --np-ff-card-h, both of
       which are already current here. */
  }
}

/* ---------- NSFF: the mark ---------- */
/*
Built and driven by ff-lottie.js. 132px is the style guide's size; it steps down on narrow
screens so it does not eat the top of a phone viewport before the title.

`order: -2` is what puts it above the eyebrow, which carries `order: -1` -- DOM position
alone cannot, since the hero header is a column flex box. Harmless on the landing page,
where the mark is a plain block before the H1.

An explicit `aspect-ratio` and width reserve the box before the player writes its <svg>,
so the title does not jump down on load. `contain` keeps the SVG's paint out of the rest
of the header.
*/
/*
`body.np-nsff` rather than `.np-nsff`, and the reason is worth recording: Kadence's
`content.min.css` carries `.entry-hero-container-inner .entry-header > div { width: 100% }`
-- exactly two classes, the same specificity as `.np-nsff .np-ff-mark` -- and it loads
after this stylesheet, so source order handed it the win. The mark rendered 1242px square
at a 1440 viewport. Adding the element selector breaks the tie without an `!important`.

Small on purpose: it is decoration above the eyebrow, not a logo lockup. One value to tune.
`flex: none` keeps the header's flex sizing off it, the same trap the h1 hit.
*/
body.np-nsff .np-ff-mark {
  order: -2;
  flex: none;
  /* Held in a property so the crop below can be expressed as a fraction of it. */
  --np-ff-mark-w: clamp(84px, 11vw, 150px);
  width: var(--np-ff-mark-w);
  height: auto;
  aspect-ratio: 1;
  margin: 0 auto .35rem;
}
body.np-nsff .np-ff-mark svg {
  display: block;
  width: 100%;
  height: 100%;
}

/* ---------- NSFF: child-page hero titles ---------- */
/*
Family Favorites child pages (/nominate/, /for-businesses/ and anything added beneath the
root) drop the site's grey title band and take the landing page's headline treatment
instead: no ground, no flexing rule, the same large centred display type.

The landing page itself is unaffected -- it carries a hero image, so `.np-has-page-hero`
already hides `.entry-hero` there.

Three classes beat the site-wide `.entry-hero .entry-header > h1` rules on specificity, so
the desktop step in that block does not need restating here.
*/
.np-nsff .entry-hero,
.np-nsff .entry-hero .entry-hero-container-inner {
  background: none;
}
.np-nsff .entry-hero .entry-header {
  justify-content: center;
  /* The eyebrow is moved in here above the title, so the row the site-wide rule sets up
     becomes a column. */
  flex-direction: column;
  align-items: center;
  /*
  Kadence sets min-height: 200px here. With the site-wide `flex: 1 1 auto` on the h1, the
  leftover height was absorbed by the h1 -- and since the rule is the h1's last child, all
  21px of it landed *below* the rule. That is what made the rule look pinned to the title:
  11.2px above it, 32px below. The header now wraps its content and the padding below does
  the spacing.
  */
  min-height: 0;
  /*
  Every box in this hero measured 0 padding once the band's ground was removed, so the
  title sat flush against the navbar. The breathing room is put here rather than on
  `.entry-hero`, so it stays inside the content container and does not fight the theme's
  own hero spacing on non-NSFF pages. One value to tune.
  */
  padding-top: clamp(3.5rem, 7vw, 7rem);
  /* The rule below the title supplies the visual break, so the header gives nothing back. */
  padding-bottom: 0;
}
.np-nsff .entry-hero .entry-header > h1 {
  /* Back to a block: the site-wide rule makes this a flex row to seat the rule beside the
     title, which is exactly what is being removed. */
  display: block;
  /* And back to hugging its content -- see the min-height note above. */
  flex: none;
  font-size: clamp(2.4rem, 5.2vw, 4.25rem);
  line-height: 1.02;
  letter-spacing: -0.02em;
  text-align: center;
  color: var(--on-surface);
}
/*
The site's flexing grey rule beside the title is replaced by a short centred one below it,
separating the title from the deck. Kept light grey so it stays a separator -- the eyebrow
and the accented word carry the colour.

`flex: none` and `min-width: 0` undo the site-wide rule's sizing, which was written for a
rule that had to absorb the leftover width of a flex row.
*/
.np-nsff .entry-hero .entry-header > h1::after {
  content: "";
  display: block;
  flex: none;
  width: 88px;
  min-width: 0;
  height: 3px;
  margin: var(--np-ff-rule-gap) auto 0;
  /* The sub-brand's own hairline grey, not the site's --np-rule -- same reasoning as the
     card ground. Swap to --on-surface-light if it wants to be a shade stronger. */
  background: var(--outline);
}

/*
The space above and below the rule under the title lives on two different elements -- the
rule's own top margin inside the h1, and `.content-area`'s top margin below the hero -- so
they are driven from one custom property. Measured at 375px they had drifted to 17.6px and
20px; one value means they cannot drift again.
*/
.np-nsff {
  --np-ff-rule-gap: 1.25rem;
}
@media (max-width: 767px) {
  /* The whole block is airier than it needs to be on a phone, where the title alone takes
     two or three lines. */
  .np-nsff {
    --np-ff-rule-gap: .7rem;
  }
}

/*
Tightening the gap between the title and the deck. It measured 148px, from three separate
boxes: the header's own bottom padding, `.content-area`'s 80px top margin and
`.entry-content-wrap`'s 32px top padding.

`:not(.np-has-page-hero)` keeps this to the pages that show a hero *title*. The landing page
has a hero image instead, and its 80px is the space that was deliberately added above the
intro card.
*/
.np-nsff:not(.np-has-page-hero) .content-area {
  margin-top: var(--np-ff-rule-gap);
}
.np-nsff:not(.np-has-page-hero) .entry-content-wrap {
  padding-top: 0;
}
.np-nsff p.np-ff-lead {
  margin-top: 0;
}

/*
The eyebrow, lifted out of the content and into the hero by ff-accordion.js.
*/
.np-nsff .np-ff-eyebrow {
  /* Belt and braces: keeps it above the title even if the DOM order ever changes. */
  order: -1;
  width: 100%;
  margin: 0 0 .75rem;
  text-align: center;
  color: var(--bloom-pink);
  font-family: var(--np-font-condensed);
  /* `has-small-font-size` is a WordPress preset class and its font-size carries
     !important, so overriding it needs the same. */
  font-size: clamp(.95rem, 1.4vw, 1.15rem) !important;
  font-weight: 700;
  letter-spacing: .12em;
  line-height: 1.2;
  text-transform: uppercase;
}

/*
The line that follows the eyebrow is the page's deck, so it is set as a heading rather than
body copy. Centred to sit under the centred title -- flip `text-align` to left if that
reads better. Applies to every FF child page that opens this way, not just /nominate/.
*/
.np-nsff p.np-ff-lead {
  font-family: var(--np-font-condensed);
  font-size: clamp(1.45rem, 2.6vw, 2.1rem);
  font-weight: 600;
  line-height: 1.15;
  letter-spacing: .01em;
  text-align: center;
  color: var(--on-surface);
  max-width: 46ch;
  margin: 0 auto 2.25rem;
}

/*
The timing note as a pill: bloom pink text on the same pink at 20%.

Written as `rgb(from ... / .2)` off the token so the two stay in step if --bloom-pink ever
changes, with a flat fallback first for browsers without relative colour syntax.

Rally red on its own 20% is a real improvement on the pink this started as -- roughly 3.4:1
against the tinted ground, against 2.2:1 before. Still under the 4.5:1 WCAG minimum for body
text, and the date is repeated in the surrounding copy, so it is not the only place this is
said. Say the word and the text can go a few steps darker while the ground stays.
*/
.np-nsff .entry-content > p.np-ff-note {
  /* `width: fit-content` shrink-wraps the pill and auto margins still centre it -- which an
     inline-flex box would not do. */
  display: flex;
  align-items: center;
  gap: .55rem;
  width: fit-content;
  max-width: 100%;
  margin: 0 auto 2rem;
  padding: .5rem 1.25rem;
  border-radius: var(--shape-full);
  background: #fdeaec;
  background: rgb(from var(--rally-red) r g b / .2);
  color: var(--rally-red);
  font-family: var(--np-font-condensed);
  font-size: clamp(.95rem, 1.5vw, 1.1rem);
  font-weight: 700;
  letter-spacing: .04em;
  text-align: center;
}
.np-nsff .entry-content > p.np-ff-note strong,
.np-nsff .entry-content > p.np-ff-note b {
  font-weight: 700;
  color: inherit;
}

/*
The style guide's "closing soon" chip: a leading dot pulsing one slow ring, 1.8s ease-out on
repeat. The guide draws it in --error red on its own deadline chip; here it takes the pill's
bloom pink so the chip stays one colour.

The ring is a box-shadow spread rather than a scaled pseudo-element, exactly as the guide has
it -- shadow spread does not affect layout, so the ring can grow past the dot without moving
the text beside it.
*/
.np-nsff .np-ff-note__dot {
  flex: none;
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--rally-red);
  animation: np-ff-ring 1.8s ease-out infinite;
}
@keyframes np-ff-ring {
  0% {
    box-shadow: 0 0 0 0 rgb(from var(--rally-red) r g b / .6);
  }
  70% {
    box-shadow: 0 0 0 9px transparent;
  }
  100% {
    box-shadow: 0 0 0 0 transparent;
  }
}
/* An indefinite pulse is exactly the kind of motion this setting is for. The dot stays --
   only the ring stops. */
@media (prefers-reduced-motion: reduce) {
  .np-nsff .np-ff-note__dot {
    animation: none;
  }
}

.np-nsff .ff-public-disclosure {
  text-align: center;
}


/*
The leading glyph on the add and remove buttons, injected by ff-form.js.

It was written as a `::before` first, and the rules were valid and parsed -- but a
`<button>` does not reliably generate pseudo-element boxes, so nothing rendered. Hence a
real span. It is a flex item inside `.ff-public-action`'s own 8px gap, so no margins here.
*/
.np-nsff .np-ff-btn-icon {
  font-size: 1.2em;
  font-weight: 400;
  line-height: 1;
}

/*
The closing legal footnote in the page content -- "Participation is subject to the ...
Rules and Privacy Notice".

Not `:last-child`: ff-accordion.js appends the closing CTA section after it, so the footnote
is never actually last. The opening eyebrow is also a `has-small-font-size` paragraph, but
it is moved into the hero card and given `.np-ff-eyebrow`, so by the time this matters the
footnote is the only one of these left in the content column.
*/
.np-nsff .entry-content > p.has-small-font-size {
  text-align: center;
}

/* ---------- NSFF: the gradient ring on the forms ---------- */
/*
Matched with `:has()` because the plugin's forms carry no class of their own -- and scoped
to forms that contain a `.ff-public-grid`, so the header search form is not caught.

The ring is the hero band's gradient, same two stops and same angle, so the two read as one
device. `border-color` cannot take a gradient, so two backgrounds are layered with
different clips: white clipped to the padding box, the gradient clipped to the border box,
which leaves the gradient visible only in the 3px ring. `border-image` would be a single
declaration but ignores `border-radius` and would square the corners off.

**On "each nomination form".** This rings the whole form. It does not ring each nomination
block individually, and that is a structural limit rather than a choice: the plugin puts the
*first* nomination's four fields in the same `.ff-public-grid` as Your Name and Your Email,
and only the second and later ones get their own `.ff-public-item` wrapper (cloned from a
`<template>`). Ringing `.ff-public-item` would therefore frame every nomination except the
first. Giving the first one a wrapper means either a plugin change -- Des's lane -- or
moving four fields out of a CSS grid in JavaScript, which is not a thing to do to a live
nomination form the day before nominations open.
*/
.np-nsff .entry-content form:has(.ff-public-grid) {
  /* Held in a property because the review row has to reach back through it -- see below. */
  --np-form-pad: clamp(1.5rem, 3vw, 2.25rem);
  padding: var(--np-form-pad);
  /*
  Not a card, at any width.

  This is what lets the entry cards be exactly as wide as the page: any padding or border on
  the wrapper makes the cards inside it narrower than the column by definition. A wrapper
  card hugging cards was also a card inside a card, which the entries' own rings had already
  made redundant. Say the word and it comes back -- at the cost of the entries no longer
  matching the page width.

  `--np-form-pad` is zero rather than `padding` being dropped, because the review row's
  overlap is calculated from that same property: one value keeps the button landing exactly
  half over the edge instead of over-pulling by padding the form no longer has.
  */
  --np-form-pad: 0px;
  max-width: 100%;
  /* Room below for the review button, which hangs over the form's lower edge. */
  margin: 0 auto 3.25rem;
  border: 0;
  background: none;
  box-shadow: none;
}

/*
The first nomination's card, added by ff-form.js.

The values are not invented: they were measured off a real `.ff-public-item` by cloning the
plugin's <template> into the page, reading its computed style and removing it again. White,
a 1px --outline hairline, 8px radius, 16px/18.4px padding. Declared on both selectors so the
first nomination and every added one cannot drift apart.
*/
/*
The entry number, written and kept in step by ff-form.js.

`#N` is not a separate element -- the whole string is one text node, so the hash and the
number take the heading's own colour. Picking the number out in pink would need the script
to emit markup, and a heading whose text is assembled from spans is harder to keep
idempotent for the observer.
*/
.np-nsff .np-ff-nom-num {
  margin: 0 0 .9rem;
  font-family: var(--np-font-condensed);
  font-size: clamp(1.3rem, 2.2vw, 1.65rem);
  font-weight: 700;
  letter-spacing: .04em;
  line-height: 1.15;
  text-transform: uppercase;
  color: var(--primary);
}

/*
The entry cards carry the gradient ring, so each entry separates from the next rather than
the whole form being framed as one block.

Two backgrounds with different clips: white to the padding box, gradient to the border box,
which leaves the gradient visible only in the 3px ring. `border-image` would be one
declaration but ignores `border-radius`. Same stops and angle as the hero band.

Padding was 16px/18.4px, measured off the plugin's own item; it goes up a little because a
3px ring needs more room inside it than a hairline did, and the margin roughly doubles so
the rings do not crowd each other.
*/
.np-nsff .np-ff-nom-card,
.np-nsff .ff-public-item {
  /* Skin from the shared rule below; this is geometry only. */
  padding: 20px 22px;
  margin: 0 0 32px;
}

/*
The shared white-card skin.

Deliberately the only place any of these four properties is set for these three selectors:
the individual rules above carry geometry and nothing else, so this does not depend on
source order. That matters -- four separate bugs today came from a second rule for the same
`ff-public-*` or `np-ff-*` class winning on source order alone.

Worth knowing about the shadow: it was the hero card's, chosen when one card sat on a vivid
band. Repeated down a column of entries it is on the heavy side -- soften
`--np-ff-card-shadow` if the stack reads too busy.
*/
.np-nsff .np-ff-hero-card,
.np-nsff .np-ff-nom-card,
.np-nsff .ff-public-item {
  border: 0;
  border-radius: var(--np-ff-card-radius);
  background: var(--np-ff-card-ground);
  box-shadow: var(--np-ff-card-shadow);
}

/*
The gradient ring, on the entries only -- the hero card sits on the gradient band already,
so ringing it too was a gradient on a gradient.

`border-color` cannot take a gradient, so two backgrounds are layered with different clips:
the ground clipped to the padding box, the gradient to the border box, which leaves the
gradient visible only in the 3px ring. `border-image` would be a single declaration but
ignores `border-radius` and would square the corners off.

**Must stay after the shared rule above** -- same specificity, so it wins on source order
alone.
*/
.np-nsff .np-ff-nom-card,
.np-nsff .ff-public-item {
  border: 3px solid transparent;
  background:
    linear-gradient(var(--np-ff-card-ground), var(--np-ff-card-ground)) padding-box,
    var(--np-ff-card-ring) border-box;
}

/*
"Review Nominations", on its own row at the foot of the form and hanging half over its
lower edge.

The offset has to clear the form's own bottom padding as well as half the button, which is
why the padding is a custom property rather than a literal: `--np-form-pad + 28px` puts the
button's centre exactly on the border. 28px is half the 56px height set below -- if that
height changes, change this with it.
*/
.np-nsff .np-ff-review-row {
  display: flex;
  justify-content: center;
  margin-bottom: calc(-1 * (var(--np-form-pad) + 28px));
}
.np-nsff .np-ff-review-row .ff-public-action {
  position: relative;
  /* Above the form's own background and border, which it overlaps. */
  z-index: 1;
  min-height: 56px;
  /* Beats the 44px touch minimum on its own, so the pointer: coarse rule is a no-op here. */
  min-height: 56px;
  padding: .8rem 34px;
  font-size: 1.05rem;
}

/* ---------- NSFF: closing "update your business" CTA ---------- */
/*
Built by ff-accordion.js from the plugin nav's one action link.

An inset rounded band rather than a full-bleed one: `.entry-content` sits inside the theme's
constrained container, so a full-bleed band would need a viewport breakout, and the rounded
inset matches the hero and the intro card. --surface-3 is a step deeper than the intro
card's --background so the two read as different things; swap the token if they should match.
*/
/*
No longer a card: a plain closing block, left aligned, separated by a hairline rather than
by a border and a shadow. The outset went with the card -- there is no box left to sit proud
of anything.
*/
.np-nsff .np-ff-cta {
  margin: 3.5rem 0 1rem;
  padding: 2.5rem 0 0;
  border: 0;
  border-top: 1px solid var(--outline);
  border-radius: 0;
  background: none;
  box-shadow: none;
  text-align: left;
}
/* Tighter now that the subtitle sits between the heading and the button. */
.np-nsff .np-ff-cta__title {
  margin: 0 0 .55rem;
  font-family: var(--np-font-condensed);
  font-size: clamp(1.7rem, 3.4vw, 2.6rem);
  font-weight: 700;
  line-height: 1.08;
  letter-spacing: -0.01em;
  color: var(--on-surface);
}
/* Scaled up from the nav chip's 36px -- inside the band this is the page's closing action,
   not one item in a row. The colours come from the nav-link rule it keeps matching. */
/*
Spacing only. The pink condensed caps come from `np-ff-eyebrow`, which this element also
carries -- see the note in ff-accordion.js. Overriding the eyebrow's own bottom margin,
which was set for sitting above a title rather than below one.
*/
.np-nsff .np-ff-cta__sub {
  margin: 0 0 1.6rem;
  /*
  The shared `np-ff-eyebrow` rule centres its text, which was right when this style only
  appeared in the hero card. The CTA is left aligned now, so the alignment is overridden
  here rather than removed from the eyebrow -- the hero still wants it centred.

  Equal specificity to the eyebrow rule, so this depends on being later in the file. It is:
  the eyebrow lives with the hero-title block, this with the CTA block below it.
  */
  text-align: left;
}

/*
"Make changes" carries no rule of its own any more -- it is listed on the shared
`.ff-public-action` block, so it is the same button as "Add Another Nomination" rather than
a separate attempt at the same look.
*/
/* Nothing visible left in the nav once the action moves out, so it stops holding space. */
.np-nsff .nsp-awards-ff-public-nav.np-ff-nav--empty {
  display: none;
}

/*
The required-field marker, back to the approved orange.

This is not a specificity fight with the child theme -- it is one inside the plugin's own
stylesheet. `simplified-public.css:843` sets `.ff-public-req { color: #e5892a }`, and a
later rule in the same file, `.ff-public-field span { color: inherit }`, outranks it
(0,1,1 against 0,1,0). So the marker has been inheriting the label colour all along, which
this stylesheet sets to --on-surface-med. The approved orange has never rendered.

Two classes to clear that, until the plugin fixes it upstream. **Remove this rule when
`.ff-public-field span` is fixed** -- routed to Des for the Fable lane.
*/
.np-nsff .ff-public-req {
  color: #e5892a;
}

/*
Touch targets. The guide's 36px button is a desktop measurement; on a phone it is under the
44px minimum, and these are the buttons that submit a nomination. `min-height` beats the
`height` above it whenever it is the larger value, so the desktop size is untouched.

Keyed on `pointer: coarse` rather than a width breakpoint -- it is the input device that
makes 36px too small, not the viewport.
*/
@media (pointer: coarse) {
  .np-nsff .ff-public-action {
    min-height: 44px;
  }
}

/*
The plugin's nav chips sit inside the "Need Help?" section on /for-businesses/, where they
are three links to elsewhere in the middle of a support answer -- one of them to the page
you are already on.

Keyed on being inside an accordion body rather than on a page id: on /nominate/ and /vote/
this nav is a top-level sibling near the top of the content, where it still belongs, so
those pages are untouched.
*/
.np-nsff .np-ff-acc__body .nsp-awards-ff-public-nav {
  display: none;
}

/*
The separator block above the landing page's legal footnote. It read as a stray rule once
the accordions supplied their own borders. Hidden rather than deleted from the page
content, because ff-accordion.js uses this <hr> as the boundary that keeps the footnote out
of the last accordion -- removing the block in the editor would swallow it.
*/
.np-nsff .entry-content > hr.wp-block-separator {
  display: none;
}

/* ---------- NSFF: Family Favorites intro card ---------- */
/*
The page's opening paragraph, phase button and "See How It Works" link now sit in a single
group (`.np-ff-card`) authored in the page content, styled as the guide's card.
*/
.np-nsff .entry-content > h1 {
  /* Deliberately much larger than the site's h1 -- this is the landing page's one big
     statement, and it replaced a small eyebrow paragraph. Centred to sit with the card
     below it; switch to `left` if that reads better. */
  font-size: clamp(2.4rem, 5.2vw, 4.25rem);
  line-height: 1.02;
  letter-spacing: -0.02em;
  text-align: center;
  margin: .25rem 0 1.5rem;
}

.np-nsff .np-ff-card {
  /* The sub-brand's own light blue-grey. Practically the same tone as the site's
     --np-band (#EEF3F7), but this is the NSFF token, so the card matches the guide
     rather than the parent site. */
  background: var(--background);
  color: var(--on-surface);
  border: 0;
  box-shadow: none;
  border-radius: var(--shape-lg);
  padding: 2.15rem 1.9rem;
  /* Slightly narrower than the content column (1178px) and centred. Tune this one value. */
  max-width: 66rem;
  margin: 2.5rem auto 2.75rem;
  text-align: center;
}
/*
Descendant, not child: the card is a core Group block, so WordPress wraps its contents in
`.wp-block-group__inner-container` and the paragraph is a grandchild. The `> p` this
replaced never matched anything -- the text only ever picked up the card's inherited
`color`, which is why the colour looked right while the size never changed.
*/
.np-nsff .np-ff-card p {
  color: var(--on-surface);
  font-size: clamp(1.12rem, 1.5vw, 1.32rem);
  line-height: 1.55;
  margin: 0 auto 1.5rem;
  /* Keeps the single merged sentence to a readable measure even though the card is wide. */
  max-width: 58ch;
}

/*
Button and link on one row. `display: flex` has to beat core's own group layout rule
(`.wp-block-group.is-layout-flow`), which is equal specificity -- this stylesheet loads
after core's inline block CSS, so it wins on order.
*/
.np-nsff .np-ff-card-actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: 1.25rem;
}
/* Core adds vertical margins between group children; unwanted on a single row. */
.np-nsff .np-ff-card-actions > * {
  margin: 0;
}
.np-nsff .np-ff-card-actions .wp-block-buttons {
  margin: 0;
  gap: .75rem;
}
.np-nsff .np-ff-card-link {
  margin: 0;
  font-size: .95rem;
}
.np-nsff .np-ff-card-link a {
  color: var(--primary);
  font-weight: 500;
  text-decoration: underline;
  text-underline-offset: 3px;
}
.np-nsff .np-ff-card-link a:hover {
  color: var(--primary-variant);
}

/* "2027" picked out in bloom pink, per the style guide. */
.np-nsff .np-ff-accent {
  color: var(--bloom-pink);
}

/*
The nominations CTA is the guide's SECONDARY button (bloom pink on deep indigo ink) -- the
same variant the guide labels "Nominate" -- and scaled up from the standard 36px, since it
is the page's single primary action rather than one button in a row.

Both `background` and `color` are restated on hover: Kadence's `button:hover` sets
`color: #ffffff`, which would otherwise wash the ink out. Same trap as the form buttons.
*/
.np-nsff .np-ff-card-actions .wp-block-button__link,
.np-nsff .np-ff-card-actions .wp-element-button {
  min-height: 52px;
  padding: .7rem 30px;
  font-size: 1.05rem;
  background: var(--btn2-ground);
  color: var(--btn2-ink);
  box-shadow: var(--btn-shadow);
}
.np-nsff .np-ff-card-actions .wp-block-button__link:hover,
.np-nsff .np-ff-card-actions .wp-element-button:hover,
.np-nsff .np-ff-card-actions .wp-block-button__link:focus,
.np-nsff .np-ff-card-actions .wp-element-button:focus {
  background: var(--btn2-ground);
  color: var(--btn2-ink);
  box-shadow: var(--btn-shadow-lift);
}

/* ---------- NSFF: section accordions ---------- */
/*
Built by js/ff-accordion.js, which groups each <h2> with the blocks that follow it. The
heading is preserved and a <button> placed inside it, so the document outline is intact and
the control reads as an expandable heading.

Sections are collapsed by default -- that is the point of the pattern here, turning a very
long page into a scannable list. The chevron sits to the LEFT of the title and rotates a
quarter turn when open.
*/
.np-nsff .np-ff-acc {
  border-top: 1px solid var(--outline);
}
/* No bottom border on the last section: it closed against the page's legal footnote and
   read as a stray divider. The per-section `border-top` already separates them. */
.np-nsff .np-ff-acc__heading {
  margin: 0;
}
.np-nsff .np-ff-acc__head {
  display: flex;
  align-items: center;
  gap: .85rem;
  width: 100%;
  padding: 1.15rem 0;
  border: 0;
  background: transparent;
  box-shadow: none;
  /* Inherit the heading's condensed face rather than the button font -- these are headings
     first and controls second. */
  font: inherit;
  color: var(--on-surface);
  text-align: left;
  cursor: pointer;
}
/*
Kadence's `button:hover, button:focus, button:active` rule sets background, colour AND a
box-shadow. All three have to be pinned here: background and colour were, but the shadow
was not, so the heading picked up a drop shadow whenever it was hovered, focused or
clicked. Third property in that same rule to bite this stylesheet -- pin all three.
*/
.np-nsff .np-ff-acc__head:hover,
.np-nsff .np-ff-acc__head:focus,
.np-nsff .np-ff-acc__head:active {
  background: transparent;
  color: var(--primary);
  box-shadow: none;
}
.np-nsff .np-ff-acc__chev {
  flex: 0 0 auto;
  display: grid;
  place-items: center;
  color: var(--bloom-pink);
  transition: transform .24s var(--ease-standard);
}
.np-nsff .np-ff-acc--open .np-ff-acc__chev {
  transform: rotate(90deg);
}
.np-nsff .np-ff-acc__title {
  flex: 1 1 auto;
}
.np-nsff .np-ff-acc__body {
  display: none;
  padding: 0 0 1.4rem;
  /* Left-aligned throughout, buttons included -- the centring belongs to the card only. */
  text-align: left;
}
.np-nsff .np-ff-acc--open > .np-ff-acc__body {
  display: block;
}
.np-nsff .np-ff-acc__body .wp-block-buttons {
  justify-content: flex-start;
}
@media (prefers-reduced-motion: reduce) {
  .np-nsff .np-ff-acc__chev {
    transition: none;
  }
}

/* ---------- NSFF cards ---------- */
/*
The guide's card component. Ported in full even though the Family Favorites pages do not
use `.card` markup yet, because `.ff-public-state` below is styled as one and the winners
pages are the obvious next place for them.
*/
.np-nsff .card {
  background: var(--surface);
  border-radius: var(--shape-lg);
  box-shadow: var(--e1, 0 2px 1px -1px rgba(15, 23, 42, .2), 0 1px 1px 0 rgba(15, 23, 42, .14), 0 1px 3px 0 rgba(15, 23, 42, .12));
  overflow: hidden;
}
.np-nsff .card--flat {
  box-shadow: none;
  border: 1px solid var(--outline);
}
.np-nsff .card--reversed {
  background: var(--primary);
  color: #fff;
  box-shadow: var(--e6, 0 3px 5px -1px rgba(15, 23, 42, .2), 0 6px 10px 0 rgba(15, 23, 42, .14), 0 1px 18px 0 rgba(15, 23, 42, .12));
}
.np-nsff .card__body {
  padding: 20px;
}
.np-nsff .card__body > .overline {
  color: var(--on-surface-med);
  font-weight: 600;
}
.np-nsff .card--reversed .card__body > .overline {
  color: inherit;
}

/* ---------- NSFF: the Family Favorites component vocabulary ---------- */
/*
These classes come from the existing bespoke nomination/voting code (`ff-public-*` and
`nsp-awards-*`). NONE of them shipped any CSS -- the forms were rendering on browser
defaults -- so this is their first styling rather than an override, and it is written
against the guide's tokens so the whole sub-brand stays consistent.

Verified present across the child pages:
  ff-public-grid / -field / -check / -disclosure / -actions / -action  (nominate, for-businesses)
  ff-public-heading / -intro                                          (nominate, for-businesses, vote)
  ff-public-state                                                     (vote)
  nsp-awards-ff-public-nav(__link)                                    (nominate, vote, for-businesses)
*/

/*
Section heading + intro.

The font-family and weight here are NOT optional. The awards plugin's
`simplified-public.css` sets `.ff-public-heading` to `Georgia, "Times New Roman", serif` at
weight 500 -- a single class, which beats this stylesheet's element-level `h1..h6` rule, so
these headings were rendering as a serif. Restating the condensed face at two classes wins
regardless of load order.

The `nsp-awards-nomination__*` heading classes are included as insurance: they belong to a
second markup variant in the plugin (`simplified-nomination.css`) that these pages do not
currently render, but would arrive already off-brand if it ever did.
*/
.np-nsff .ff-public-heading,
.np-nsff .nsp-awards-nomination__heading,
.np-nsff .nsp-awards-nomination__h1,
.np-nsff .nsp-awards-nomination__section-heading {
  font-family: var(--np-font-condensed);
  font-weight: 800;
  color: var(--on-surface);
  font-size: clamp(1.5rem, 2.2vw, 2rem);
  line-height: 1.15;
  margin: 2rem 0 .5rem;
}
/*
A rule above the form heading, separating the page's own copy from the form itself.

Its own block rather than folded into the rule above, because that one is shared with the
`nsp-awards-nomination__*` headings, which are section headings inside a form and should not
each be preceded by a divider. **Must stay immediately after it** -- it overrides that rule's
`margin` shorthand at equal specificity, so source order is what decides.

The spacing is deliberately generous and asymmetric-looking in the source but even on the
page: `margin-top` is the gap above the line, `padding-top` the gap below it.
*/
.np-nsff .ff-public-heading {
  border-top: 1px solid var(--outline);
  margin-top: 4.5rem;
  padding-top: 3rem;
}

.np-nsff .ff-public-intro {
  color: var(--on-surface-med);
  font-size: 1.02rem;
  line-height: 1.6;
  margin: 0 0 1.25rem;
}

/* Sub-navigation. Styled as the guide's chip: pill, surface-3 ground, and the active item
   filled with primary -- keyed on `aria-current`, which the markup already sets, rather
   than a class the template would have to add. */
/*
On /vote/ this nav is the page's main choice -- Nominate or Vote -- rather than a strip of
filters, so the two sit centred and at button size. It is the only page where both are
visible: /nominate/ hides them both, and on /for-businesses/ the whole nav is hidden inside
the Need Help section.
*/
.np-nsff .nsp-awards-ff-public-nav {
  justify-content: center;
  gap: 12px;
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin: 0 0 1.5rem;
}
/*
Scoped to the nav itself, not just the link class.

ff-accordion.js lifts the "Update My Business" link out of this nav and into the closing
CTA, where it is styled as the guide's outlined button. Without the ancestor these rules
kept matching it there -- a chip ground, a fixed 32px height and a contained button
treatment all still landing on an element that had left the nav. The outline rule out-ranked
them on the properties it declares, but the leftovers stayed and the two treatments were
genuinely overlapping.

Requiring `.nsp-awards-ff-public-nav` makes that structural: in the nav it is a chip, out of
the nav it is not.
*/
.np-nsff .nsp-awards-ff-public-nav .nsp-awards-ff-public-nav__link {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  /* `min-height`, not `height`: a fixed height clips a label that wraps, which is how the
     phase button broke earlier. */
  min-height: 48px;
  padding: .7rem 28px;
  border-radius: var(--shape-full);
  background: var(--surface-3);
  color: var(--on-surface);
  font-family: var(--font-ui);
  font-size: .95rem;
  font-weight: 500;
  letter-spacing: .3px;
  text-decoration: none;
  transition: background var(--dur-short) var(--ease-standard),
              color var(--dur-short) var(--ease-standard);
}
.np-nsff .nsp-awards-ff-public-nav .nsp-awards-ff-public-nav__link:hover {
  background: var(--outline-strong);
}
/*
Nominate is the action being offered here, so it takes the sub-brand's pink, reversing to
midnight navy on hover -- the same pair as the Family Favorites button in the site nav.

Keyed on the href, because the markup gives every link in this nav the same class and there
is no per-item hook. Four selector levels, so it outranks the base chip rule above without
depending on source order.
*/
.np-nsff .nsp-awards-ff-public-nav .nsp-awards-ff-public-nav__link[href*="/family-favorites/nominate"] {
  background: var(--bloom-pink);
  color: #fff;
  /*
  `simplified-public.css` puts a 1px #165272 border on every link in this nav. Setting the
  ground and the text left that stroke behind, ringing the pink button in dark blue.
  Transparent rather than `border: 0`, so the button keeps the same footprint as the chip
  beside it -- removing the border outright would shrink it by 2px in each direction.
  */
  border-color: transparent;
}
.np-nsff .nsp-awards-ff-public-nav .nsp-awards-ff-public-nav__link[href*="/family-favorites/nominate"]:hover,
.np-nsff .nsp-awards-ff-public-nav .nsp-awards-ff-public-nav__link[href*="/family-favorites/nominate"]:focus-visible {
  background: var(--midnight-navy);
  color: #fff;
}

.np-nsff .nsp-awards-ff-public-nav .nsp-awards-ff-public-nav__link[aria-current="page"] {
  background: var(--primary);
  color: #fff;
}

/* Form grid. Two columns on desktop; `[data-wide]` fields span both -- the markup already
   marks the textarea that way, so no template change is needed. */
.np-nsff .ff-public-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 1.15rem 1.25rem;
  margin: 0 0 1.25rem;
}
.np-nsff .ff-public-field[data-wide="true"] {
  grid-column: 1 / -1;
}
@media (max-width: 700px) {
  .np-nsff .ff-public-grid {
    grid-template-columns: minmax(0, 1fr);
  }
}

/*
Fields. The guide's outlined field floats its label inside the border, but this markup has
the <label> WRAPPING its control with the text first -- so the treatment is adapted rather
than copied: the label text takes the guide's small label styling and the control itself
carries the outline, radius and focus behaviour.
*/
/*
`display: block`, not the plugin's column flex.

The plugin sets `.ff-public-field { display: flex; flex-direction: column }`, which makes the
label's text, its `<span> *</span>` and the control three separate flex items -- so the
asterisk dropped onto a line of its own under the field name. As a block the text and the
span are inline and share a line, and the control, already `width: 100%`, takes the next one.
The flex `gap` goes with it, replaced by a margin on the controls below.
*/
.np-nsff .ff-public-field {
  display: block;
  font-family: var(--font-ui);
  font-size: .75rem;
  font-weight: 500;
  letter-spacing: .4px;
  color: var(--on-surface-med);
  text-transform: uppercase;
}
/* The "(optional)" span inside a label should not shout. */
.np-nsff .ff-public-field span {
  font-weight: 400;
  text-transform: none;
  letter-spacing: .2px;
}
/* Inline by default, and the label is a block now, so the error needs its own line back. */
.np-nsff .ff-public-field .ff-public-error-text {
  display: block;
  margin-top: 4px;
}
.np-nsff .ff-public-field input,
.np-nsff .ff-public-field select,
.np-nsff .ff-public-field textarea {
  width: 100%;
  box-sizing: border-box;
  /* Replaces the flex `gap` the label no longer has. */
  margin-top: 6px;
  padding: 11px 12px;
  border: 1px solid var(--outline-strong);
  border-radius: var(--shape-sm);
  background: var(--surface);
  color: var(--on-surface);
  font-family: var(--font-ui);
  font-size: 1rem;
  font-weight: 400;
  letter-spacing: .5px;
  text-transform: none;
  transition: border-color var(--dur-short) var(--ease-standard),
              box-shadow var(--dur-short) var(--ease-standard);
}
.np-nsff .ff-public-field textarea {
  min-height: 120px;
  resize: vertical;
}
/*
Focus uses a box-shadow ring rather than a thicker border: the guide compensates for its
2px border by shaving a pixel of padding, which only works because its label is absolutely
positioned. Here a border change would shift the control and everything under it, so the
ring keeps the geometry stable.
*/
.np-nsff .ff-public-field input:focus,
.np-nsff .ff-public-field select:focus,
.np-nsff .ff-public-field textarea:focus {
  outline: none;
  border-color: var(--primary);
  box-shadow: 0 0 0 2px rgba(12, 79, 115, .18);
}
.np-nsff .ff-public-field input::placeholder,
.np-nsff .ff-public-field textarea::placeholder {
  color: var(--on-surface-dis);
  text-transform: none;
  letter-spacing: .3px;
}
/* Required marker, generated so the templates need no extra markup. */
.np-nsff .ff-public-field:has(input[required]) > span:first-of-type::after,
.np-nsff .ff-public-field:has(select[required])::after {
  content: "";
}

/*
Checkbox row.

Centred here rather than in a second rule near the disclosure: an added
`.np-nsff .ff-public-check` block lower in the file would have equal specificity and later
source order, and this rule's `margin` shorthand would have reset the auto margins right
back. Third time that has bitten in this stylesheet today -- `.ff-public-item` and
`.ff-public-field` were the others. **Grep for an existing `ff-public-*` rule before adding
one.**

Centring a flex row takes `justify-content`, not `text-align`, plus `width: fit-content` so
the box is only as wide as its content and the auto margins have something to centre.
*/
.np-nsff .ff-public-check {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  width: fit-content;
  max-width: 100%;
  margin: 0 auto 1rem;
  color: var(--on-surface);
  font-family: var(--font-ui);
  font-size: .95rem;
  cursor: pointer;
}
.np-nsff .ff-public-check input[type="checkbox"] {
  width: 18px;
  height: 18px;
  /* Paints the native control in brand colour without rebuilding it. */
  accent-color: var(--primary);
  flex: none;
  margin: 0;
}

/* Fine print, matching the guide's field help text. */
.np-nsff .ff-public-disclosure {
  font-size: .75rem;
  line-height: 1.55;
  letter-spacing: .4px;
  color: var(--on-surface-med);
  margin: 0 0 1.25rem;
}
.np-nsff .ff-public-disclosure a {
  color: var(--primary);
  text-decoration: underline;
}

/* Action row. The primary submit is the guide's contained button; the secondary
   ("Add Another Nomination") is its outlined variant, so the two read as a hierarchy. */
.np-nsff .ff-public-actions {
  display: flex;
  flex-wrap: wrap;
  /* Only "Add Another Nomination" lives here now -- Review moved to its own row -- so it
     centres rather than sitting hard left. */
  justify-content: center;
  gap: 10px;
  margin: 0 0 1rem;
}
/*
`.np-ff-cta__btn` is in this list on purpose. It was previously styled by a rule of its own
that restated the outlined look with different geometry, and it drifted: eleven computed
properties apart from these buttons -- font family, size, tracking, case, display, gap,
min-height, padding, line-height, radius, shadow. Sharing the declaration is the only way
they stay identical.
*/
.np-nsff .ff-public-action,
.np-nsff .np-ff-cta__btn {
  appearance: none;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  min-height: 36px;
  padding: .45rem 16px;
  border-radius: var(--shape-full);
  border: 1px solid var(--outline-strong);
  background: transparent;
  color: var(--primary);
  font-family: var(--font-ui);
  font-size: .875rem;
  font-weight: 500;
  letter-spacing: 1.25px;
  text-transform: uppercase;
  transition: box-shadow var(--dur-med) var(--ease-standard),
              background var(--dur-short) var(--ease-standard),
              transform var(--dur-short) var(--ease-standard);
}
/*
`:not([data-primary])` is essential. This hover fill is `--state-hover`, i.e.
rgba(15,23,42,.04) -- fine behind the outlined variant's navy text, but the primary button
keeps white text, so applying it there put white on a near-transparent ground and the
button vanished on hover. The primary re-asserts its own fill below.
*/
.np-nsff .ff-public-action:not([data-primary="true"]):hover:not(:disabled),
.np-nsff .np-ff-cta__btn:hover,
.np-nsff .np-ff-cta__btn:focus-visible {
  background: var(--state-hover);
  /*
  `color` must be restated, not inherited from the idle state. Kadence's
  `button:hover, button:focus, button:active` rule also sets
  `color: var(--global-palette-btn-hover)`, which resolves to #ffffff -- so without this
  the outlined button's ink went white on a 4%-opacity ground and vanished, exactly the
  bug the primary button had. Any hover state on these controls has to declare BOTH
  background and colour.
  */
  color: var(--primary);
}
.np-nsff .ff-public-action[data-primary="true"] {
  border-color: transparent;
  background: var(--btn-ground);
  color: var(--btn-ink);
  box-shadow: var(--btn-shadow);
}
.np-nsff .ff-public-action[data-primary="true"]:hover:not(:disabled) {
  /* Fill and ink restated, not just the lift -- see the note above. */
  background: var(--btn-ground);
  color: var(--btn-ink);
  transform: translateY(-2px);
  box-shadow: var(--btn-shadow-lift);
}
.np-nsff .ff-public-action[data-primary="true"]:active:not(:disabled) {
  transform: translateY(0);
  box-shadow: var(--btn-shadow);
}
/* Kadence's orange button focus fill, pinned as everywhere else. */
.np-nsff .ff-public-action:focus {
  background: transparent;
  color: var(--primary);
}
.np-nsff .ff-public-action[data-primary="true"]:focus {
  background: var(--btn-ground);
  color: var(--btn-ink);
}

/*
"Update My Business" is promoted from a chip to the guide's contained button, so it reads
as a real action rather than a filter. Targeted by href because the markup gives every nav
link the same class -- there is no per-item hook to key off.
*/
.np-nsff .nsp-awards-ff-public-nav .nsp-awards-ff-public-nav__link[href*="/family-favorites/for-businesses"] {
  min-height: 36px;
  padding: .45rem 16px;
  background: var(--btn-ground);
  color: var(--btn-ink);
  font-size: .875rem;
  letter-spacing: 1.25px;
  text-transform: uppercase;
  box-shadow: var(--btn-shadow);
  transition: box-shadow var(--dur-med) var(--ease-standard),
              background var(--dur-short) var(--ease-standard),
              transform var(--dur-short) var(--ease-standard);
}
.np-nsff .nsp-awards-ff-public-nav .nsp-awards-ff-public-nav__link[href*="/family-favorites/for-businesses"]:hover {
  background: var(--btn-ground);
  color: var(--btn-ink);
  transform: translateY(-2px);
  box-shadow: var(--btn-shadow-lift);
}

/*
On the Nominate page itself, the Nominate and Vote chips are dropped -- the first is where
you already are and the second is a different stage of the programme. Scoped to
`.page-id-31018` so the same nav keeps them on /vote/ and /for-businesses/, where they are
still useful. `display: none` also takes them out of the accessibility tree, so they are
genuinely gone rather than merely invisible.

Matched by href for the same reason as above: every link carries the identical class.
*/
.page-id-31018 .nsp-awards-ff-public-nav__link[href*="/family-favorites/nominate"],
.page-id-31018 .nsp-awards-ff-public-nav__link[href*="/family-favorites/vote"] {
  display: none;
}

/* ---------- NSFF: conditional form states ---------- */
/*
These only render after a submission -- validation errors, the review list, the success
confirmation -- so they never appear in the page source and were missed on the first pass.
The plugin styles them in raw web colours (#c00, #fff8f8, #3f744f, #ddd); these map them
onto the guide's own `--error` / `--success` / `--outline` tokens so a failed submit still
looks like the sub-brand.
*/
.np-nsff .ff-public-errors {
  border: 1px solid var(--error);
  background: #FDF4F3;
  border-radius: var(--shape-sm);
  padding: .85rem 1rem;
  margin: 0 0 1.25rem;
  color: var(--on-surface);
  font-size: .95rem;
}
.np-nsff .ff-public-error-text {
  color: var(--error);
  font-size: .75rem;
  font-weight: 500;
  letter-spacing: .3px;
  text-transform: none;
}
.np-nsff .ff-public-field--error input,
.np-nsff .ff-public-field--error select,
.np-nsff .ff-public-field--error textarea {
  border-color: var(--error);
}
/* Matches the valid-state focus ring so an errored field still reads as focused. */
.np-nsff .ff-public-field--error input:focus,
.np-nsff .ff-public-field--error select:focus,
.np-nsff .ff-public-field--error textarea:focus {
  border-color: var(--error);
  box-shadow: 0 0 0 2px rgba(178, 58, 52, .18);
}

/*
The flat-card rule that used to live here has been removed. It was a second
`.np-nsff .ff-public-item` block -- same specificity as the gradient-ring rule further up,
but later in the file, so source order handed it the win and only the first entry (which
uses this theme's own `.np-ff-nom-card`) showed a ring. That is why the cards "all looked
subtly different". The ring rule now serves the form entries and the review list alike.
*/
.np-nsff .ff-public-item-actions {
  display: flex;
  /* Left, with the entry title above it, rather than pushed to the right margin. */
  justify-content: flex-start;
  gap: 8px;
  margin: 0 0 .75rem;
}

/* Secondary metadata under a nominee. */
.np-nsff .ff-public-meta {
  color: var(--on-surface-med);
  font-size: .8125rem;
  letter-spacing: .2px;
}

/*
Success tick. The plugin sizes and rounds the circle already; the colour comes from the
guide's --success, and the tick itself is redrawn as a stroked icon by ff-form.js.
*/
.np-nsff .ff-public-success {
  background: var(--success);
  color: #fff;
  /*
  The plugin sets `margin-bottom: 0.75em` on this element, and because its font-size is 1.5em
  that em is a large one -- about 18px of gap under the circle on top of whatever the heading
  below already brings. Zeroed; the heading's own top margin is the spacing now.
  */
  margin-bottom: 0;
}
/*
Centred, which it was not.

`simplified-public.css` centres it with auto margins at line 238 and then **cancels that at
line 462** -- `.ff-public-state .ff-public-success { margin-left: 0; margin-right: 0 }`, a
later rule in the same file at the same specificity, so the circle sat hard left inside a
panel whose text is centred. Three classes here beats the plugin's two, so this does not
depend on load order.

Same shape as the `.ff-public-req` bug: a rule in that stylesheet undone by a later one in
the same stylesheet. **Worth Des knowing there are now two of these.**
*/
.np-nsff .ff-public-state .ff-public-success {
  margin-left: auto;
  margin-right: auto;
}
/*
The circle is 3.5em against a 1.5em font size, so the glyph filled most of it. A stroked
icon needs to sit smaller than that to read as an icon rather than as a cramped mark.
*/
.np-nsff .ff-public-success .np-ff-tick {
  width: 55%;
  height: 55%;
}

/* Status box (e.g. "Voting is not yet open") -- the guide's flat card. */
.np-nsff .ff-public-state {
  background: var(--surface-2);
  border: 1px solid var(--outline);
  border-radius: var(--shape-lg);
  padding: 20px;
  margin: 0 0 1.5rem;
}
.np-nsff .ff-public-state > :last-child {
  margin-bottom: 0;
}

@media (prefers-reduced-motion: reduce) {
  .np-nsff .ff-public-action,
  .np-nsff .nsp-awards-ff-public-nav .nsp-awards-ff-public-nav__link,
  .np-nsff .ff-public-field input,
  .np-nsff .ff-public-field select,
  .np-nsff .ff-public-field textarea {
    transition: none;
  }
  .np-nsff .ff-public-action:hover {
    transform: none;
  }
}

/* ---------- NSFF: the rules page ---------- */
/*
The three paragraphs that open the Rules and Privacy Notice, centred.

`> p` reaches exactly those three and nothing else: every other paragraph on the page has
been moved inside an accordion body by ff-accordion.js, so the only direct-child paragraphs
left in the content column are the intro.

`.np-ff-page--rules` comes from the per-page body class added in functions.php -- one hook
that gives every Family Favorites page a slug-named class, so a page-specific adjustment
like this needs no new PHP.
*/
.np-ff-page--rules .entry-content > p {
  text-align: center;
}

/* ---------- NSFF: the winners pages ---------- */
/*
Announcement pages rather than reference pages: a title, a short statement and a link. They
get no rule under the title and their content is centred.

The rule under the title is the one every other Family Favorites hero carries. It earns its
place there by separating a title from the deck beneath it -- these pages have no deck, so
it was a line under a heading with nothing on the other side of it.
*/
.np-ff-plain .entry-hero .entry-header > h1::after {
  content: none;
}
.np-ff-plain .entry-content {
  text-align: center;
}
/* Button blocks are flex, so they need `justify-content`, not `text-align`. */
.np-ff-plain .entry-content .wp-block-buttons {
  justify-content: center;
}
/*
Except inside an accordion. Centring suits an announcement page -- a title, a sentence and a
link -- but /winners/ is a reference page whose sections are collapsible, and a centred
button under a run of section copy reads as adrift.

Scoped on the accordion body rather than on the page, so /winners/2027/ keeps its centred
button: that page has no accordions, its whole content being a single section.
*/
.np-ff-plain .entry-content .np-ff-acc__body .wp-block-buttons {
  justify-content: flex-start;
}

/* ---------- NSFF: accordion buttons take the outlined style ---------- */
/*
Every button inside a section accordion on the landing page becomes the guide's outlined
variant: transparent ground, `--primary` text, an `--outline-strong` hairline, no shadow,
`--state-hover` behind it on hover.

They are secondary by position. The landing page has one primary action -- the phase button
in the intro card, which stays the guide's pink secondary-contained treatment -- and these
six are reference links tucked inside collapsed sections. Six contained navy buttons down
the page competed with it.

Three classes against the two of `.np-nsff .wp-element-button`, so this wins on specificity
and does not depend on where it sits in the file.

`color` and `box-shadow` are restated on hover because Kadence's own
`button:hover, button:focus, button:active` sets `color: #ffffff` and a shadow, and these
carry `.wp-element-button`. That rule has leaked into this stylesheet three times now.
*/
.np-nsff .np-ff-acc__body .wp-block-button__link,
.np-nsff .np-ff-acc__body .wp-element-button {
  background: transparent;
  color: var(--primary);
  border: 1px solid var(--outline-strong);
  box-shadow: none;
}
.np-nsff .np-ff-acc__body .wp-block-button__link:hover,
.np-nsff .np-ff-acc__body .wp-block-button__link:focus-visible,
.np-nsff .np-ff-acc__body .wp-element-button:hover,
.np-nsff .np-ff-acc__body .wp-element-button:focus-visible {
  background: var(--state-hover);
  color: var(--primary);
  border-color: var(--outline-strong);
  box-shadow: none;
  transform: none;
}

/* ---------- NSFF: buttons wrap instead of clipping ---------- */
/*
Every button in the sub-brand had a fixed `height`, which is fine until a label outgrows it:
"Nominations Open September 1" measured 1px taller than its 52px box at 390px, and narrower
phones wrapped it to two lines with the second clipped off. All of those are now
`min-height` with real vertical padding, so a long label grows the button instead.

`line-height` matters here too -- the inherited one was tuned for single-line labels and a
wrapped label needs the leading.
*/
.np-nsff .wp-element-button,
.np-nsff .ff-public-action,
.np-nsff .np-ff-cta__btn,
.np-nsff .np-ff-card-actions .wp-block-button__link {
  line-height: 1.25;
  /* Labels come from Page Copy and the phase table, so they can be any length. */
  white-space: normal;
}

/* ---------- NSFF: centred layout on /nominate/ and /vote/ ---------- */
/*
Body copy, headings, the entry titles and the closing CTA all centre on these two pages.

Field labels are deliberately excluded. Centred labels sitting above left-aligned inputs
read as misaligned and are harder to scan down a column -- say the word if you want them
centred too, but it is worth seeing this first.

Placed here rather than beside each base rule because several of these override a rule of
the same specificity, and source order is the only thing deciding it.
*/
.np-ff-hero-page .entry-content > p,
.np-ff-hero-page .entry-content > h2,
.np-ff-hero-page .entry-content > h3,
.np-ff-hero-page .ff-public-heading,
.np-ff-hero-page .ff-public-intro,
.np-ff-hero-page .np-ff-nom-num,
.np-ff-hero-page .np-ff-cta,
.np-ff-hero-page .np-ff-cta__sub,
.np-ff-hero-page .ff-public-disclosure {
  text-align: center;
}
/* Button rows: these are flex, so they need `justify-content`, not `text-align`. */
.np-ff-hero-page .ff-public-item-actions,
.np-ff-hero-page .ff-public-actions {
  justify-content: center;
}
/* The fields themselves stay left -- see the note above. */
.np-ff-hero-page .ff-public-field {
  text-align: left;
}

/* ---------- Content top spacing (sitewide) ---------- */
/*
The space above a post or page's own body content -- above the first paragraph, or above
the listicle's own eyebrow when it renders hero-less (see single-entry.php) -- was two
separate sources stacking to 64px (32px + 32px), not one deliberate value:

- `.entry-content-wrap { padding: 2rem; ... }` -- Kadence's own Customizer-generated CSS,
  injected inline in <head>, not anything in this theme's own files. 2rem at desktop and
  tablet, 1.5rem (24px) below 767px.
- `.single-content { margin: var(--global-md-spacing) 0 0; }` -- Kadence's own static
  content.min.css, using a token shared across many unrelated components sitewide (block
  spacing, templates, etc.), so redefining --global-md-spacing itself was not an option
  without changing all of those too.

Both overridden here instead, to one deliberate, shared, configurable value
(--np-content-top-spacing, declared once in :root) rather than left as an
un-inspected sum of two other systems' own defaults. `!important` on both: each is a
plain single-class selector at equal specificity to what it overrides, decided by
enqueue/injection order rather than anything guaranteed to favour this stylesheet, and
this session already found two separate bugs caused by trusting that order.
*/
.entry-content-wrap {
  padding-top: var(--np-content-top-spacing) !important;
}
.single-content {
  margin-top: var(--np-content-top-spacing) !important;
}
@media (max-width: 767px) {
  :root {
    --np-content-top-spacing: .75rem;
  }
}

/* ---------- Mobile: content gutter and button sizing ---------- */
/*
Deliberately placed here, near the end of the stylesheet, and not with the rules it
overrides. Every rule below is a media query at the same specificity as its base rule, and
a media query buys no specificity -- only source order decides. Putting these blocks up
beside their base rules is exactly how the mobile form-wrapper override failed earlier.
*/
@media (max-width: 767px) {
  /*
  Content gutter, matched to the footer's.

  The 32px gutter was the sum of three boxes, not one value:
  `.content-container` padding 24px, then `article.entry.content-bg` pulling back with a
  -16px margin, then `.entry-content-wrap` adding 24px again. 24 - 16 + 24 = 32, against
  the footer's flat 24. Reducing the last of the three to 16px brings the total to 24 and
  leaves the theme's own full-bleed mechanism (that negative margin) alone.

  Site-wide, not sub-brand: the mismatch is the theme's, and it applies to every post and
  page on a phone.
  */
  .entry-content-wrap {
    padding-left: 16px;
    padding-right: 16px;
  }

  /*
  Button text, small enough not to wrap.

  These labels are uppercase with 1.25px of tracking, which makes them far wider than their
  character count suggests -- "Add Another Nomination" runs 274px of a 326px column at
  14px, so anything narrower than a 390px viewport wrapped it. The tracking comes down with
  the size, since letter-spacing is what most of the width was.
  */
  .np-nsff .ff-public-action {
    font-size: .75rem;
    letter-spacing: .6px;
    padding: 0 12px;
  }
  /* Same specificity as the base rule for each of these, so they rely on being later. */
  .np-nsff .np-ff-review-row .ff-public-action {
    font-size: .875rem;
    padding: 0 18px;
  }
  /* Nothing here either: the mobile sizing above already lists .ff-public-action, which
     this button now shares. */
}

/* ---------- Nominations announcement bar ---------- */
/*
Fixed to the bottom of the viewport, held back on load, then reveal-on-scroll-up. Driven by
js/announce-bar.js.

`fixed`, not `sticky`. Sticky was right while the bar was always visible -- it stayed in
flow and so covered nothing -- but a bar that slides away cannot hold flow space, and a
sticky element cannot be translated out of view without leaving its gap behind. The overlap
that normally costs you compensating body padding is answered by the behaviour itself: the
bar is hidden whenever the reader is scrolling *down*, which is exactly when they arrive at
the footer.

`visibility` is switched alongside the transform, and the timing is the point: it flips to
`hidden` only after the slide-out finishes (`visibility 0s .45s`) and back to `visible`
immediately on the way in (`0s 0s`). Without it the links inside stay in the tab order while
the bar sits off-screen, so a keyboard user could focus a control they cannot see.

Type is Acumin condensed throughout: the programme name at 800 -- the heaviest weight the kit
ships, there being no 900 -- and the rest at 700.
*/
.np-announce {
  position: fixed;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 997;   /* under the header (999) and the section nav (998) */
  /* Midnight navy into bloom pink, matching the Family Favorites hero band's angle. */
  background: linear-gradient(45deg, var(--np-midnight-navy) 0%, var(--np-bloom-pink) 100%);
  color: #fff;
  transform: translateY(100%);
  visibility: hidden;
  transition: transform .45s cubic-bezier(.2, .75, .25, 1), visibility 0s .45s;
}
.np-announce.is-in {
  transform: translateY(0);
  visibility: visible;
  transition: transform .45s cubic-bezier(.2, .75, .25, 1), visibility 0s 0s;
}
/* Three classes, so it beats `.is-in` above on specificity rather than source order. */
.np-announce.is-in.is-out {
  transform: translateY(100%);
  visibility: hidden;
  transition: transform .45s cubic-bezier(.2, .75, .25, 1), visibility 0s .45s;
}
.np-announce__inner {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: clamp(.75rem, 2vw, 1.5rem);
  padding: .85rem 1.5rem;
  /* Wraps to two rows on a phone rather than shrinking the type to fit. */
  flex-wrap: wrap;
}
/*
The announcement mark, left of the sentence, in a white disc.

The disc is a wrapper rather than a background on the SVG itself: the megaphone's mouth
reaches the edge of its 24-unit box, so a background there would have the artwork touching
the circle's rim. Padding on a wrapper gives it room to sit inside.

`flex: none` so the disc never squashes when the row wraps on a phone, and the gap on the
inner row does the spacing rather than a margin here.
*/
.np-announce__badge {
  flex: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 36px;
  height: 36px;
  border-radius: 50%;
  background: #fff;
  /* The mark, not the bar's white text. */
  color: var(--np-bloom-pink);
}
.np-announce__icon {
  font-family: var(--np-font-condensed);
  font-size: 1.6rem;
  /* The heaviest weight the Acumin kit ships; there is no 900. */
  font-weight: 800;
  line-height: 1;
  /* An exclamation mark's dot sits on the baseline, so the glyph's visual mass is high in
     its line box and the character reads as riding up in the disc without this. */
  margin-top: 2px;
}

.np-announce__text {
  margin: 0;
  font-family: var(--np-font-condensed);
  font-size: clamp(.95rem, 1.6vw, 1.15rem);
  font-weight: 700;
  letter-spacing: .06em;
  line-height: 1.2;
  text-transform: uppercase;
  text-align: center;
}
/* The programme name, heavier than the sentence around it. */
.np-announce__text strong {
  font-weight: 800;
}
.np-announce__cta {
  flex: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: 40px;
  padding: .5rem 1.25rem;
  border-radius: var(--shape-full, 999px);
  background: #fff;
  color: var(--np-harbor-blue);
  font-family: var(--np-font-condensed);
  font-size: clamp(.85rem, 1.4vw, 1rem);
  font-weight: 700;
  letter-spacing: .06em;
  line-height: 1.2;
  text-transform: uppercase;
  text-decoration: none;
  white-space: nowrap;
  transition: background-color var(--dur-short, .15s) ease, color var(--dur-short, .15s) ease;
}
/*
`:focus` and `:active` are named as well as `:hover`. Kadence's `a:hover, a:focus, a:active`
sets its highlight colour at (0,1,1), which outranks a single class -- the same leak that
turned the section nav orange on click.
*/
.np-announce__cta:hover,
.np-announce__cta:focus,
.np-announce__cta:active,
.np-announce__cta:focus-visible {
  background: var(--np-bloom-pink);
  color: #fff;
}
/*
The dismiss control. Pushed to the end of the row by `margin-left: auto`, which leaves the
message and its button centred as a group rather than spreading all three across the bar.
*/
.np-announce__close {
  flex: none;
  margin-left: auto;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  /* 40px, so it clears the touch-target minimum on its own. */
  width: 40px;
  height: 40px;
  padding: 0;
  border: 0;
  border-radius: 50%;
  background: transparent;
  color: #fff;
  cursor: pointer;
  transition: background-color var(--dur-short, .15s) ease;
}
/*
`color` is restated on every state. Kadence's `button:hover, button:focus, button:active`
sets `color: #ffffff` *and* a background and a shadow -- harmless here since white is what
this wants, but the shadow is not, so all three are pinned. That rule has leaked into this
stylesheet four times now.
*/
.np-announce__close:hover,
.np-announce__close:focus,
.np-announce__close:active,
.np-announce__close:focus-visible {
  background: rgba(255, 255, 255, .18);
  color: #fff;
  box-shadow: none;
}

/*
On a phone the row wraps, and a dismiss control sitting at the end of the wrapped flow ends
up beside the button rather than clearly separate from it. Lifted out to the top right
corner instead, with the CTA left alone on its own line to centre.
*/
@media (max-width: 767px) {
  .np-announce__inner {
    position: relative;
    /* Room above for the close control, which is now out of the flow. */
    padding-top: 2.35rem;
  }
  .np-announce__close {
    position: absolute;
    top: .1rem;
    right: .1rem;
    /* The `margin-left: auto` that placed it at the end of the row no longer applies. */
    margin-left: 0;
  }
  /*
  The badge straddles the bar's top edge, half the disc above the bar and half inside.

  Taken out of the flow to do it. A negative margin would have had to add up the inner
  padding and the row gap to know where the top edge was; positioning against the edge itself
  and pulling back by half the disc is exact, and it stays exact if the message rewraps.

  The shadow is what keeps it legible where it overhangs: above the edge the disc sits on the
  page, not on the gradient, and white on white would otherwise disappear.
  */
  .np-announce__badge {
    position: absolute;
    top: 0;
    left: 50%;
    transform: translate(-50%, -50%);
    box-shadow: 0 6px 16px rgba(15, 23, 42, .28);
  }
  /* Below the message, centred, in normal flow. */
  .np-announce__cta {
    margin-inline: auto;
  }
}

@media (prefers-reduced-motion: reduce) {
  .np-announce,
  .np-announce.is-in,
  .np-announce.is-in.is-out {
    /* The bar still appears and still hides -- it just does not slide. */
    transition: none;
  }
  .np-announce__cta,
  .np-announce__close {
    transition: none;
  }
}

/* ---------- Footer ---------- */
/*
Content rendered by np_render_footer_main() on the kadence_top_footer hook, and the
copyright by our own kadence_footer_html callback (Kadence's, with its "Theme by
Kadence WP" credit, is removed in functions.php).
*/
.np-footer {
  padding: 3rem 0 2rem;
}
.np-footer-inner {
  width: min(1178px, 100% - 3rem);
  margin-inline: auto;
  display: grid;
  grid-template-columns: minmax(0, 1.1fr) minmax(0, 1fr);
  gap: 2.5rem 3rem;
}
@media (max-width: 860px) {
  .np-footer-inner {
    grid-template-columns: minmax(0, 1fr);
  }
}
.np-footer-heading {
  color: var(--np-chrome-ink-strong);
  font-size: clamp(1.5rem, 2vw, 2rem);
  line-height: 1.1;
  margin: 0 0 .4rem;
}
.np-footer-subtitle {
  margin: 0 0 1.1rem;
  color: var(--np-chrome-ink-muted);
  font-size: 1rem;
}
/* The section-title rule adds a flexing rule via ::after; the footer headings are not
   section titles, so nothing to undo -- but they do inherit the condensed headline face. */

.np-contact-list,
.np-social-list,
.np-footer-menu {
  list-style: none;
  margin: 0;
  padding: 0;
}
.np-contact-list {
  display: grid;
  gap: .7rem;
  margin-bottom: 1.4rem;
}
.np-contact {
  display: inline-flex;
  align-items: center;
  gap: .7rem;
  color: var(--np-chrome-ink);
  text-decoration: none;
  font-size: 1rem;
  transition: color .2s ease;
}
.np-contact:hover .np-contact-text,
.np-contact:focus-visible .np-contact-text {
  color: var(--np-chrome-accent);
  text-decoration: underline;
}

/* Filled yellow circles with navy glyphs, shared by the contact rows and the socials. */
.np-icon {
  flex: 0 0 auto;
  width: 1.65rem;
  height: 1.65rem;
  display: grid;
  place-items: center;
  border-radius: 50%;
  background: var(--np-gold);
  color: var(--np-dark-blue);
  transition: background-color .2s ease, color .2s ease, transform .2s ease;
}
/* Kadence's icons come through at their own intrinsic size, so they are constrained
   here rather than by the width/height attributes used on the fallback SVGs. */
.np-icon svg,
.np-icon-svg {
  display: block;   /* avoid inline baseline shifting the glyph off-centre */
  width: .95rem;
  height: .95rem;
}
.np-contact:hover .np-icon,
.np-social:hover .np-icon,
.np-contact:focus-visible .np-icon,
.np-social:focus-visible .np-icon {
  /* Was --np-cream, which is invisible on the light footer. Dark circle, inverted glyph. */
  background: var(--np-chrome-accent);
  color: #fff;
  transform: translateY(-1px);
}

.np-social-list {
  display: flex;
  gap: .7rem;
}
.np-social {
  display: block;
  text-decoration: none;
}

/*
Accordion. Multi-column layout is deliberately not used here -- `columns` would split a
parent from its own sub-menu across a column break.
*/
.np-footer-menu > li {
  margin: 0;
  /* Separator between accordions. */
  border-bottom: 2px solid var(--np-chrome-rule);
}
/* The list's own bottom edge needs no rule -- it closes against the footer. */
.np-footer-menu > li:last-child {
  border-bottom: 0;
}
/* The whole row is the hover and click target, so it carries the padding and the
   pointer -- not the link alone. */
.np-acc-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: .75rem;
  padding: .7rem 0;
  cursor: pointer;
}
/* Same treatment as the nav bar: condensed semibold caps with tracking. Size is a touch
   larger than the mixed-case setting it replaces, since condensed caps take less width per
   character. Applied to the accordion titles only -- child links stay mixed-case, or the
   lists would read as a wall of capitals. */
/*
The second selector is `> li > .np-acc-head > a`, not a bare `.np-acc-head > a`. On items
WITH children the anchor sits inside the injected head wrapper, so it is not a direct child
of the <li> and the first selector never matched it. The bare form tied on specificity with
the generic `.np-footer-menu a { font-size: .96rem }` below and lost on source order --
which is why titles with a dropdown rendered 15.36px against 17.92px for those without.
*/
.np-footer-menu > li > a,
.np-footer-menu > li > .np-acc-head > a {
  font-family: var(--np-font-condensed);
  font-weight: 600;
  font-size: 1.12rem;
  letter-spacing: .05em;
  text-transform: uppercase;
}
/*
Hover only -- `:focus-within` was removed deliberately. Collapsing an accordion leaves
focus on the chevron button, so the wrapper kept matching `:focus-within` and the title
stayed gold after it had been toggled shut. Keyboard users still get the chevron's own
focus ring, so nothing is lost.
*/
.np-acc-head:hover > a,
.np-acc-head:hover .np-acc-toggle {
  color: var(--np-chrome-accent);
}
/* Items with no children keep their own padding, since they get no .np-acc-head. */
.np-footer-menu > li:not(.np-has-sub) > a {
  display: block;
  padding: .7rem 0;
}
.np-footer-menu a {
  color: var(--np-chrome-ink);
  text-decoration: none;
  font-size: .96rem;
  transition: color .2s ease;
}

/* Chevron toggle, injected by js/footer-nav.js. */
.np-acc-toggle {
  flex: 0 0 auto;
  width: 2rem;
  height: 2rem;
  display: grid;
  place-items: center;
  padding: 0;
  border: 0;
  border-radius: 50%;
  background: transparent;
  color: var(--np-chrome-ink);
  cursor: pointer;
  transition: transform .24s ease, color .2s ease;
}
/*
Hover and press only -- no focus highlight, as requested. `:focus` still has to be
declared, pinned back to the resting look, because Kadence paints bare buttons orange on
focus and these keep focus after a click. The browser's own focus ring is left intact
rather than suppressed, so keyboard users can still see where they are.
*/
.np-acc-toggle:hover,
.np-acc-toggle:active {
  background: var(--np-chrome-hover-bg);
  color: var(--np-chrome-accent);
}
.np-acc-toggle:focus:not(:hover):not(:active) {
  background: transparent;
  color: var(--np-chrome-ink);
}
.np-footer-menu > li.np-open > .np-acc-toggle {
  transform: rotate(180deg);
}

/* Collapsed only once the script has marked the item -- without JS the children stay
   visible instead of being trapped behind a button that cannot open. */
.np-footer-menu > li.np-has-sub > .sub-menu {
  display: none;
}
.np-footer-menu > li.np-has-sub.np-open > .sub-menu {
  display: block;
}
.np-footer-menu a:hover,
.np-footer-menu a:focus-visible {
  color: var(--np-chrome-accent);
}
/* Sub-menu items sit under their parent as a plain indented list; the header's dropdown
   behaviour is irrelevant here, so any absolute positioning from it is undone. */
.np-footer-menu .sub-menu {
  list-style: none;
  position: static;
  display: block;
  opacity: 1;
  visibility: visible;
  transform: none;
  box-shadow: none;
  background: none;
  width: auto;
  min-width: 0;
  /* No left inset: child links line up with the accordion titles above them. */
  padding: 0 0 .35rem;
  margin: 0;
}
/* Divider above every child, which also separates the first one from its title row. */
.np-footer-menu .sub-menu > li {
  border-top: 1px solid var(--np-chrome-rule);
}
.np-footer-menu .sub-menu a {
  display: block;
  padding: .72rem 0;
  color: var(--np-chrome-ink-muted);
  font-size: .98rem;
}
.np-footer-menu .dropdown-nav-toggle,
.np-footer-menu .dropdown-nav-special-toggle {
  display: none;   /* no dropdowns to toggle in a static list */
}

/* Darker navy band across the very bottom. Kadence's own .site-bottom-footer-wrap is
   already full width at the viewport's left edge, so it can take the background
   directly -- no negative margins or box-shadow spread required. */
.site-footer .site-bottom-footer-wrap {
  background: var(--np-chrome-footer-bottom-bg);
}
.np-copyright {
  margin: 0;
  text-align: left;
  color: var(--np-chrome-ink);
  font-size: .9rem;
}
/* Kadence centres the footer info area by default; the copyright is asked to sit left. */
.site-footer .site-info,
.site-footer .site-info-inner {
  text-align: left;
}

/* ---------- Footer (brand colours) ---------- */
.site-footer, .site-footer-wrap { background: var(--np-chrome-footer-bg); color: var(--np-chrome-ink); }
.site-footer a { color: var(--np-chrome-ink-muted); }
.site-footer a:hover { color: var(--np-chrome-accent); }
.site-footer .widget-title { color: var(--np-chrome-ink-strong); }

/* The "2027 AWARDS" eyebrow this styled is now an <h1>, so the selector matched nothing;
   removed rather than left as a dead rule. Its replacement is the NSFF card block. */
.page-id-31017 .wp-block-buttons { justify-content: center; }

/* ---------- Lightbox (sitewide) ---------- */
/*
Started life as a listicle-only feature; moved here once it was asked for on every article
image sitewide, not just those. One shared modal (js/lightbox.js prints and drives it, see
functions.php for where) and one shared trigger class, `.np-lightbox-trigger`, used by
whatever puts an image in front of a reader: this file's own `.entry-content img` auto-wiring
for ordinary post/page content, and the listicle template's own richer, per-photo triggers
(nsp_listicle_lightbox_trigger() in inc/listicles.php, which knows a real caption and the
attachment's full-size URL that a generic content image doesn't carry). Never wired onto an
image that's already a link to somewhere else -- opening a photo full-size and navigating
away are two different clicks, and a reader who follows a thumbnail expects to land on the
page it links to, not a bigger copy of the thumbnail itself.

The icon is Feather's own `maximize` glyph (the corner-bracket frame) at this site's usual
stroke weight, not a magnifying glass or some other library's take on "expand".

Hover/mouse and touch get two different affordances on purpose. A hover-revealed icon over
a dark scrim (`@media (hover: hover)`) is the expected desktop convention; on a touchscreen
that would either need a tap to reveal before the tap that opens it, or would leave the
photo permanently darkened, so `@media (hover: none)` pins a small badge in the corner
instead, always visible, without touching the rest of the image.
*/
.np-lightbox-trigger {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  padding: 0;
  border: 0;
  background: transparent;
  cursor: pointer;
  color: #fff;
}
@media (hover: hover) {
  .np-lightbox-trigger {
    display: flex;
    align-items: center;
    justify-content: center;
    opacity: 0;
    background: rgba(12, 16, 26, 0);
    transition: opacity .15s, background-color .15s;
  }
  .np-lightbox-trigger svg { width: 30px; height: 30px; filter: drop-shadow(0 1px 3px rgba(0,0,0,.5)); }
  /*
  `.np-lightbox-wrap:hover` alone missed the listicle template's own photos: those sit
  inside `.nspl-photo`/`.nspl-slide` (already their own positioned box, for the fixed-ratio
  crop -- see listicle.css), never inside a `.np-lightbox-wrap`, which only js/lightbox.js's
  auto-detected content images get. Both container classes named here so hovering either
  kind of photo reveals its own trigger.
  */
  .np-lightbox-wrap:hover .np-lightbox-trigger,
  .nspl-photo:hover .np-lightbox-trigger,
  .nspl-slide:hover .np-lightbox-trigger,
  .np-lightbox-trigger:focus-visible {
    opacity: 1;
    background: rgba(12, 16, 26, .32);
  }
  .np-lightbox-trigger:focus { outline: none; }
  .np-lightbox-trigger:focus-visible { outline: 2px solid #fff; outline-offset: -4px; }
}
@media (hover: none) {
  .np-lightbox-trigger { background: transparent; }
  .np-lightbox-trigger svg {
    position: absolute;
    right: 10px;
    bottom: 10px;
    width: 16px;
    height: 16px;
    padding: 6px;
    box-sizing: content-box;
    border-radius: 50%;
    background: rgba(12, 16, 26, .45);
  }
}
/* The wrapper `js/lightbox.js` inserts around each auto-detected content image, sized to
   the image itself (inline-block shrinks to fit it) so the trigger's `inset: 0` lines up
   with the photo and nothing wider that happens to contain it. The listicle template
   supplies its own equivalent box (`.nspl-photo`/`.nspl-slide`, already `position: relative`
   in listicle.css) rather than needing this wrapper, since those are already sized to the
   photo for unrelated reasons (the fixed-ratio crop). */
.np-lightbox-wrap { position: relative; display: inline-block; max-width: 100%; }

/*
The modal itself: one per page, printed once in wp_footer and filled in by whichever
trigger was clicked. `visibility` alongside `opacity` so the closed state is also
unreachable by keyboard/screen-reader focus, not just invisible.
*/
.np-lightbox {
  position: fixed;
  inset: 0;
  z-index: 9999;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 2rem;
  opacity: 0;
  visibility: hidden;
  transition: opacity .2s ease;
}
.np-lightbox.is-open { opacity: 1; visibility: visible; }
.np-lightbox-backdrop { position: absolute; inset: 0; background: rgba(10, 13, 20, .9); }
.np-lightbox-figure {
  position: relative;
  z-index: 1;
  max-width: 100%;
  max-height: 100%;
  margin: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
}
.np-lightbox-img {
  display: block;
  max-width: 100%;
  max-height: calc(100vh - 8rem);
  width: auto;
  height: auto;
  border-radius: var(--np-img-radius);
  object-fit: contain;
}
.np-lightbox-credit {
  font-size: .8rem;
  letter-spacing: .04em;
  color: rgba(255, 255, 255, .78);
  margin: .85rem 0 0;
}
.np-lightbox-close {
  position: absolute;
  top: 1.25rem;
  right: 1.25rem;
  z-index: 2;
  width: 40px;
  height: 40px;
  display: flex;
  align-items: center;
  justify-content: center;
  border: 0;
  border-radius: 50%;
  background: rgba(255, 255, 255, .12);
  color: #fff;
  cursor: pointer;
  transition: background .15s;
}
.np-lightbox-close svg { width: 18px; height: 18px; }
.np-lightbox-close:hover, .np-lightbox-close:focus-visible { background: rgba(255, 255, 255, .22); }
.np-lightbox-close:focus { outline: none; }
.np-lightbox-close:focus-visible { outline: 2px solid #fff; outline-offset: 2px; }
body.np-lightbox-lock { overflow: hidden; }

/* ---------- Focus states (sitewide) ---------- */
/*
Kadence's own default focus styling -- the browser's native outline where Kadence sets
none of its own, plus a scattering of its own box-shadow rings on specific components --
fires on every mouse click, not just real keyboard navigation, and has been fought
component-by-component all session (the carousel arrows, the Guide Card flip button, each
needing their own `:focus { outline: none }` / `:focus-visible { outline: ... }` pair).
One sitewide rule instead of another one-off every time a new interactive element gets
built: `:focus-visible` is the real, spec-defined "this was reached by keyboard (or
otherwise merits a visible indicator)" state, so silencing plain `:focus` and drawing the
indicator only on `:focus-visible` removes Kadence's default everywhere at once without
removing keyboard accessibility. `!important` on purpose -- this is deliberately a hard
override of whatever a specific Kadence or Kadence Blocks component ships, not a normal-
specificity rule meant to coexist with them.

`box-shadow: none` used to be part of both halves below, and was removed: `box-shadow` is
one property, and this rule cannot tell "an ugly Kadence focus-ring shadow" from "a
component's own decorative shadow" apart -- it silenced both, sitewide, every time
anything was clicked or tabbed to. The category-carousel arrows' own drop shadow
(".np-carousel-btn") was the confirmed case, but the mechanism could reach any future
component's shadow the same way. A specific Kadence component's own box-shadow-based
focus ring, if one is ever found bleeding through, gets handled at that component instead
(the way ".np-carousel-btn" already re-asserts its own shadow) -- not by another blanket
rule that goes back to fighting every component's shadow at once.
*/
:focus:not(:focus-visible) {
  outline: none !important;
}
:focus-visible {
  outline: 2px solid var(--np-focus-ring) !important;
  outline-offset: 2px !important;
}

/* ---------- Neutralise Kadence's orange button hover/focus/active ---------- */
/*
Kadence's own global.min.css carries one generic rule reaching every bare button, .button,
.wp-block-button__link, .wp-element-button and input[type=button/reset/submit] sitewide:

  button:hover, button:focus, button:active, ... {
    color: var(--global-palette-btn-hover);          -- resolves to #ffffff
    background: var(--global-palette-btn-bg-hover);  -- resolves to #e5892a, this site's own orange
    box-shadow: 0px 15px 25px -7px rgba(0,0,0,.1);
  }

Confirmed live: both variables resolve to this theme's own accent orange (--np-orange,
literally the same #e5892a) and white -- not a colour anyone chose for this purpose, just
Kadence's own default customizer palette slot. It bleeds through on any button that
doesn't happen to have a more specific hover/focus/active rule of its own, most recently
the listicle carousel's own arrow buttons (.nspl-caro-nav has :hover/:focus-visible/:focus
rules but never needed an :active one, since nothing used to press it -- this rule filled
that gap with orange). The exact same problem was already fought once, per-component, for
the Family Favorites buttons (see ".np-nsff .ff-public-action" further up, and its own
comment about `color: var(--global-palette-btn-hover)` resolving to white) -- fixed at the
one shared source here instead of finding and patching every future component that forgets
its own :active rule.

Overriding the two variables Kadence's rule reads from, not the rule itself: this makes
the rule a no-op everywhere it would otherwise fire (transparent ground, inherited text
colour -- effectively "no visible change") without an ever-growing set of higher-specificity
counter-rules racing it component by component. Any button that WANTS its own hover/focus/
active colour still gets it -- those rules set color/background explicitly and are
unaffected by what these two variables hold.
*/
:root {
  --global-palette-btn-hover: inherit;
  --global-palette-btn-bg-hover: transparent;
}

/* ---------- Image fade-in on load ---------- */
/*
Content and post images fade in once loaded rather than popping in abruptly -- pairs
naturally with lazy-loading (already active sitewide by default: WordPress core adds
`loading="lazy"` to content, thumbnail and card images on its own, and nothing in this
theme turns that off -- see js/img-fade.js's own comment). A lazy image's fade starts the
moment it scrolls into view and finishes downloading, instead of snapping in.

Scoped to the same three reader-facing contexts js/img-fade.js watches, deliberately NOT a
bare `img` selector: in-post body images (`.entry-content img`, which also covers listicle
photos -- they render inside `.entry-content` too), the single-post hero
(`.post-thumbnail.article-post-thumbnail img`), and every card thumbnail -- archive,
homepage, "Similar Posts" -- (`.wp-block-post-featured-image img`). Site chrome (the logo,
nav icons, comment avatars) sits outside all three ancestors already, confirmed by their
actual markup, so nothing here needs an extra `:not()` to keep them out. An unscoped `img`
selector matching things it shouldn't -- the lightbox's own hidden placeholder, comment
avatars -- is exactly what broke an earlier image feature this same site had; these three
are the same selectors already proven safe elsewhere (this file's own image-width rule,
js/lightbox.js's own scoping).

Gated on `.js`, added to `<html>` by a tiny inline script in wp_head (functions.php) that
runs before the page body starts rendering. Without it, `opacity: 0` would apply
unconditionally and any image this rule reached would stay invisible forever if JavaScript
never runs (disabled, blocked, or an unrelated script error on the page) -- .js is only
ever added by that inline script itself succeeding, so the opacity rule and the class that
removes it live or die together.
*/
.js .entry-content img,
.js .single .post-thumbnail.article-post-thumbnail img,
.js .wp-block-post-featured-image img {
  opacity: 0;
  transition: opacity var(--np-img-fade-duration) ease;
}
.js .entry-content img.np-img-loaded,
.js .single .post-thumbnail.article-post-thumbnail img.np-img-loaded,
.js .wp-block-post-featured-image img.np-img-loaded {
  opacity: 1;
}

/* ---------- Blog post hero ---------- */
/*
A regular blog post's featured image, stacked above its title/category/meta -- the two-
column grid this used to be, its own full-bleed light blue-grey band, and the wider
--global-content-width measure are all scrapped in favour of matching the article body
exactly: same width, same horizontal padding, no background of its own. Built by
$np_use_hero in template-parts/content/single-entry.php.

The listicle template shares this component now too ($np_use_hero_media, same file), image
and category chips AND its own eyebrow/title/dek/meta/intro (nsp_listicle_render_header(),
inc/listicles.php) -- printed inside .np-post-hero-info the same way entry_header is for a
regular post, not built separately by a `the_content` filter and left to fight the article
body's own spacing for room. Every rule below (ratio, mobile/tablet behaviour including the
full-bleed breakout, the 2rem gap to whatever comes after) applies to both identically, same
class, no plain-post-only carve-out -- reusing this component's CSS unchanged is the point,
not a parallel copy for the listicle to drift from. `.np-post-hero--listicle` exists only
for the one genuine design difference left: nsp_listicle_render_header()'s own header
(listicle.css) is centred at every width, so the chips beside it centre too, near the chip
styling further down.

No width rule at all on `.np-post-hero`: it renders before <article>, inside the same
already-940px-constrained `.content-container.site-container` the article body does, so it
inherits that width automatically, the same way the body column does -- nothing to match
by hand.
*/
.np-post-hero {
  /* Horizontal matches .entry-content-wrap's own padding at this width (32px, down to
     768px) rather than a value of its own, so the image and title line up with the body
     copy under them instead of sitting at a different inset. Below 767px this stops being
     true -- see that media query -- because the article's own -16px margin trick doesn't
     apply here; 32px is correct everywhere from 768px up, confirmed live (hero and article
     text share the same inset at both tablet and desktop widths). Top padding is this
     component's own -- there's nothing below the navbar to match, only clear space above
     the image so it isn't flush against it. */
  padding: 2.5rem 32px 0;
  margin: 0 0 2rem;
}
@media (max-width: 1024px) {
  /* Smaller top padding at tablet and below, matching the same "less space on a smaller
     screen" step the horizontal padding already takes at 767px. Every piece of hero
     content centres too -- category chips, title, meta -- since a stacked, single-column
     layout reads better centred than left-aligned at this width, the same choice this
     component made before the grid was scrapped. */
  .np-post-hero {
    padding: 1.5rem 32px 0;
    text-align: center;
  }
  .np-post-hero-cats,
  .np-post-hero-info .entry-meta {
    justify-content: center;
  }
}
@media (max-width: 767px) {
  /* Not 16px, unlike .entry-content-wrap at this same width: .np-post-hero sits directly
     in .content-container with no compensating negative margin, and .content-container's
     own padding is already 24px here on its own -- the article body only nets to 24px
     itself because entry.content-bg's -16px margin cancels part of that 24px first, then
     entry-content-wrap's own 16px padding adds back on top. Giving .np-post-hero that same
     16px would double it on top of .content-container's 24px (40px total, measured live)
     instead of replacing it, so this is 0: .content-container's 24px alone already matches
     the article's inset exactly. */
  .np-post-hero {
    /* All four sides now, not just top: this rule used to be a `padding-top` longhand
       only, on the assumption the horizontal zeroing above was still in effect from
       elsewhere -- it wasn't. A longhand only ever touches the one property it names, so
       the max-width:1024px block's own 32px horizontal value (a *separate*, earlier media
       query, still active at this width too) was never actually overridden here, and every
       piece of .np-post-hero-info's content -- the listicle's eyebrow/title/dek/meta/intro
       included -- was silently squeezed into a 32px-narrower column than the chips above
       it. Confirmed live: .np-post-hero computed 32px left/right padding at 375px width,
       and its content measured 263px wide against the chips' own 327px. `padding: 0`
       covers top (full-bleed, no gap above the image), horizontal (matches
       .content-container's inset, per the comment above), and bottom (always 0 already,
       inherited unchanged from the 1024px block). */
    padding: 0;
  }
  /* Same full-bleed-breakout technique used elsewhere on the site (safe because .site sets
     overflow:clip): 100vw centred via left:50%/translateX(-50%) escapes .content-container's
     own inset instead of just matching it, so the image runs edge to edge with nothing
     beside it, not merely as wide as the text column above/below it. */
  .np-post-hero-media {
    position: relative;
    left: 50%;
    width: 100vw;
    transform: translateX(-50%);
  }
}
.np-post-hero-media {
  margin-bottom: 1.5rem;
}
/*
The old single-image hero's own treatment (full-bleed viewport breakout, its own 3:1
ratio) doesn't apply here: this is an ordinary image in the normal content column now, not
a full-bleed band, so every one of those properties is reset. Scoped to `.np-post-hero`
specifically, so the listicle template and anything else still using the original single-
image hero (`.single .post-thumbnail.article-post-thumbnail`, further up this file) are
completely untouched.
*/
.np-post-hero .post-thumbnail.article-post-thumbnail {
  position: relative;
  width: 100% !important;
  max-width: none !important;
  height: auto !important;
  min-height: 0 !important;
  padding: 0 !important;
  margin: 0 !important;
  left: auto;
  transform: none;
  aspect-ratio: 16 / 9;
  border-radius: var(--np-hero-radius);
  overflow: hidden;
}
@media (max-width: 767px) {
  /*
  Has to come after the base rule immediately above, not inside the other max-width:767px
  block further up this section: that block and this rule target the exact same selector
  and property at equal specificity (three classes each), so whichever comes LAST in the
  file wins regardless of which one is inside the active media query -- the base rule above
  used to be the one written later, silently beating this override at every width including
  mobile. Confirmed live: computed border-radius stayed 14px at a 375px viewport despite this
  rule's condition genuinely matching (window.matchMedia('(max-width: 767px)').matches was
  true) -- the override was simply losing the cascade, not failing to apply.

  Corners only read as rounded against a background on both sides; flush with the viewport
  edge, they would just clip the image's own corner pixels into a diagonal cut instead.
  */
  .np-post-hero .post-thumbnail.article-post-thumbnail {
    border-radius: 0;
  }
}
/*
Category "chips" -- see template-parts/title/categories.php for why each one is a plain
`.np-tag`. Ink colour is left at `.np-tag`'s own fallback (--np-dark-blue), which already
matches this site's category colour; only the ground (--np-tag-bg) is overridden below, to
--np-hero-chip-bg rather than the default --np-aqua-4, since the default sits too close in
lightness to page white to read as a distinct pill against it. `.np-tag` itself only ever
sets colour/font-family/border -- shape (size, padding, radius) is left to whoever uses it,
by design (see that rule's own comment in this file) -- so the shape properties below are
copied verbatim from the listicle's own "Perfect For" chip (`.nspl-chips
li:not(.nspl-chips-lbl)` in listicle.css) rather than chosen fresh, for the "exact same
styling" this was asked for.
*/
.np-post-hero-cats {
  display: flex;
  flex-wrap: wrap;
  gap: .45rem;
  margin-bottom: .5rem;
}
.np-post-hero-cats .np-tag {
  --np-tag-bg: var(--np-hero-chip-bg);
  font-weight: 600;
  font-size: .74rem;
  letter-spacing: .04em;
  border-radius: 999px;
  padding: .3em .9em;
  /* The listicle's own chips are plain <li> text, never links, so text-decoration never
     came up there -- these are real <a href> tags to the category archive and need it
     said explicitly. */
  text-decoration: none;
}
/*
Kadence's own `a:hover, a:focus, a:active { color: var(--global-palette-highlight-alt) }`
is (0,1,1) -- higher than the shared `.np-tag` rule that actually paints this chip's colour
(a bare `.np-tag`, (0,1,0)), so on hover it would otherwise win regardless of what
`.np-post-hero-cats .np-tag`'s own (0,2,0) sets, since that selector never touches `color`
itself (only shape). Overwritten completely here instead of layered on top of it: an
explicit background and colour, not a token swap on `--np-tag-bg`/`--np-tag-ink` that the
shared rule would still lose the cascade race to apply. `!important` because "completely
overwrite" was the ask, not just outrank it by one specificity tier.
*/
.np-post-hero-cats .np-tag:hover,
.np-post-hero-cats .np-tag:focus-visible {
  background: var(--global-palette3) !important;
  color: #fff !important;
}

/*
The only remaining listicle-only difference: nsp_listicle_render_header()'s own header
(.nspl-head, listicle.css) is centred at every width, not just tablet/mobile like the
plain-post hero's title -- a real, deliberate design choice, not a structural workaround --
so the chips beside it centre too, to match. No margin override needed here any more: now
that the header itself renders inside .np-post-hero-info (single-entry.php) instead of
separately inside the article, .np-post-hero's own shared 2rem bottom margin is the actual
gap between this whole component -- image, chips, eyebrow, title, dek, meta, intro -- and
the listicle's body items below it, same role that margin already plays for a regular post.
*/
.np-post-hero--listicle .np-post-hero-cats {
  justify-content: center;
}

