@charset "utf-8";

/*
  Google Fonts (DM Sans, Poppins) are loaded via <link> tags in each page's
  <head>, not @import here - @import blocks on style.css being fetched and
  parsed first, adding a full extra round-trip before the fonts even start
  downloading.

  Heading font on the live site is "termina" (a paid TypeNetwork font, loaded via
  Adobe/Typekit or a licensed webfont). Substituted here with Poppins (free, similar
  geometric-grotesque proportions) as a close stand-in until you supply the licensed
  font files or a Google Fonts equivalent.
*/

:root {
  --ink: #262320;
  --body-text: #6f6d6d;
  --dark: #1a1a1a;
  --cream: #efe9de;
  --teal: #67bcc1;
  --brown: #917e67;
  --dark-cream: #d8d1c5;
  --border: #e4ded1;
  --white: #ffffff;
  --font-heading: "Poppins", sans-serif;
  --font-body: "DM Sans", sans-serif;
  --row-max: 1200px;
  --narrow-max: 760px;
  --space: 90px;     /* between blocks */
  --space-sm: 50px;  /* under an Intro, before the block it introduces */
  --radius: 4px;
}

* { box-sizing: border-box; }
img { max-width: 100%; display: block; }
a { text-decoration: none; color: var(--teal); }

body {
  margin: 0;
  font-family: var(--font-body);
  font-size: 16px;
  line-height: 1.6;
  color: var(--body-text);
  background: var(--white);
  /* Full-bleed touch scrollers (compare table, image cards, gallery carousel,
     portfolio grid) break out of their ancestor's padding with a negative
     margin. Belt-and-braces safety net in case any of them ever ends up
     wider than the viewport - without this, that would turn the whole page
     horizontally scrollable, so a swipe on the scroller drags the page
     instead of just the scroller. */
  overflow-x: hidden;
}

h1, h2, h3, h4 {
  font-family: var(--font-heading);
  color: var(--ink);
  font-weight: 600;
  line-height: 1.2;
  margin: 0 0 20px;
}

h1 { font-size: 46px; }
h2 { font-size: 36px; }
h3 { font-size: 20px; }

p { margin: 0 0 15px; }

.row {
  width: 100%;
  max-width: var(--row-max);
  margin: 0 auto;
  padding: 0 25px;
}

/* p.cms-editor-eyebrow is the CMS editor's "Eyebrow" paragraph style */
.eyebrow,
p.cms-editor-eyebrow {
  text-transform: uppercase;
  letter-spacing: 2px;
  font-size: 13px;
  font-weight: 600;
  color: var(--teal);
  margin-bottom: 12px;
  display: block;
}

p.cms-editor-quote {
	font-family: var(--font-heading);
	font-size: 20px;
	font-style: italic;
	line-height: 1.5;
	color: var(--ink);
	border-left: 3px solid var(--teal);
	padding-left: 20px;
	margin: 30px 0;
}

/* Back to back quotes read as one: the -30px top margin cancels the 30px
   margin below the previous quote, and padding keeps the paragraph spacing
   inside the border so the left rule has no gap. */
p.cms-editor-quote + p.cms-editor-quote {
	margin-top: -30px;
	padding-top: 15px;
}

/* p.cms-editor-buttons > a is the CMS editor's "Buttons" paragraph style */
.btn,
.cms-editor-buttons a {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  font-family: var(--font-heading);
  font-size: 14px;
  font-weight: 600;
  color: var(--ink);
  border: 1px solid var(--ink);
  padding: 12px 22px;
  border-radius: var(--radius);
  white-space: nowrap;
}

/*
  p.cms-editor-dark-button is the CMS editor's "Dark Button" paragraph style.
  The paragraph itself is the button, so it works in a linked image caption
  (the whole card is the <a>, and links can't nest) as well as around a link
  in a text region, where the link just inherits the colour.
  p.cms-editor-dark-buttons > a ("Dark Buttons") is the group version: each
  link in the paragraph is a dark button, laid out like "Buttons".
*/
/* inline-flex so a lone button follows the block's alignment */
p.cms-editor-dark-button { display: inline-flex; }

.cms-editor-dark-buttons a { display: inline-flex; }

p.cms-editor-dark-button,
.cms-editor-dark-buttons a {
  align-items: center;
  gap: 8px;
  font-family: var(--font-heading);
  font-size: 14px;
  font-weight: 600;
  color: var(--white);
  background: var(--ink);
  border: 1px solid var(--ink);
  padding: 12px 22px;
  border-radius: var(--radius);
  white-space: nowrap;
}

.link-arrow {
  color: var(--teal);
  font-weight: 500;
  font-size: 15px;
}

/* ---------- block alignment ---------- */

/*
  Blocks with the CMS composer's alignment toggle (cbAllowAlign = 1). The
  toggle cycles cms-align-center / -right / -left on the block's top-level
  element; no class means left. A block that starts centred ships with
  cms-align-center in its template. Block rules mustn't set text-align
  themselves, or no class stops meaning left - pills and buttons are inline
  so they follow text-align without extra rules.
*/
.cms-align-left { text-align: left; }
.cms-align-center { text-align: center; }
.cms-align-right { text-align: right; }

/* Redactor's alignment button uses the same classes per paragraph (plus justify) */
.cms-align-justify { text-align: justify; }

/* ---------- block widths ---------- */

/*
  Blocks that have the CMS composer's width toggle (cbAllowWidth = 1). The
  toggle sets .wide or .narrow on the block's top-level element (a direct
  child of .page); no class means standard. Only width and side padding live
  here - each block's own rule sets its vertical spacing.
    standard - the site row (--row-max)
    .wide    - full viewport width, keeping the side gutter
    .narrow  - text measure (--narrow-max)
  Blocks that don't use it (hero, page hero, split rows, process steps,
  reviews, CTA band, feature stack) have the toggle off and aren't listed.
  Case Study has it off too but takes the row width from this rule.
  Comparison Table has it on.
*/
.section-intro,
.services-grid,
.portfolio,
.values-grid,
.image-cards,
.callout-strip,
.gallery-grid,
.gallery-carousel,
.faq,
.case-study,
.image-strip,
.compare-table,
.article-body {
  width: 100%;
  max-width: var(--row-max);
  margin-left: auto;
  margin-right: auto;
  padding-left: 25px;
  padding-right: 25px;
}

.page > .wide { max-width: none; }
.page > .narrow { max-width: var(--narrow-max); }

/* ---------- block spacing ---------- */

/*
  Blocks with no background of their own sit on the page white and take
  equal space above and below. Adjacent vertical margins collapse, so two of
  them in a row are --space apart rather than double that, and one that
  follows a full-bleed coloured block (page hero, split row, process steps,
  reviews, CTA band) still gets its space above. Coloured blocks pad inside
  their own background and have no margin.
  An Intro heads the block after it, so it keeps the tighter --space-sm
  below and that block drops its top margin. In edit mode the composer bar
  sits between the two, hence the second selector.
*/
.section-intro,
.services-grid,
.portfolio,
.values-grid,
.image-cards,
.callout-strip,
.gallery-grid,
.gallery-carousel,
.faq,
.case-study,
.image-strip,
.compare-table,
.article-body {
  margin-top: var(--space);
  margin-bottom: var(--space);
}

.section-intro { margin-bottom: var(--space-sm); }

.page > .section-intro + *,
.page > .section-intro + .composer + * { margin-top: 0; }

/* A full-bleed first block (hero, page hero, split row) sits flush under the
   header, so it takes no margin above */
.page > :first-child { margin-top: 0; }

/* An uncoloured first block has nothing behind it, so with no margin its
   content would sit right under the header. Give it some breathing room. */
.page > .section-intro:first-child,
.page > .services-grid:first-child,
.page > .portfolio:first-child,
.page > .values-grid:first-child,
.page > .image-cards:first-child,
.page > .callout-strip:first-child,
.page > .gallery-grid:first-child,
.page > .gallery-carousel:first-child,
.page > .faq:first-child,
.page > .case-study:first-child,
.page > .image-strip:first-child,
.page > .compare-table:first-child,
.page > .article-body:first-child {
  margin-top: var(--space-sm);
}

/* ---------- background utilities ---------- */

.bg-white { background: var(--white); }
.bg-cream { background: var(--cream); }

/* ---------- header ---------- */

.site-header {
  position: relative;
  display: grid;
  /* Just logo + nav - .nav-toggle is display: none at this width, but as a
     grid item it would still claim a third (empty) column track, and grid
     gap applies between tracks regardless of whether they hold content.
     The @media (max-width: 1100px) block below defines its own columns for
     when the toggle becomes visible. */
  grid-template-columns: auto 1fr;
  align-items: center;
  gap: 30px;
  padding: 20px 25px;
  max-width: var(--row-max);
  margin: 0 auto;
}

/*
  Solid header (every page without the homepage hero): sticks to the top as
  the page scrolls. Full width so content can't show either side of it; the
  side padding keeps its contents on the site row. js/main.js adds
  .is-stuck once the page has scrolled, matching the hero pages' scrolled
  header (cream with a shadow).
*/
.site-header:not(.site-header--transparent) {
  position: sticky;
  top: 0;
  z-index: 20;
  max-width: none;
  /* % padding is of the body's width, so it lines up with .row with or without a scrollbar */
  padding-left: max(25px, calc((100% - var(--row-max)) / 2 + 25px));
  padding-right: max(25px, calc((100% - var(--row-max)) / 2 + 25px));
  background: var(--white);
  transition: background-color .3s ease, box-shadow .3s ease;
}

.site-header.is-stuck {
  background: var(--cream);
  box-shadow: 0 2px 12px rgba(0,0,0,.08);
}

/* max-height (not a fixed height) so the logo scales down as a unit if its
   grid column ever gets narrower than its natural width - a fixed height
   paired with the global `img { max-width: 100% }` would otherwise cap the
   width without shrinking the height, stretching the logo out of ratio. */
.site-header .logo img { max-height: 42px; height: auto; width: auto; max-width: 100%; }

.site-nav ul {
  list-style: none;
  display: flex;
  align-items: center;
  gap: 28px;
  margin: 0;
  padding: 0;
  justify-content: center;
}

