/* ===== template/css/tokens.css ===== */
/*
  Design tokens: the named values every other stylesheet uses.

  Why: instead of typing a colour like #1f4e79 in twenty places, stylesheets
  use var(--color-primary). A theme (template/css/themes/) or a client's
  custom.css changes the look by redefining these names in one place.

  These defaults are a plain, neutral look. All text colours meet WCAG 2.2 AA
  contrast (4.5:1 or more) on both --color-bg and --color-surface. If you
  change a colour, re-check its contrast (e.g. webaim.org/resources/contrastchecker).
*/

:root {
  /* ---------- Colours ---------- */
  --color-bg: #ffffff;            /* page background */
  --color-surface: #f4f5f7;       /* slightly tinted panels, e.g. the hero */
  --color-text: #1b1d21;          /* main text: 16.9:1 on bg */
  --color-text-muted: #4a4f57;    /* secondary text: 8.2:1 on bg, 7.6:1 on surface */
  --color-primary: #1f4e79;       /* links and buttons: 8.7:1 on bg */
  --color-primary-hover: #163a5a;
  --color-on-primary: #ffffff;    /* text placed on top of --color-primary */
  --color-border: #d5d8dd;        /* decorative lines only (not text) */
  --color-input-border: #767b84;  /* form field edges: 4.3:1 on bg (WCAG needs 3:1) */
  --color-focus: #1f4e79;         /* keyboard focus outline */

  /* Form messages: text colour on its matching background, 5.7:1+ */
  --color-success: #1d6b3a;
  --color-success-bg: #e8f4ec;
  --color-error: #b42318;
  --color-error-bg: #fdecea;

  /* ---------- Fonts ---------- */
  /* System fonts: free, load instantly, and look native on every device. */
  --font-body: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
  --font-heading: var(--font-body);
  --line-height-body: 1.6;
  --line-height-heading: 1.2;
  --font-weight-heading: 700;

  /*
    Fluid type scale: text grows smoothly from small phones (360px wide)
    to large screens (1240px wide). Each step is about 1.2x the one below.
    Using rem inside clamp() keeps text resizable when visitors zoom in,
    which WCAG requires.
  */
  --step--1: clamp(0.83rem, 0.81rem + 0.1vw, 0.9rem);
  --step-0: clamp(1rem, 0.95rem + 0.23vw, 1.125rem);
  --step-1: clamp(1.2rem, 1.12rem + 0.36vw, 1.41rem);
  --step-2: clamp(1.44rem, 1.31rem + 0.57vw, 1.76rem);
  --step-3: clamp(1.73rem, 1.53rem + 0.86vw, 2.2rem);
  --step-4: clamp(2.07rem, 1.79rem + 1.25vw, 2.75rem);
  --step-5: clamp(2.49rem, 2.1rem + 1.77vw, 3.43rem);

  /* ---------- Spacing ---------- */
  --space-3xs: 0.25rem;
  --space-2xs: 0.5rem;
  --space-xs: 0.75rem;
  --space-s: 1rem;
  --space-m: 1.5rem;
  --space-l: 2rem;
  --space-xl: 3rem;
  --space-2xl: 4.5rem;
  --space-3xl: 6rem;

  /* ---------- Layout ---------- */
  --container-width: 72rem;       /* widest the content gets */
  --container-gutter: var(--space-s); /* gap at the screen edges (16px) */
  --measure: 65ch;                /* comfortable line length for reading */

  /* ---------- Shapes and effects ---------- */
  --radius-s: 0.25rem;
  --radius-m: 0.5rem;
  --radius-l: 1rem;
  --shadow-s: 0 1px 2px rgb(0 0 0 / 0.08);
  --shadow-m: 0 4px 12px rgb(0 0 0 / 0.1);
  --transition: 150ms ease;

  /* Buttons and links must be easy to tap (WCAG 2.2 asks for 24px minimum;
     44px is the comfortable size most guidelines recommend). */
  --tap-target: 2.75rem;
}

/* ===== template/css/base.css ===== */
/*
  Base styles: a light "reset" plus sensible defaults for plain HTML elements,
  and a few small helper classes used everywhere.
  Colours, fonts and sizes all come from tokens.css.
*/

/* ---------- Reset ---------- */

/* Width and height include padding and border, which makes sizing predictable. */
*,
*::before,
*::after {
  box-sizing: border-box;
}

/* Browsers add uneven default margins; we add spacing deliberately instead. */
body,
h1, h2, h3, h4, h5, h6,
p, figure, blockquote,
ul, ol, dl, dd {
  margin: 0;
}

