/* Privian Academy site stylesheet.
   Mobile-first: authored at 360px, then widened. Every screen this dresses
   is a parent on a cheap Android phone on Zimbabwean mobile data, so no
   image is more than needs be, no font is more than needs be, and no
   control requires JavaScript to work. */

/* ---------------------------------------------------------------------
   Fonts — self-hosted, font-display: swap so text is never invisible
   while a face loads on a slow connection.
   ------------------------------------------------------------------ */

@font-face {
  font-family: "Outfit";
  src: url("/static/fonts/outfit-600.woff2") format("woff2");
  font-weight: 600;
  font-style: normal;
  font-display: swap;
}

@font-face {
  font-family: "Outfit";
  src: url("/static/fonts/outfit-700.woff2") format("woff2");
  font-weight: 700;
  font-style: normal;
  font-display: swap;
}

@font-face {
  font-family: "Public Sans";
  src: url("/static/fonts/public-sans-400.woff2") format("woff2");
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}

@font-face {
  font-family: "Public Sans";
  src: url("/static/fonts/public-sans-600.woff2") format("woff2");
  font-weight: 600;
  font-style: normal;
  font-display: swap;
}

@font-face {
  font-family: "Fraunces";
  src: url("/static/fonts/fraunces-italic-400.woff2") format("woff2");
  font-weight: 400;
  font-style: italic;
  font-display: swap;
}

/* ---------------------------------------------------------------------
   Reset
   ------------------------------------------------------------------ */

*,
*::before,
*::after {
  box-sizing: border-box;
}

* {
  margin: 0;
}

html {
  -webkit-text-size-adjust: 100%;
  text-size-adjust: 100%;
}

img,
svg {
  max-width: 100%;
  height: auto;
  display: block;
}

a {
  color: inherit;
}

ul,
ol {
  list-style: none;
  padding: 0;
}

button {
  font: inherit;
  color: inherit;
}

/* ---------------------------------------------------------------------
   Base
   ------------------------------------------------------------------ */

body {
  font-family: var(--font-body);
  font-size: 16px;
  line-height: 1.6;
  color: var(--ink);
  background: var(--surface);
  overflow-x: clip;
}

h1,
h2,
h3,
h4 {
  font-family: var(--font-display);
  font-weight: 700;
  line-height: 1.15;
  color: var(--brand-deep);
}

h1 {
  font-size: var(--step-4);
}

h2 {
  font-size: var(--step-3);
}

h3 {
  font-size: var(--step-2);
}

h4 {
  font-size: var(--step-1);
  font-weight: 600;
}

p {
  max-width: 65ch;
}

.wrap {
  width: min(100% - 2 * var(--gutter), var(--wrap));
  margin-inline: auto;
}

section {
  padding-block: clamp(2.5rem, 2rem + 2vw, 4.5rem);
}

/* ---------------------------------------------------------------------
   Accessibility helpers
   ------------------------------------------------------------------ */

.skip {
  position: absolute;
  top: -100%;
  left: var(--gutter);
  z-index: 100;
  background: var(--brand-deep);
  color: var(--on-brand);
  padding: 0.75rem 1.25rem;
  border-radius: 0 0 var(--radius) var(--radius);
  font-weight: 600;
  transition: top 0.15s ease;
}

.skip:focus {
  top: 0;
}

/* Gold on the page surface (#FDC70C on #FAFAFB) measures about 1.51:1,
   far under the 3:1 floor for non-text contrast, so the nav pills - the
   first stop on every page - got a focus ring keyboard users could not
   see. --brand-primary on that same surface is about 10.4:1. Gold is
   only correct on the deep ground it was designed for (about 10.5:1 on
   --brand-deep) and stays there via the override below; --brand-primary
   itself falls to about 1.5:1 on deep, so it must not leak onto those
   grounds either. */
:focus-visible {
  outline: 3px solid var(--brand-primary);
  outline-offset: 2px;
}

.hero :focus-visible,
.band--deep :focus-visible,
.site-footer :focus-visible {
  outline-color: var(--brand-gold);
}

/* ---------------------------------------------------------------------
   Draft banner
   ------------------------------------------------------------------ */

