/* ==========================================================================
   OptiGlaze Systems — child theme stylesheet
   --------------------------------------------------------------------------
   Layer order is fixed. Add new rules to the layer they belong to; never
   append an override block at the end of the file. Every selector should
   appear exactly once.

     1. Tokens
     2. Base
     3. Layout primitives
     4. Header
     5. Hero
     6. Components
     7. Footer
     8. Motion
     9. Responsive   (three breakpoints only: 1200 / 900 / 600)

   Brand values are from logo-pack/README.txt. Do not eyeball them.
   ========================================================================== */

/* 0. Fonts =============================================================== */

/* Plus Jakarta Sans is the body and heading face. Variable, so one file covers
   400 through 600. Astra was requesting this from fonts.googleapis.com even
   with its own "load fonts locally" setting switched on - it had never written
   the local files - so it is served from here and Astra's request is dequeued
   in functions.php. */
@font-face {
  font-family: "Plus Jakarta Sans";
  font-style: normal;
  font-weight: 400 600;
  font-display: swap;
  src: url("../fonts/pjs-latin.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+20AC, U+2122,
    U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
  font-family: "Plus Jakarta Sans";
  font-style: normal;
  font-weight: 400 600;
  font-display: swap;
  src: url("../fonts/pjs-latin-ext.woff2") format("woff2");
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7,
    U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F,
    U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F,
    U+A720-A7FF;
}

/* Self-hosted so no visitor IP is sent to Google on page load. Only the
   weight actually used (600) is shipped. */
@font-face {
  font-family: "Poppins";
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url("../fonts/poppins-600-latin.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+20AC, U+2122,
    U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
  font-family: "Poppins";
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url("../fonts/poppins-600-latin-ext.woff2") format("woff2");
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7,
    U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F,
    U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F,
    U+A720-A7FF;
}

/* 1. Tokens ============================================================== */

:root {
  /* Brand — exact values from the logo pack */
  --og-navy: #0d1f3c;
  --og-navy-deep: #12294d;
  --og-blue: #1668c9;
  --og-blue-dark: #12518f;
  --og-blue-light: #4a92e0;

  /* Warm neutrals — these carry the premium feel */
  --og-paper: #faf7f2;
  --og-sand: #ede7dc;
  --og-sand-deep: #d9cfbe;

  /* Ink */
  --og-ink: #16202d;
  --og-ink-soft: rgba(22, 32, 45, 0.74);
  --og-ink-faint: rgba(22, 32, 45, 0.55);
  --og-white: #fff;

  /* Lines and shadow */
  --og-line: rgba(13, 31, 60, 0.12);
  --og-line-warm: rgba(13, 31, 60, 0.16);
  --og-shadow-sm: 0 2px 8px rgba(13, 31, 60, 0.05);
  --og-shadow-md: 0 18px 44px rgba(13, 31, 60, 0.09);
  --og-shadow-lg: 0 30px 70px rgba(13, 31, 60, 0.13);

  /* Measure */
  --og-max: 1240px;
  --og-gutter: clamp(1.25rem, 4vw, 2rem);
  /* One rail for every full-bleed band, so hero, sections, CTA and footer
     share a left edge.

     Viewport units, not percentages: a percentage resolves against each
     element's own containing block, and differently again for padding versus
     margin, so the same token produced a different rail per band. 100vw is
     one global value, so every band lands on the same left edge.

     100vw does include the scrollbar, so content sits about half a scrollbar
     left of true centre. That offset applies equally to every band, so
     nothing misaligns; correcting it would mean a JS-set variable and a
     layout that depends on script, which is not worth it. */
  --og-rail: max(var(--og-gutter), calc((100vw - var(--og-max)) / 2));

  /* Rhythm */
  --og-space-section: clamp(4.5rem, 9vw, 8rem);
  --og-space-tight: clamp(3rem, 6vw, 5rem);

  /* Type scale */
  --og-display: clamp(2.75rem, 5.6vw, 5.25rem);
  /* 3.2vw, not the 3.8vw this started as, so that once .og-heading actually
     wins the cascade (see the body-scoped rule in section 2) the desktop size
     lands on 48px at ~1485px - exactly what Astra had been imposing, and the
     proportion already signed off. The gain is that h2 now scales with the
     viewport the way h1 does, holding the same ~1.75 ratio at every width
     instead of staying pinned at 48px until the phone breakpoint. */
  --og-h2: clamp(2rem, 3.2vw, 3rem);
  --og-h3: clamp(1.4rem, 2vw, 1.85rem);
  --og-h4: clamp(1.15rem, 1.5vw, 1.4rem);
  --og-lead: clamp(1.1rem, 1.35vw, 1.28rem);
  --og-kicker: 0.72rem;

  /* Motion */
  --og-ease: cubic-bezier(0.22, 0.61, 0.36, 1);
  --og-fast: 0.2s;
  --og-slow: 0.6s;

  /* Header. Change the nav size here, not in the component rules. */
  --og-header-h: 104px;
  --og-header-rail: clamp(1.25rem, 3vw, 2.5rem);
  --og-font-fallback: "Plus Jakarta Sans", "Inter", system-ui, sans-serif;
  /* Mark is ~43% of the wordmark width in the supplied artwork; keep that
     ratio so the rebuilt lockup reads as the same logo. */
  --og-mark-size: 62px;
  --og-brand-name-size: 33px;
  --og-brand-tag-size: 10.5px;
  --og-nav-size: 15px;
  --og-nav-gap: clamp(1rem, 1.6vw, 1.75rem);
}

/* 2. Base =============================================================== */

html {
  scroll-behavior: smooth;
  scroll-padding-top: calc(var(--og-header-h) + 1rem);
}

body.optiglaze-site {
  background: var(--og-white);
  color: var(--og-ink);
  font-size: 17px;
  line-height: 1.68;
  -webkit-font-smoothing: antialiased;
}

body.optiglaze-site .site-content {
  overflow: clip;
}

/* Keep base element rules at single-class specificity so component rules
   (.og-hero h1, .og-header-cta) can override them without !important. */
.optiglaze-site :is(h1, h2, h3, h4) {
  color: var(--og-navy);
  letter-spacing: -0.025em;
  text-wrap: balance;
}

.optiglaze-site :is(p, li) {
  font-size: 1rem;
}

.optiglaze-site a {
  color: var(--og-blue);
}

/* Astra ships its own header and footer; we render our own. Hide theirs
   rather than fighting its markup. */
body.optiglaze-site .site-header,
body.optiglaze-site .site-footer {
  display: none !important;
}

/* Strip Astra's page wrapper so full-width block sections can span the
   viewport. Inner width is then controlled by --og-rail alone. */
body.optiglaze-site:is(.page, .error404) .site-content > .ast-container {
  display: block;
  width: 100%;
  max-width: none;
  padding-inline: 0;
}

/* margin: 0 here also removes Astra's 60px .content-area top margin, which
   otherwise leaves a white band between the header and a full-bleed hero. */
body.optiglaze-site:is(.page, .error404) .content-area,
body.optiglaze-site:is(.page, .error404) .site-main,
body.optiglaze-site:is(.page, .error404) article.page,
body.optiglaze-site:is(.page, .error404) .entry-content {
  width: 100%;
  max-width: none;
  margin: 0;
  padding: 0;
}

/* Astra sets this on #primary with higher specificity than the rule above. */
body.optiglaze-site:is(.page, .error404) #primary.content-area {
  margin-top: 0;
  margin-bottom: 0;
}

body.optiglaze-site:is(.page, .error404).ast-separate-container .ast-article-single {
  padding: 0;
}

body.optiglaze-site:is(.page, .error404) .entry-content > .alignfull {
  width: 100%;
  max-width: none;
  margin-inline: 0;
}

/* 3. Layout primitives ================================================== */

.og-section {
  position: relative;
  padding: var(--og-space-section) var(--og-rail);
}

/* Ambient horizontal padding reaches these bands from Astra's container
   variables and from theme.json alignment handling, which is why hero,
   sections, CTA and footer were landing on four different left edges.
   Scoping to body.optiglaze-site puts the shared rail beyond their reach.
   These four selectors are the single source of horizontal alignment. */
body.optiglaze-site .og-section,
body.optiglaze-site .og-hero,
body.optiglaze-site .og-site-footer {
  padding-inline: var(--og-rail);
}

body.optiglaze-site .og-cta {
  width: auto;
  max-width: none;
  /* margin-block is repeated here, not just margin-inline: the CTA is the last
     child of .entry-content, and a *:last-child { margin-bottom: 0 } rule was
     stripping the gap above the footer. This selector out-ranks it. */
  margin-block: 0 clamp(5rem, 9vw, 8rem);
  margin-inline: var(--og-rail);
  padding-inline: clamp(2.4rem, 5vw, 4.25rem);
}

.og-section--tight {
  padding-block: var(--og-space-tight);
}

.og-section--navy {
  background: var(--og-navy);
  color: rgba(255, 255, 255, 0.82);
}

.og-section--navy h1,
.og-section--navy h2,
.og-section--navy h3,
.og-section--navy h4 {
  color: var(--og-white);
}

.og-section--sand {
  background: var(--og-sand);
}

.og-section--paper {
  background: var(--og-paper);
}

/* Legacy aliases — older page content still uses these class names. */
.og-section--stone {
  background: var(--og-sand);
}

.og-section--mist {
  background: var(--og-paper);
}

.og-kicker {
  margin: 0 0 1rem;
  color: var(--og-blue);
  font-size: var(--og-kicker);
  font-weight: 800;
  letter-spacing: 0.19em;
  text-transform: uppercase;
}

.og-section--navy .og-kicker {
  color: var(--og-blue-light);
}

.og-display {
  max-width: 16ch;
  font-size: var(--og-display);
  line-height: 0.98;
  letter-spacing: -0.045em;
}

/* body-scoped for the font-size only. Astra's dynamic CSS emits
   `h2, .entry-content :where(h2) { font-size: 2.66667rem }`, and because
   :where() contributes nothing that selector is (0,1,0) - a dead tie with a
   bare .og-heading, which Astra then wins on document order, its dynamic style
   being printed after the theme stylesheet.

   That is why every h2 on the site was 48px rather than the fluid scale below,
   and why the hierarchy collapsed on phones: 2.66667rem is a rem value, so it
   only shrinks with the root size (18px -> 16.4px, down 9%), while the h1 clamp
   drops to its floor (84px -> 45px, down 46%). The two met in the middle at
   45px against 44px. */
body.optiglaze-site .og-heading {
  font-size: var(--og-h2);
}

.og-heading {
  max-width: 20ch;
  line-height: 1.06;
}

.og-lead {
  max-width: 62ch;
  color: var(--og-ink-soft);
  font-size: var(--og-lead);
  line-height: 1.65;
}

.og-section--navy .og-lead {
  color: rgba(255, 255, 255, 0.72);
}

.og-section > .og-heading + .og-lead {
  margin-top: 1.25rem;
}

/* 4. Header ============================================================= */

/* Single full-width bar: brand left, nav and booking action right.
   Solid white rather than translucent — the logo PNG has a white background
   baked in, which shows as a pale box over a see-through header. */
/* No border at rest: against the dark hero the white bar separates itself, and
   a hairline just reads as a stray rule. Separation appears on scroll instead,
   via .is-scrolled, when the header sits over light page content. */
.og-site-header {
  position: sticky;
  top: 0;
  z-index: 999;
  background: var(--og-white);
  border-bottom: 1px solid transparent;
  transition:
    box-shadow var(--og-fast) var(--og-ease),
    border-color var(--og-fast) var(--og-ease);
}

.admin-bar .og-site-header {
  top: 32px;
}

.og-site-header.is-scrolled {
  border-bottom-color: var(--og-line);
  box-shadow: var(--og-shadow-sm);
}

/* Slightly more room above the logo than below it. Optically the brand needs
   to sit a touch low in the bar, not dead centre. */
.og-site-header__inner {
  position: relative;
  display: grid;
  grid-template-columns: auto 1fr auto;
  align-items: center;
  gap: clamp(1.5rem, 3vw, 2.75rem);
  width: 100%;
  min-height: 103px;
  padding-block: 6px 0;
  padding-inline: var(--og-header-rail);
}

/* Brand lockup, rebuilt from parts.
   The supplied horizontal PNG bakes the tagline into the artwork at ~1/17 of
   its height, so at any sensible header width it renders around 4px tall and
   is unreadable. Here the mark stays an image and the wordmark and tagline are
   live Poppins text, which stays crisp and legible at a compact header size. */
.og-brand {
  display: inline-flex;
  align-items: center;
  gap: 0.7rem;
  max-width: 100%;
  text-decoration: none;
}

.og-brand__mark {
  flex: none;
  width: var(--og-mark-size);
  height: auto;
}

.og-brand__text {
  display: flex;
  flex-direction: column;
  gap: 0.3em;
}

.og-brand__name {
  color: var(--og-navy);
  font-family: Poppins, var(--og-font-fallback);
  font-size: var(--og-brand-name-size);
  font-weight: 600;
  letter-spacing: -0.015em;
  line-height: 1;
}

.og-brand__name span {
  color: var(--og-blue);
}

/* Rules either side, as in the supplied lockup. They flex, so the tagline row
   always matches the width of the wordmark above it. */
.og-brand__tag {
  display: flex;
  align-items: center;
  gap: 0.5em;
  color: var(--og-navy);
  font-family: Poppins, var(--og-font-fallback);
  font-size: var(--og-brand-tag-size);
  font-weight: 600;
  letter-spacing: 0.11em;
  line-height: 1;
  text-transform: uppercase;
  white-space: nowrap;
}

.og-brand__tag::before,
.og-brand__tag::after {
  content: "";
  flex: 1 0 0.6em;
  height: 1px;
  background: var(--og-blue-dark);
}

.og-nav {
  justify-self: end;
}

.og-header-cta {
  justify-self: end;
}

.og-nav__list {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: var(--og-nav-gap);
  margin: 0;
  padding: 0;
  list-style: none;
}

.og-nav__list a {
  color: var(--og-navy);
  font-size: var(--og-nav-size);
  font-weight: 700;
  letter-spacing: 0.03em;
  line-height: 1.3;
  text-decoration: none;
  text-transform: uppercase;
  white-space: nowrap;
  transition: color var(--og-fast) var(--og-ease);
}

/* Vertical padding on the top-level links, so the hover target is the height
   of the bar rather than the 19px of the text itself. inline-block is required:
   vertical padding on an inline box does not grow the line box, so the list
   item stayed 30px tall and the dead gap remained. */
.og-nav__list > li > a {
  display: inline-block;
  padding-block: 0.9rem;
}

.og-nav__list a:hover,
.og-nav__list .current-menu-item > a {
  color: var(--og-blue);
}

/* Dropdowns */
.og-nav__list .menu-item-has-children {
  position: relative;
}

/* Chevron marking the items that have a dropdown. Drawn from two borders
   rather than an icon file, so it inherits the link colour on hover and needs
   no extra request. Decorative only: it is generated content, so it is not
   announced by screen readers, which reach the submenu links directly. */
.og-nav__list .menu-item-has-children > a::after {
  content: "";
  display: inline-block;
  width: 0.4em;
  height: 0.4em;
  margin-left: 0.55em;
  border-right: 2px solid currentColor;
  border-bottom: 2px solid currentColor;
  transform: translateY(-0.16em) rotate(45deg);
  transition: transform var(--og-fast) var(--og-ease);
}

/* Flip it while the panel is open. */
.og-nav__list .menu-item-has-children:hover > a::after,
.og-nav__list .menu-item-has-children:focus-within > a::after,
.og-nav__list .menu-item-has-children.is-sub-open > a::after {
  transform: translateY(0.04em) rotate(-135deg);
}

/* Bridge the gap between the menu item and its dropdown.
   The link is ~19px tall inside a 104px header, so the dropdown opens about
   26px below the item. Crossing that gap left nothing hovered and the menu
   closed before you could reach it. This transparent strip belongs to the
   list item itself, so it keeps :hover alive the whole way down - and being
   on the item rather than the panel, it works even mid transition. */
.og-nav__list .menu-item-has-children::after {
  content: "";
  position: absolute;
  top: 100%;
  left: 0;
  right: 0;
  height: 1.75rem;
}

.og-nav__list .sub-menu {
  position: absolute;
  /* Slightly less than the bridge height above, so the bridge overlaps the
     panel and there is no boundary pixel where neither is hovered. */
  top: calc(100% + 1.5rem);
  left: 50%;
  z-index: 20;
  min-width: 220px;
  margin: 0;
  padding: 0.65rem;
  list-style: none;
  background: var(--og-white);
  border: 1px solid var(--og-line);
  box-shadow: var(--og-shadow-md);
  opacity: 0;
  visibility: hidden;
  transform: translate(-50%, 8px);
  transition:
    opacity var(--og-fast) var(--og-ease),
    transform var(--og-fast) var(--og-ease),
    visibility var(--og-fast) var(--og-ease);
}

/* .is-sub-open is set by theme.js and held briefly after the pointer leaves,
   so a slightly imprecise path to the panel does not close it. The :hover and
   :focus-within selectors remain so the menu still works without JavaScript. */
.og-nav__list .menu-item-has-children:hover > .sub-menu,
.og-nav__list .menu-item-has-children:focus-within > .sub-menu,
.og-nav__list .menu-item-has-children.is-sub-open > .sub-menu {
  opacity: 1;
  visibility: visible;
  transform: translate(-50%, 0);
}

.og-nav__list .sub-menu li {
  border: 0;
}

.og-nav__list .sub-menu a {
  display: block;
  padding: 0.75rem 0.85rem;
  font-size: 0.67rem;
  line-height: 1.35;
}

.og-nav__list .sub-menu a:hover {
  background: var(--og-paper);
}

/* Booking action duplicated inside the menu panel. Hidden on desktop, where the
   header already carries it; shown once the header collapses so the primary
   call to action is never unreachable on a phone. */
.og-nav__cta {
  display: none;
}

.og-menu-toggle {
  display: none;
  grid-column: 3;
  justify-self: end;
  padding: 0.5rem;
  background: transparent;
  border: 0;
  cursor: pointer;
}

.og-menu-toggle span:not(.screen-reader-text) {
  display: block;
  width: 24px;
  height: 2px;
  margin: 5px 0;
  background: var(--og-navy);
  transition: transform var(--og-fast) var(--og-ease), opacity var(--og-fast);
}

.og-menu-toggle[aria-expanded="true"] span:nth-of-type(2) {
  transform: translateY(7px) rotate(45deg);
}

.og-menu-toggle[aria-expanded="true"] span:nth-of-type(3) {
  opacity: 0;
}

.og-menu-toggle[aria-expanded="true"] span:nth-of-type(4) {
  transform: translateY(-7px) rotate(-45deg);
}

/* 5. Hero =============================================================== */

.og-hero {
  position: relative;
  isolation: isolate;
  display: grid;
  align-items: end;
  min-height: clamp(640px, 80vh, 840px);
  overflow: hidden;
  padding-inline: var(--og-rail);
  color: var(--og-white);
  background-color: var(--og-navy);
}

/* Layering, back to front:
     ::before  the photo, drifting slowly (z -2)
     ::after   the scrim that keeps the headline legible (z -1)
     content   at the default layer
   The photo must never sit above the scrim, or the text loses contrast. */
.og-hero::before {
  content: "";
  position: absolute;
  inset: -4%;
  z-index: -2;
  background-image: var(--og-hero-image, none);
  background-position: center;
  background-size: cover;
  animation: og-drift 24s var(--og-ease) infinite alternate;
}

.og-hero::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  background:
    linear-gradient(to top, rgba(6, 15, 30, 0.58), transparent 54%),
    linear-gradient(
      95deg,
      rgba(13, 31, 60, 0.92) 0%,
      rgba(13, 31, 60, 0.76) 42%,
      rgba(13, 31, 60, 0.34) 76%,
      rgba(13, 31, 60, 0.16) 100%
    );
}