html {
  /* Stops phones enlarging text on their own when rotated. */
  -webkit-text-size-adjust: 100%;
  text-size-adjust: 100%;
}

/* Menu links glide to their section instead of jumping, but only for
   visitors who have not asked their device to reduce motion. */
@media (prefers-reduced-motion: no-preference) {
  html {
    scroll-behavior: smooth;
  }
}

/* Images never overflow their container, and sit on their own line. */
img,
svg,
video,
picture {
  display: block;
  max-width: 100%;
  height: auto;
}

/* Form controls use the page font instead of the browser's default. */
input,
button,
textarea,
select {
  font: inherit;
}

/* ---------- Page defaults ---------- */

body {
  min-height: 100vh;
  /* Footer stays at the bottom even when a page is short. */
  display: flex;
  flex-direction: column;
  background-color: var(--color-bg);
  color: var(--color-text);
  font-family: var(--font-body);
  font-size: var(--step-0);
  line-height: var(--line-height-body);
}

main {
  flex: 1;
}

h1, h2, h3, h4 {
  font-family: var(--font-heading);
  font-weight: var(--font-weight-heading);
  line-height: var(--line-height-heading);
  /* Avoids a single word left alone on the last line of a heading. */
  text-wrap: balance;
}

h1 { font-size: var(--step-5); }
h2 { font-size: var(--step-4); }
h3 { font-size: var(--step-2); }
h4 { font-size: var(--step-1); }

p {
  text-wrap: pretty;
}

a {
  color: var(--color-primary);
  /* Underlines make links recognisable without relying on colour alone. */
  text-decoration-thickness: 1px;
  text-underline-offset: 0.2em;
}

a:hover {
  color: var(--color-primary-hover);
  text-decoration-thickness: 2px;
}

address {
  font-style: normal;
}

/* ---------- Keyboard focus ---------- */

/* A clear outline shows keyboard users where they are on the page.
   :focus-visible only shows it for keyboard use, not mouse clicks. */
:focus-visible {
  outline: 3px solid var(--color-focus);
  outline-offset: 3px;
}

/* ---------- Helper classes ---------- */

/* Centres content and keeps it off the screen edges. */
.container {
  width: 100%;
  max-width: var(--container-width);
  margin-inline: auto;
  padding-inline: var(--container-gutter);
}

/* Hidden on screen but still read out by screen readers. */
.visually-hidden {
  position: absolute !important;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

/* "Skip to main content": hidden until a keyboard user presses Tab,
   then it appears so they can jump past the header. */
.skip-link {
  position: absolute;
  top: var(--space-2xs);
  left: var(--space-2xs);
  z-index: 100;
  padding: var(--space-2xs) var(--space-s);
  background-color: var(--color-bg);
  color: var(--color-primary);
  font-weight: 700;
  border-radius: var(--radius-s);
  transform: translateY(-200%);
}

.skip-link:focus {
  transform: none;
}

/* Main call-to-action button (used on <a> and <button>). */
.button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: var(--tap-target);
  padding: var(--space-2xs) var(--space-m);
  background-color: var(--color-primary);
  color: var(--color-on-primary);
  font-weight: 700;
  text-decoration: none;
  border: 2px solid var(--color-primary);
  border-radius: var(--radius-m);
  cursor: pointer;
  transition: background-color var(--transition), border-color var(--transition);
}

.button:hover {
  background-color: var(--color-primary-hover);
  border-color: var(--color-primary-hover);
  color: var(--color-on-primary);
}

/* Less important action: outlined instead of filled. */
.button--secondary {
  background-color: transparent;
  color: var(--color-primary);
}

.button--secondary:hover {
  background-color: var(--color-primary);
  border-color: var(--color-primary);
  color: var(--color-on-primary);
}

/* Buttons placed on a --color-primary background (e.g. the call-to-action
   banner): the colours swap so the button still stands out. */
.button--inverse {
  background-color: var(--color-on-primary);
  border-color: var(--color-on-primary);
  color: var(--color-primary);
}

.button--inverse:hover {
  background-color: transparent;
  border-color: var(--color-on-primary);
  color: var(--color-on-primary);
}

.button--inverse-secondary {
  background-color: transparent;
  border-color: var(--color-on-primary);
  color: var(--color-on-primary);
}

.button--inverse-secondary:hover {
  background-color: var(--color-on-primary);
  border-color: var(--color-on-primary);
  color: var(--color-primary);
}

/* A row of buttons that wraps onto new lines on small screens. */
.button-group {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-xs);
}

/* ---------- Reduced motion ---------- */