.site-nav a {
  font-family: var(--font-heading);
  font-size: 16px;
  font-weight: 500;
  color: var(--ink);
}

.site-nav li.has-dropdown {
  position: relative;
}

/* The last top-level nav item (index.asp's navCount/isLastNav) doubles as
   the header CTA - pushed to the far right of the nav row and styled as an
   outline button, matching the rounding of .btn elsewhere on the site.
   Border/text colour swaps with the header the same way the rest of
   .site-nav a does, so it stays visible over a hero image (white) and over
   the solid/scrolled header (ink). */
.site-nav li.nav-cta {
  margin-left: auto;
}

.site-nav li.nav-cta > a {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  background: transparent;
  border: 2px solid var(--ink);
  color: var(--ink) !important;
  font-size: 14px;
  font-weight: 600;
  padding: 10px 22px;
  border-radius: var(--radius);
  white-space: nowrap;
  transition: background-color .2s ease, border-color .2s ease, color .2s ease;
}

.site-nav li.nav-cta > a:hover {
  background: rgba(38, 35, 32, .08);
}

.site-header--transparent .site-nav li.nav-cta > a {
  border-color: var(--white);
  color: var(--white) !important;
}

.site-header--transparent .site-nav li.nav-cta > a:hover {
  background: rgba(255, 255, 255, .15);
}

.site-header--transparent.is-scrolled .site-nav li.nav-cta > a {
  border-color: var(--ink);
  color: var(--ink) !important;
}

.site-header--transparent.is-scrolled .site-nav li.nav-cta > a:hover {
  background: rgba(38, 35, 32, .08);
}

.site-nav li.has-dropdown > a {
  display: inline-flex;
  align-items: center;
  gap: 6px;
}

.site-nav .caret {
  display: inline-block;
  flex-shrink: 0;
  width: 10px;
  height: 10px;
  background-color: currentColor;
  -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M6 9l6 6 6-6'/%3E%3C/svg%3E") no-repeat center / contain;
  mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M6 9l6 6 6-6'/%3E%3C/svg%3E") no-repeat center / contain;
  transition: transform .2s ease;
}

.site-nav .dropdown {
  display: block;
  position: absolute;
  top: 100%;
  left: 0;
  min-width: 240px;
  list-style: none;
  margin: 0;
  padding: 10px 0;
  background: var(--white);
  border-radius: var(--radius);
  box-shadow: 0 16px 32px rgba(0,0,0,.14);
  opacity: 0;
  visibility: hidden;
  transform: translateY(6px);
  transition: opacity .2s ease, transform .2s ease, visibility .2s;
  z-index: 30;
}

.site-nav li.has-dropdown:hover .dropdown,
.site-nav li.has-dropdown:focus-within .dropdown {
  opacity: 1;
  visibility: visible;
  transform: translateY(0);
}

.site-nav li.has-dropdown:hover > a .caret {
  transform: rotate(180deg);
}

.site-nav .dropdown a {
  display: block;
  padding: 10px 22px;
  font-size: 14px;
  font-weight: 400;
  color: var(--ink) !important;
  white-space: nowrap;
}

.site-nav .dropdown a:hover { background: var(--cream); }

.nav-toggle {
  display: none;
  flex-direction: column;
  justify-content: center;
  gap: 5px;
  width: 30px;
  height: 24px;
  background: none;
  border: none;
  padding: 0;
  cursor: pointer;
  z-index: 20;
}

.nav-toggle span {
  display: block;
  width: 100%;
  height: 2px;
  background: var(--ink);
  transition: transform .3s ease, opacity .3s ease, background-color .3s ease;
}

.site-header--transparent .nav-toggle span { background: var(--white); }

.site-header--transparent.is-scrolled .nav-toggle span { background: var(--ink); }

.nav-toggle.is-active span:nth-child(1) { transform: translateY(7px) rotate(45deg); }
.nav-toggle.is-active span:nth-child(2) { opacity: 0; }
.nav-toggle.is-active span:nth-child(3) { transform: translateY(-7px) rotate(-45deg); }

.logo { display: inline-block; max-height: 42px; outline: none; }
.logo-dark { display: none; }

.site-header--transparent {
  position: absolute;
  top: 0; left: 0; right: 0;
  z-index: 10;
  /* Same box model as the solid header (max-width: none + calc side
     padding, not max-width + margin: auto) so the two headers size and
     centre identically - margin: auto centers against the viewport here,
     which can be a scrollbar's width off from the solid header's in-flow
     100%. */
  max-width: none;
  padding-left: max(25px, calc((100% - var(--row-max)) / 2 + 25px));
  padding-right: max(25px, calc((100% - var(--row-max)) / 2 + 25px));
  background: transparent;
  transition: background-color .3s ease, box-shadow .3s ease;
}

.site-header--transparent .site-nav a {
  color: var(--white);
  transition: color .3s ease, border-color .3s ease;
}

.site-header--transparent.is-scrolled {
  position: fixed;
  width: 100%;
  max-width: none;
  background: var(--cream);
  box-shadow: 0 2px 12px rgba(0,0,0,.08);
  transform: translateY(-100%);
  transition: transform .35s ease, background-color .3s ease, box-shadow .3s ease;
}

.site-header--transparent.is-scrolled.is-visible {
  transform: translateY(0);
}

.site-header--transparent.no-transition,
.site-header--transparent.no-transition * {
  transition: none !important;
}

.site-header--transparent.is-scrolled .site-nav a {
  color: var(--ink);
}

.site-header--transparent.is-scrolled .logo-light { display: none; }
.site-header--transparent.is-scrolled .logo-dark { display: block; }

/* ---------- breadcrumbs ---------- */

/* Drawn by index.asp from getBreadcrumbTrail() (_inc/page-schema.asp, which
   also outputs the matching BreadcrumbList JSON-LD). Not on the homepage.
   Sits between the header and the first block. */
.breadcrumbs {
  background: var(--cream);
  font-size: 13px;
  padding: 10px max(25px, calc((100% - var(--row-max)) / 2 + 25px));
}

.breadcrumbs ol { list-style: none; display: flex; flex-wrap: wrap; gap: 6px; margin: 0; padding: 0; }
.breadcrumbs li + li::before { content: "/"; margin-right: 6px; opacity: .5; }
.breadcrumbs a { color: var(--ink); opacity: .75; }
.breadcrumbs a:hover { opacity: 1; }
.breadcrumbs [aria-current] { color: var(--ink); font-weight: 500; }

/* ---------- split hero ---------- */

.hero-split {
  display: grid;
  grid-template-columns: 1fr 1fr;
  height: 82vh;
  min-height: 520px;
  max-height: 760px;
}

.hero-panel {
  position: relative;
  overflow: hidden;
  display: flex;
  align-items: flex-end;
  color: var(--white);
}

.hero-panel img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition: transform .5s ease;
}

@media (hover: hover) {
  .hero-panel:hover img { transform: scale(1.04); }
}

.panel-overlay {
  position: absolute;
  inset: 0;
  background: linear-gradient(0deg, rgba(0,0,0,.75) 0%, rgba(0,0,0,.15) 55%, rgba(0,0,0,.35) 100%);
}

.panel-content {
  position: relative;
  z-index: 2;
  padding: 50px;
  max-width: 480px;
}

.panel-content .eyebrow { color: var(--white); opacity: .8; }
.panel-content h2 { color: var(--white); font-size: 34px; margin-bottom: 14px; }
.panel-content p { color: #e6e6e6; font-size: 15px; margin-bottom: 22px; }

.btn-light {
  border-color: var(--white);
  color: var(--white);
}

.hero-split .panel-bespoke::after {
  content: "";
  position: absolute;
  top: 0; right: 0; bottom: 0;
  width: 1px;
  background: rgba(255,255,255,.25);
  z-index: 3;
}

/* ---------- hero (legacy single-image, unused by default) ---------- */

.hero {
  position: relative;
  height: 82vh;
  min-height: 480px;
  max-height: 720px;
  background: #111 url("../uploads/hero.jpg") center/cover no-repeat;
  display: flex;
  align-items: flex-end;
}

.hero::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(0deg, rgba(0,0,0,.55) 0%, rgba(0,0,0,0) 45%);
}

.hero .row {
  position: relative;
  z-index: 2;
  display: grid;
  grid-template-columns: 1fr auto;
  align-items: end;
  gap: 30px;
  padding-bottom: 50px;
}

.hero h1 {
  color: var(--white);
  font-size: 48px;
  max-width: 640px;
  margin-bottom: 0;
}

.hero-copy {
  max-width: 280px;
  color: var(--white);
  text-align: right;
}

.hero-copy p { font-size: 15px; margin-bottom: 12px; }
.hero-copy a { font-weight: 600; color: var(--white); border-bottom: 1px solid var(--white); padding-bottom: 3px; }

.hero-progress {
  position: absolute;
  left: 25px;
  right: 25px;
  bottom: 22px;
  z-index: 2;
  display: flex;
  align-items: center;
  gap: 15px;
  color: var(--white);
  font-family: var(--font-heading);
  font-size: 13px;
  max-width: var(--row-max);
  margin: 0 auto;
}

.hero-progress .bar {
  flex: 1;
  height: 1px;
  background: rgba(255,255,255,.35);
  position: relative;
}

.hero-progress .bar::before {
  content: "";
  position: absolute;
  left: 0; top: 0; bottom: 0;
  width: 33%;
  background: var(--white);
}

/* ---------- full-viewport splash hero ---------- */

.hero-splash {
  position: relative;
  width: 100%;
  height: 100vh;
  /* dvh accounts for mobile browsers' collapsing address/toolbar chrome -
     100vh there is the TALLEST the viewport ever gets (chrome hidden),
     so the page overflows and scrolls by that chrome's height whenever it's
     showing. Unsupported browsers just ignore this line and keep 100vh. */
  height: 100dvh;
  overflow: hidden;
  /* Content and cards stack in flow from the bottom, so the content always
     sits just above the cards whatever height they are at any width. */
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  padding-bottom: 70px;
}

.hero-splash-bg {
  position: absolute;
  inset: 0;
}