.og-hero__inner {
  width: 100%;
  max-width: var(--og-max);
  margin-inline: auto;
  padding-block: clamp(7rem, 15vh, 11rem) clamp(4rem, 9vh, 7rem);
}

.og-hero__inner > * {
  max-width: 680px;
}

.og-hero .og-kicker {
  color: var(--og-blue-light);
}

/* Measure is in em so it tracks the clamped font size and holds the same wrap
   at every viewport width.

   For the homepage headline, measured in Plus Jakarta Sans 600:
     "doors and conservatories,"        = 12.57em  -> lower bound
     "Replacement windows, doors"       = 14.36em  -> upper bound
   Below 12.57em the longest line cannot fit and the heading breaks to four
   lines; at or above 14.36em "doors" pulls onto line one and the wrap goes
   lopsided. 13.4em sits mid-window and gives three balanced lines.

   If the headline copy changes, re-measure before adjusting this. */
.og-hero h1 {
  max-width: 13.4em;
  margin-bottom: 1.4rem;
  color: var(--og-white);
  font-size: var(--og-display);
}

.og-hero p {
  max-width: 58ch;
  color: rgba(255, 255, 255, 0.84);
  font-size: clamp(1.08rem, 1.45vw, 1.24rem);
  line-height: 1.62;
}