/* Some visitors get dizzy or ill from animation and turn it off in their
   device settings. This respects that choice everywhere on the site. */
@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
}

/* ===== template/css/layout.css ===== */
/* Styles for the parts of the page every site shares: header, navigation,
   footer, and the common spacing of page sections. */

/* ---------- Header ---------- */

.site-header {
  border-bottom: 1px solid var(--color-border);
  background-color: var(--color-bg);
}

/*
  Phones (mobile-first):          Wide screens (60em = 960px and up):
  +---------------------+         +-----------------------------------------+
  | Name          Phone |         | Name      Services About ...      Phone |
  | Services About ...  |         +-----------------------------------------+
  +---------------------+
  On very narrow screens, or with a long business name, the phone number
  moves onto its own line instead of being cut off.
*/
.site-header__inner {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3xs) var(--space-m);
  padding-block: var(--space-xs);
}

@media (min-width: 60em) {
  .site-header__inner {
    display: grid;
    grid-template-columns: auto 1fr auto;
    grid-template-areas: "name nav phone";
  }
}

.site-header__name {
  grid-area: name;
  color: var(--color-text);
  font-family: var(--font-heading);
  font-size: var(--step-1);
  font-weight: var(--font-weight-heading);
  line-height: var(--line-height-heading);
  text-decoration: none;
}

.site-header__phone {
  grid-area: phone;
  /* Big enough to tap comfortably on a phone. */
  display: inline-flex;
  align-items: center;
  min-height: var(--tap-target);
  font-weight: 700;
  white-space: nowrap; /* never split the number across two lines */
}

/* ---------- Navigation ---------- */

.site-nav {
  grid-area: nav;
  /* On phones: always last, on a full-width line of its own. */
  order: 3;
  flex-basis: 100%;
  /* On phones the row stretches to the screen edges so links can be swiped
     right up to the edge; the padding below keeps them lined up with the name. */
  margin-inline: calc(var(--container-gutter) * -1);
}

.site-nav__list {
  display: flex;
  gap: var(--space-3xs);
  margin: 0;
  /* Each link has its own padding (for a bigger tap area), so the row starts
     that much further left. This lines up the first link's text with the
     business name above it. */
  padding-block: 0;
  padding-inline: calc(var(--container-gutter) - var(--space-2xs));
  list-style: none;
  /* If the links don't fit, the row can be swiped sideways. */
  overflow-x: auto;
  scrollbar-width: none; /* the shadows below show there is more instead */

  /*
    "Scroll shadows": a soft shadow appears at an edge only while there are
    more links hidden past that edge, hinting that the row can be swiped.
    Pure CSS: the two solid "covers" scroll with the links and hide the two
    shadows (which stay fixed) when the row is scrolled all the way to an end.
  */
  background:
    linear-gradient(to right, var(--color-bg) 30%, transparent) left center / 2rem 100% no-repeat local,
    linear-gradient(to left, var(--color-bg) 30%, transparent) right center / 2rem 100% no-repeat local,
    radial-gradient(farthest-side at 0 50%, rgb(0 0 0 / 0.18), transparent) left center / 0.75rem 100% no-repeat scroll,
    radial-gradient(farthest-side at 100% 50%, rgb(0 0 0 / 0.18), transparent) right center / 0.75rem 100% no-repeat scroll;
  background-color: var(--color-bg);
}

.site-nav__list::-webkit-scrollbar {
  display: none;
}

@media (min-width: 60em) {
  .site-nav {
    margin-inline: 0;
  }

  .site-nav__list {
    justify-content: flex-end;
    padding: 0;
    overflow-x: visible;
    background: none;
  }
}

.site-nav__link {
  display: inline-flex;
  align-items: center;
  min-height: var(--tap-target);
  padding-inline: var(--space-2xs);
  color: var(--color-text);
  text-decoration: none;
  white-space: nowrap;
}

.site-nav__link:hover {
  color: var(--color-primary);
  text-decoration: underline;
}

/* Focus outline is drawn inside the link here, so the scrolling row
   doesn't cut it off. */
.site-nav__link:focus-visible {
  outline-offset: -3px;
}

/* ---------- Page sections (shared by all sections) ---------- */

.section {
  padding-block: var(--space-2xl);
}

.section__header {
  max-width: var(--measure);
  margin-bottom: var(--space-l);
}

.section__intro {
  margin-top: var(--space-xs);
  color: var(--color-text-muted);
  font-size: var(--step-1);
}

/* ---------- Simple pages (e.g. "thank you") ---------- */

.simple-page {
  padding-block: var(--space-3xl);
}

