/*
Theme Name: NP Theme
Theme URI: https://nationalparkgetaways.com
Description: The parent theme for the National Park network. Layout, components and the Page Builder contract; no brand. Child themes supply colour, type, logo and region ownership through NP_Brand.
Author: Tom Tash
Version: 0.1.0
Requires PHP: 7.4
Text Domain: np-theme
*/

/* ==========================================================================
   0. Fonts — self-hosted
   Google Fonts is a render-blocking stylesheet on two extra origins: the
   browser must do DNS + TLS + fetch + parse before it can paint anything.
   PageSpeed measured ~1,772 ms on mobile for that alone on Nor'East.
   ../README.md §2.1. It also stops handing every visitor's IP to Google.

   These are the LATIN subset, VARIABLE-weight builds: one file per family
   covers 400 through 700, so the whole type system is 80 KB in two requests
   rather than five files. Both are SIL OFL 1.1 — see assets/fonts/LICENSE.txt.
   ========================================================================== */

@font-face {
  font-family: 'Source Sans 3';
  font-style: normal;
  font-weight: 400 700;
  font-display: swap;
  src: url('assets/fonts/source-sans-3-latin-var.woff2') format('woff2');
  /* U+0000-00FF etc. — the latin subset these files actually contain. Naming
     it stops the browser downloading them for a page with no latin glyphs. */
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
                 U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+2074, U+20AC,
                 U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
  font-family: 'Source Serif 4';
  font-style: normal;
  font-weight: 400 700;
  font-display: swap;
  src: url('assets/fonts/source-serif-4-latin-var.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
                 U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+2074, U+20AC,
                 U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

/* ==========================================================================
   1. Reset and the two rules that are not optional
   ========================================================================== */

*, *::before, *::after { box-sizing: border-box; }

/*
 * The `hidden` attribute's ONLY enforcement is a UA stylesheet rule of
 * display:none, which ANY author declaration outranks. So `.thing{display:grid}`
 * leaves an element marked hidden permanently on screen, and `el.hidden = true`
 * appears to do nothing whatsoever. This exact bug put "Nobody matches those
 * filters" over a fully populated canvas. ../README.md §5.
 */
[hidden] { display: none !important; }

/*
 * <picture> creates a box that breaks object-fit sizing on anything inside a
 * fixed-ratio container. ../README.md §2.3.
 */
picture { display: contents; }

img, svg, video { max-width: 100%; height: auto; }
/* ...but a canvas drawing whose width is set explicitly must not be squashed. */
svg.np-canvas { max-width: none; }

body, h1, h2, h3, h4, h5, h6, p, figure, blockquote, dl, dd, ul, ol { margin: 0; }
ul[class], ol[class] { list-style: none; padding: 0; }

/* ==========================================================================
   2. Tokens
   The child theme overrides the --np-brand-* and --np-font-* values only.
   Everything else is the platform's, shared by every site.
   ========================================================================== */

:root {
  /* Brand hooks — neutral until a child theme registers a palette. NP_Brand
     also emits --np-<slug> / --np-on-<slug> for every swatch it holds. */
  --np-brand:        var(--np-primary, #1f4d3a);
  --np-on-brand:     var(--np-on-primary, #ffffff);
  --np-accent:       var(--np-secondary, #c2703d);
  --np-on-accent:    var(--np-on-secondary, #ffffff);

  /* Neutrals */
  --np-ink:          #161a17;
  --np-ink-soft:     #5a6460;   /* 5.31:1 on --np-paper. Checked, not guessed. */
  --np-line:         #dfe3e0;
  --np-paper:        #ffffff;
  --np-paper-soft:   #f6f7f5;

  /* Type — the child sets the families; these are the fallbacks. */
  /* The fallback stack is not decoration: font-display:swap paints in it
     first, so a stack that metrically mismatches causes a visible reflow. */
  --np-font-body:    var(--np-body-stack, 'Source Sans 3', ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, Helvetica, Arial, sans-serif);
  --np-font-display: var(--np-display-stack, 'Source Serif 4', Georgia, "Times New Roman", serif);

  /* A modular scale, so a heading is never a one-off pixel value. */
  --np-text-xs:   0.8125rem;
  --np-text-sm:   0.9375rem;
  --np-text-base: 1.0625rem;
  --np-text-lg:   1.1875rem;
  /* The deck / standfirst. The scale used to go 3.5rem → 2rem → 1.06rem with
     nothing in between, so a hero's supporting line was the same size as body
     copy and the page had one voice. DESIGN-DEBT.md §7. */
  --np-text-deck: 1.3125rem;
  --np-text-xl:   1.5rem;
  --np-text-2xl:  2rem;
  --np-text-3xl:  2.75rem;
  --np-text-4xl:  3.5rem;

  /*
   * Space scale — the 'space' choice scale maps onto these.
   *
   * The vw terms used to be 4/7/10 with an 8rem ceiling, and the arithmetic is
   * what made the page read as broken: at desktop the hero contributed 8rem
   * below itself and the next band 5rem above, so ~208px of nothing sat between
   * them — and the 10vw term meant it GREW on wide screens, exactly where it was
   * already too much. Generous spacing and an empty page look identical from
   * far enough away. DESIGN-DEBT.md §4.
   */
  --np-space-sm: clamp(1.75rem, 3vw, 2.5rem);
  --np-space-md: clamp(2.75rem, 4.5vw, 4rem);
  --np-space-lg: clamp(4rem, 6vw, 6.5rem);

  /* Content widths — the 'width' choice scale. */
  --np-wrap-sm: 44rem;
  --np-wrap-md: 68rem;
  --np-wrap-lg: 84rem;
  --np-gutter:  clamp(1.25rem, 4vw, 2.5rem);

  /*
   * THE HERO SCRIM ALPHA. Arithmetic, not taste.
   *
   * A photograph can be any luminance, so legible overlaid text needs a known
   * amount of darkening and a worst case: a pure white photograph. Compositing
   * black at alpha A over white leaves sRGB 255(1-A). White text at 4.5:1 needs
   * that composite at or below sRGB 119, so A >= 0.535. This is 0.58 for
   * margin, and tools/contrast.php recomputes the composite rather than
   * trusting the number. Lower it and the gate fails.
   */
  --np-scrim-min: .58;

  --np-radius: 10px;
  --np-shadow: 0 1px 2px rgba(22,26,23,.06), 0 8px 24px rgba(22,26,23,.07);

  /*
   * One documented stacking scale, in one place.
   * z-index is capped by the nearest positioned ancestor with a z-index: a
   * sticky header at 60 creates a stacking context, so a drawer INSIDE it can
   * never beat a sibling backdrop at 65 however high its own z-index. Symptom:
   * the menu looks dimmed and taps do nothing, because the backdrop is
   * receiving them. ../README.md §5.
   */
  --np-z-rail:     10;
  --np-z-header:   60;
  --np-z-backdrop: 70;
  --np-z-drawer:   80;

  /*
   * The one number in this file that is deliberately outside the scale.
   *
   * The scale above orders the THEME's own layers, and 90 was right for that.
   * But the consent bar has to outrank things the theme does not control:
   * bloggy-trip-planner's floating CTA sits at 9999999 and its overlay at
   * 99999999, and on the live site that tab covered the banner's body text —
   * a consent notice a visitor cannot fully read is not a consent notice.
   *
   * So this is not "a bigger number to win an argument", it is the one
   * component whose whole job requires it to be on top of everything. It stays
   * correct after bloggy-trip-planner is retired (PLUGIN-AUDIT.md §5 step 7)
   * and after whatever gets installed next.
   */
  --np-z-consent:  2147483000;
}

/* ==========================================================================
   3. Base
   ========================================================================== */

html { -webkit-text-size-adjust: 100%; }

body {
  margin: 0;
  font-family: var(--np-font-body);
  font-size: var(--np-text-base);
  line-height: 1.65;
  color: var(--np-ink);
  background: var(--np-paper);
  -webkit-font-smoothing: antialiased;
}

h1, h2, h3, h4 {
  font-family: var(--np-font-display);
  line-height: 1.15;
  text-wrap: balance;
  font-weight: 600;
}

h1 { font-size: var(--np-text-3xl); letter-spacing: -0.02em; }
h2 { font-size: var(--np-text-2xl); letter-spacing: -0.01em; }
h3 { font-size: var(--np-text-xl); }
h4 { font-size: var(--np-text-lg); }

a { color: inherit; }

/*
 * TWO-TONE focus ring. Not decoration — a single-colour ring is invisible on
 * some band somewhere, and you will never notice because you use a mouse.
 *
 * Measured: the accent ring against a filled band was 2.13:1 on Explore Maine's
 * spruce, 1.05:1 for Visit Southwest's sky on canyon (indistinguishable), and
 * 2.23:1 on Getaways' basalt. All three brands, all failing WCAG 2.4.11's 3:1
 * for non-text contrast, and all three passing every check that only looked at
 * text.
 *
 * The fix is a light ring inside a dark one. Whatever colour the band behind it
 * is, one of the two contrasts with it, and the two contrast with each other
 * (ink vs paper is 17:1 in every brand). Brand-neutral by construction, so a
 * new region cannot reintroduce this.
 */
:focus-visible {
  outline: 3px solid var(--np-ink);
  outline-offset: 3px;
  box-shadow: 0 0 0 3px var(--np-paper);
  border-radius: 2px;
}

/* Inside a filled band the roles swap, so the ring stays a light/dark pair
   rather than becoming ink-on-ink. */
.np-section--filled :focus-visible {
  outline-color: var(--np-paper);
  box-shadow: 0 0 0 3px var(--np-ink);
}

.np-skip {
  position: absolute;
  left: -9999px;
  top: 0;
  background: var(--np-paper);
  color: var(--np-ink);
  padding: .75rem 1.25rem;
  z-index: var(--np-z-drawer);
}
.np-skip:focus { left: 0; }

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

/* ==========================================================================
   4. Layout — sections, wraps
   ========================================================================== */

.np-wrap {
  width: 100%;
  margin-inline: auto;
  /* padding-block, never the shorthand — the side gutter must survive. */
  padding-inline: var(--np-gutter);
}
.np-wrap--sm { max-width: var(--np-wrap-sm); }
.np-wrap--md { max-width: var(--np-wrap-md); }
.np-wrap--lg { max-width: var(--np-wrap-lg); }

.np-section { padding-block: var(--np-space-md); }
.np-section--space-sm { padding-block: var(--np-space-sm); }
.np-section--space-lg { padding-block: var(--np-space-lg); }
.np-section--align-center { text-align: center; }

/*
 * ADJACENT BANDS MUST NEVER SUM THEIR PADDING.
 *
 * Two filled bands are one continuous colour; two unfilled bands are one
 * continuous sheet of white. In both cases the space between them is ONE
 * space, and the band above owns it. Only the filled+filled half of this
 * existed, which is why the gaps between the white bands were double.
 *
 * A filled band meeting an unfilled one is deliberately NOT collapsed: there
 * the padding is the coloured band's internal breathing room and the boundary
 * is visible, so the two are not reading as one surface.
 */
.np-section--filled + .np-section--filled { padding-block-start: 0; }
.np-section:not(.np-section--filled):not(:has(> .np-wrap > .np-hero--media)) + .np-section:not(.np-section--filled) { padding-block-start: 0; }

/*
 * ...but a MEDIA HERO is excluded from being the band above, because it has no
 * bottom padding to donate — the bleed rule set its padding to zero. Without
 * this exclusion the band after a full-bleed photograph collapsed to nothing
 * and its first line sat tight against the image. It does not show on the front
 * page, where the hero is followed by a filled band; it showed on a park page.
 */

/*
 * The first band on the page is asymmetric. It wants air beneath the sticky
 * header and ordinary space below it; symmetrical padding-block is what gave
 * the hero 8rem of nothing underneath before the next band had even started.
 */
#np-main > .np-section--space-lg:first-child { padding-block-end: var(--np-space-md); }

/* ==========================================================================
   5. Prose
   ========================================================================== */

.np-prose { max-width: 40rem; }
.np-prose > * + * { margin-block-start: 1.1em; }
.np-prose h2 { margin-block-start: 2em; }
.np-prose h3 { margin-block-start: 1.6em; }
.np-prose ul, .np-prose ol { padding-inline-start: 1.4em; }
.np-prose li + li { margin-block-start: .4em; }

/*
 * Links in running text must not rely on colour alone. Nav, buttons and card
 * links are exempt — they are distinguishable by shape and position.
 * ../README.md §4.
 */
.np-prose a {
  text-decoration: underline;
  text-underline-offset: 2px;
  text-decoration-thickness: 1px;
}

.np-prose blockquote {
  border-inline-start: 3px solid var(--np-accent);
  padding-inline-start: 1.25rem;
  font-size: var(--np-text-lg);
}

.np-prose figure { margin-block: 2em; }
.np-prose figcaption {
  font-size: var(--np-text-sm);
  color: var(--np-ink-soft);
  margin-block-start: .5em;
}

/* ==========================================================================
   6. The article layout and its sticky planner rail
   The rail is the funnel. Every article carries it. REBUILD-PLAN.md §3.4.
   ========================================================================== */

.np-article {
  display: grid;
  gap: clamp(2rem, 5vw, 4rem);
  grid-template-columns: 1fr;
}

@media (min-width: 62rem) {
  .np-article { grid-template-columns: minmax(0, 1fr) 20rem; }
}

.np-rail { position: relative; z-index: var(--np-z-rail); }

@media (min-width: 62rem) {
  .np-rail__inner { position: sticky; top: 5.5rem; }
}

.np-planner {
  border: 1px solid var(--np-line);
  border-radius: var(--np-radius);
  padding: 1.5rem;
  background: var(--np-paper-soft);
}
.np-planner h2 { font-size: var(--np-text-lg); }
.np-planner p { margin-block-start: .5rem; font-size: var(--np-text-sm); color: var(--np-ink-soft); }
.np-planner__note { font-size: var(--np-text-xs); }

/* ==========================================================================
   7. Components
   ========================================================================== */

.np-btn {
  display: inline-block;
  padding: .8em 1.5em;
  border: 1px solid transparent;
  border-radius: var(--np-radius);
  font-weight: 600;
  font-size: var(--np-text-sm);
  text-decoration: none;
  cursor: pointer;
  transition: transform .15s ease, opacity .15s ease;

  /*
   * A <button> inherits NONE of this on its own. The UA gives it buttonface
   * chrome and its own font family, and neither is inherited from an ancestor
   * the way it is on every other element -- so .np-btn, written and eyeballed
   * on <a>, looked correct everywhere it was tested and silently fell back to
   * a grey system button wherever a real <button> used it.
   *
   * Live on this site right now: the trip plan's Print button, and (until this
   * line) the consent banner's Reject. The Reject case is the one that matters
   * -- a reject control that renders as default browser chrome next to a
   * designed Accept button reads as the discouraged option, which is the exact
   * dark pattern the banner is written to avoid.
   *
   * Safe to set on the base: --primary and --accent declare their own
   * background and colour after this and still win. `a { color: inherit }` is
   * already the rule (§3), so this changes nothing for the anchor cases.
   */
  background: transparent;
  color: inherit;
  font-family: inherit;
}
.np-btn:hover { transform: translateY(-1px); opacity: .94; }
.np-btn--primary { background: var(--np-brand); color: var(--np-on-brand); }
.np-btn--accent  { background: var(--np-accent); color: var(--np-on-accent); }
.np-btn--ghost   { border-color: currentColor; }
.np-btn--block   { display: block; width: 100%; text-align: center; }

.np-grid { display: grid; gap: clamp(1.25rem, 3vw, 2rem); }

/*
 * A grid named --3 IS three columns at desktop.
 *
 * auto-fit alone gave four at 1280px, so a six-card grid wrapped to a row of
 * four and two orphans — which reads as a mistake rather than as a layout, and
 * is what DESIGN-DEBT.md §7 meant by "the worst of both". auto-fit still does
 * the work below the breakpoint, where the count genuinely should vary.
 */
.np-grid--2 { grid-template-columns: repeat(auto-fit, minmax(min(100%, 20rem), 1fr)); }
.np-grid--3 { grid-template-columns: repeat(auto-fit, minmax(min(100%, 16rem), 1fr)); }

@media (min-width: 62rem) {
  .np-grid--2 { grid-template-columns: repeat(2, 1fr); }
  .np-grid--3 { grid-template-columns: repeat(3, 1fr); }

  /*
   * The editorial variant: one story is the lead.
   *
   * Four identical cards read as a directory; a lead at double width beside two
   * stacked ones reads as a publication with an opinion about what matters.
   * That was DESIGN-DEBT.md §7 — "nothing on the page says look here first".
   *
   * Below 62rem it is a plain single column: a lead card only means anything
   * when there is something beside it to be bigger than.
   */
  .np-grid--feature > :first-child { grid-column: span 2; grid-row: span 2; }
  .np-grid--feature > :first-child .np-card__title { font-size: var(--np-text-2xl); }
  .np-grid--feature > :first-child .np-card__meta { font-size: var(--np-text-base); }
  .np-grid--feature > :first-child .np-card__media { aspect-ratio: 16 / 10; }
  .np-grid--feature > :first-child .np-card__label { font-size: var(--np-text-3xl); }
}

.np-card { display: flex; flex-direction: column; gap: .75rem; }
.np-card__media {
  aspect-ratio: 3 / 2;
  max-width: 100%;
  overflow: hidden;
  border-radius: var(--np-radius);
  background: var(--np-paper-soft);
}
.np-card__media img { width: 100%; height: 100%; object-fit: cover; }
.np-card__title { font-size: var(--np-text-lg); }
.np-card__title a { text-decoration: none; }
.np-card__title a:hover { text-decoration: underline; }
.np-card__meta { font-size: var(--np-text-sm); color: var(--np-ink-soft); }

.np-eyebrow {
  font-size: var(--np-text-xs);
  text-transform: uppercase;
  letter-spacing: .08em;
  font-weight: 700;
  color: var(--np-ink-soft);
}

/* Spec table — trails and places. Scrolls on its own, never the page. */
.np-specs { overflow-x: auto; }
.np-specs table { width: 100%; border-collapse: collapse; font-size: var(--np-text-sm); }
.np-specs th, .np-specs td { text-align: start; padding: .6rem 0; border-block-end: 1px solid var(--np-line); }
.np-specs th { width: 40%; font-weight: 600; color: var(--np-ink-soft); }

.np-notice {
  padding: 1rem 1.25rem;
  border-inline-start: 3px solid var(--np-accent);
  background: var(--np-paper-soft);
  font-size: var(--np-text-sm);
}
/* Two notices in a row are two things being said, not one paragraph with a
   line break. Without this they share an edge and read as a single block —
   which on a plan means "we shortened your trip" and "we could not use half
   your interests" arrive as one shrug. */
.np-notice + .np-notice { margin-block-start: .75rem; }

/* ==========================================================================
   8. Header and footer
   ========================================================================== */

.np-header {
  position: sticky;
  top: 0;
  z-index: var(--np-z-header);
  background: var(--np-paper);
  border-block-end: 1px solid var(--np-line);
}
.np-header__inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1.5rem;
  padding-block: .85rem;
}
.np-logo { font-family: var(--np-font-display); font-size: var(--np-text-lg); font-weight: 700; text-decoration: none; }
.np-logo img { max-height: 2.5rem; width: auto; }

/* list-style here as well as in the reset: this markup comes from
   wp_nav_menu on a real site, so the theme does not control its classes. */
.np-nav ul { display: flex; gap: 1.5rem; align-items: center; flex-wrap: wrap; list-style: none; padding: 0; margin: 0; }
.np-nav a { text-decoration: none; font-size: var(--np-text-sm); font-weight: 600; }
.np-nav a:hover { text-decoration: underline; }

.np-nav-toggle { display: none; }

@media (max-width: 61.9375rem) {
  .np-nav-toggle {
    display: inline-flex; align-items: center; gap: .5rem;
    background: none; border: 1px solid var(--np-line);
    border-radius: var(--np-radius); padding: .55rem .9rem;
    font: inherit; font-size: var(--np-text-sm); cursor: pointer;
  }
  /* The drawer is a SIBLING of the header, not a child — see the stacking
     scale note in §2. Inside the header it could never beat the backdrop. */
  .np-drawer {
    position: fixed; inset: 0 0 0 auto; width: min(22rem, 86vw);
    z-index: var(--np-z-drawer);
    background: var(--np-paper);
    padding: 5rem var(--np-gutter) 2rem;
    transform: translateX(100%);
    transition: transform .25s ease;
    overflow-y: auto;
  }
  .np-drawer[data-open="true"] { transform: translateX(0); }
  .np-backdrop {
    position: fixed; inset: 0; z-index: var(--np-z-backdrop);
    background: rgba(22,26,23,.5);
  }
  .np-drawer ul { flex-direction: column; align-items: stretch; gap: 0; }
  .np-drawer li + li { border-block-start: 1px solid var(--np-line); }
  .np-drawer a { display: block; padding: .9rem 0; font-size: var(--np-text-lg); }
  .np-header__nav { display: none; }
}

/*
 * A filled band running straight into the footer must not leave a white
 * stripe between two coloured areas. :has() so the footer can react to how the
 * page above it ended; where it is unsupported this degrades to the ordinary
 * gap, which is what it did before.
 */
#np-main:has(> .np-section--filled:last-child) + .np-footer { margin-block-start: 0; }

.np-footer {
  margin-block-start: var(--np-space-lg);
  padding-block: var(--np-space-md);
  background: var(--np-paper-soft);
  border-block-start: 1px solid var(--np-line);
  font-size: var(--np-text-sm);
}
.np-footer__cols { display: grid; gap: 2rem; grid-template-columns: repeat(auto-fit, minmax(min(100%, 14rem), 1fr)); }
/*
 * Footer column headings are <h2> styled small, NOT <h4>.
 * A footer <h4> after a section <h2> is a skipped level on every page.
 * Visual weight and outline depth are separate concerns. ../README.md §4.
 */
.np-footer h2 { font-size: var(--np-text-sm); text-transform: uppercase; letter-spacing: .06em; font-family: var(--np-font-body); }
.np-footer ul { margin-block-start: .75rem; list-style: none; padding: 0; }
.np-footer li + li { margin-block-start: .35rem; }
.np-footer p a, .np-footer__legal a { text-decoration: underline; }
.np-footer__legal { margin-block-start: 2rem; color: var(--np-ink-soft); }

/* ==========================================================================
   9. Motion
   ========================================================================== */

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .01ms !important;
    scroll-behavior: auto !important;
  }
}

/* ==========================================================================
   10. Forms — the planner intake
   ========================================================================== */

/* --------------------------------------------------------------------------
   The hero, in both its states. DESIGN-DEBT.md §2.
   -------------------------------------------------------------------------- */

.np-hero__title { font-size: var(--np-text-4xl); }
.np-hero__copy {
  margin-block-start: 1.25rem;
  font-size: var(--np-text-deck);
  line-height: 1.5;
  max-width: 34rem;
}
.np-hero__actions { display: flex; flex-wrap: wrap; gap: .75rem; margin-block-start: 2rem; }

/* The media state. The band around it carries np-section--bleed, so the wrap
   has already stopped constraining and there is nothing to break out of --
   no 100vw, no negative margins, and therefore no scrollbar overflow. */
.np-hero--media {
  position: relative;
  isolation: isolate;
  display: flex;
  align-items: flex-end;
  min-height: clamp(22rem, 58vh, 34rem);
  color: #ffffff;
}

/*
 * THE HERO STACKS UPWARDS FROM 1, NOT DOWNWARDS FROM -1.
 *
 * It used to be img:-2, scrim:-1, body static, and on the live site that
 * rendered the scrim over NOTHING: a flat grey gradient where a photograph
 * should be, on all 47 park pages. The image was fetched, decoded and
 * complete — naturalWidth 1800, opacity 1, correctly sized and positioned —
 * and simply never painted. Hiding the scrim revealed it instantly.
 *
 * Negative z-index is the fragile half of the stacking algorithm: a negative
 * child paints in a band underneath its stacking context's own content, and
 * exactly where it lands depends on which ancestor is the context and what
 * that ancestor paints. Nothing in the page was even sitting between -2 and
 * -1 — those two were the only negative elements in the whole document — and
 * it still failed.
 *
 * .np-hero--media already declares `isolation: isolate`, so a positive stack
 * is completely contained and cannot reach the sticky header or anything else.
 * Positive z-index within an isolated context is the deterministic path, so
 * that is what this uses. The ordering is the whole point of the component:
 * photograph, then the scrim that makes text legible over it, then the text.
 *
 * Do not "tidy" these back to negative values. The gate for this is your eyes;
 * a rendered page is the only thing that catches it.
 */
.np-hero--media .np-hero__img {
  position: absolute;
  inset: 0;
  z-index: 1;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* Darkest where the text sits. The stop that matters is the last one: that is
   the alpha the contrast gate checks, and the text block sits inside it. */
.np-hero--media .np-hero__scrim {
  position: absolute;
  inset: 0;
  z-index: 2;
  background: linear-gradient(
    to bottom,
    rgba(0, 0, 0, .22) 0%,
    rgba(0, 0, 0, .40) 45%,
    rgba(0, 0, 0, var(--np-scrim-min)) 100%
  );
}

/* Positioned, so it sits above the scrim rather than relying on in-flow
   content happening to paint after a negatively-stacked sibling. */
.np-hero--media .np-hero__body {
  position: relative;
  z-index: 3;
  width: 100%;
  padding-block: clamp(2.5rem, 6vw, 4.5rem);
}

/* Over a photograph the eyebrow cannot be ink-soft — it must inherit. */
.np-hero--media .np-eyebrow { color: inherit; opacity: .92; }
.np-hero--media .np-hero__title { text-shadow: 0 1px 24px rgba(0, 0, 0, .35); }
.np-hero--media .np-hero__copy { color: inherit; }

/* The two-tone ring again: over a photograph, ink-on-ink would vanish. */
.np-hero--media :focus-visible {
  outline-color: #ffffff;
  box-shadow: 0 0 0 3px var(--np-ink);
}

/*
 * A media hero fills its band edge to edge.
 *
 * Keyed off the hero itself with :has(), NOT a flag passed into
 * np_section_open(), because that flag would have to be computed before the
 * section opens — and the image field has to be REGISTERED INSIDE it, or it
 * lands in the page's furniture instead of the band (inc/sections.php: "ORDER
 * MATTERS AND IS SILENT WHEN WRONG").
 *
 * No 100vw and no negative margins, so there is no scrollbar overflow to chase.
 * Where :has() is unsupported the hero simply renders inside the wrap —
 * narrower than intended, and still correct.
 */
/* #np-main to outrank the first-child rule below, which is an id + a class.
   Without it the photograph ended above 57px of white before the next band —
   a seam, which looks like a mistake rather than like space. */
#np-main > .np-section:has(> .np-wrap > .np-hero--media),
.np-section:has(> .np-wrap > .np-hero--media) { padding-block: 0; }
.np-section:has(> .np-wrap > .np-hero--media) > .np-wrap { max-width: none; padding-inline: 0; }

/* --------------------------------------------------------------------------
   The card with no photograph. DESIGN-DEBT.md §3.
   Same box, same ratio, same rhythm — so a grid stays a grid.
   -------------------------------------------------------------------------- */

.np-card__media--empty {
  display: flex;
  align-items: flex-end;
  padding: 1rem 1.1rem;
  background: var(--np-paper-soft);
  border: 1px solid var(--np-line);
  text-decoration: none;
}
/* NOT np-card__media-label: that name is a prefix of np-card__media, so every
   grep and every substr_count over the rendered markup counted it twice. It
   cost a wrong FAIL on the raggedness gate before it cost anything worse. */
.np-card__label {
  font-family: var(--np-font-display);
  font-size: var(--np-text-xl);
  line-height: 1.15;
  font-weight: 600;
  color: var(--np-ink-soft);
  text-wrap: balance;
}

.np-planner-form { display: grid; gap: 2rem; max-width: 42rem; }

.np-field { border: 0; padding: 0; margin: 0; display: grid; gap: .5rem; }
.np-field__label { font-weight: 600; font-size: var(--np-text-sm); padding: 0; }
.np-field__help { font-size: var(--np-text-xs); color: var(--np-ink-soft); margin: 0; }

.np-field-row { display: grid; gap: 1.25rem; grid-template-columns: repeat(auto-fit, minmax(min(100%, 12rem), 1fr)); }

.np-field input[type="date"],
.np-field input[type="email"],
.np-field input[type="text"],
.np-field select {
  font: inherit;
  font-size: var(--np-text-base);
  padding: .7rem .85rem;
  border: 1px solid var(--np-line);
  border-radius: var(--np-radius);
  background: var(--np-paper);
  color: var(--np-ink);
  width: 100%;
  max-width: 100%;
}

.np-chip-group + .np-chip-group { margin-block-start: 1.25rem; }
.np-chip-group__title {
  font-family: var(--np-font-body);
  font-size: var(--np-text-xs);
  text-transform: uppercase;
  letter-spacing: .07em;
  color: var(--np-ink-soft);
  margin-block-end: .6rem;
}

.np-chips { display: flex; flex-wrap: wrap; gap: .5rem; }

.np-chip { position: relative; }
/* The input stays in the accessibility tree and keeps keyboard focus; only its
   box is hidden. Never display:none here — that removes it from tab order. */
.np-chip input {
  position: absolute; opacity: 0;
  width: 100%; height: 100%; margin: 0; cursor: pointer;
}
.np-chip span {
  display: inline-block;
  padding: .45rem .85rem;
  border: 1px solid var(--np-line);
  border-radius: 999px;
  font-size: var(--np-text-sm);
  transition: background-color .12s ease, border-color .12s ease;
}
.np-chip input:checked + span {
  background: var(--np-brand);
  color: var(--np-on-brand);
  border-color: var(--np-brand);
}
/* Same two-tone pair as the global rule — a second, single-colour focus style
   here would quietly undo it for every chip on the planner. */
.np-chip input:focus-visible + span {
  outline: 3px solid var(--np-ink);
  outline-offset: 3px;
  box-shadow: 0 0 0 3px var(--np-paper);
}
.np-chip input:checked:focus-visible + span {
  outline-color: var(--np-paper);
  box-shadow: 0 0 0 3px var(--np-ink);
}

/* The honeypot. Off-screen rather than display:none, which some bots detect. */
.np-hp {
  position: absolute !important;
  left: -9999px !important;
  width: 1px; height: 1px; overflow: hidden;
}

/* ==========================================================================
   11. Section heads
   A band's heading, its standfirst and the link out of it, on one line at
   desktop and stacked below. The link belongs UP HERE as well as at the foot
   of the band: someone scanning for "where is all the news" should not have to
   read five cards to find out.
   ========================================================================== */

.np-sechead {
  display: flex;
  flex-wrap: wrap;
  align-items: end;
  justify-content: space-between;
  gap: 1rem 2rem;
}
.np-sechead__text { max-width: 46ch; }
.np-sechead__action { margin: 0; flex: 0 0 auto; }
.np-sechead__footer { margin-block-start: 2.25rem; text-align: center; }
.np-sechead__footer .np-btn { display: inline-flex; align-items: center; gap: .5rem; }

/* The standfirst. --np-text-deck exists precisely so this is not body copy
   pretending to be a supporting line. DESIGN-DEBT.md §7. */
.np-deck {
  margin-block-start: .6rem;
  font-size: var(--np-text-deck);
  line-height: 1.45;
  color: var(--np-ink-soft);
  text-wrap: pretty;
}

.np-icon { flex: 0 0 auto; vertical-align: -0.15em; }

/* ==========================================================================
   12. The park rail
   All 47 parks as drifting pills. template-parts/park-rail.php carries the
   full reasoning; the short version is that six alphabetical cards made a
   sixty-three-park network look like a truncated directory.

   THREE MODES, ONE PER INPUT — a marquee that also scrolls is two mechanisms
   fighting over one offset, and the fight is visible:

     pointer + motion OK   drifts, pauses on hover, hands over to a real
                           scroller on click or focus (assets/js/main.js adds
                           .np-rail--stopped and removes the clone track).
     touch / reduced motion never drifts. A plain snap-scroll rail.
   ========================================================================== */

.np-rail { margin-block-start: 2rem; display: grid; gap: .7rem; }

.np-rail__row {
  overflow: hidden;
  /* Fades the pills out at both edges so the rail reads as continuous rather
     than as a list that has been cut off. Dropped once the rail is a real
     scroller, where a faded edge would hide a focused pill. */
  -webkit-mask-image: linear-gradient(90deg, transparent, #000 4rem, #000 calc(100% - 4rem), transparent);
          mask-image: linear-gradient(90deg, transparent, #000 4rem, #000 calc(100% - 4rem), transparent);
}

.np-rail__flow { display: flex; width: max-content; }

/*
 * NO `gap` ON THE FLOW, and that is not a style preference.
 *
 * The loop works by translating the flow exactly -50%, which lands the clone
 * track where the original started. A gap between the two tracks makes the
 * flow 2*track + gap wide, so -50% lands half a gap short and the rail visibly
 * stutters once per cycle. The spacing is a margin on each item instead, which
 * is inside the track's own width.
 */
.np-rail__track { display: flex; flex: 0 0 auto; list-style: none; margin: 0; padding: 0; }
.np-rail__item  { flex: 0 0 auto; margin-inline-end: .6rem; }

@keyframes np-rail-drift {
  from { transform: translate3d(0, 0, 0); }
  to   { transform: translate3d(-50%, 0, 0); }
}

/* Different speeds, opposite directions: two rows at one speed read as a single
   block sliding, which is the grid problem again in motion. */
.np-rail__row--1 .np-rail__flow { animation: np-rail-drift 95s linear infinite; }
.np-rail__row--2 .np-rail__flow { animation: np-rail-drift 130s linear infinite reverse; }

.np-rail:hover .np-rail__flow,
.np-rail:focus-within .np-rail__flow { animation-play-state: paused; }

/* Handed over to the visitor. main.js has frozen the offset into scrollLeft and
   removed the clone, so this is an ordinary horizontal scroller from here on. */
.np-rail--stopped .np-rail__row {
  overflow-x: auto;
  scroll-snap-type: x proximity;
  -webkit-mask-image: none;
          mask-image: none;
}
.np-rail--stopped .np-rail__flow { animation: none; }
.np-rail--stopped .np-rail__item { scroll-snap-align: start; }

/*
 * Touch and reduced motion never drift at all.
 *
 * §9's blanket reduced-motion rule kills the animation, but that alone would
 * leave a frozen rail inside `overflow: hidden` with half its pills
 * unreachable — the motion rule has to be paired with giving the scroll back.
 */
@media (prefers-reduced-motion: reduce), (hover: none) {
  .np-rail__row {
    overflow-x: auto;
    scroll-snap-type: x proximity;
    -webkit-mask-image: none;
            mask-image: none;
  }
  .np-rail__flow { animation: none; }
  .np-rail__item { scroll-snap-align: start; }
  /* The clone exists only to make a loop seamless. With no loop it is a second
     copy of the roster in the scroll range. */
  .np-rail__track[aria-hidden="true"] { display: none; }
}

.np-parkpill {
  display: inline-flex;
  align-items: center;
  gap: .6rem;
  padding: .35rem 1rem .35rem .35rem;
  border: 1px solid var(--np-line);
  border-radius: 999px;
  background: var(--np-paper);
  color: var(--np-ink);
  text-decoration: none;
  font-size: var(--np-text-sm);
  font-weight: 600;
  white-space: nowrap;
  transition: transform .15s ease, border-color .15s ease, box-shadow .15s ease;
}
.np-parkpill:hover {
  transform: translateY(-2px);
  border-color: var(--np-brand);
  box-shadow: var(--np-shadow);
}
.np-parkpill__thumb {
  inline-size: 2.25rem;
  block-size: 2.25rem;
  flex: 0 0 auto;
  border-radius: 999px;
  overflow: hidden;
  background: var(--np-paper-soft);
  display: grid;
  place-items: center;
}
.np-parkpill__thumb img { inline-size: 100%; block-size: 100%; object-fit: cover; display: block; }
/* No photograph yet: a designed initial, same circle, same size. A park with no
   picture must not be a hole in the rail. */
.np-parkpill__thumb--empty {
  background: var(--np-brand);
  color: var(--np-on-brand);
  font-family: var(--np-font-display);
  font-weight: 700;
  font-size: var(--np-text-lg);
}

/* ==========================================================================
   13. The news grid and the news card
   One lead story, four beside it in a 2x2. Four equal cards read as a
   directory; this reads as a front page with an opinion. DESIGN-DEBT.md §7.
   ========================================================================== */

.np-newsgrid { display: grid; gap: clamp(1.25rem, 3vw, 2rem); margin-block-start: 2rem; }
.np-newsgrid__side {
  display: grid;
  gap: clamp(1.25rem, 3vw, 2rem);
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 14rem), 1fr));
}

@media (min-width: 62rem) {
  /* The lead is wider, not equal: a lead that is the same width as the block
     beside it is not leading anything. */
  .np-newsgrid { grid-template-columns: 1.1fr 1fr; align-items: start; }
  .np-newsgrid__side { grid-template-columns: repeat(2, 1fr); }
}

.np-card--news { transition: transform .18s ease; }
.np-card--news:hover { transform: translateY(-3px); }
.np-card__mediawrap { position: relative; }
.np-card__body { display: flex; flex-direction: column; gap: .55rem; }

/* The accent rule under the photograph, which the topic pill overlaps. It is
   what ties the colour to the card rather than leaving a pill floating on a
   picture. */
.np-card--news .np-card__media { border-block-end: 3px solid var(--np-topic-rule); }

.np-card__stamp {
  display: flex;
  align-items: center;
  gap: .4rem;
  margin: 0;
  font-size: var(--np-text-xs);
  font-weight: 700;
  letter-spacing: .06em;
  text-transform: uppercase;
  color: var(--np-ink-soft);
}

.np-topic {
  position: absolute;
  inset-inline-start: .75rem;
  inset-block-end: .75rem;
  display: inline-flex;
  align-items: center;
  gap: .35rem;
  max-inline-size: calc(100% - 1.5rem);
  padding: .3rem .7rem;
  border: 1px solid var(--np-topic-edge, transparent);
  border-radius: 999px;
  background: var(--np-topic-bg);
  color: var(--np-topic-ink);
  font-size: var(--np-text-xs);
  font-weight: 700;
  text-decoration: none;
  box-shadow: 0 1px 4px rgba(0, 0, 0, .22);
}
.np-topic__label { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
a.np-topic:hover { text-decoration: underline; }

/*
 * THREE TINTS, ALL OF THEM BRAND PAIRS.
 *
 * np_accent_index() picks one deterministically from the topic slug, so a park
 * is always the same colour and a reload never reshuffles the page — a colour
 * that moves reads as a fault, not as a design.
 *
 * Every pair here is a swatch and its registered foreground, which is what
 * tools/contrast.php already measures. A free hue would be unmeasured, and
 * would be the one thing on the page that belongs to no brand.
 */
.np-card--accent-1 {
  --np-topic-bg: var(--np-accent);
  --np-topic-ink: var(--np-on-accent);
  --np-topic-rule: var(--np-accent);
}
.np-card--accent-2 {
  --np-topic-bg: var(--np-brand);
  --np-topic-ink: var(--np-on-brand);
  --np-topic-rule: var(--np-brand);
}
.np-card--accent-3 {
  --np-topic-bg: var(--np-paper);
  --np-topic-ink: var(--np-ink);
  --np-topic-edge: var(--np-line);
  --np-topic-rule: var(--np-ink);
}

@media (min-width: 62rem) {
  .np-card--lead .np-card__media { aspect-ratio: 16 / 10; }
  .np-card--lead .np-card__title { font-size: var(--np-text-2xl); }
  .np-card--lead .np-card__meta  { font-size: var(--np-text-base); }
  .np-card--lead .np-card__label { font-size: var(--np-text-3xl); }
  .np-card--lead .np-topic { font-size: var(--np-text-sm); padding: .4rem .9rem; }
}

/* ==========================================================================
   14. Filters, pagination and the listing pages
   ========================================================================== */

.np-filters { margin-block-start: 1.5rem; }
.np-filters__list {
  display: flex;
  flex-wrap: wrap;
  gap: .5rem;
  list-style: none;
  margin: 0;
  padding: 0;
}

.np-filter {
  display: inline-flex;
  align-items: baseline;
  gap: .4rem;
  padding: .35rem .85rem;
  border: 1px solid var(--np-line);
  border-radius: 999px;
  font-size: var(--np-text-sm);
  font-weight: 600;
  text-decoration: none;
  background: var(--np-paper);
  transition: border-color .12s ease, background-color .12s ease;
}
.np-filter:hover { border-color: var(--np-brand); }
.np-filter__count { font-size: var(--np-text-xs); font-weight: 400; color: var(--np-ink-soft); }

/*
 * The selected chip is filled AND carries aria-current, which is what the
 * attribute selector keys off. Styling a class instead would let the two drift:
 * a chip that looks selected but is not marked selected is exactly the
 * colour-only distinction the house rules forbid. ../README.md §4.
 */
.np-filter[aria-current="true"] {
  background: var(--np-brand);
  border-color: var(--np-brand);
  color: var(--np-on-brand);
}
.np-filter[aria-current="true"] .np-filter__count { color: inherit; opacity: .75; }

.np-pagination { margin-block-start: 2.5rem; }
.np-pagination__list {
  display: flex;
  flex-wrap: wrap;
  gap: .4rem;
  list-style: none;
  margin: 0;
  padding: 0;
  justify-content: center;
}
.np-pagination__list a,
.np-pagination__list span {
  display: inline-block;
  min-inline-size: 2.5rem;
  padding: .45rem .75rem;
  border: 1px solid var(--np-line);
  border-radius: var(--np-radius);
  text-align: center;
  font-size: var(--np-text-sm);
  font-weight: 600;
  text-decoration: none;
}
.np-pagination__list .current {
  background: var(--np-brand);
  border-color: var(--np-brand);
  color: var(--np-on-brand);
}
/* WordPress emits the same markup for the pagination on the parks archive
   through the_posts_pagination(), which wraps it in .navigation.pagination. */
.navigation.pagination { margin-block-start: 2.5rem; text-align: center; }
.navigation.pagination .nav-links { display: inline-flex; flex-wrap: wrap; gap: .4rem; }
.navigation.pagination .page-numbers {
  display: inline-block;
  min-inline-size: 2.5rem;
  padding: .45rem .75rem;
  border: 1px solid var(--np-line);
  border-radius: var(--np-radius);
  font-size: var(--np-text-sm);
  font-weight: 600;
  text-decoration: none;
}
.navigation.pagination .page-numbers.current {
  background: var(--np-brand);
  border-color: var(--np-brand);
  color: var(--np-on-brand);
}

/* The parks type-to-narrow box. Ships hidden; main.js reveals it. */
.np-parkfilter { margin-block-start: 1.75rem; max-inline-size: 26rem; }
.np-parkfilter input {
  inline-size: 100%;
  padding: .7rem .9rem;
  border: 1px solid var(--np-line);
  border-radius: var(--np-radius);
  background: var(--np-paper);
  color: var(--np-ink);
  font: inherit;
  font-size: var(--np-text-base);
}
.np-parkfilter__count {
  margin-block-start: .4rem;
  min-block-size: 1.4em; /* Reserve the line, or the grid jumps on first keystroke. */
  font-size: var(--np-text-sm);
  color: var(--np-ink-soft);
}

/* The contact page: form beside the ways to reach a human. */
.np-contact { display: grid; gap: clamp(2rem, 5vw, 3.5rem); }
@media (min-width: 62rem) {
  .np-contact { grid-template-columns: minmax(0, 1fr) 18rem; align-items: start; }
}
.np-contact__aside h2 { font-size: var(--np-text-lg); margin-block-start: 1.75rem; }
.np-contact__aside h2:first-child { margin-block-start: 0; }
.np-contact__aside p { margin-block-start: .5rem; font-size: var(--np-text-sm); }
.np-contact__links { list-style: none; margin: .5rem 0 0; padding: 0; font-size: var(--np-text-sm); }
.np-contact__links li + li { margin-block-start: .35rem; }
.np-contact__form h2 { margin-block-end: 1rem; }

/* The disclosure sits above the copyright line, quieter than it but present on
   every page — it is a legal notice, not a design element. */
.np-footer__legal--disclosure { font-size: var(--np-text-xs); opacity: .85; }

/* ==========================================================================
   15. The trip plan
   ========================================================================== */

.np-day + .np-day { margin-block-start: clamp(2rem, 4vw, 3rem); }
.np-day__head {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: space-between;
  gap: .5rem 1.5rem;
  padding-block-end: .75rem;
  border-block-end: 2px solid var(--np-brand);
}
.np-day__title { font-size: var(--np-text-xl); margin: 0; }
.np-day__date { font-weight: 400; color: var(--np-ink-soft); margin-inline-start: .5rem; }
.np-day__budget {
  display: inline-flex;
  align-items: center;
  gap: .4rem;
  margin: 0;
  font-size: var(--np-text-sm);
  color: var(--np-ink-soft);
}

.np-day__stops { list-style: none; margin: 1.25rem 0 0; padding: 0; }

.np-stop { display: flex; gap: 1rem; padding-block: 1rem; }
.np-stop + .np-stop { border-block-start: 1px solid var(--np-line); }
.np-stop__mark {
  flex: 0 0 auto;
  inline-size: 2.25rem;
  block-size: 2.25rem;
  border-radius: 999px;
  display: grid;
  place-items: center;
  background: var(--np-paper-soft);
  color: var(--np-brand);
}
/* A trail is a different KIND of stop from a place, and the shape says so as
   well as the icon — an icon alone is a colour-only distinction for anyone who
   cannot make out a 16px glyph. */
.np-stop--trail .np-stop__mark { background: var(--np-brand); color: var(--np-on-brand); }
.np-stop__body { min-inline-size: 0; }
.np-stop__title { font-size: var(--np-text-lg); margin: 0; }
.np-stop__title a { text-decoration: none; }
.np-stop__title a:hover { text-decoration: underline; }
.np-stop__meta {
  display: flex;
  flex-wrap: wrap;
  gap: .35rem .9rem;
  margin-block-start: .3rem;
  font-size: var(--np-text-sm);
  color: var(--np-ink-soft);
}
.np-stop__type {
  font-size: var(--np-text-xs);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: .06em;
}
.np-stop__note { margin-block-start: .45rem; font-size: var(--np-text-sm); }

.np-day--spare { margin-block-start: clamp(2rem, 4vw, 3rem); }
.np-spare { list-style: none; margin: 1rem 0 0; padding: 0; display: grid; gap: .6rem; }
.np-spare li { display: flex; flex-wrap: wrap; gap: .5rem; align-items: baseline; }

.np-stay {
  padding: 1.25rem;
  border: 1px solid var(--np-line);
  border-radius: var(--np-radius);
  background: var(--np-paper);
}
.np-stay__title { font-size: var(--np-text-lg); margin: 0; }
.np-stay__title a { text-decoration: none; }
.np-stay__title a:hover { text-decoration: underline; }
.np-stay__meta {
  display: flex;
  flex-wrap: wrap;
  gap: .35rem .9rem;
  margin-block-start: .45rem;
  font-size: var(--np-text-sm);
  color: var(--np-ink-soft);
}
.np-stay__price { font-weight: 600; color: var(--np-ink); }

.np-pack__title { font-size: var(--np-text-lg); }
.np-pack__list { list-style: none; margin: .75rem 0 0; padding: 0; font-size: var(--np-text-sm); }
.np-pack__list li { padding-block: .35rem; border-block-end: 1px solid var(--np-line); }

.np-plan__disclosure {
  margin-block-start: 1.5rem;
  font-size: var(--np-text-xs);
  color: var(--np-ink-soft);
}
.np-plan__notestitle { font-size: var(--np-text-lg); }
.np-plan__notes {
  list-style: none;
  margin: .75rem 0 0;
  padding: 0;
  font-size: var(--np-text-sm);
  color: var(--np-ink-soft);
}
.np-plan__notes li { padding-block: .3rem; }

.np-notice--error { border-inline-start-color: var(--np-brand); font-weight: 600; }

/*
 * A plan is the one page on this site people print and take with them, so the
 * print stylesheet is a feature rather than a courtesy: without it the sticky
 * header lands on top of Day 1 and the nav, the footer and the planner CTA eat
 * two of the pages.
 */
/* ==========================================================================
   16. Consent
   The banner NP_Consent renders. Nothing in here decides WHETHER it shows --
   that is the `hidden` attribute in the markup, removed by consent.js once it
   has read the cookie. class-np-consent.php explains why the server is not
   allowed to make that decision on a full-page-cached site.
   ========================================================================== */

.np-consent {
  position: fixed;
  inset-inline: 0;
  inset-block-end: 0;
  z-index: var(--np-z-consent);

  /*
   * A PAPER bar, not an ink one, and the reason is the buttons rather than the
   * bar.
   *
   * An ink bar looks better in isolation and was the first version. But
   * .np-btn--primary fills itself with --np-brand, and Getaways' brand is
   * #23343d against an ink of #151d22 -- about 1.3:1. The Accept button stopped
   * reading as a button at all; it was white text floating on the bar, next to
   * a Reject that had a crisp white outline. The discouraged-looking option was
   * the one the site wants people to be able to press.
   *
   * The parent cannot fix that by reaching for --np-accent instead: it does not
   * know what any child's accent is, and a region registering a dark one would
   * reintroduce it invisibly. That is the same trap as the parent naming a
   * child's swatch (README §5), one level up.
   *
   * On paper, both buttons are exactly what they are everywhere else on the
   * site, and contrast.php already gates brand-on-paper for every brand -- so
   * this is legible in the three that exist and in the fourth nobody has
   * written yet. The line and the shadow do the separating.
   */
  background: var(--np-paper);
  color: var(--np-ink);
  border-block-start: 1px solid var(--np-line);
  box-shadow: 0 -6px 28px rgba(22, 26, 23, .13);

  /*
   * Fixed, so it occupies no space in the flow and causes no layout shift when
   * it appears. It does cover the foot of the page while open, and that is the
   * deliberate trade: a banner that pushes the document down moves the line
   * somebody is reading, and CLS is a ranking signal on a site that lives on
   * search.
   */
  animation: np-consent-in .25s ease-out;
}

@keyframes np-consent-in {
  from { transform: translateY(100%); }
  to   { transform: translateY(0); }
}

.np-consent__inner {
  max-width: var(--np-wrap-lg);
  margin-inline: auto;
  padding: clamp(1rem, 2.5vw, 1.5rem) var(--np-gutter);
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 1rem 2rem;
}

.np-consent__text  { flex: 1 1 26rem; }
.np-consent__title { font-size: var(--np-text-lg); margin-block-end: .25rem; }

/* 62ch, not the full bar. At 1800px a single line of explanation running the
   whole width is unreadable, and the actions end up a screen away from it. */
.np-consent__body  { font-size: var(--np-text-sm); max-width: 62ch; }
.np-consent__link  { color: inherit; text-decoration: underline; }

.np-consent__actions { display: flex; flex-wrap: wrap; gap: .75rem; flex: 0 0 auto; }

/*
 * NO focus-ring override here, deliberately. The bar is paper, so §3's base
 * ring -- an ink outline inside a paper halo -- is already the correct pair.
 * The ink-bar version of this banner needed the .np-section--filled swap; if
 * anyone darkens the bar again, that rule has to come back with it.
 */

/*
 * Both buttons are real buttons and both are reachable. The reject button is
 * .np-btn--ghost, which takes its border from currentColor -- paper, in here --
 * so it reads as an equal choice rather than as the greyed-out one. A reject
 * that is visibly harder to find is a dark pattern and, in the EEA, is the
 * thing regulators actually fine people for.
 */

@media (max-width: 40rem) {
  .np-consent__inner   { gap: 1rem; }
  .np-consent__actions { width: 100%; }
  .np-consent__actions .np-btn { flex: 1 1 12rem; text-align: center; }
}

/* The footer control that brings it back. A button, because it acts on this
   page rather than navigating -- styled as the link it reads as. */
.np-consent__reopen {
  background: none;
  border: 0;
  padding: 0;
  font: inherit;
  color: inherit;
  text-decoration: underline;
  cursor: pointer;
}

@media print {
  .np-header, .np-footer, .np-rail, .np-sechead__action, .np-drawer, .np-backdrop { display: none !important; }
  .np-section { padding-block: .75rem !important; }
  .np-day { break-inside: avoid; }
  .np-stop { break-inside: avoid; }
  a[href]::after { content: ""; } /* the URLs are long and nobody types them */
  body { font-size: 11pt; }
  .np-consent, .np-consent__reopen { display: none; }
}