.banner {
  background: var(--brand-gold);
  color: var(--brand-deep);
  font-weight: 600;
  font-size: 0.875rem;
  text-align: center;
  padding: 0.6rem var(--gutter);
}

/* ---------------------------------------------------------------------
   Header + nav
   Mobile nav is a horizontally scrolling row of pills — never a
   hamburger, because a hamburger needs JavaScript to open and no page
   may require JavaScript to be read.
   ------------------------------------------------------------------ */

.site-header {
  position: sticky;
  top: 0;
  z-index: 50;
  background: var(--surface);
  border-bottom: 1px solid var(--line);
}

.site-header__bar {
  display: flex;
  align-items: center;
  gap: 1rem;
  padding-block: 0.75rem;
}

.site-header__lockup {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  flex-shrink: 0;
}

.site-header__lockup img,
.site-header__lockup svg {
  height: 40px;
  width: auto;
}

.site-nav {
  display: flex;
  gap: 0.5rem;
  overflow-x: auto;
  overflow-y: clip;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: thin;
  padding-block: 0.25rem;
  /* scrollbar-width is Firefox-only; this phone's Chrome ignores it, so
     nine pills in a 360px row otherwise give no hint that more exist.
     Two mask layers stand in for a real scroll shadow: the first is
     pinned to the true end of the CONTENT (mask-attachment: local, so it
     scrolls with the pills) and is fully opaque there; the second is a
     fade pinned to the VIEWPORT's trailing edge (mask-attachment: scroll,
     fixed in place). Wherever the two coincide - nothing left to scroll
     to, whether because the row never overflowed or because the user
     has scrolled all the way - the opaque layer wins and the fade
     disappears. Anywhere content still extends past the visible edge,
     only the fade layer paints, so the affordance shows exactly when
     it's true. Fully unsupported browsers just keep the current no-fade
     look, which is the current known-safe fallback, not a regression. */
  -webkit-mask-image:
    linear-gradient(#000, #000),
    linear-gradient(to left, transparent, #000 28px);
  mask-image:
    linear-gradient(#000, #000),
    linear-gradient(to left, transparent, #000 28px);
  -webkit-mask-size: 28px 100%, 28px 100%;
  mask-size: 28px 100%, 28px 100%;
  -webkit-mask-position: 100% 0, 100% 0;
  mask-position: 100% 0, 100% 0;
  -webkit-mask-repeat: no-repeat;
  mask-repeat: no-repeat;
  -webkit-mask-attachment: local, scroll;
  mask-attachment: local, scroll;
  /* Let a focused pill scroll fully clear of the fade zone instead of
     settling half inside it, and give the current-page pill the same
     berth so it never sits flush under the mask either. */
  scroll-padding-inline: 28px;
}

.site-nav ul {
  display: flex;
  gap: 0.5rem;
  flex-wrap: nowrap;
}

.site-nav a {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  padding: 0 1rem;
  border-radius: 999px;
  background: var(--surface);
  border: 1px solid var(--line);
  font-family: var(--font-display);
  font-weight: 600;
  font-size: 0.9rem;
  white-space: nowrap;
  text-decoration: none;
  color: var(--brand-deep);
  /* Matches .site-nav's scroll-padding-inline, so a pill that scrolls
     into view (on focus, or because it is the current page) settles
     with breathing room instead of landing flush under the fade mask. */
  scroll-margin-inline: 28px;
}

.site-nav a[aria-current="page"] {
  background: var(--brand-primary);
  border-color: var(--brand-primary);
  color: var(--on-brand);
}

@media (min-width: 900px) {
  .site-header__bar {
    justify-content: space-between;
  }

  .site-nav {
    overflow-x: visible;
  }
}

/* ---------------------------------------------------------------------
   Buttons
   ------------------------------------------------------------------ */

.btn {
  min-height: 48px;
  min-width: 48px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.5rem;
  padding: 0 1.5rem;
  border-radius: 999px;
  border: 2px solid transparent;
  font-family: var(--font-display);
  font-weight: 600;
  font-size: 1rem;
  text-decoration: none;
  cursor: pointer;
  transition: transform 0.15s ease, box-shadow 0.15s ease;
}

.btn--gold {
  background: var(--brand-gold);
  color: var(--brand-deep);
}

.btn--ghost {
  background: transparent;
  border-color: var(--on-brand);
  color: var(--on-brand);
}

.btn--ghost.btn--on-light {
  border-color: var(--brand-primary);
  color: var(--brand-primary);
}

.btn:hover {
  transform: translateY(-1px);
  box-shadow: 0 6px 16px rgba(29, 15, 91, 0.18);
}

.btn-row {
  display: flex;
  flex-wrap: wrap;
  gap: 0.75rem;
}

/* ---------------------------------------------------------------------
   Hero
   The scrim declares background-color alongside its gradient: a
   gradient alone zeroes background-color, and a contrast checker then
   has no ground to measure a failure against.
   ------------------------------------------------------------------ */

.hero {
  position: relative;
  color: var(--on-brand);
  overflow: clip;
}

.hero__media {
  position: relative;
  min-height: 60vh;
  display: flex;
  align-items: flex-end;
}

.hero__media img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  z-index: 0;
}

.hero__media::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 1;
  background-color: var(--brand-deep);
  background-image: linear-gradient(
    180deg,
    rgba(29, 15, 91, 0.72) 0%,
    rgba(29, 15, 91, 0.88) 100%
  );
}

.hero__body {
  position: relative;
  z-index: 2;
  padding-block: 2.5rem;
  width: 100%;
}

.hero__body h1,
.hero .motto {
  color: var(--on-brand);
}

@media (min-width: 900px) {
  .hero__media::before {
    background-image: linear-gradient(
      90deg,
      rgba(29, 15, 91, 0.92) 0%,
      rgba(29, 15, 91, 0.72) 45%,
      rgba(29, 15, 91, 0.15) 100%
    );
  }
}

/* ---------------------------------------------------------------------
   Eyebrow + motto
   ------------------------------------------------------------------ */

.eyebrow {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 0.8rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--brand-tint);
}

.motto {
  font-family: var(--font-motto);
  font-style: italic;
  font-size: var(--step-2);
  color: var(--brand-primary);
}

/* ---------------------------------------------------------------------
   Bands (full-bleed section backgrounds)
   ------------------------------------------------------------------ */

.band {
  background: var(--surface);
}

.band--deep {
  background: var(--brand-deep);
  color: var(--on-brand);
}

.band--deep h2,
.band--deep h3,
.band--deep h4,
.band--deep .eyebrow {
  color: var(--on-brand);
}

.band--tint {
  background: color-mix(in srgb, var(--brand-tint) 12%, var(--surface));
}

/* ---------------------------------------------------------------------
   Grids + cards
   ------------------------------------------------------------------ */

.grid {
  display: grid;
  gap: 1.25rem;
  grid-template-columns: 1fr;
}

@media (min-width: 620px) {
  .grid--2 {
    grid-template-columns: repeat(2, 1fr);
  }

  .grid--4 {
    grid-template-columns: repeat(2, 1fr);
  }
}

@media (min-width: 900px) {
  .grid--3 {
    grid-template-columns: repeat(3, 1fr);
  }

  .grid--4 {
    grid-template-columns: repeat(4, 1fr);
  }
}

.card {
  background: #fff;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  overflow: clip;
  display: flex;
  flex-direction: column;
}

.card img {
  aspect-ratio: 3 / 2;
  object-fit: cover;
  width: 100%;
}

.card__body {
  padding: 1.25rem;
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
  flex: 1;
}

.pillar {
  background: #fff;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 1.5rem 1.25rem;
  text-align: left;
  display: flex;
  flex-direction: column;
  gap: 0.6rem;
}

.pillar__icon {
  width: 44px;
  height: 44px;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 50%;
  background: color-mix(in srgb, var(--brand-tint) 16%, #fff);
  color: var(--brand-primary);
  flex-shrink: 0;
}

.facility {
  background: #fff;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  overflow: clip;
}

.facility img {
  aspect-ratio: 4 / 3;
  object-fit: cover;
  width: 100%;
}

.facility__body {
  padding: 1rem 1.1rem;
}

.news-card {
  background: #fff;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  overflow: clip;
  display: flex;
  flex-direction: column;
  /* The gallery template never wraps its text in .news-card__body, so
     that wrapper's own padding never applied and the copy sat flush
     against the card edge. The card itself now owns the inset. */
  padding: 1.1rem;
  gap: 0.4rem;
}

.news-card img {
  aspect-ratio: 16 / 10;
  object-fit: cover;
  /* The image is a direct child, so it inherited the card's new padding
     too. Pull it back out on three sides so it still bleeds to the
     card's (rounded, clipped) edge exactly as before. */
  margin: -1.1rem -1.1rem 0;
  width: calc(100% + 2.2rem);
}

.news-card__body {
  /* If a template DOES wrap the text in .news-card__body, the card's
     own padding above already provides the inset - stacking this
     wrapper's padding on top of it would double it. */
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 0.4rem;
}

.news-card__date {
  font-size: 0.85rem;
  color: var(--ink-muted);
  font-weight: 600;
}

.gallery-grid {
  display: grid;
  gap: 0.75rem;
  grid-template-columns: repeat(2, 1fr);
}

.gallery-grid img {
  aspect-ratio: 1 / 1;
  object-fit: cover;
  width: 100%;
  border-radius: var(--radius);
}

@media (min-width: 620px) {
  .gallery-grid {
    grid-template-columns: repeat(3, 1fr);
  }
}

@media (min-width: 900px) {
  .gallery-grid {
    grid-template-columns: repeat(4, 1fr);
  }
}

/* ---------------------------------------------------------------------
   Footer
   ------------------------------------------------------------------ */

.site-footer {
  background: var(--brand-deep);
  color: var(--on-brand);
  padding-block: 3rem 2rem;
}

.site-footer a {
  color: var(--on-brand);
  text-decoration: none;
}

.site-footer a:hover {
  text-decoration: underline;
}

.site-footer__grid {
  display: grid;
  gap: 2rem;
  grid-template-columns: 1fr;
}

@media (min-width: 720px) {
  .site-footer__grid {
    grid-template-columns: repeat(3, 1fr);
  }
}

.site-footer h4 {
  color: var(--on-brand);
  margin-bottom: 0.75rem;
}

.site-footer__meta {
  margin-top: 2.5rem;
  padding-top: 1.5rem;
  border-top: 1px solid rgba(255, 255, 255, 0.18);
  font-size: 0.85rem;
  color: rgba(255, 255, 255, 0.7);
}

.site-footer__socials {
  display: flex;
  gap: 0.75rem;
  margin-top: 0.75rem;
}

.site-footer__socials a {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 44px;
  height: 44px;
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.12);
}

/* ---------------------------------------------------------------------
   Reduced motion
   ------------------------------------------------------------------ */

@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
}