.simple-page__heading {
  max-width: var(--measure);
  font-size: var(--step-4);
}

.simple-page__text {
  margin-top: var(--space-s);
  color: var(--color-text-muted);
  font-size: var(--step-1);
}

.simple-page__actions {
  margin-top: var(--space-l);
}

/* Long text pages (e.g. privacy): comfortable reading width and spacing. */
.prose .container > * {
  max-width: var(--measure);
}

.prose h2 {
  margin-top: var(--space-xl);
  margin-bottom: var(--space-xs);
  font-size: var(--step-2);
}

.prose p + p {
  margin-top: var(--space-s);
}

.prose__meta {
  margin-top: var(--space-xs);
  color: var(--color-text-muted);
}

/* ---------- Footer ---------- */

.site-footer {
  padding-block: var(--space-xl);
  border-top: 1px solid var(--color-border);
  background-color: var(--color-surface);
  font-size: var(--step--1);
}

.site-footer__inner {
  display: grid;
  gap: var(--space-s);
}

.site-footer__name {
  font-weight: 700;
  font-size: var(--step-0);
}

.site-footer__contact a {
  /* Extra line height gives stacked links more room to tap. */
  display: inline-block;
  padding-block: var(--space-3xs);
}

.site-footer__social {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-3xs) var(--space-m);
  margin: 0;
  padding: 0;
  list-style: none;
}

.site-footer__social a,
.site-footer__legal a {
  display: inline-block;
  padding-block: var(--space-3xs); /* more room to tap */
}

.site-footer__legal {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-3xs) var(--space-m);
  color: var(--color-text-muted);
}

/* ===== template/sections/hero.css ===== */
/* Hero section: the opening message at the top of the page. */

.hero {
  padding-block: var(--space-2xl) var(--space-xl);
  background-color: var(--color-surface);
}

.hero__inner {
  display: grid;
  gap: var(--space-xl);
  align-items: center;
}

/* Long lines are hard to read, so the text is kept narrow while the
   container itself stays lined up with the header and footer. */
.hero__content > * {
  max-width: var(--measure);
}

.hero__heading {
  font-size: var(--step-5);
}

.hero__text {
  margin-top: var(--space-s);
  font-size: var(--step-1);
  color: var(--color-text-muted);
}

.hero__content .button-group {
  margin-top: var(--space-l);
}

.hero__image {
  width: 100%;
  border-radius: var(--radius-l);
  /* Keeps every hero photo the same shape, whatever its original size. */
  aspect-ratio: 4 / 3;
  object-fit: cover;
}

/* ---------- Variant: split (the default) ----------
   Phones: text first, photo below. Wide screens: side by side. */

@media (min-width: 56em) {
  .hero--split.hero--with-image .hero__inner {
    grid-template-columns: 1.1fr 1fr;
  }
}

/* ---------- Variant: centered ---------- */

.hero--centered .hero__content {
  text-align: center;
}

.hero--centered .hero__content > * {
  margin-inline: auto;
}

.hero--centered .button-group {
  justify-content: center;
}

.hero--centered .hero__image {
  aspect-ratio: 16 / 7; /* a wide strip under the text */
}

/* ---------- Variant: background ----------
   The photo is placed behind the text and darkened.

   Why a flat 60% black layer: white text must contrast 4.5:1 with whatever
   is behind it, even over the brightest part of a photo. Pure white under
   60% black becomes #666666, which still gives white text 5.7:1. */

/* ".hero.hero--background" (two classes) so themes, which style ".hero" and
   load later, cannot replace the dark colour or the padding here. */
.hero.hero--background {
  position: relative;
  isolation: isolate; /* keeps the photo's z-index inside this section */
  padding-block: var(--space-3xl);
  background-color: #1a1a1a; /* shown while the photo loads */
  color: #ffffff;
}

.hero--background .hero__media {
  position: absolute;
  inset: 0;
  z-index: -1;
}

.hero--background .hero__media::after {
  content: "";
  position: absolute;
  inset: 0;
  background-color: rgb(0 0 0 / 0.6);
}

.hero--background .hero__image {
  width: 100%;
  height: 100%;
  aspect-ratio: auto;
  border-radius: 0;
}

.hero--background .hero__text {
  color: #ffffff;
}

.hero--background :focus-visible {
  outline-color: #ffffff;
}

@media (min-width: 56em) {
  .hero.hero--background {
    padding-block: calc(var(--space-3xl) * 1.5);
  }
}

/* ===== template/sections/services.css ===== */
/* Services section: a grid of service cards. */

/* As many columns as fit, each at least 17rem wide: one column on phones,
   two on tablets, three on wide screens. No breakpoints needed. */