.og-hero .wp-block-buttons {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.85rem;
  margin-top: 2rem;
}

@keyframes og-drift {
  from {
    transform: scale(1) translate3d(0, 0, 0);
  }
  to {
    transform: scale(1.07) translate3d(-1.2%, -0.8%, 0);
  }
}

/* Shorter hero for pages that are all text - legal pages and similar, where a
   full-height photo band would just delay the content. */
/* Body-scoped to clear Astra, which sets

     .entry-content > .wp-block-group { padding: var(--wp--custom--ast-default-block-top-padding) }

   - 100px on all four sides. The base .og-hero only overrides padding-inline,
   so every hero has been carrying 100px top and bottom from Astra. On the
   full-height hero that is invisible inside a 640px minimum; on a short one it
   was most of the height. Measured on the booking page: 154px from the top of
   the band to the kicker, of which 100px was this.

   Zeroed here so the inner padding below is the whole story. */
body.optiglaze-site .og-hero--short {
  min-height: 0;
  padding-block: 0;
}

.og-hero--short .og-hero__inner {
  padding-block: clamp(3rem, 6vh, 4.5rem) clamp(2.5rem, 4vh, 3.25rem);
}

/* Display size is set for a marketing headline. On a legal page it just pushes
   the content people came for below the fold. */