/* ---------------------------------------------------------------------
   Print
   A parent printing a page for a file should still keep the address
   and phone numbers; the nav is useless on paper.
   ------------------------------------------------------------------ */

@media print {
  .site-header,
  .site-nav,
  .banner,
  .hero__media,
  .btn,
  .skip {
    display: none;
  }

  .site-footer {
    background: none;
    color: #000;
  }

  .site-footer a {
    color: #000;
  }

  .site-footer__address,
  .site-footer__phones {
    display: block !important;
  }

  body {
    background: #fff;
  }
}

/* --- Added by the controller: classes the page templates also use ------- */

/* An opening paragraph, one step up from body text. */
.lede {
  font-size: var(--step-1);
  line-height: 1.55;
  color: var(--ink-muted);
  max-width: 62ch;
}

.band--deep .lede,
.hero .lede {
  color: rgba(255, 255, 255, 0.88);
}

/* A list that carries no bullets: contact details, nav-like link groups. */
.plain {
  list-style: none;
  margin: 0;
  padding: 0;
}

.plain > li {
  margin-block: 0.4rem;
}

/* .plain doubles as the grid list wrapper wherever a card pattern (the
   pillars, for one) is marked up as <ul class="plain grid grid--4"><li
   class="pillar">. .grid already sets the gap; the li's own margin-block
   would stack on top of it and desync row heights across columns, so
   zero it in that combination. .pillar itself needs no change - it
   already sets display:flex, which overrides the li's default
   list-item display. */