/* Each .e_images item is stacked full-bleed so main.js can crossfade between
   them when there's more than one; the first item is the fallback with no
   JS (or in the CMS editor) and stays fully visible on its own. */
.hero-splash-bg > div {
  position: absolute;
  inset: 0;
  height: 100%;
  opacity: 0;
  transition: opacity 1.2s ease;
}

.hero-splash-bg > div:first-child { opacity: 1; }

.hero-splash-bg img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* pointer-events off so clicks reach the background image in the CMS editor */
.hero-splash-overlay {
  position: absolute;
  inset: 0;
  background: linear-gradient(0deg, rgba(0,0,0,.8) 0%, rgba(0,0,0,.15) 45%, rgba(0,0,0,.45) 100%);
  pointer-events: none;
}

.hero-splash-content {
  position: relative;
  z-index: 2;
  margin-bottom: 40px;
  color: var(--white);
}

.hero-splash-content .eyebrow,
.hero-splash-content .cms-editor-eyebrow { color: var(--white); opacity: .85; }
.hero-splash-content h1 { color: var(--white); max-width: 640px; margin-bottom: 18px; }
.hero-splash-content p:not(.cms-editor-eyebrow) { color: #e6e6e6; max-width: 520px; font-size: 16px; margin-bottom: 26px; }

.hero-splash-cards {
  position: relative;
  z-index: 3;
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 25px;
}

/* Theme cards: each direct child of .hero-splash-cards (an .e_images list) is
   one card, optionally wrapped in a link by the CMS image editor. The caption's
   last paragraph renders as the button when there's more than one. */
.hero-splash-cards > div {
  position: relative;
  display: flex;
  align-items: stretch;
  overflow: hidden;
  background: rgba(255,255,255,0.9 );
}

.hero-splash-cards > div > a {
  display: flex;
  align-items: stretch;
  flex: 1;
  min-width: 0;
}

.hero-splash-cards img {
  width: 190px;
  height: 190px;
  flex-shrink: 0;
  object-fit: cover;
  padding: 2px;
}

.hero-splash-cards .e_caption {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  flex: 1;
  min-width: 0;
  padding: 22px;
}

.hero-splash-cards h3 { color: var(--ink); margin-bottom: 8px; }
.hero-splash-cards p { color: var(--body-text); font-size: 14px; margin-bottom: 14px; }

.hero-splash-cards p.cms-editor-dark-button { margin: auto 0 0; color: var(--white); }

/* ---------- section intro (shared text block, alignment from the toggle) ---------- */

/* Pages saved before the width toggle have a .row inside; the block handles width now */
.section-intro > .row { max-width: none; padding: 0; }

/* ---------- intro ---------- */

/* Scoped to section: a bare .intro would also hit the CMS editor's "Intro" paragraph style (p.intro) */
section.intro {
  padding-top: 90px;
  padding-bottom: 60px;
}

section.intro p,
p.intro { font-size: 17px; }

/* Feature stack */
.feature-stack {
  padding: 50px 25px;
}

.feature-row {
  position: relative;
  z-index: 1;
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  grid-template-rows: 1fr;
  align-items: center;
  gap: 0 50px;
  max-width: var(--row-max);
  margin: 0 auto;
}

.feature-row:not(:last-child) { margin-bottom: 20px; }

.feature-row:not(:last-child)::after {
  content: "";
  position: absolute;
  left: 50%;
  top: 100%;
  height: 70px;
  width: 1px;
  transform: translate(-50%, -50%);
  background: linear-gradient(to bottom, transparent 0%, var(--border) 20px, var(--border) calc(100% - 20px), transparent 100%);
}

.feature-photo {
  grid-column: 1;
  grid-row: 1;
  justify-self: end;
}

/* Every second row puts the photo on the right - by position, not a class */
.feature-stack .feature-row:nth-child(even) .feature-photo {
  grid-column: 3;
  grid-row: 1;
  justify-self: start;
}

.feature-photo img {
  width: 253px;
  height: 253px;
  border-radius: 50%;
  object-fit: cover;
  box-shadow: 0 15px 30px rgba(0,0,0,.12);
}

.feature-text {
  grid-column: 2;
  grid-row: 1;
  max-width: 320px;
  text-align: center;
}

.feature-text h3 { font-size: 22px; margin-bottom: 12px; }
.feature-text p { margin: 0; }

/* ---------- services grid ---------- */

.services-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 30px;
  background: var(--white);
}

/* Each direct child of .services-grid (an .e_images list) is one card */
.services-grid > div {
  background: var(--cream);
  padding-bottom: 30px;
  border-radius: var(--radius);
  overflow: hidden;
}

.services-grid > div > a { display: block; }

.services-grid img {
  width: 100%;
  height: 220px;
  object-fit: cover;
  transition: transform .4s ease;
  /* Grow upward only (into the card's already-clipped top edge), never
     down into the caption below - .e_caption has no background of its
     own, so downward growth would show through as an overflow. */
  transform-origin: center bottom;
}

@media (hover: hover) {
  .services-grid > div:hover img { transform: scale(1.05); }
}

.services-grid .e_caption { padding: 25px 30px 0; }

.services-grid h3 { font-size: 22px; margin-bottom: 14px; }

.services-grid .e_caption p {
  color: var(--body-text);
  font-weight: 400;
  font-size: 15px;
  margin: 0;
}

/* p.cms-editor-accent is the CMS editor's "Accent" paragraph style, opt-in
   for a service card's link/CTA paragraph (registered on the caption editor
   for the Service Cards block type) */
.services-grid .e_caption p.cms-editor-accent {
  color: var(--teal);
  font-weight: 500;
}

/* ---------- values grid (about page) ---------- */

.values-grid {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 30px;
}

.value-card {
  background: var(--cream);
  border-radius: var(--radius);
  padding: 28px 24px;
}

.value-card h4 {
  font-family: var(--font-heading);
  font-size: 17px;
  color: var(--ink);
  margin: 0 0 8px;
}

.value-card p { font-size: 15px; margin: 0; }

/* ---------- split row (flippable text/photo block) ---------- */

.split-row {
  --split-inset: max(25px, calc((100vw - var(--row-max)) / 2 + 25px));
  display: grid;
  grid-template-columns: 1fr 1fr;
  align-items: stretch;
}

.split-row-text {
  order: 1;
  display: flex;
  flex-direction: column;
  justify-content: center;
  padding: 80px 60px 80px var(--split-inset);
}

.split-row-media { order: 2;position:relative;overflow:hidden;}

.split-row--flip .split-row-text { order: 2; padding: 80px var(--split-inset) 80px 60px; }
.split-row--flip .split-row-media { order: 1; }

.split-row-media img {
  position:absolute;
  width:100%;
  height:100%;
  offset: 0;
  object-fit: cover;
  object-position: center;
  display: block;
}

.why {
  background: var(--dark-cream);
  color: var(--body-text);
}

.why h2 { color: var(--ink); }
.why .split-row-text p { font-size: 16px; margin-bottom: 30px; }
.why .split-row-text p:last-child { margin-bottom: 0; }

/* ---------- pill links ---------- */

/* p.cms-editor-pill-links is the CMS editor's "Pill Links" paragraph style: each link is a pill */
/* Inline pills rather than flex, so the row follows the block's text-align */
p.cms-editor-pill-links { margin: 30px 0 0; }

.cms-editor-pill-links a {
  display: inline-block;
  margin: 0 4px 12px;
  font-size: 14px;
  font-weight: 600;
  color: var(--ink);
  border: 1px solid var(--teal);
  border-radius: 20px;
  padding: 8px 18px;
  transition: background-color .3s ease;
}

.cms-editor-pill-links a:hover { background-color: var(--teal); }

/* ---------- portfolio ---------- */

.portfolio-intro {
  padding: 90px 0 40px;
  display: grid;
  grid-template-columns: 1fr auto;
  align-items: end;
}

.portfolio .portfolio-intro { padding-top: 0; }

.portfolio-intro h2 { margin-bottom: 0; }

.portfolio-intro p:last-child { margin-bottom: 0; }

.portfolio-intro > div:last-child a {
  color: var(--teal);
  font-weight: 500;
  font-size: 15px;
}

.portfolio-scroller-wrap {
  position: relative;
}

.portfolio-grid {
  display: flex;
  gap: 20px;
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: none;
  /* See .gallery-carousel-track: stops edge-swipes rubber-banding into the
     page and stops scroll-anchoring re-targeting scrollLeft as tiles load. */
  overscroll-behavior-x: contain;
  overflow-anchor: none;
}

.portfolio-grid::-webkit-scrollbar { display: none; }

/* The .e_images list is display: contents so its items sit directly in the
   scrolling flex track alongside the CTA tile. */
.portfolio-grid > .e_images { display: contents; }

.portfolio-grid .e_images > div,
.portfolio-cta {
  position: relative;
  overflow: hidden;
  flex: 0 0 calc((100% - 40px) / 3);
  aspect-ratio: 4/3;
  scroll-snap-align: start;
  border-radius: var(--radius);
}

.portfolio-grid .e_images > div > a {
  display: block;
  height: 100%;
}

.portfolio-cta {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  background: var(--dark-cream);
  text-align: center;
  padding: 25px;
}

.portfolio-cta p { margin: 0; }

.portfolio-cta a {
  font-family: var(--font-heading);
  font-size: 19px;
  font-weight: 600;
  color: var(--ink);
}

/* Makes the whole tile clickable */
.portfolio-cta a::after {
  content: "";
  position: absolute;
  inset: 0;
}

.portfolio-arrow {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  width: 44px;
  height: 44px;
  border-radius: 50%;
  border: none;
  background: var(--white);
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  z-index: 5;
  transition: opacity .2s ease;
}

/* Sit in the block's side gutter, overlapping the first/last tile slightly */
.portfolio-arrow--prev { left: -25px; }
.portfolio-arrow--next { right: -25px; }

.portfolio-arrow .arrow-shape {
  width: 0;
  height: 0;
  border-top: 7px solid transparent;
  border-bottom: 7px solid transparent;
}

.portfolio-arrow--prev .arrow-shape {
  border-right: 11px solid var(--teal);
  margin-right: 3px;
}