.og-hero--short .og-display {
  max-width: 22ch;
  font-size: clamp(2.25rem, 3.6vw, 3.4rem);
}

/* 404 page. The hero photo is a front door, which is the joke; keep the
   overlay lighter than usual so it reads as a door rather than a mood shot. */
.og-404::after {
  background:
    linear-gradient(to top, rgba(6, 15, 30, 0.45), transparent 60%),
    linear-gradient(
      95deg,
      rgba(13, 31, 60, 0.86) 0%,
      rgba(13, 31, 60, 0.62) 45%,
      rgba(13, 31, 60, 0.2) 100%
    );
}

.og-404 .wp-block-buttons {
  display: flex;
  flex-wrap: wrap;
  gap: 0.85rem;
  margin-top: 2rem;
}

.og-404__links {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 0.75rem 1.5rem;
  margin: clamp(1.75rem, 3vw, 2.5rem) 0 0;
  padding: 0;
  list-style: none;
}

.og-404__links a {
  display: block;
  padding: 0.9rem 1.1rem;
  background: var(--og-white);
  border: 1px solid var(--og-line);
  color: var(--og-navy);
  font-weight: 700;
  text-decoration: none;
  transition:
    border-color var(--og-fast) var(--og-ease),
    transform var(--og-fast) var(--og-ease);
}

.og-404__links a:hover {
  border-color: var(--og-blue);
  color: var(--og-blue);
  transform: translateY(-2px);
}

/* Long-form text. Held to a comfortable reading measure and given its own
   vertical rhythm, since legal copy is mostly headings, paragraphs and lists. */
.og-prose > * {
  max-width: 68ch;
}

.og-prose h2 {
  margin-top: clamp(2.5rem, 5vw, 3.5rem);
  margin-bottom: 0.75rem;
  font-size: clamp(1.35rem, 2vw, 1.75rem);
}

.og-prose h2:first-of-type {
  margin-top: 1.5rem;
}

.og-prose p,
.og-prose li {
  color: var(--og-ink-soft);
  line-height: 1.75;
}

.og-prose p {
  margin-bottom: 1.1rem;
}

.og-prose ul {
  margin: 0 0 1.5rem;
  padding-left: 1.2rem;
}

.og-prose li {
  margin-bottom: 0.5rem;
}

.og-prose strong {
  color: var(--og-ink);
}

/* 6. Components ========================================================= */

/* Buttons */
body.optiglaze-site .wp-block-button__link {
  /* inline-block is stated rather than inherited from core's block stylesheet:
     that stylesheet is only enqueued when a page actually contains blocks, so
     on template-rendered pages like 404.php the padded link overflowed its
     wrapper and collided with the button below it. */
  display: inline-block;
  border-radius: 0;
  font-weight: 700;
  letter-spacing: 0.02em;
  transition:
    transform var(--og-fast) var(--og-ease),
    box-shadow var(--og-fast) var(--og-ease),
    background-color var(--og-fast) var(--og-ease);
}