.plain.grid > li {
  margin-block: 0;
}

/* Contact and footer links are tapped on a phone, so give them the height. */
.plain a,
.tel {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
}

/* =====================================================================
   Controller fixes after driving all nine pages at 360px in Chrome.
   Every one of these was invisible to a green test suite.
   ================================================================== */

/* 1. The hero.
   The stylesheet assumed .hero__body was nested inside .hero__media and
   overlaid on the scrim. All nine templates place it as a SIBLING below
   the image, so its white heading, lead and ghost button were rendering
   white on the near-white page ground: the whole headline was invisible.
   Giving .hero itself the deep ground makes the image and the copy read
   as one purple block, and works for either markup. */
.hero {
  background-color: var(--brand-deep);
}

.hero__body,
.hero__body p,
.hero__body li {
  color: var(--on-brand);
}

/* --brand-tint on --brand-deep is about 3.4:1. Gold is the accent that
   actually carries on this ground. */
.hero .eyebrow {
  color: var(--brand-gold);
}

/* 2. Cards inside a deep band.
   The band sets colour on itself and the card inherited it, so white text
   sat on a white card. Both dated cards in the intake band read as empty
   boxes. */
.band--deep .card,
.band--deep .news-card,
.band--deep .facility {
  background: #ffffff;
  color: var(--ink);
}