.services__list {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(min(100%, 17rem), 1fr));
  gap: var(--space-m);
  margin: 0;
  padding: 0;
  list-style: none;
}

.service-card {
  display: flex;
  flex-direction: column;
  overflow: hidden; /* keeps the photo inside the rounded corners */
  background-color: var(--color-bg);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-m);
}

.service-card__image {
  width: 100%;
  aspect-ratio: 3 / 2;
  object-fit: cover;
}

.service-card__body {
  display: flex;
  flex-direction: column;
  flex: 1;
  gap: var(--space-2xs);
  padding: var(--space-m);
}

.service-card__title {
  font-size: var(--step-1);
}

.service-card__text {
  color: var(--color-text-muted);
}

/* Pushed to the bottom so prices line up across a row of cards. */
.service-card__price {
  margin-top: auto;
  padding-top: var(--space-2xs);
  font-weight: 700;
}

/* ---------- Variant: list (a price list) ---------- */

.services__price-list {
  max-width: 48rem;
  margin: 0;
  padding: 0;
  list-style: none;
  border-top: 1px solid var(--color-border);
}

.service-row {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: var(--space-m);
  padding-block: var(--space-s);
  border-bottom: 1px solid var(--color-border);
}

.service-row__title {
  font-size: var(--step-1);
}

.service-row__text {
  margin-top: var(--space-3xs);
  color: var(--color-text-muted);
}

.service-row__price {
  flex-shrink: 0; /* the price never gets squashed onto two lines */
  font-weight: 700;
  white-space: nowrap;
}

/* ===== template/sections/gallery.css ===== */
/* Gallery section: photo grid and lightbox. */

/* Two columns on phones, more on wider screens. */
.gallery__grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(min(45%, 15rem), 1fr));
  gap: var(--space-xs);
  margin: 0;
  padding: 0;
  list-style: none;
}

.gallery__item {
  margin: 0;
}

.gallery__link {
  display: block;
  overflow: hidden;
  border-radius: var(--radius-m);
}

/* Every thumbnail is cropped to the same square shape so the grid is tidy.
   The full photo, uncropped, is shown in the lightbox. */
.gallery__image {
  width: 100%;
  aspect-ratio: 1 / 1;
  object-fit: cover;
  transition: transform var(--transition);
}

.gallery__link:hover .gallery__image {
  transform: scale(1.03);
}

.gallery__caption {
  margin-top: var(--space-3xs);
  color: var(--color-text-muted);
  font-size: var(--step--1);
}

/* ---------- Lightbox ---------- */

/* Stop the page behind from scrolling while the viewer is open. */
html:has(.lightbox[open]) {
  overflow: hidden;
}

.lightbox {
  width: 100%;
  max-width: 100%;
  height: 100%;
  max-height: 100%;
  margin: 0;
  padding: var(--space-xl) var(--space-s);
  border: 0;
  background: transparent;
  color: #ffffff;
}

/* Only lay it out when open: a closed <dialog> must stay hidden. */
.lightbox[open] {
  display: grid;
  place-items: center;
}

.lightbox::backdrop {
  background-color: rgb(0 0 0 / 0.88);
}

.lightbox__figure {
  display: grid;
  justify-items: center;
  gap: var(--space-xs);
  width: 100%;
  margin: 0;
}

/*
  As large as possible while the whole photo stays on screen. The width is
  the smallest of:
    - the full width available
    - the height available (screen minus room for buttons and caption),
      converted to a width using the photo's shape (--ratio = width / height)
    - the photo's real width, so small photos are not blown up and blurry
  gallery.js sets --ratio and --natural-width for each photo.
*/
.lightbox__image {
  --available-height: calc(100vh - 9rem);
  width: min(100%, calc(var(--available-height) * var(--ratio, 1.5)), var(--natural-width, 100%));
  height: auto;
  aspect-ratio: var(--ratio, auto);
}

/* dvh = the visible screen height on phones, excluding browser toolbars.
   Used where supported; the vh value above is the fallback. */
@supports (height: 100dvh) {
  .lightbox__image {
    --available-height: calc(100dvh - 9rem);
  }
}

.lightbox__caption,
.lightbox__counter {
  text-align: center;
  font-size: var(--step--1);
}

.lightbox__counter {
  position: absolute;
  top: var(--space-s);
  left: 50%;
  transform: translateX(-50%);
  margin: 0;
  white-space: nowrap;
}