body.optiglaze-site .wp-block-button:not(.is-style-outline) .wp-block-button__link {
  background: var(--og-blue);
  color: var(--og-white);
}

body.optiglaze-site .wp-block-button:not(.is-style-outline) .wp-block-button__link:hover {
  background: var(--og-blue-dark);
  transform: translateY(-2px);
  box-shadow: 0 14px 30px rgba(22, 104, 201, 0.26);
}

/* Outline buttons. Astra injects its own accent background here, so this has
   to be explicit rather than relying on the core outline style. */
/* .wp-element-button is included to reach (0,5,1) and clear Astra's own outline
   rule at (0,5,0). The border colour is stated outright rather than left to
   currentColor, which Astra was resolving to transparent. */
body.optiglaze-site .wp-block-button.is-style-outline > .wp-block-button__link.wp-element-button {
  background-color: transparent;
  background-image: none;
  color: var(--og-navy);
  border: 1px solid var(--og-navy);
}

body.optiglaze-site
  .wp-block-button.is-style-outline
  > .wp-block-button__link.wp-element-button:hover {
  background-color: var(--og-navy);
  color: var(--og-white);
  border-color: var(--og-navy);
}

.og-hero .wp-block-button__link,
.og-cta .wp-block-button__link {
  min-width: 190px;
  text-align: center;
}

/* The 680px measure above is there to keep headline and body copy readable.
   It must not apply to the button row: two buttons come to ~670px and were
   wrapping by ten pixels inside the 660px column. */
.og-hero__inner > .wp-block-buttons {
  max-width: none;
}

body.optiglaze-site
  .og-hero
  .wp-block-button.is-style-outline
  > .wp-block-button__link.wp-element-button {
  color: var(--og-white);
  border-color: rgba(255, 255, 255, 0.72);
}

body.optiglaze-site
  .og-hero
  .wp-block-button.is-style-outline
  > .wp-block-button__link.wp-element-button:hover {
  background-color: rgba(255, 255, 255, 0.12);
  color: var(--og-white);
  border-color: var(--og-white);
}

/* Header / footer action button.
   Typed as a.* so it outranks the base `.optiglaze-site a` colour rule. */
a.og-header-cta,
a.og-footer-book {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: 48px;
  min-width: 182px;
  padding: 0.85rem 1.25rem;
  background: var(--og-blue);
  color: var(--og-white);
  font-size: 0.71rem;
  font-weight: 800;
  letter-spacing: 0.075em;
  text-decoration: none;
  text-transform: uppercase;
  white-space: nowrap;
  transition: background-color var(--og-fast) var(--og-ease);
}

a.og-header-cta:hover,
a.og-footer-book:hover {
  background: var(--og-blue-dark);
  color: var(--og-white);
}

/* Service cards.
   The image is supplied by the block markup as an <img>, NOT hardcoded here —
   so it stays editable in WordPress. */
.og-grid-3 {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: clamp(1.25rem, 2.5vw, 2rem);
  width: 100%;
  margin-top: clamp(2rem, 4vw, 3.5rem);
}

/* WordPress blockGap adds margin-block-start to every sibling after the first.
   In flow layout that is the gap; inside a grid it just pushes items 2..n down
   and breaks the top alignment. The grid's own gap handles spacing here. */
.og-grid-3 > *,
.og-split > * {
  margin-block: 0;
}

.og-grid-2 {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: clamp(1.25rem, 2.5vw, 2rem);
  width: 100%;
  margin-top: clamp(2rem, 4vw, 3.5rem);
}

.og-grid-2 > * {
  margin-block: 0;
}

.og-card {
  position: relative;
  display: flex;
  flex-direction: column;
  height: 100%;
  overflow: hidden;
  background: var(--og-white);
  border: 1px solid var(--og-line);
  box-shadow: var(--og-shadow-sm);
  transition:
    transform var(--og-fast) var(--og-ease),
    box-shadow var(--og-fast) var(--og-ease);
}

.og-card:hover {
  transform: translateY(-4px);
  box-shadow: var(--og-shadow-md);
}

.og-card__media {
  aspect-ratio: 4 / 3;
  overflow: hidden;
  background: var(--og-sand);
}

/* The image block wraps the img in a figure, which has to fill the box or the
   picture collapses to its intrinsic height. */
.og-card__media figure,
.og-card__media .wp-block-image {
  height: 100%;
  margin: 0;
}

.og-card__media img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition: transform var(--og-slow) var(--og-ease);
}

.og-card:hover .og-card__media img {
  transform: scale(1.05);
}

/* Cards work in two shapes:
     structured — .og-card > .og-card__media + .og-card__body  (preferred)
     flat       — .og-card > headings and paragraphs directly  (current pages)
   Both must look right, so existing page content is never forced to change. */
.og-card__body,
.og-card:not(:has(.og-card__body)) {
  display: flex;
  flex-direction: column;
  flex: 1;
  padding: clamp(1.5rem, 2.6vw, 2.25rem);
}

.og-card h3 {
  margin-top: 0.45rem;
  font-size: var(--og-h3);
  line-height: 1.1;
}

.og-card p {
  color: var(--og-ink-soft);
}

.og-card p:last-child {
  margin-top: auto;
  padding-top: 1.25rem;
}

.og-card a {
  font-weight: 700;
  text-decoration: none;
}

.og-card a:hover {
  text-decoration: underline;
}

/* Accent rule across the top of the card, fills on hover */
.og-card::before {
  content: "";
  position: absolute;
  top: 0;
  left: 0;
  z-index: 2;
  width: 100%;
  height: 3px;
  background: var(--og-blue);
  transform: scaleX(0.25);
  transform-origin: left;
  transition: transform var(--og-fast) var(--og-ease);
}

.og-card:hover::before {
  transform: scaleX(1);
}

/* Split — image one side, copy the other */
.og-split {
  display: grid;
  grid-template-columns: minmax(0, 0.95fr) minmax(0, 1.05fr);
  align-items: center;
  gap: clamp(3rem, 7vw, 7.5rem);
  width: 100%;
}

.og-split--reverse > :first-child {
  order: 2;
}

.og-split p,
.og-split li {
  line-height: 1.7;
}

.og-split li + li {
  margin-top: 0.45rem;
}

/* Set --og-frame-image on the block to change the picture, or drop a real
   <img> inside it. The default keeps the section from rendering empty. */