.portfolio-arrow--next .arrow-shape {
  border-left: 11px solid var(--teal);
  margin-left: 3px;
}

.portfolio-arrow:disabled,
.portfolio-arrow.is-hidden {
  opacity: 0;
  pointer-events: none;
}

.portfolio-grid .e_images img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.portfolio-grid .e_caption {
  position: absolute;
  left: 0; right: 0; bottom: 0;
  padding: 20px;
  background: linear-gradient(0deg, rgba(0,0,0,.75), rgba(0,0,0,0));
  color: var(--white);
}

.portfolio-grid .e_caption h4 {
  font-family: var(--font-heading);
  font-size: 16px;
  margin: 0 0 4px;
  color: var(--white);
}

/* ---------- reviews ---------- */

.reviews {
  background: var(--cream);
  padding: 80px 25px;
}

.reviews .row { max-width: var(--row-max); }

.reviews h2 { margin-bottom: 20px; }

.reviews-more {
  margin: 40px 0 0;
  text-align: center;
}

.reviews-more p { margin: 0; }

.reviews-more a {
  color: var(--teal);
  font-weight: 500;
  font-size: 15px;
}

.reviews-head { margin-bottom: 40px; }

.reviews-head p {
  display: flex;
  align-items: center;
  gap: 20px;
  margin: 0;
  font-family: var(--font-heading);
  font-size: 14px;
  color: var(--body-text);
}

.reviews-head strong { font-size: 20px; letter-spacing: 1px; color: var(--ink); }

.reviews-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 25px;
}

.review-card {
  background: var(--white);
  padding: 25px;
  border-radius: var(--radius);
}

.review-card::before {
  content: "\2605\2605\2605\2605\2605";
  display: block;
  color: #f5a623;
  font-size: 14px;
  margin-bottom: 10px;
}

.review-card p { font-size: 14px; margin-bottom: 14px; }
.review-card h4 { font-family: var(--font-heading); font-weight: 600; color: var(--ink); font-size: 14px; margin: 0; }

/* ---------- cta band ---------- */

/* Background image is set inline on the block (.e_bg, editable in the CMS) */
.cta-band {
  position: relative;
  background: var(--ink) url("../uploads/hero.jpg") center/cover no-repeat;
  padding: 100px 25px;
  text-align: center;
}

.cta-band::before {
  content: "";
  position: absolute;
  inset: 0;
  background: rgba(38,35,32,.6);
}

.cta-band .row { position: relative; max-width: 640px; }

.cta-band h2 { color: var(--white); }