/* Round buttons on a dark circle so they show up on any photo. */
.lightbox__button {
  position: absolute;
  display: grid;
  place-items: center;
  width: var(--tap-target);
  height: var(--tap-target);
  padding: 0;
  border: 2px solid rgb(255 255 255 / 0.85);
  border-radius: 50%;
  background-color: rgb(0 0 0 / 0.6);
  color: #ffffff;
  font-size: 1.75rem;
  line-height: 1;
  cursor: pointer;
}

.lightbox__button:hover {
  background-color: rgb(0 0 0 / 0.9);
}

.lightbox__button:focus-visible {
  outline-color: #ffffff;
}

.lightbox__close {
  top: var(--space-2xs);
  right: var(--space-2xs);
}

.lightbox__prev,
.lightbox__next {
  top: 50%;
  transform: translateY(-50%);
}

.lightbox__prev {
  left: var(--space-2xs);
}

.lightbox__next {
  right: var(--space-2xs);
}

.lightbox__button[hidden] {
  display: none;
}

/* ===== template/sections/about.css ===== */
/* About section: story text, optional photo and highlights. */

.about__layout {
  display: grid;
  gap: var(--space-xl);
  align-items: center;
}

@media (min-width: 56em) {
  .about__layout--with-image {
    grid-template-columns: 1fr 1fr;
  }

  /* Variant image-left: only the visual order changes. The text still
     comes first in the HTML, so screen readers read it first either way. */
  .about--image-left .about__media {
    order: -1;
  }
}

.about__content {
  display: grid;
  gap: var(--space-s);
  max-width: var(--measure);
}

.about__highlights {
  display: grid;
  gap: var(--space-2xs);
  margin: var(--space-xs) 0 0;
  padding: 0;
  list-style: none;
}

.about__highlights li {
  display: flex;
  gap: var(--space-2xs);
  font-weight: 600;
}

/* A tick before each highlight. The text after "/" is what screen readers
   say for it: nothing, because the tick is only decoration. The first line
   is a fallback for older browsers that don't understand the "/" form. */
.about__highlights li::before {
  content: "✓";
  content: "✓" / "";
  color: var(--color-primary);
  font-weight: 700;
}

.about__image {
  width: 100%;
  border-radius: var(--radius-l);
  /* Square: tall enough to feel substantial next to a few paragraphs
     without leaving big empty gaps above and below the text. */
  aspect-ratio: 1 / 1;
  object-fit: cover;
}

/* ===== template/sections/reviews.css ===== */
/* Reviews section: customer quotes. */

.section--reviews {
  background-color: var(--color-surface);
}

.reviews__list {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(min(100%, 19rem), 1fr));
  gap: var(--space-m);
  margin: 0;
  padding: 0;
  list-style: none;
}

.review {
  display: flex;
  flex-direction: column;
  gap: var(--space-s);
  height: 100%;
  margin: 0;
  padding: var(--space-m);
  background-color: var(--color-bg);
  border-radius: var(--radius-m);
}

.review__rating {
  color: var(--color-primary);
  font-size: var(--step-1);
  letter-spacing: 0.1em;
  line-height: 1;
}

.review__quote {
  margin: 0;
  font-size: var(--step-0);
}

/* Name and detail sit at the bottom, so they line up across a row. */
.review__author {
  display: flex;
  flex-direction: column;
  margin-top: auto;
  font-size: var(--step--1);
}

.review__name {
  font-weight: 700;
}

.review__detail {
  color: var(--color-text-muted);
}

/* ===== template/sections/cta.css ===== */
/* Call-to-action banner: a full-width band in the main brand colour. */

.section--cta {
  background-color: var(--color-primary);
  color: var(--color-on-primary);
  text-align: center;
}

.section--cta .section__header {
  margin-inline: auto;
  margin-bottom: var(--space-s);
}

.cta__text {
  max-width: var(--measure);
  margin: 0 auto var(--space-l);
  font-size: var(--step-1);
}

.section--cta .button-group {
  justify-content: center;
}

/* The normal focus outline uses the brand colour, which would be invisible
   on this background, so it switches to the text colour used here. */
.section--cta :focus-visible {
  outline-color: var(--color-on-primary);
}

/* ---------- Variant: card ----------
   The section itself has the normal page background; the coloured box is
   the container inside it, kept off the screen edges on phones. */

.section--cta.cta--card {
  background-color: transparent;
  color: inherit;
}

.cta--card > .container {
  width: calc(100% - 2 * var(--container-gutter));
  max-width: calc(var(--container-width) - 2 * var(--container-gutter));
  padding: var(--space-2xl) var(--space-m);
  border-radius: var(--radius-l);
  background-color: var(--color-primary);
  color: var(--color-on-primary);
}