.og-image-frame {
  position: relative;
  min-height: clamp(440px, 45vw, 640px);
  overflow: hidden;
  background-color: var(--og-sand);
  background-image: var(
    --og-frame-image,
    url("../images/projects/feature-windows.webp")
  );
  background-position: center;
  background-size: cover;
  box-shadow: var(--og-shadow-lg);
}

.og-image-frame img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* Offset outline, the detail that reads as "designed" */
.og-image-frame::before {
  content: "";
  position: absolute;
  inset: 8% -7% -7% 12%;
  z-index: -1;
  border: 1px solid rgba(22, 104, 201, 0.22);
}

/* Process timeline */
.og-process {
  border-left: 1px solid var(--og-sand-deep);
  padding-left: 2.5rem;
}

.og-process > * {
  position: relative;
  padding: 0 0 2.4rem 0.35rem;
}

.og-process > *:last-child {
  padding-bottom: 0;
}

.og-process > *::before {
  content: "";
  position: absolute;
  top: 0.38rem;
  left: -2.95rem;
  width: 0.82rem;
  height: 0.82rem;
  border-radius: 50%;
  background: var(--og-blue);
  box-shadow: 0 0 0 7px var(--og-sand);
}

.og-process h4 {
  margin-bottom: 0.55rem;
  font-size: var(--og-h4);
  line-height: 1.2;
}

.og-process p {
  color: var(--og-ink-soft);
  font-size: 0.98rem;
  line-height: 1.65;
}

/* Projects gallery. Core's gallery uses flex with percentage widths; a grid
   gives even rows regardless of how many images are in the last one. */
.og-gallery.wp-block-gallery {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: clamp(0.75rem, 1.5vw, 1.25rem);
  margin-top: clamp(2rem, 4vw, 3.5rem);
}

/* Core sizes gallery figures with
     .wp-block-gallery.has-nested-images figure.wp-block-image:not(#individual-image)
   which carries ID-level specificity (1,3,1) on purpose. The :not(#…) is
   repeated here so the grid can size its own tracks; without it the figures
   keep core's flex-basis and collapse to ~119px. */
.og-gallery.wp-block-gallery.has-nested-images figure.wp-block-image:not(#individual-image) {
  width: 100%;
  flex-basis: auto;
  margin: 0;
}

.og-gallery .wp-block-image {
  overflow: hidden;
  background: var(--og-sand);
}

.og-gallery .wp-block-image img {
  display: block;
  width: 100%;
  aspect-ratio: 4 / 3;
  object-fit: cover;
  transition: transform var(--og-slow) var(--og-ease);
}

.og-gallery .wp-block-image:hover img {
  transform: scale(1.04);
}

/* Contact form. SureForms renders its own markup; these keep it in keeping
   with the rest of the site without fighting the plugin's internals. */
.og-contact__form .srfm-form-container,
.og-contact__form form {
  margin-top: 1.5rem;
}

/* Booking page form.
   On the contact page the form sits in a two-column split, which constrains it.
   The booking page has no second column, so without a cap the fields stretch to
   the full content width - measured at 1225px, with 605px name and email boxes.
   That reads badly and looks unfinished.

   Capped and boxed so the page presents one thing to complete. Body-scoped
   because Astra's dynamic CSS is printed after this stylesheet and wins ties,
   which has caught the h2 sizing and the brochure list already. */
body.optiglaze-site .og-booking__form {
  max-width: 730px;
  margin-inline: auto;
  padding: clamp(1.5rem, 4vw, 2.75rem);
  background: var(--og-white);
  border: 1px solid var(--og-line);
  box-shadow: var(--og-shadow-sm);
}

/* The form is the first thing in the box, so the 1.5rem it carries on the
   contact page would sit on top of the padding here. */
body.optiglaze-site .og-booking__form .srfm-form-container,
body.optiglaze-site .og-booking__form form {
  margin-top: 0;
}

/* The split defaults to vertically centred, which is right for image-and-copy
   sections but leaves the contact details floating beside a tall form. */
.og-split:has(.og-contact__form) {
  align-items: start;
}

.og-contact__details p {
  margin-bottom: 1.1rem;
}

.og-contact__details strong {
  color: var(--og-navy);
}

/* Areas covered */
.og-location-list {
  display: flex;
  flex-wrap: wrap;
  gap: 0.65rem;
  margin-top: 1.5rem;
  padding: 0;
  list-style: none;
}

.og-location-list li {
  padding: 0.55rem 0.8rem;
  background: rgba(255, 255, 255, 0.66);
  border: 1px solid var(--og-line-warm);
  font-size: 0.85rem;
}

/* Closing CTA band */
.og-cta {
  margin-inline: var(--og-rail);
  margin-block: 0 clamp(5rem, 9vw, 8rem);
  padding: clamp(2.4rem, 5vw, 4.25rem);
  background: linear-gradient(135deg, var(--og-navy) 0%, var(--og-navy-deep) 100%);
  color: var(--og-white);
}

.og-cta__grid {
  display: grid;
  grid-template-columns: 1fr auto;
  align-items: center;
  gap: 2rem;
  width: 100%;
}

.og-cta h2 {
  margin-bottom: 0.5rem;
  color: var(--og-white);
  font-size: clamp(1.8rem, 3vw, 2.8rem);
}

.og-cta p {
  margin: 0;
  color: rgba(255, 255, 255, 0.75);
  font-size: 1rem;
}

.og-cta__action {
  align-self: center;
  justify-self: end;
}

/* FAQ */
.og-faq details {
  border-top: 1px solid var(--og-line);
  padding: 1.1rem 0;
}

.og-faq details:last-child {
  border-bottom: 1px solid var(--og-line);
}

.og-faq summary {
  color: var(--og-navy);
  cursor: pointer;
  font-weight: 700;
}

/* Scheme logo, e.g. Certass on the building regulations page.
   Sits between the kicker and the heading, so the margins are tight above and
   generous below. Capped in px rather than a percentage: this is a wordmark and
   it should not grow with the column. */
.og-scheme-logo {
  margin: 0.9rem 0 1.4rem;
}

.og-scheme-logo img {
  display: block;
  width: 100%;
  max-width: 190px;
  height: auto;
}

/* Brochure downloads.
   Supplier literature, so the list is deliberately plain - these are documents
   to fetch, not products to sell. The meta line carries the file size because
   one of these is 17MB and nobody should discover that mid-download. */