.band--deep .card h2,
.band--deep .card h3,
.band--deep .card h4,
.band--deep .card p,
.band--deep .news-card h3,
.band--deep .news-card p {
  color: var(--ink);
}

.band--deep .card .eyebrow,
.band--deep .news-card .eyebrow {
  color: var(--brand-primary);
}

/* 3. The footer motto was --brand-primary on --brand-deep: about 1.5:1,
   which is not dim, it is gone. */
.site-footer .motto {
  color: var(--brand-gold);
}

/* 4. The crest mark used in pillar and list bullets had no fill, so it
   painted solid black: a row of black blobs down the page. */
svg.mark {
  fill: currentColor;
  width: 1.75rem;
  height: 2rem;
  flex: none;
}

.pillar__icon,
.pillar__icon svg.mark {
  color: var(--brand-primary);
}

.band--deep .pillar__icon,
.hero .pillar__icon {
  color: var(--brand-gold);
}

/* 5. The WhatsApp button rendered as a bare icon with no visible label at
   narrow widths. Keep the icon and the words together. */
.btn--wa {
  gap: 0.55rem;
}

.btn--wa .ico {
  width: 1.15rem;
  height: 1.15rem;
  fill: currentColor;
  flex: none;
}

.btn--wa span {
  white-space: nowrap;
}

/* 6. The scrim was built for text sitting ON the image. Nothing does: the
   copy is a sibling below it on all nine pages. At 0.72 rising to 0.88 the
   image was almost entirely painted out, which is why the hero read as a
   blank purple slab. Lighten it to a tint that ties the photograph to the
   brand instead of erasing it. The background-color stays declared so a
   contrast check still has a ground. */
.hero__media::before {
  background-image: linear-gradient(
    180deg,
    rgba(29, 15, 91, 0.22) 0%,
    rgba(29, 15, 91, 0.52) 100%
  );
}

@media (min-width: 900px) {
  .hero__media::before {
    background-image: linear-gradient(
      90deg,
      rgba(29, 15, 91, 0.55) 0%,
      rgba(29, 15, 91, 0.3) 45%,
      rgba(29, 15, 91, 0.12) 100%
    );
  }
}

/* A 60vh slab of placeholder is a lot of nothing on a phone. */
.hero__media {
  min-height: 42vh;
}

@media (min-width: 900px) {
  .hero__media {
    min-height: 56vh;
  }
}

/* 7. The scrim was painting the photograph out completely.
   I asked for background-color to be declared alongside the gradient so a
   contrast check would have a ground to measure. That is right, but it was
   put on the ::before OVERLAY, where an opaque colour hides everything
   beneath it: the hero image was never visible at all, on any page.
   The ground belongs behind the image, on .hero__media itself. The overlay
   then carries only the gradient, and a contrast check still resolves a
   real background by walking up the tree. */
.hero__media {
  background-color: var(--brand-deep);
}

.hero__media::before {
  background-color: transparent;
}