/* ===== template/sections/faq.css ===== */
/* FAQ section: questions that open to reveal answers (<details>). */

.faq__list {
  max-width: 48rem;
  border-top: 1px solid var(--color-border);
}

.faq__item {
  border-bottom: 1px solid var(--color-border);
}

.faq__question {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: var(--space-s);
  min-height: var(--tap-target);
  padding-block: var(--space-s);
  font-weight: 600;
  font-size: var(--step-1);
  line-height: var(--line-height-heading);
  cursor: pointer;
  /* Hide the browser's own triangle; we draw a + / − sign instead. */
  list-style: none;
}

.faq__question::-webkit-details-marker {
  display: none;
}

/* The + sign turns into − when the answer is open. Screen readers skip it
   (the text after "/" is empty) because <details> already announces
   "collapsed" or "expanded". */
.faq__question::after {
  content: "+";
  content: "+" / "";
  flex-shrink: 0;
  font-size: 1.5em;
  font-weight: 400;
  line-height: 1;
  color: var(--color-primary);
}

.faq__item[open] > .faq__question::after {
  content: "−";
  content: "−" / "";
}

.faq__question:hover {
  color: var(--color-primary);
}

.faq__answer {
  display: grid;
  gap: var(--space-xs);
  max-width: var(--measure);
  padding-bottom: var(--space-m);
  color: var(--color-text-muted);
}

/* ===== template/sections/contact.css ===== */
/* Contact section: the contact form. */

.contact-form {
  display: grid;
  gap: var(--space-m);
  max-width: 40rem;
}

.contact-form__hint,
.contact-form__privacy {
  color: var(--color-text-muted);
  font-size: var(--step--1);
}

.contact-form__demo {
  padding: var(--space-xs) var(--space-s);
  border-left: 4px solid var(--color-primary);
  background-color: var(--color-surface);
  font-weight: 600;
}

/* ---------- Fields ---------- */

.field {
  display: grid;
  gap: var(--space-3xs);
}

.field__label {
  font-weight: 600;
}

.field__optional {
  color: var(--color-text-muted);
  font-weight: 400;
}

.field__input {
  width: 100%;
  min-height: var(--tap-target);
  padding: var(--space-2xs) var(--space-xs);
  /* Field borders must contrast 3:1 with the background (WCAG 1.4.11),
     so they use a stronger colour than decorative lines. */
  border: 1px solid var(--color-input-border);
  border-radius: var(--radius-s);
  background-color: var(--color-bg);
  color: var(--color-text);
}

textarea.field__input {
  resize: vertical;
  min-height: 9rem;
}

.field__input:focus-visible {
  outline-offset: 0;
}

/* Red border on fields that are still wrong after the visitor has typed in
   them or tried to send. (:user-invalid waits for interaction, so a fresh
   form doesn't start covered in red.) The browser's own message explains
   the problem in words, so colour is never the only signal. */
.field__input:user-invalid {
  border-color: var(--color-error);
  border-width: 2px;
}

/* ---------- Button and status message ---------- */

.button[aria-disabled="true"] {
  opacity: 0.7;
  cursor: progress;
}

.contact-form__status:empty {
  display: none;
}

.contact-form__status {
  padding: var(--space-xs) var(--space-s);
  border-left: 4px solid currentColor;
  font-weight: 600;
}

.contact-form__status[data-kind="success"] {
  color: var(--color-success);
  background-color: var(--color-success-bg);
}

.contact-form__status[data-kind="error"] {
  color: var(--color-error);
  background-color: var(--color-error-bg);
}

/* ===== template/sections/location.css ===== */
/* Hours & location section. */

.location__layout {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 18rem), 1fr));
  gap: var(--space-xl);
}

.location__subheading {
  margin-bottom: var(--space-s);
  font-size: var(--step-1);
}

/* Opening hours: days on the left, times on the right. */
.hours {
  display: grid;
  margin: 0;
  max-width: 26rem;
}

.hours__row {
  display: flex;
  justify-content: space-between;
  gap: var(--space-s);
  padding-block: var(--space-2xs);
  border-bottom: 1px solid var(--color-border);
}

.hours dt {
  font-weight: 600;
}

.hours dd {
  margin: 0;
  text-align: right;
}

.location__note {
  margin-top: var(--space-s);
  color: var(--color-text-muted);
}

.location__contact address {
  display: grid;
  gap: var(--space-2xs);
}

.location__directions {
  margin-top: var(--space-m);
}