/* Body-scoped for the same reason as .og-heading in section 2: Astra's dynamic
   CSS is printed after this stylesheet, so its `.entry-content ul` padding and
   its link underline win any tie. Measured before the fix: padding-left 20px
   and an underline neither of which is wanted here. */
body.optiglaze-site .og-brochures__list {
  margin-top: 2rem;
  padding-left: 0;
  list-style: none;
}

.og-brochures__list li {
  padding-block: 1rem;
  border-bottom: 1px solid var(--og-line);
}

.og-brochures__list li:first-child {
  border-top: 1px solid var(--og-line);
}

body.optiglaze-site .og-brochures__list a {
  display: inline-block;
  color: var(--og-navy);
  font-size: 1.06rem;
  font-weight: 700;
  text-decoration: none;
}

body.optiglaze-site .og-brochures__list a:hover,
body.optiglaze-site .og-brochures__list a:focus-visible {
  color: var(--og-blue-dark);
  text-decoration: underline;
}

/* Typed as a span so it beats nothing in particular - it just needs to read as
   secondary to the link above it. */
.og-brochure__meta {
  display: block;
  margin-top: 0.2rem;
  color: var(--og-ink-soft);
  font-size: 0.88rem;
}

/* 7. Footer ============================================================= */

.og-site-footer {
  padding-inline: var(--og-rail);
  background: var(--og-navy);
  color: rgba(255, 255, 255, 0.7);
  font-size: 0.96rem;
}

.og-site-footer__top {
  display: grid;
  grid-template-columns: 1.5fr 0.75fr 1fr 1fr;
  gap: clamp(2rem, 5vw, 5rem);
  width: 100%;
  max-width: var(--og-max);
  margin-inline: auto;
  padding-block: clamp(4.5rem, 8vw, 7rem) clamp(3rem, 6vw, 5rem);
}

.og-site-footer__brand img {
  width: 185px;
  height: auto;
  margin-bottom: 1.25rem;
  filter: brightness(0) invert(1);
  opacity: 0.96;
}

.og-site-footer__brand p {
  max-width: 390px;
}

.og-site-footer ul {
  margin: 0;
  padding: 0;
  list-style: none;
}

.og-site-footer li {
  margin-bottom: 0.65rem;
}

.og-site-footer p,
.og-site-footer li,
.og-site-footer a {
  font-size: 0.93rem;
  line-height: 1.7;
}

.og-site-footer a {
  color: rgba(255, 255, 255, 0.82);
  text-decoration: none;
  transition: color var(--og-fast) var(--og-ease);
}

.og-site-footer a:hover {
  color: var(--og-white);
}

.og-footer-label {
  margin-bottom: 1.4rem;
  color: var(--og-white);
  font-size: 0.72rem;
  font-weight: 800;
  letter-spacing: 0.16em;
  text-transform: uppercase;
}

.og-footer-book {
  margin-top: 1.2rem;
}

.og-site-footer__bottom {
  display: flex;
  justify-content: space-between;
  gap: 1rem;
  width: 100%;
  max-width: var(--og-max);
  margin-inline: auto;
  padding-block: 1.5rem;
  border-top: 1px solid rgba(255, 255, 255, 0.11);
}

.og-site-footer__bottom,
.og-site-footer__bottom a,
.og-site-footer__bottom p {
  font-size: 0.82rem;
}

.og-site-footer__bottom p {
  margin: 0;
}

/* Two lines: copyright, then the registered company details. */
.og-site-footer__legal {
  display: flex;
  flex-direction: column;
  gap: 0.3rem;
}

.og-site-footer__legal p:last-child {
  color: rgba(255, 255, 255, 0.55);
}

.og-site-footer__legal-links {
  display: flex;
  gap: 1.4rem;
  flex: none;
}

/* 8. Motion ============================================================= */

.og-motion-ready .og-reveal {
  opacity: 0;
  transform: translateY(22px);
  transition:
    opacity var(--og-slow) var(--og-ease),
    transform var(--og-slow) var(--og-ease);
}

.og-motion-ready .og-reveal.is-visible {
  opacity: 1;
  transform: none;
}

/* Visible keyboard focus, on every interactive element */
body.optiglaze-site :is(a, button, summary, input, textarea, select):focus-visible {
  outline: 2px solid var(--og-blue);
  outline-offset: 3px;
}

/* 9. Responsive ========================================================= */

/* --- 1440px: tighten the header before it runs out of room ---

   Measured on the live homepage at 1380px: brand 260px, the seven nav items
   782px, the booking button 225px, plus two 41px grid gaps and two 41px rails
   comes to 1433px. The grid does not resolve that shortfall gracefully. The nav
   track is 1fr, whose automatic minimum is its own min-content, so rather than
   the nav giving way the third track is squeezed to its 182px min-width and the
   button - inline-flex, wider than that - overflows leftwards. The result was
   "Contact" ending at 1125px and the button starting at 1124px: touching, with
   no gap at all.

   Tightening the rail, the grid gap, the nav gap and the button's own padding
   buys back about 150px, which carries the full nav down to the menu-button
   breakpoint below with room to spare. */
/* Bounded below at 1321 so it stops exactly where the menu button takes over -
   otherwise the rail override would also reach the open mobile drawer, whose
   padding reads var(--og-header-rail). */
@media (min-width: 1321px) and (max-width: 1440px) {
  .og-site-header__inner {
    --og-header-rail: 1.5rem;
    --og-nav-gap: 0.75rem;
    gap: 1.5rem;
  }

  /* Typed to match a.og-header-cta above, or the base padding wins. */
  a.og-header-cta {
    padding-inline: 1rem;
  }
}

/* --- 1320px: desktop nav no longer fits, collapse to the menu button --- */
/* 1320 rather than 1200: at 15px the seven nav items need more room before
   they crowd the booking button. Raised from 1280 once the arithmetic above
   was done - even fully tightened, the nav needs ~1304px, so 1280 was inside
   the range where it overflowed. */