/* 8. .band--deep whitened h2, h3, h4 and .eyebrow but not h1.
   The Cambridge Curriculum page puts its h1 in a deep band, so its headline
   rendered near-black on deep purple: the one line the page exists to say
   was invisible. Same class of failure as the hero, found the same way. */
.band--deep h1,
.band--deep .lede,
.band--deep p,
.band--deep li {
  color: var(--on-brand);
}

/* But not inside a card sitting on that band: those are white cards and
   their text must stay dark. Restated after the rule above so it wins. */
.band--deep .card,
.band--deep .card h1,
.band--deep .card h2,
.band--deep .card h3,
.band--deep .card h4,
.band--deep .card p,
.band--deep .card li,
.band--deep .news-card,
.band--deep .news-card p,
.band--deep .facility,
.band--deep .facility p {
  color: var(--ink);
}

/* 9. Contrast, measured in the browser on the element that actually renders
   rather than on the token declaration. A custom property inherits downward,
   so grepping the CSS proves nothing about what a parent sees.

   .eyebrow used --brand-tint on white: 4.41:1, just under the 4.5:1 floor,
   and it is small uppercase text at 0.8rem which is exactly where a marginal
   ratio hurts. --brand-primary on white is about 9:1. */
.eyebrow {
  color: var(--brand-primary);
}

.band--deep .eyebrow,
.hero .eyebrow,
.site-footer .eyebrow {
  color: var(--brand-gold);
}

/* The footer's column headings were --brand-deep on --brand-deep: a ratio of
   1.0, which is not low contrast, it is the same colour. The stylesheet
   styled .site-footer h4 and the footer markup uses h2. */
.site-footer h2,
.site-footer h3,
.site-footer h4 {
  color: var(--brand-gold);
  font-family: var(--font-display);
  font-size: 0.95rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  margin-block: 0 0.6rem;
}

.site-footer,
.site-footer p,
.site-footer li,
.site-footer address {
  color: rgba(255, 255, 255, 0.92);
}

/* 10. .btn--ghost defaulted to white text and a white border, correct only
   over a dark ground, with a .btn--on-light modifier to opt out. The Gallery
   & News page put one on a white card without the modifier and it rendered
   white on white: "Contact us for cost and enrolment" was invisible, and
   that link is the ONLY route to fee information on the whole site, because
   no fee figure is published.

   A rule that is correct only when an author remembers a modifier will be
   got wrong. Inverted: dark on light is the default, white applies where the
   ground is actually dark. Forgetting now fails safe. */
.btn--ghost {
  border-color: var(--brand-primary);
  color: var(--brand-primary);
}

.hero .btn--ghost,
.band--deep .btn--ghost,
.site-footer .btn--ghost,
.btn--ghost.btn--on-dark {
  border-color: var(--on-brand);
  color: var(--on-brand);
}

/* ...but a white card sitting inside a deep band is a light ground again. */
.band--deep .card .btn--ghost,
.band--deep .news-card .btn--ghost,
.band--deep .facility .btn--ghost {
  border-color: var(--brand-primary);
  color: var(--brand-primary);
}

/* 11. .hero__body carries the .wrap class in every template, but
   `.hero__body { width: 100% }` is declared after .wrap and beats it on
   source order at equal specificity. The result: the hero eyebrow, headline,
   lead and both buttons sat hard against the left edge of the viewport, with
   no gutter, at every width. Restate the wrap geometry for the combined
   selector so it cannot be lost to ordering again. */
.hero__body.wrap {
  width: min(100% - 2 * var(--gutter), var(--wrap));
  margin-inline: auto;
}

/* =====================================================================
   12. Header and footer layout.

   The templates use .site-header__inner, .site-footer__inner,
   .site-footer__brand, .site-footer__col, .site-footer__name,
   .site-footer__legal, .brand, .error-nav and .band--error. NONE of them
   had a single rule in this stylesheet, which had .site-header__bar and
   .site-footer__grid instead, neither of which appears in any template.

   Nothing was broken enough to notice: the header and footer fell back to
   plain block stacking, so every link still rendered and every phone number
   was still tappable. The intended flex header and multi-column footer
   simply never applied, on any page, at any width.
   ================================================================== */

.site-header__inner {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.75rem;
  padding-block: 0.9rem;
}