/* ===== template/css/themes/bold-modern.css ===== */
/*
  Theme: Bold & Modern

  Confident and energetic. Crisp white and cool slate, one strong blue,
  big heavy headings in a geometric font (Space Grotesk), short accent bars
  under section headings, and cards that lift off the page with shadows.
  Suits trades, gyms, auto repair, tech and cleaning services: businesses
  that want to look capable and efficient.

  How themes work: this file is added after all the other CSS and
  redefines design tokens (see template/css/tokens.css), plus a few
  finishing touches. Section and layout files never need to change.

  All text colours below meet WCAG 2.2 AA contrast (4.5:1+) on both
  --color-bg and --color-surface. Ratios are noted next to each colour.
*/

/* ---------- Heading font ---------- */

/*
  Space Grotesk by the Space Grotesk Project Authors, free under the SIL
  Open Font License (template/fonts/SpaceGrotesk-OFL.txt). Saved in the
  template rather than loaded from Google Fonts; see docs/DECISIONS.md.
*/
@font-face {
  font-family: "Space Grotesk";
  src: url("/assets/fonts/space-grotesk-latin-wght.woff2") format("woff2");
  font-weight: 300 700; /* one "variable" file contains every weight */
  font-style: normal;
  font-display: swap;
}

/* ---------- Tokens ---------- */

:root {
  --color-bg: #ffffff;
  --color-surface: #f1f5f9;       /* cool slate, for tinted sections */
  --color-text: #0f172a;          /* 17.9:1 on bg, 16.3:1 on surface */
  --color-text-muted: #475569;    /* 7.6:1 on bg, 6.9:1 on surface */
  --color-primary: #1d4ed8;       /* strong blue: 6.7:1 on bg, 6.1:1 on surface */
  --color-primary-hover: #1e3a8a;
  --color-on-primary: #ffffff;    /* 6.7:1 on primary */
  --color-border: #e2e8f0;        /* decorative lines only */
  --color-input-border: #64748b;  /* form field edges: 4.8:1 on bg (needs 3:1) */
  --color-focus: #1d4ed8;
  --color-success: #166534;       /* 6.5:1 on its background */
  --color-success-bg: #dcfce7;
  --color-error: #b91c1c;         /* 5.3:1 on its background */
  --color-error-bg: #fee2e2;

  --font-heading: "Space Grotesk", system-ui, -apple-system, "Segoe UI", Roboto, Arial, sans-serif;
  --font-weight-heading: 700;
  --line-height-heading: 1.1;

  --radius-s: 0.375rem;
  --radius-m: 0.75rem;
  --radius-l: 1.25rem;
  --shadow-s: 0 1px 3px rgb(15 23 42 / 0.1), 0 1px 2px rgb(15 23 42 / 0.06);
  --shadow-m: 0 10px 25px -5px rgb(15 23 42 / 0.12), 0 4px 10px -4px rgb(15 23 42 / 0.08);
}

/* ---------- Finishing touches ---------- */

h1, h2, h3, h4 {
  letter-spacing: -0.02em;
}

/* A short, thick bar under each section heading: the theme's signature. */
.section__heading::after {
  content: "";
  display: block;
  width: 3rem;
  height: 0.3rem;
  margin-top: var(--space-xs);
  border-radius: 999px;
  background-color: var(--color-primary);
}

/* On the call-to-action the background is already blue, so the bar
   switches to white and is centred like the heading. */
.section--cta .section__heading::after {
  margin-inline: auto;
  background-color: var(--color-on-primary);
}

.hero {
  background-color: var(--color-surface);
}

.hero__heading {
  letter-spacing: -0.03em;
}

.site-header__name {
  font-size: var(--step-1);
  letter-spacing: -0.02em;
}

.site-nav__link {
  font-weight: 600;
}

.button {
  font-weight: 700;
  box-shadow: var(--shadow-s);
}

/* Cards float on soft shadows instead of outlines. */
.service-card,
.review {
  border-color: transparent;
  box-shadow: var(--shadow-m);
}

.service-card__price {
  color: var(--color-primary);
}

.section--services {
  background-color: var(--color-bg);
}

.hours__row {
  border-bottom-color: var(--color-border);
}

.faq__item {
  border-color: var(--color-border);
}

/* ===== clients/demo-plumber/custom.css ===== */
/*
  Optional styles for this client only. Loaded last, so anything here wins.

  The easiest change is to redefine a design token from template/css/tokens.css,
  for example to use the client's brand colour:

    :root {
      --color-primary: #0b6e4f;
      --color-primary-hover: #08533b;
    }

  Check the contrast of any new colour against white (4.5:1 or more).
  Keep this file small; if many clients need the same change, it belongs
  in the template instead.
*/