@media (max-width: 1320px) {
  :root {
    --og-header-h: 84px;
    --og-mark-size: 42px;
    --og-brand-name-size: 27px;
    --og-brand-tag-size: 9px;
  }

  .admin-bar .og-site-header {
    top: 46px;
  }

  .og-site-header__inner {
    grid-template-columns: auto 1fr auto auto;
    min-height: 84px;
  }

  .og-header-cta {
    grid-column: 3;
  }

  .og-menu-toggle {
    display: block;
    grid-column: 4;
  }

  /* justify-self: stretch is load-bearing. The desktop rule sets
     `justify-self: end` to push the nav to the right of the header grid, and
     that keeps applying to the drawer: self-alignment still applies to an
     absolutely positioned grid child, and `end` makes it shrink-to-fit and sit
     against the right edge. So despite `left: 0; right: 0` the panel opened
     208px wide down the right-hand side of a 390px phone instead of spanning
     the screen. Resetting to stretch lets the two offsets resolve the width,
     which is what they were written to do. */
  .og-nav {
    display: none;
    position: absolute;
    justify-self: stretch;
    top: 100%;
    left: 0;
    right: 0;
    background: var(--og-white);
    border-top: 1px solid var(--og-line);
    box-shadow: var(--og-shadow-md);
  }

  .og-nav.is-open {
    display: block;
  }

  .og-nav__list {
    display: block;
    width: 100%;
    padding: 1.1rem var(--og-header-rail) 1.5rem;
  }

  .og-nav__list > li {
    border-bottom: 1px solid var(--og-line);
  }

  .og-nav__list a,
  .og-nav__list > li > a {
    display: block;
    padding: 0.9rem 0.25rem;
    font-size: var(--og-nav-size);
  }

  /* No hover bridge in the stacked menu - it is tap-driven, and the strip
     would add dead space between items. */
  .og-nav__list .menu-item-has-children::after {
    display: none;
  }

  /* No chevron either: the sub-items are already listed underneath, so an
     arrow would imply something collapsible that is not. */
  .og-nav__list .menu-item-has-children > a::after {
    display: none;
  }

  .og-nav__list .sub-menu {
    position: static;
    left: auto;
    min-width: 0;
    padding: 0 0 0.65rem 1rem;
    background: transparent;
    border: 0;
    box-shadow: none;
    opacity: 1;
    visibility: visible;
    transform: none;
    /* Nothing moves or fades in the drawer - the sub-items are always visible -
       so the inherited transition would only animate the jump fixed below. */
    transition: none;
  }

  /* The desktop open-state rule centres the panel under its parent with
     `left: 50%; transform: translate(-50%, 0)`, and it is declared outside any
     media query, so it reaches the drawer too. Worse, it outranks the reset
     above: `.menu-item-has-children.is-sub-open > .sub-menu` is four classes
     against two. The panel therefore sat correctly indented until it was
     hovered, focused, or tapped (theme.js adds .is-sub-open), then jumped left
     by half its own width.

     `left` is inert while the panel is static, but it is cleared anyway so the
     two properties cannot disagree if this ever stops being static. Repeating
     all three selectors matches their specificity exactly; being later in the
     stylesheet wins the tie. */
  .og-nav__list .menu-item-has-children:hover > .sub-menu,
  .og-nav__list .menu-item-has-children:focus-within > .sub-menu,
  .og-nav__list .menu-item-has-children.is-sub-open > .sub-menu {
    left: auto;
    transform: none;
  }

  .og-nav__list .sub-menu a {
    padding: 0.6rem 0.25rem;
    color: var(--og-ink-soft);
  }

  /* Typed as a.* for the same reason as a.og-header-cta above: `.optiglaze-site
     a` sets the link colour at equal specificity, so an untyped .og-nav__cta
     lost the tie and the label rendered blue on the blue button - invisible. */
  a.og-nav__cta {
    display: flex;
    align-items: center;
    justify-content: center;
    min-height: 52px;
    margin: 0 var(--og-header-rail) 1.5rem;
    background: var(--og-blue);
    color: var(--og-white);
    font-size: 0.72rem;
    font-weight: 800;
    letter-spacing: 0.075em;
    text-decoration: none;
    text-transform: uppercase;
  }

  a.og-nav__cta:hover {
    background: var(--og-blue-dark);
    color: var(--og-white);
  }
}

/* --- 900px: multi-column layouts become single column --- */
@media (max-width: 900px) {
  .og-grid-3 {
    grid-template-columns: 1fr;
  }

  .og-grid-2 {
    grid-template-columns: 1fr;
  }

  .og-gallery.wp-block-gallery {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }

  .og-404__links {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }

  .og-split {
    grid-template-columns: 1fr;
  }

  .og-split--reverse > :first-child {
    order: initial;
  }

  .og-image-frame {
    min-height: 380px;
  }

  .og-image-frame::before {
    display: none;
  }

  .og-cta__grid {
    grid-template-columns: 1fr;
  }

  .og-cta__action {
    justify-self: start;
  }

  .og-site-footer__top {
    grid-template-columns: 1fr 1fr;
  }
}

/* --- 600px: mobile --- */
@media (max-width: 600px) {
  :root {
    --og-header-h: 72px;
    --og-gutter: 1.1rem;
    --og-mark-size: 36px;
    --og-brand-name-size: 23px;
    --og-brand-tag-size: 8px;
  }

  body.optiglaze-site {
    font-size: 16px;
  }

  /* Brand and toggle only; the booking action lives in the open menu instead. */
  .og-site-header__inner {
    grid-template-columns: auto 1fr auto;
    min-height: 72px;
  }

  /* Must match a.og-header-cta's specificity, or its inline-flex wins and the
     button overlaps the menu toggle. */
  a.og-header-cta {
    display: none;
  }

  .og-menu-toggle {
    grid-column: 3;
  }

  .og-hero {
    min-height: 640px;
    background-position: 68% center;
  }

  /* The base padding is 15vh, which suits a desktop hero sized to the viewport.
     On a phone the hero is taller than the screen anyway - 987px of content in
     an 844px window - so 15vh (127px) just pushed the headline down and the
     buttons further below the fold. 8vh gives about 68px, trimming roughly 60px
     from above the kicker while keeping the hero from feeling cramped. */
  .og-hero__inner {
    padding-block-start: clamp(3rem, 8vh, 6rem);
  }

  .og-hero .wp-block-buttons {
    align-items: stretch;
  }

  .og-hero .wp-block-button,
  .og-hero .wp-block-button__link {
    width: 100%;
  }

  .og-image-frame {
    min-height: 300px;
  }

  .og-site-footer__top {
    grid-template-columns: 1fr;
  }

  .og-site-footer__bottom {
    display: block;
  }

  .og-site-footer__legal-links {
    margin-top: 0.9rem;
  }
}

/* --- Reduced motion --- */
@media (prefers-reduced-motion: reduce) {
  html {
    scroll-behavior: auto;
  }

  .og-hero::before {
    animation: none;
  }

  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    transition-duration: 0.01ms !important;
  }
}