.brand {
  display: inline-flex;
  align-items: center;
  text-decoration: none;
}

.brand img {
  width: auto;
  height: 68px;
}

@media (min-width: 900px) {
  .site-header__inner {
    flex-direction: row;
    justify-content: space-between;
    align-items: center;
    gap: 1.5rem;
    padding-block: 0.6rem;
  }

  .brand img {
    height: 76px;
  }
}

/* ---------------------------------------------------------------- footer */

.site-footer__inner {
  display: grid;
  gap: 2rem;
  padding-block: 2.75rem 1.75rem;
}

@media (min-width: 640px) {
  .site-footer__inner {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}

@media (min-width: 1000px) {
  .site-footer__inner {
    /* The brand column carries the crest, so it gets the extra room. */
    grid-template-columns: 1.4fr 1fr 1.2fr 0.8fr;
    gap: 2.5rem;
  }
}

.site-footer__brand img {
  height: 84px;
  width: auto;
  margin-bottom: 0.75rem;
}

.site-footer__name {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 1.05rem;
  color: var(--on-brand);
  margin: 0;
}

.site-footer__brand .motto {
  font-size: 1rem;
  margin-block: 0.15rem 0;
}

.site-footer__col address {
  font-style: normal;
  margin: 0 0 0.5rem;
  max-width: 26ch;
}

.site-footer__legal {
  border-top: 1px solid rgba(255, 255, 255, 0.16);
  padding-block: 1rem 1.5rem;
}

.site-footer__legal p {
  margin: 0;
  font-size: 0.9rem;
  color: rgba(255, 255, 255, 0.7);
}

/* ------------------------------------------------------- error pages */

.band--error {
  padding-block: 3rem 4rem;
}

.error-nav {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem 1.25rem;
  margin-block: 1rem 2rem;
}

.error-nav a {
  font-weight: 600;
}

/* =====================================================================
   13. The nav pushed the whole page 692px wide at a 360px viewport.

   Making .site-header__inner a flex row (block 12) turned .site-nav into a
   flex item, and a flex item defaults to min-width: auto, meaning it refuses
   to shrink below the width of its own content. The nav's content is nine
   nowrap pills, 1025px of them, so the header forced the document to 692px
   and every page sat in the left half of the phone with white space beside
   it. overflow-x: auto cannot help: the box was never narrowed in the first
   place.

   min-width: 0 is what lets a flex item be smaller than its content and
   scroll internally, which is the behaviour that was intended all along.
   ================================================================== */

.site-nav {
  min-width: 0;
  max-width: 100%;
  flex: 1 1 auto;
}

/* Belt and braces: nothing in the header may widen the page. */
.site-header,
.site-header__inner {
  max-width: 100%;
}

/* =====================================================================
   14. The nav was showing a 28px sliver, not a fade.

   The scroll affordance was built as two mask layers at
   mask-size: 28px 100%, mask-repeat: no-repeat. A mask only reveals what it
   covers, so a 28px non-repeating layer revealed a 28px strip at the right
   edge and masked out the entire rest of the nav. On the home page that left
   the word "Cur" floating alone where nine pills should be, on every page.

   Replaced with one layer that covers the whole box and fades only the last
   32px. The nine pills never fit under 900px and always fit above it, so the
   fade is applied by width rather than computed from overflow: deterministic,
   and it cannot reveal nothing.

   scroll-padding-inline from the earlier fix still applies, so a focused pill
   scrolls clear of the faded zone rather than being dimmed inside it.
   ================================================================== */

@media (max-width: 899px) {
  .site-nav {
    -webkit-mask-image: linear-gradient(to right, #000 calc(100% - 32px), transparent);
    mask-image: linear-gradient(to right, #000 calc(100% - 32px), transparent);
    -webkit-mask-size: 100% 100%;
    mask-size: 100% 100%;
    -webkit-mask-repeat: no-repeat;
    mask-repeat: no-repeat;
    -webkit-mask-composite: source-over;
    mask-composite: add;
  }
}

@media (min-width: 900px) {
  /* All nine fit, so nothing is off screen and a fade would be a lie. */
  .site-nav {
    -webkit-mask-image: none;
    mask-image: none;
  }
}