.cta-band p { color: #cfcfcf; font-size: 16px; margin-bottom: 30px; }

.cta-band p:last-child { margin-bottom: 0; }

.cta-band .btn,
.cta-band .cms-editor-buttons a {
  margin: 0 auto;
  border-color: var(--white);
  color: var(--white);
}

/* ==========================================================================
   Service page blocks (first draft - see service.html)
   ========================================================================== */

/* ---------- page hero (inner-page banner) ---------- */

/* Shorter than the homepage splash and sits under the solid header. The
   background image is inline on the block (.e_bg). */
.page-hero {
  position: relative;
  display: flex;
  align-items: flex-end;
  min-height: 520px;
  background: var(--ink) center/cover no-repeat;
  color: var(--white);
}

.page-hero::before {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(0deg, rgba(0,0,0,.8) 0%, rgba(0,0,0,.35) 60%, rgba(0,0,0,.2) 100%);
  pointer-events: none;
}

.page-hero .row { position: relative; padding-top: 80px; padding-bottom: 70px; }

.page-hero .cms-editor-eyebrow { color: var(--white); opacity: .85; }
.page-hero h1 { color: var(--white); max-width: 720px; margin-bottom: 18px; }
.page-hero p:not(.cms-editor-eyebrow) { color: #e6e6e6; max-width: 620px; font-size: 17px; margin-bottom: 30px; }
.page-hero p:last-child { margin-bottom: 0; }

/* Buttons on the dark banner: first is solid teal, the rest outlined white */
.page-hero .cms-editor-buttons a { color: var(--white); border-color: var(--white); margin: 0 10px 10px 0; }
.page-hero .cms-editor-buttons a:first-child { background: var(--teal); border-color: var(--teal); color: var(--ink); }

/* ---------- image cards (photo + description) ---------- */

/* .image-cards is .e_repeatable: each direct child is one card, duplicated /
   deleted / reordered with the CMS item bar. The photo is an .e_image (edited
   directly; it can link or zoom) in its own holder, so a link wraps only the
   photo, and the text is a normal .e_editor. */
.image-cards {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 30px;
}

.image-cards > div {
  background: var(--cream);
  border-radius: var(--radius);
  overflow: hidden;
}

.image-cards-media a { display: block; }

.image-cards img {
  width: 100%;
  aspect-ratio: 3/2;
  object-fit: cover;
}

.image-cards .e_editor { padding: 24px 28px 28px; }
.image-cards h3 { margin-bottom: 10px; }
.image-cards p { font-size: 15px; }
.image-cards .e_editor > :last-child { margin-bottom: 0; }

/* ---------- galleries ---------- */

/* Gallery Grid and Gallery Carousel are .e_images lists, edited in the CMS
   image panel (sortable thumbnails). Setting an image's action to Zoom makes
   it an a.zoom that opens the lightbox (fslightbox, loaded by index.asp). */
.gallery-grid {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 16px;
}

.gallery-grid > div {
  position: relative;
  overflow: hidden;
  aspect-ratio: 1;
  border-radius: var(--radius);
}

.gallery-grid > div > a { display: block; height: 100%; }

/* An item can be a photo or a video - the CMS image editor swaps the tag,
   not a class, so both are sized the same way here. */
.gallery-grid img,
.gallery-grid video {
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition: transform .4s ease;
}

@media (hover: hover) {
  .gallery-grid > div:hover img,
  .gallery-grid > div:hover video { transform: scale(1.05); }
}

/* Filmstrip: a fixed height with each photo at its own aspect ratio, so
   portrait and landscape shots sit together uncropped. Arrows reuse the
   portfolio scroller's buttons and script (js/main.js). */
.gallery-carousel-wrap { position: relative; }

.gallery-carousel-track {
  display: flex;
  gap: 16px;
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: none;
  /* Without these, a swipe that reaches either end can rubber-band/scroll-
     chain into the page itself (felt as page-level horizontal movement even
     though scrollWidth never actually changes), and a late-loading image/
     video changing the track's content width can trigger the browser's
     scroll-anchoring to silently re-target scrollLeft away from the first
     tile once it settles. */
  overscroll-behavior-x: contain;
  overflow-anchor: none;
}

.gallery-carousel-track::-webkit-scrollbar { display: none; }

.gallery-carousel-track > div {
  position: relative;
  flex: 0 0 auto;
  height: 380px;
  /* Cap each tile below the track's full width so a wide/landscape photo
     can never fill the whole visible area at rest - there's always a peek
     of the next tile to signal there's more to scroll. Images narrower than
     this (by their own aspect ratio at the fixed height) are unaffected. */
  max-width: 82%;
  overflow: hidden;
  scroll-snap-align: start;
  border-radius: var(--radius);
}

.gallery-carousel-track > div > a { display: block; height: 100%; }

.gallery-carousel-track img,
.gallery-carousel-track video {
  height: 100%;
  width: auto;
  /* Clamp to the tile's own (possibly capped) width rather than the old
     max-width: none, and crop rather than squash when that clamp kicks in -
     object-position (set inline per image) still controls which part of the
     photo stays in frame. */
  max-width: 100%;
  object-fit: cover;
}

/* A gallery-carousel nested inside body copy (case study / blog repeatable
   sections) isn't a standalone page block, so it shouldn't take the 90px
   page-block margin or the row width/gutter meant for a carousel sitting
   directly on .page - that's designed for the standalone use in service.html
   and blocks.html. Here it sits between paragraphs, so it takes a smaller
   margin and drops the row treatment in favour of the article's own width. */
.article-copy .gallery-carousel,
.case-study-body .gallery-carousel {
  max-width: none;
  padding-left: 0;
  padding-right: 0;
  margin-top: var(--space-sm);
  margin-bottom: var(--space-sm);
}

/* zoom hint on lightbox images */
.gallery-grid a.zoom::after,
.gallery-carousel a.zoom::after,
.image-cards a.zoom::after,
.image-strip a.zoom::after {
  content: "";
  position: absolute;
  right: 12px;
  bottom: 12px;
  width: 36px;
  height: 36px;
  border-radius: 50%;
  background: rgba(255,255,255,.9) url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23262320' stroke-width='2' stroke-linecap='round'%3E%3Ccircle cx='10.5' cy='10.5' r='6'/%3E%3Cpath d='M15 15l5 5M10.5 8v5M8 10.5h5'/%3E%3C/svg%3E") center / 18px no-repeat;
  opacity: 0;
  transition: opacity .2s ease;
}

@media (hover: hover) {
  .gallery-grid a.zoom:hover::after,
  .gallery-carousel a.zoom:hover::after,
  .image-cards a.zoom:hover::after,
  .image-strip a.zoom:hover::after { opacity: 1; }
}

.image-cards-media { position: relative; }

/* ---------- values grid, 3 across ---------- */

/* "Values Grid (3 across)" - same cards, for sets of 3 or 6 */
.values-grid--3 { grid-template-columns: repeat(3, 1fr); }

/* ---------- callout strip ---------- */

/* Inline prompt between sections: text on the left, button(s) on the right */
.callout-strip-inner {
  display: grid;
  grid-template-columns: 1fr auto;
  align-items: center;
  gap: 40px;
  background: var(--dark-cream);
  border-left: 4px solid var(--teal);
  border-radius: var(--radius);
  padding: 40px 45px;
}

.callout-strip h3 { font-size: 24px; margin-bottom: 8px; }
.callout-strip p { margin: 0; }
.callout-strip .cms-editor-dark-buttons a { margin: 4px 0; }

/* ---------- process steps ---------- */

/* One editor region: intro copy, then an <ol> whose items render as numbered
   steps. Each item's <strong> is the step title. Add or remove steps by
   editing the list. */
.process-steps {
  background: var(--ink);
  color: #cfcfcf;
  padding: 90px 25px;
}

.process-steps .row { padding: 0; }

.process-steps h2 { color: var(--white); }
.process-steps .cms-editor-eyebrow { color: var(--teal); }
.process-steps p { max-width: 640px; }

.process-steps ol {
  list-style: none;
  counter-reset: step;
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 40px 30px;
  margin: 50px 0 0;
  padding: 0;
}

.process-steps li {
  counter-increment: step;
  border-top: 1px solid rgba(255,255,255,.2);
  padding-top: 22px;
  font-size: 15px;
}

.process-steps li::before {
  content: counter(step, decimal-leading-zero);
  display: block;
  font-family: var(--font-heading);
  font-size: 34px;
  font-weight: 600;
  line-height: 1;
  color: var(--teal);
  margin-bottom: 14px;
}

.process-steps li strong {
  display: block;
  font-family: var(--font-heading);
  font-size: 19px;
  font-weight: 600;
  color: var(--white);
  margin-bottom: 8px;
}

/* ---------- tick lists in split rows ---------- */

.split-row-text ul {
  list-style: none;
  margin: 0 0 30px;
  padding: 0;
}

.split-row-text ul:last-child { margin-bottom: 0; }

.split-row-text li {
  position: relative;
  padding-left: 32px;
  margin-bottom: 14px;
}

.split-row-text li::before {
  content: "";
  position: absolute;
  left: 0;
  top: 3px;
  width: 20px;
  height: 20px;
  border-radius: 50%;
  background: var(--teal) url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='white' stroke-width='3' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M5 12l5 5 9-10'/%3E%3C/svg%3E") center / 12px no-repeat;
}

.split-row-text li strong { color: var(--ink); }

/* ---------- faq ---------- */

/* Intro region on the left, questions on the right. .faq-list is
   .e_repeatable: each .faq-item is one question (its first line) and answer
   (everything after), added/removed/reordered with the CMS item bar.
   js/main.js turns the items into an accordion on the public site only
   (not in edit mode). */
.faq {
  display: grid;
  grid-template-columns: 1fr 2fr;
  gap: 60px;
}

.faq-intro p:last-child { margin-bottom: 0; }

.faq-intro a {
  color: var(--teal);
  font-weight: 500;
}

.faq-item {
  border-top: 1px solid var(--border);
  padding: 22px 0;
}

.faq-item:first-child { border-top: none; padding-top: 0; }
.faq-item:last-child { padding-bottom: 0; }

.faq-item > :first-child {
  position: relative;
  font-family: var(--font-heading);
  font-size: 18px;
  font-weight: 600;
  line-height: 1.3;
  color: var(--ink);
  margin: 0;
  padding-right: 40px;
}

.faq-item > :first-child + * { margin-top: 14px; }
.faq-item > :last-child { margin-bottom: 0; }
.faq-item p { font-size: 15px; }

/* accordion state, added by js/main.js */
.faq-list.is-accordion .faq-item > :first-child { cursor: pointer; }
.faq-list.is-accordion .faq-item > :first-child:focus-visible { outline: 2px solid var(--teal); outline-offset: 4px; }

.faq-list.is-accordion .faq-item > :first-child::after {
  content: "";
  position: absolute;
  right: 4px;
  top: 50%;
  width: 12px;
  height: 12px;
  margin-top: -6px;
  background-color: var(--teal);
  -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='3' stroke-linecap='round'%3E%3Cpath d='M12 4v16M4 12h16'/%3E%3C/svg%3E") no-repeat center / contain;
  mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='3' stroke-linecap='round'%3E%3Cpath d='M12 4v16M4 12h16'/%3E%3C/svg%3E") no-repeat center / contain;
  transition: transform .2s ease;
}

.faq-list.is-accordion .faq-item > [aria-expanded="true"]::after { transform: rotate(45deg); }

.faq-list.is-accordion .faq-item > :not(:first-child) { padding-right: 40px; }

/* ---------- case study ---------- */

/* The write-up on the left; on the right a sticky panel of project facts
   and a contact button. .case-study-stats is .e_repeatable: each item is one
   fact, its first line the label (Type, Location...) and the rest the value.
   The panel sticks while the copy scrolls past; align-items: start keeps it
   at its own height so it has room to travel. */
.case-study {
  display: grid;
  grid-template-columns: minmax(0, 2fr) minmax(0, 1fr);
  gap: 70px;
  align-items: start;
}

.case-study-body h3 { font-size: 22px; margin-top: 35px; }
.case-study-body > :last-child { margin-bottom: 0; }

.case-study-aside {
  position: sticky;
  top: calc(var(--header-h, 80px) + 30px);
  background: var(--cream);
  border-radius: var(--radius);
  padding: 35px;
}

.case-study-stats {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 22px 24px;
  margin-bottom: 30px;
}

.case-study-stats > div {
  border-top: 1px solid var(--dark-cream);
  padding-top: 14px;
}

.case-study-stats > div > * { margin: 0; }

.case-study-stats > div > :first-child {
  text-transform: uppercase;
  letter-spacing: 1.5px;
  font-size: 12px;
  font-weight: 600;
  color: var(--brown);
  margin-bottom: 4px;
}

.case-study-stats > div > :not(:first-child) {
  font-family: var(--font-heading);
  font-size: 16px;
  font-weight: 500;
  line-height: 1.35;
  color: var(--ink);
}

.case-study-cta p { font-size: 15px; }
.case-study-cta strong { color: var(--ink); }
.case-study-cta > :last-child { margin-bottom: 0; }

/* Full-width button in the panel */
.case-study-cta .cms-editor-dark-buttons a { display: flex; justify-content: center; }

/* ---------- image strip ---------- */

/* A single photo cropped to a band: 40vh, or 80vh for "Image Strip (tall)".
   The .e_image sits in its own holder so a link or zoom wraps only it.
   Ships with .wide, which here is edge to edge - standard and .narrow keep
   the gutter and round the corners. */
.image-strip.wide { padding-left: 0; padding-right: 0; }

.image-strip-media { position: relative; }
.image-strip-media a { display: block; }

.image-strip img,
.image-strip video {
  width: 100%;
  height: 40vh;
  min-height: 240px;
  object-fit: cover;
  border-radius: var(--radius);
}

.image-strip--tall img, .image-strip--tall video { height: 80vh; min-height: 420px; }

.image-strip.wide img, .image-strip.wide video { border-radius: 0; }

/* ==========================================================================
   Blocks from the layout previews (see audit.html / layout-*.html)
   ========================================================================== */

/* ---------- fixed blocks ---------- */

/* A fixed block sits inside the engine's <section class="cms_fixed"
   data-id="N"> wrapper, so rules that look at a block's place in .page see
   the wrapper instead. These pass them through to the block inside. */
.page > .section-intro + .cms_fixed > :first-child,
.page > .section-intro + .composer + .cms_fixed > :first-child,
.page > .cms_fixed:first-child > :first-child { margin-top: 0; }

/* ---------- trust strip (fixed block) ---------- */

/* Sits straight under the Page Hero on every inner page. A fixed block
   (edited once in the Block Manager, updates every page): each direct child
   is one proof point, its first line the headline (a rating, a claim, a
   badge) and the rest a short qualifier. No width toggle - it keeps the
   site row itself. */
.trust-strip {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 0;
  width: 100%;
  max-width: var(--row-max);
  margin: 0 auto var(--space);
  padding: 26px 25px;
  border-bottom: 1px solid var(--border);
}

.trust-strip > div { padding: 0 22px; border-left: 1px solid var(--border); }
.trust-strip > div:first-child { border-left: none; padding-left: 0; }
.trust-strip > div > * { margin: 0; }

.trust-strip > div > :first-child {
  font-family: var(--font-heading);
  font-size: 17px;
  font-weight: 600;
  line-height: 1.3;
  color: var(--ink);
  margin-bottom: 2px;
}

.trust-strip > div > :not(:first-child) { font-size: 14px; }

.trust-strip .stars { color: #f5a623; letter-spacing: 1px; }

/* ---------- service cards, 4 across ---------- */

/* "Service Cards (4 across)" variation - for four materials or products,
   avoiding an orphan card on the 3-up grid. A caption paragraph before the
   p.cms-editor-accent one is a short description. The image keeps a 3:2
   ratio (rather than a fixed height) so it doesn't go slim as columns
   narrow at smaller widths. */
/* Gap between cards matches the page's side gutter (.row's 25px padding),
   and each card's bottom padding matches its own top caption padding (the
   last caption paragraph has no margin of its own - see the base
   .e_caption p rule - so padding-bottom is the whole effective gap). */
.services-grid--4 { grid-template-columns: repeat(4, 1fr); gap: 25px; }
.services-grid--4 > div { padding-bottom: 22px; }
.services-grid--4 img { aspect-ratio: 3 / 2; height: auto; }
.services-grid--4 .e_caption { padding: 22px 24px 0; }
.services-grid--4 h3 { font-size: 19px; margin-bottom: 8px; }
.services-grid--4 .e_caption p:not(.cms-editor-accent) { margin-bottom: 12px; }

/* ---------- comparison table ---------- */

/* One .e_editor holding intro copy and a plain RedactorX table (its
   "header row" option gives the <thead>). Everything is styled by structure
   - first column = row labels, a paragraph after the table = the footnote -
   because Redactor won't keep classes or wrapper divs inside the region.
   The region itself scrolls sideways on small screens. Width toggle on. */
.compare-table .e_editor { overflow-x: auto; -webkit-overflow-scrolling: touch; overscroll-behavior-x: contain; }

.compare-table table {
  width: 100%;
  min-width: 640px;
  border-collapse: collapse;
  font-size: 15px;
  margin-top: 30px;
}

.compare-table th,
.compare-table td {
  text-align: left;
  padding: 16px 18px;
  border-bottom: 1px solid var(--border);
  vertical-align: top;
}

.compare-table thead th {
  font-family: var(--font-heading);
  font-size: 16px;
  color: var(--ink);
  background: var(--cream);
}

.compare-table thead th:first-child { background: none; }
.compare-table tbody td:first-child { font-weight: 600; color: var(--ink); width: 18%; }
.compare-table tbody tr:hover td { background: #faf8f4; }

.compare-table table + p { font-size: 14px; margin-top: 18px; }

/* ---------- related cards (related pages template) ---------- */

/* "Related Cards" is a related-pages template (cbRelated, System db/018):
   its code is one card with {{tokens}}, repeated per page. The editor places
   it as <section><div><div class="e_editor">intro</div><div class="cms_related"
   ...>cards</div></div></section> (admin/cms/blocks.asp) - a bare section,
   so it's matched by what it holds. Pages come from the composer's related
   button: newest, or hand-picked from any page type (e.g. the aluminium case
   studies on the Aluminium Gates page). Hidden when there's nothing to show. */
.page > section:has(> div > .cms_related) {
  width: 100%;
  max-width: var(--row-max);
  margin: var(--space) auto;
  padding: 0 25px;
}

.page > .section-intro + section:has(> div > .cms_related),
.page > .section-intro + .composer + section:has(> div > .cms_related) { margin-top: 0; }

.page > section:has(> div > .cms_related:empty) { display: none; }

.page > section:has(> div > .cms_related) > div > .e_editor { margin-bottom: 30px; }
.page > section:has(> div > .cms_related) > div > .e_editor > :last-child { margin-bottom: 0; }

.project-cards {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 34px 30px;
}

.project-card a { display: block; }
.project-card[hidden] { display: none; }

.project-card-media { overflow: hidden; border-radius: var(--radius); aspect-ratio: 4/3; margin-bottom: 16px; }
.project-card img { width: 100%; height: 100%; object-fit: cover; transition: transform .4s ease; }
@media (hover: hover) {
  .project-card a:hover img { transform: scale(1.05); }
}

.project-card-tags { display: flex; flex-wrap: wrap; gap: 6px; margin: 0 0 8px; padding: 0; list-style: none; }

.project-card-tags li {
  font-size: 12px;
  font-weight: 600;
  letter-spacing: .5px;
  color: var(--brown);
  background: var(--cream);
  border-radius: 2px;
  padding: 3px 8px;
}

.project-card h3 { font-size: 18px; line-height: 1.35; margin: 0 0 6px; }
.project-card p { font-size: 14px; margin: 0; }
.project-card p:empty { display: none; }

/* Pager for a paginated "Newest pages" related block (?page=, shared across
   the page). Previous is missing on page 1, Next on the last page, so
   position comes from the classes, not :first-child/:last-child. The count
   stays centred between whichever links are present. */
#cms-related { scroll-margin-top: 110px; }

.cms_pager {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  margin-top: 40px;
}

.cms_pager_count {
  order: 0;
  flex: 1;
  text-align: center;
  font-size: 14px;
  color: var(--body-text);
}

.cms_pager_prev { order: -1; }
.cms_pager_next { order: 1; margin-left: auto; }

.cms_pager_prev,
.cms_pager_next {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  font-family: var(--font-heading);
  font-size: 14px;
  font-weight: 600;
  color: var(--ink);
  border: 1px solid var(--ink);
  padding: 12px 22px;
  border-radius: var(--radius);
  white-space: nowrap;
  text-decoration: none;
}

.cms_pager_prev:hover,
.cms_pager_next:hover,
.cms_pager_prev:focus-visible,
.cms_pager_next:focus-visible {
  color: var(--white);
  background: var(--ink);
}

.cms_pager_prev::before { content: "\2190"; }
.cms_pager_next::after { content: "\2192"; }

@media (max-width: 480px) {
  .cms_pager { flex-wrap: wrap; }
  .cms_pager_count { order: -2; flex-basis: 100%; }
}

/* ---------- article body ---------- */

/* Long-form copy for blog posts: a narrow measure with a sticky contents
   list beside it (built by js/main.js from the article's h2s, so editors
   only write the article). The Key Takeaways box is a <blockquote>: it ships
   in the block's default copy, since the editor's add menu hides quotes. */
.article-body {
  display: grid;
  grid-template-columns: 220px minmax(0, var(--narrow-max));
  gap: 70px;
  justify-content: center;
  align-items: start;
}

.article-toc { position: sticky; top: 110px; font-size: 14px; }
.article-toc p { font-size: 12px; font-weight: 600; letter-spacing: 1.5px; text-transform: uppercase; color: var(--brown); margin-bottom: 10px; }
.article-toc ol { list-style: none; margin: 0; padding: 0; border-left: 2px solid var(--border); }
.article-toc a { display: block; padding: 6px 0 6px 14px; color: var(--body-text); line-height: 1.35; }
.article-toc a:hover { color: var(--ink); }

.article-copy { font-size: 17px; line-height: 1.75; }
.article-copy h2 { font-size: 28px; margin-top: 50px; scroll-margin-top: 110px; }
.article-copy h3 { font-size: 20px; margin-top: 30px; }
.article-copy a { color: var(--teal); font-weight: 500; text-decoration: underline; text-underline-offset: 3px; }
.article-copy ul, .article-copy ol { padding-left: 22px; margin: 0 0 20px; }
.article-copy li { margin-bottom: 8px; }
.article-copy strong { color: var(--ink); }
.article-copy img { border-radius: var(--radius); margin: 30px 0 8px; }
.article-copy figcaption { font-size: 14px; margin-bottom: 30px; }

.article-copy blockquote {
  margin: 0 0 40px;
  padding: 26px 30px;
  background: var(--cream);
  border-left: 4px solid var(--teal);
  border-radius: var(--radius);
  font-size: 16px;
}

.article-copy blockquote > :first-child { font-family: var(--font-heading); font-weight: 600; color: var(--ink); }
.article-copy blockquote ul { margin-bottom: 0; }

.article-copy table { width: 100%; border-collapse: collapse; font-size: 15px; margin: 10px 0 30px; }
.article-copy th, .article-copy td { text-align: left; padding: 12px 14px; border-bottom: 1px solid var(--border); }
.article-copy thead th { background: var(--cream); color: var(--ink); }

/* Blog post meta line in the Page Hero - the editor's "Meta" paragraph style
   (author, date, read time) */
.page-hero p.cms-editor-meta { font-size: 14px; color: #d0d0d0; margin-bottom: 0; }

@media (max-width: 900px) {
  .project-cards { grid-template-columns: 1fr; }

  .article-body { grid-template-columns: minmax(0, 1fr); gap: 20px; }
  .article-toc { position: static; background: var(--cream); padding: 18px 20px; border-radius: var(--radius); }
  .article-copy { font-size: 16px; }

  .trust-strip { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 18px 0; }
  .trust-strip > div:nth-child(odd) { border-left: none; padding-left: 0; }

  .services-grid--4 { grid-template-columns: repeat(2, 1fr); gap: 25px; }
  .services-grid--4 > div { padding-bottom: 16px; }
  .services-grid--4 .e_caption { padding: 16px 16px 0; }
}

@media (min-width: 901px) and (max-width: 1100px) {
  .project-cards { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .article-body { grid-template-columns: 180px minmax(0, 1fr); gap: 40px; }
  .trust-strip > div { padding: 0 14px; }
  .services-grid--4 { grid-template-columns: repeat(2, 1fr); }
}

/* ---------- footer ---------- */

.site-footer {
  background: var(--dark-cream);
  color: var(--ink);
  padding: 70px 25px 30px;
}

.footer-columns {
  max-width: var(--row-max);
  display: grid;
  grid-template-columns: 180px repeat(3, 1fr) auto;
  gap: 30px;
  padding: 0 0 50px;
  border-bottom: 1px solid rgba(38,35,32,.2);
}

.site-footer .logo img { height: 38px; margin-bottom: 20px; }

.footer-nav-col h4 {
  color: var(--ink);
  font-size: 13px;
  text-transform: uppercase;
  letter-spacing: 1px;
  margin-bottom: 6px;
}

.footer-nav-col ul {
  list-style: none;
  display: flex;
  flex-direction: column;
  gap: 2px;
  margin: 0;
  padding: 0;
}

.footer-nav-col a {
  font-size: 15px;
  color: var(--ink);
  opacity: .75;
  transition: opacity .2s ease;
}

.footer-nav-col a:hover { opacity: 1; }

/* A second list in the same column (e.g. Commercial under Company) */
.footer-nav-col ul + h4 { margin-top: 22px; }

.footer-contact { text-align: right; }
.footer-contact .phone { color: var(--ink); font-family: var(--font-heading); font-size: 20px; margin-bottom: 8px; }
.footer-contact a { color: var(--ink); opacity: .75; }
.footer-contact .footer-meta { margin: 14px 0 0; font-size: 14px; opacity: .75; }

/* ---------- mobile call bar ---------- */

/* Fixed to the foot of the screen on phones (index.asp, every page but
   edit mode), so calling or getting a quote is one tap from anywhere. */
.mobile-cta-bar { display: none; }

.footer-bottom {
  max-width: var(--row-max);
  margin: 0 auto;
  padding-top: 25px;
  display: flex;
  flex-wrap: wrap;
  gap: 8px 24px;
  font-size: 13px;
}

.footer-bottom a { color: var(--ink); opacity: .75; }

/* Copyright stays left; the links after it sit together on the right. */
.footer-bottom span { margin-right: auto; }

/* ---------- responsive ---------- */

/* Header switches to the hamburger menu well before the general 900px
   tablet breakpoint below - on a mid-size tablet the full nav + logo + CTA
   button no longer have room to sit on one line without squeezing the logo
   out of its aspect ratio. */
@media (max-width: 1100px) {
  .site-header { grid-template-columns: auto auto; justify-content: space-between; }
  .nav-toggle { display: flex; }

  /* In the dropdown the nav-cta item is just another row - the pulled-right
     pill only makes sense in the inline desktop nav. */
  .site-nav li.nav-cta { margin-left: 0; }
  .site-nav li.nav-cta > a {
    display: block;
    background: none;
    border: none;
    color: var(--ink) !important;
    font-weight: 500;
    padding: 10px;
    border-radius: 0;
  }

  .site-nav {
    display: block;
    position: absolute;
    top: 100%;
    left: 0;
    right: 0;
    height: 0;
    overflow: hidden;
    background: var(--white);
    box-shadow: 0 12px 24px rgba(0,0,0,.12);
    transition: height .3s ease;
  }

  .site-nav.is-open {
    height: calc(100vh - var(--header-h, 80px));
    overflow-y: auto;
  }

  .site-nav ul {
    flex-direction: column;
    align-items: stretch;
    gap: 0;
    padding: 10px 25px 20px;
  }

  .site-header .site-nav a,
  .site-header--transparent .site-nav a,
  .site-header--transparent.is-scrolled .site-nav a {
    display: block;
    padding: 10px;
    border-bottom: 1px solid var(--border);
    color: var(--ink);
  }

  .site-nav li.has-dropdown > a {
    display: flex;
    justify-content: space-between;
  }

  .site-nav li.has-dropdown.is-open > a .caret { transform: rotate(180deg); }

  .site-nav .dropdown {
    position: static;
    opacity: 1;
    visibility: visible;
    transform: none;
    box-shadow: none;
    border-radius: 0;
    background: var(--cream);
    padding: 0;
    max-height: 0;
    overflow: hidden;
    transition: max-height .3s ease;
  }

  .site-nav li.has-dropdown.is-open .dropdown { max-height: 400px; }

  .site-nav .dropdown a {
    padding: 8px 32px;
    border-bottom: 1px solid var(--border);
  }

  body.nav-open {
    overflow: hidden;
    height: 100%;
  }
}

@media (max-width: 900px) {
  :root { --space: 60px; --space-sm: 40px; }

  .services-grid,
  .reviews-grid,
  .split-row,
  .values-grid { grid-template-columns: 1fr; }

  /* Break the scroller out of .portfolio's 25px side padding so it runs to
     the true edge; its own matching padding + scroll-padding put the gutter
     back only at the rest positions (first/last tile). Cancel with a fixed
     -25px margin rather than the vw-based 100vw/calc(50% - 50vw) trick -
     that trick's percentage math can misresolve on elements nested inside a
     CSS grid item (as this can be, several levels down), which either
     leaves the breakout not reaching the edge or, worse, throws in a large
     wrong negative margin that overflows the page. A fixed margin has no
     such dependency. */
  .portfolio-grid {
    margin-left: -25px;
    margin-right: -25px;
    padding-left: 25px;
    padding-right: 25px;
    scroll-padding: 0 25px;
  }

  .portfolio-grid .e_images > div,
  .portfolio-cta { flex-basis: 78%; }

  .portfolio-arrow { display: none; }

  .split-row-text,
  .split-row--flip .split-row-text {
    order: 2;
    padding: 50px 25px;
  }

  .split-row-media,
  .split-row--flip .split-row-media {
    order: 1;
  }

  .split-row-media img { min-height: 280px; }

  .feature-row:not(:last-child)::after { display: none; }

  .feature-row {
    grid-template-columns: 1fr;
    justify-items: center;
    text-align: center;
    gap: 20px;
  }

  .feature-row:not(:last-child) { margin-bottom: 40px; }

  .feature-photo,
  .feature-stack .feature-row:nth-child(even) .feature-photo {
    grid-column: 1;
    grid-row: 1;
    justify-self: center;
  }

  .feature-text { grid-column: 1; grid-row: 2; max-width: 100%; }

  .hero h1 { font-size: 34px; }
  .hero .row { grid-template-columns: 1fr; }
  .hero-copy { text-align: left; max-width: 100%; }

  .hero-split { grid-template-columns: 1fr; height: auto; max-height: none; }
  .hero-panel { height: 60vh; min-height: 420px; }
  .panel-content { padding: 30px; }
  .panel-content h2 { font-size: 28px; }
  .hero-split .panel-bespoke::after { display: none; }

  .hero-splash-content { margin-bottom: 28px; padding: 0 25px; }
  .hero-splash-content h1 { font-size: 30px; }
  .hero-splash-content p:not(.cms-editor-eyebrow) { display: none; }

  /* Below 900px the image cards are replaced by two plain buttons - the
     intro copy carries the message, so the cards' photos and descriptions
     are redundant weight on a small screen. Only the dark-button paragraph
     (each card's link text) stays visible. Stacked full-width (rather than
     side by side) so the label always has room to sit on one line. */
  .hero-splash-cards {
    grid-template-columns: 1fr;
    width: calc(100% - 32px);
    gap: 10px;
  }
  /* + safe-area-inset-bottom so the buttons clear the home indicator on
     notched phones rather than sitting under/behind it. */
  .hero-splash { padding-bottom: calc(30px + env(safe-area-inset-bottom, 0px)); }
  .hero-splash-cards img,
  .hero-splash-cards h3,
  .hero-splash-cards p:not(.cms-editor-dark-button) { display: none; }
  .hero-splash-cards > div { background: none; }
  .hero-splash-cards .e_caption { padding: 0; align-items: stretch; }
  .hero-splash-cards p.cms-editor-dark-button {
    margin: 0;
    width: 100%;
    justify-content: center;
    padding: 14px 16px;
    font-size: 14px;
    background: var(--border);
    border-color: var(--border);
    color: var(--ink);
  }

  .page-hero { min-height: 440px; }
  .page-hero h1 { font-size: 32px; }

  .values-grid--3,
  .process-steps ol,
  .faq { grid-template-columns: 1fr; }

  /* Image cards become a swipe row: each card is 82% wide so the next one
     peeks in. The section's 25px side padding stays as the gutter, and
     scroll-padding keeps snapped cards aligned with it. */
  .image-cards {
    display: flex;
    gap: 16px;
    overflow-x: auto;
    scroll-snap-type: x mandatory;
    scroll-padding: 0 25px;
    -webkit-overflow-scrolling: touch;
    scrollbar-width: none;
    /* .image-cards already carries the row's own 25px padding (see the
       shared row rule); cancel it with a fixed negative margin so the track
       bleeds to the true edge. Fixed, not the vw-based 100vw/calc(50% -
       50vw) trick: that trick's percentage math can misresolve on elements
       nested inside a CSS grid item (as .gallery-carousel-track can be,
       several levels down), either leaving the breakout short of the edge
       or, worse, applying a large wrong negative margin that overflows the
       page. A fixed margin has no such dependency. */
    margin-left: -25px;
    margin-right: -25px;
    /* See .gallery-carousel-track: stops edge-swipes rubber-banding into the
       page and stops scroll-anchoring re-targeting scrollLeft as cards load. */
    overscroll-behavior-x: contain;
    overflow-anchor: none;
  }
  .image-cards::-webkit-scrollbar { display: none; }
  .image-cards > div { flex: 0 0 82%; scroll-snap-align: start; }

  /* Comparison table: same full-bleed-with-gutter treatment for the
     sideways-scrolling table region nested inside .compare-table's padding. */
  .compare-table .e_editor {
    margin-left: -25px;
    margin-right: -25px;
    padding-left: 25px;
    padding-right: 25px;
  }

  .gallery-grid { grid-template-columns: repeat(2, 1fr); gap: 10px; }

  /* Same full-bleed-with-gutter treatment as the other touch scrollers:
     break out of whichever ancestor padding it's nested in (.gallery-carousel
     directly on .page, or .article-copy/.case-study-body's own column) so it
     always reaches the true viewport edge, not just the page/column edge.
     Fixed -25px margin, not 100vw/calc(50% - 50vw) - see .portfolio-grid. */
  .gallery-carousel-track {
    margin-left: -25px;
    margin-right: -25px;
    padding-left: 25px;
    padding-right: 25px;
    scroll-padding: 0 25px;
  }
  .gallery-carousel-track > div { height: 260px; }

  .faq { gap: 30px; }

  /* Case study stacks: copy, then the facts panel (no longer sticky).
     minmax(0, 1fr), not plain 1fr: a grid item's automatic minimum width
     otherwise defaults to its content's intrinsic size, and a full-bleed
     scroller nested inside .case-study-body (several levels down) has a
     wide, non-wrapping strip of images as that intrinsic content - which
     then blows out this column and overflows the page. Matches the same
     guard .article-body already uses for the blog layout. */
  .case-study { grid-template-columns: minmax(0, 1fr); gap: 40px; }
  .case-study-aside { position: static; padding: 28px 25px; }

  .image-strip--tall img { height: 60vh; min-height: 320px; }

  .callout-strip-inner { grid-template-columns: 1fr; gap: 20px; padding: 30px 25px; }

  .process-steps { padding: 60px 25px; }

  .footer-columns { grid-template-columns: 1fr 1fr; gap: 30px; }
  .footer-brand,
  .footer-contact { grid-column: 1 / -1; }
  .footer-contact { text-align: left; }

  /* Already in the sticky footer below, so drop it from the dropdown to
     avoid showing the same link twice. */
  .site-nav li.nav-cta { display: none; }

  .mobile-cta-bar {
    display: block;
    position: fixed;
    left: 0;
    right: 0;
    bottom: 0;
    z-index: 50;
    padding-bottom: env(safe-area-inset-bottom, 0px);
    background: var(--border);
    border-top: 1px solid var(--border);
    box-shadow: 0 -4px 16px rgba(0,0,0,.12);
  }
  .mobile-cta-bar a {
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 15px 10px;
    font: 600 15px/1 var(--font-heading);
    color: var(--ink);
  }
  body.has-cta-bar { padding-bottom: 50px; }

  /* On a page with the 100vh splash hero, the hero already has its own
     buttons at the bottom of the screen - the CTA bar would sit right on
     top of them. Start it off-screen and let main.js slide it in (.is-visible)
     once the hero has scrolled past, instead of showing both at once. */
  body.has-hero-splash .mobile-cta-bar {
    transform: translateY(100%);
    transition: transform .3s ease;
  }
  body.has-hero-splash .mobile-cta-bar.is-visible { transform: translateY(0); }
}

@media (min-width: 901px) and (max-width: 1100px) {
  .footer-columns { grid-template-columns: repeat(4, 1fr); }
  .footer-brand,
  .footer-contact { grid-column: 1 / -1; }
  .footer-contact { text-align: left; }

  .image-cards,
  .values-grid--3,
  .process-steps ol { grid-template-columns: repeat(2, 1fr); }
  .gallery-grid { grid-template-columns: repeat(3, 1fr); }
}

/* ---------- empty image regions ---------- */

/* Repeatable items (case study / blog sections) ship with an .e_image whose
   img has no src until an editor picks one - hide it on the public page so
   an unfilled slot doesn't show a broken image icon. admin/cms/cms.css
   overrides this back to a visible placeholder while editing (body.edit_mode),
   so editors can still see and click an empty slot to add one. */
.e_image:has(> img[src=""]), .e_image:has(> a > img[src=""]), .e_image:has(> video[src=""]) {
  display: none;
}

/* Same idea for any .e_images list site-wide (Gallery Carousel/Grid, hero
   cards, service cards, split-row media, portfolio grid, ...): it's either
   optional (can be emptied down to zero photos via the slideover, or never
   filled in) or shipped with just its one starting unfilled placeholder -
   either way, hide it on the public page rather than show a broken image
   icon or an empty card row. An item can be a video (the CMS image editor
   can swap any item between photo and video), which sits as a bare
   <video>, same as an unlinked photo. admin/cms/cms.css overrides this
   back to a visible "click to add photos" placeholder while editing. */
.e_images:empty,
.e_images:has(> div:only-child > img[src=""]),
.e_images:has(> div:only-child > a > img[src=""]),
.e_images:has(> div:only-child > video[src=""]) {
  display: none;
}

/* A gallery-carousel's own wrapper chrome (arrows, fixed-height track) isn't
   itself .e_images, so it needs its own visibility rule on top of the one
   above, which only hides the track. */
.gallery-carousel:has(.gallery-carousel-track:empty),
.gallery-carousel:has(.gallery-carousel-track > div:only-child > img[src=""]),
.gallery-carousel:has(.gallery-carousel-track > div:only-child > a > img[src=""]),
.gallery-carousel:has(.gallery-carousel-track > div:only-child > video[src=""]) {
  display: none;
}

/* A section carousel left with just one photo shouldn't look like a
   filmstrip with dead arrows (the arrows already auto-hide via js/main.js
   when there's nothing to scroll) - show it full width, capped height,
   instead of the fixed-height/auto-width carousel tile. */
.gallery-carousel-track > div:only-child {
  flex: 1 1 auto;
  width: 100%;
  max-width: none;
  height: auto;
  max-height: 480px;
}

.gallery-carousel-track > div:only-child img,
.gallery-carousel-track > div:only-child video {
  width: 100%;
  height: auto;
  max-height: 480px;
  object-fit: cover;
}

/* ---------- video block ---------- */

/* "Video" is a bare <section><div><div class="e_image"><video></video></div></div></section>
   (System db/021), so it's matched by what it holds. Same row, gutter and
   block spacing as the other blocks; the player is centred and capped so it
   doesn't stretch across the full row. */
.page > section:has(> div > .e_image > video) {
  width: 100%;
  max-width: var(--row-max);
  margin: var(--space) auto;
  padding: 0 25px;
}

.page > .section-intro + section:has(> div > .e_image > video),
.page > .section-intro + .composer + section:has(> div > .e_image > video) { margin-top: 0; }

.page > section:has(> div > .e_image > video) > div > .e_image {
  display: flex;
  justify-content: center;
}

.page > section:has(> div > .e_image > video) video {
  display: block;
  width: 100%;
  max-width: 960px;
  height: auto;
  aspect-ratio: 16 / 9;
  background: #000;
  border-radius: var(--radius);
}

/* The engine's "add form" picker (admin/cms/blocks.asp) emits a bare
   <section><div>...<div class="cms_form">...</div></div></section> with no
   block-specific class (unlike the named blocks in the width/spacing list
   above), so match it by structure, the same way cms_related and the video
   block are matched elsewhere in this file. :where() keeps this rule's
   specificity below .page > .narrow / .page > .wide so the width toggle
   still overrides max-width if it's ever switched. */
.page > section:where(:has(> div > .cms_form)) {
  width: 100%;
  max-width: var(--row-max);
  margin: var(--space) auto;
  padding-left: 25px;
  padding-right: 25px;
}

.page > .section-intro + section:where(:has(> div > .cms_form)),
.page > .section-intro + .composer + section:where(:has(> div > .cms_form)) { margin-top: 0; }

/* ---------- forms module (.cmsf) ---------- */

/* Rendered by the shared forms engine (inc/form-render.css), whose default
   rules are all :where()-wrapped (zero specificity), so these override it
   without !important. Re-colour via the module's own variables, then match
   the rest to the site's inputs/buttons (see .form-field, .btn above). */
.cmsf {
  --cmsf-accent: var(--teal);
  --cmsf-line: var(--dark-cream);
  --cmsf-bad: #b3261e;
  --cmsf-radius: var(--radius);
  max-width: none;
}

.cmsf-label {
  display: block;
  font-family: var(--font-heading);
  font-size: 14px;
  font-weight: 600;
  color: var(--ink);
  margin-bottom: 6px;
}

.cmsf-req { color: var(--cmsf-bad); }

.cmsf input[type="text"],
.cmsf input[type="email"],
.cmsf input[type="tel"],
.cmsf select,
.cmsf textarea {
  width: 100%;
  font: 16px/1.4 var(--font-body);
  color: var(--ink);
  background: var(--white);
  border: 1px solid var(--cmsf-line);
  border-radius: var(--cmsf-radius);
  padding: 12px 14px;
}

.cmsf textarea { min-height: 130px; resize: vertical; }

.cmsf input:focus-visible,
.cmsf select:focus-visible,
.cmsf textarea:focus-visible {
  outline: 2px solid var(--teal);
  outline-offset: 1px;
}

.cmsf-check {
  display: flex;
  align-items: flex-start;
  gap: 8px;
  font-size: 14px;
  color: var(--ink);
  cursor: pointer;
}

.cmsf-error {
  font-size: 13px;
  color: var(--cmsf-bad);
  margin-top: 6px;
}

.cmsf-error[hidden] { display: none; }

.cmsf [aria-invalid="true"] { border-color: var(--cmsf-bad); }

.cmsf-drop {
  position: relative;
  border: 1px dashed var(--brown);
  border-radius: var(--cmsf-radius);
  padding: 20px;
  text-align: center;
  font-size: 14px;
  color: var(--body-text);
  background: #fbfaf7;
}

.cmsf-drop.cmsf-over { border-color: var(--teal); background: var(--cream); }

.cmsf-drop-text strong { color: var(--ink); font-weight: 600; }

.cmsf-files {
  position: relative;
  z-index: 1;
  list-style: none;
  margin: 14px 0 0;
  padding: 0;
  text-align: left;
}

.cmsf-files li {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 6px 0;
  font-size: 13px;
  color: var(--ink);
}

.cmsf-fsize { color: var(--body-text); }

.cmsf-remove {
  margin-left: auto;
  border: none;
  background: none;
  color: var(--body-text);
  cursor: pointer;
}

.cmsf-privacy { font-size: 13px; color: var(--body-text); }

.cmsf-privacy a { color: var(--teal); text-decoration: underline; }

.cmsf-btn {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  font-family: var(--font-heading);
  font-size: 15px;
  font-weight: 600;
  color: var(--ink);
  background: var(--teal);
  border: none;
  border-radius: var(--cmsf-radius);
  padding: 14px 26px;
  cursor: pointer;
}

.cmsf-btn:disabled { opacity: 0.6; cursor: default; }

.cmsf-status { font-size: 14px; margin-top: 10px; }

.cmsf-status.cmsf-bad { color: var(--cmsf-bad); }

.cmsf-thanks {
  background: var(--cream);
  border-radius: var(--radius);
  padding: 30px;
  font-size: 16px;
  color: var(--ink);
}

/* Short fields share a row, long ones (textarea, file) take the full width. */
.cmsf { display: grid; grid-template-columns: 1fr 1fr; column-gap: 20px; }
.cmsf > * { grid-column: 1 / -1; }
.cmsf > .cmsf-text,
.cmsf > .cmsf-email,
.cmsf > .cmsf-tel,
.cmsf > .cmsf-select { grid-column: auto; }

@media (max-width: 600px) {
  .cmsf { grid-template-columns: 1fr; }
  .cmsf > .cmsf-text,
  .cmsf > .cmsf-email,
  .cmsf > .cmsf-tel,
  .cmsf > .cmsf-select { grid-column: 1 / -1; }
}

/* Cookie banner (/_inc/cookie-consent.css supplies the base layout; this
   rethemes it with the --cc-* variables and makes it a lighter, smaller bar
   with Accept as the prominent button. */
.cc-banner {
  --cc-bg: var(--white);
  --cc-text: var(--ink);
  --cc-accent: var(--dark);
  --cc-accent-text: var(--white);
  --cc-line: var(--border);
  font-family: var(--font-body);
  font-size: 14px;
  max-width: 34rem;
  padding: 14px 18px;
  gap: 10px 18px;
  border-radius: var(--radius);
  box-shadow: 0 4px 18px rgba(0,0,0,.12);
}
/* Text on top, buttons underneath, left-aligned with Accept first. */
.cc-banner { flex-direction: column; align-items: flex-start; }
.cc-text { flex: 0 0 auto; }
.cc-text a { color: var(--dark); text-decoration: underline; }
.cc-actions { flex-wrap: nowrap; align-items: center; justify-content: flex-start; gap: 10px; }
.cc-btn {
  font-family: var(--font-heading);
  font-weight: 500;
  font-size: 14px;
  border-radius: var(--radius);
}
/* Accept (first in the markup) is filled; the other choice is a pale grey button. */
.cc-btn[data-cc="accept"] { padding: 10px 24px; font-weight: 600; }
.cc-banner .cc-btn.cc-btn-alt {
  padding: 10px 20px;
  border: 0 none;
  background: #eeeeee;
  color: #777777;
}
@media (max-width: 900px) {
  /* Sit above the sticky call bar rather than on top of it. */
  .cc-banner { bottom: 62px; }
}
