/* ---- tokens.css ---- */
/* The variables every rule below reads. Colours come from site.json as
   :root variables the build writes; the rest is here. */

/* ==========================================================================
   Shared theme for every site.
   - Colors come from site.json and are injected as :root variables.
   - Typography and shape come from the [data-preset] blocks below.
   Edit here once and every site picks it up on the next build.
   ========================================================================== */

:root {
  --tint: color-mix(in srgb, var(--brand) 7%, var(--paper));
  --muted: color-mix(in srgb, var(--ink) 74%, var(--paper));
  --line: color-mix(in srgb, var(--ink) 14%, transparent);
  --link: var(--brand);
  --focus: var(--brand);

  --font-display: system-ui, sans-serif;
  --font-body: system-ui, sans-serif;
  --display-weight: 700;
  --display-stretch: 100%;
  --display-tracking: -0.01em;

  --radius: 6px;
  --radius-lg: 10px;
  /* Buttons get their own token because two presets make them pills and two
     make them square, and those were written as `border-radius` on `.btn`
     directly — which outranks the variable, so the Corners knob moved every
     corner on the page except the one people look at. */
  --radius-btn: var(--radius);
  --rule: 1px solid var(--line);
  /* Cards carry no shadow by design — a page of identical drop-shadowed boxes
     is the flattest hierarchy there is. It is a token so a `style` can put one
     back deliberately, which is most of what separates Playful and Bold from
     the rest. */
  --card-shadow: none;
  --card-edge: 1px solid var(--line);

  /* Type scale: 17px base, ~1.25 ratio */
  --step--1: 0.875rem;
  --step-0: 1.0625rem;
  --step-1: 1.3rem;
  --step-2: 1.6rem;
  --step-3: clamp(1.75rem, 1.35rem + 1.6vw, 2.3rem);
  --step-4: clamp(2.05rem, 1.45rem + 2.6vw, 3rem);
  --hero-size: clamp(2.6rem, 1.6rem + 4.4vw, 4.4rem);
  --hero-measure: 16ch;
  --plate-size: clamp(2.2rem, 1rem + 5vw, 3.6rem);

  --text-scale: 1;
  --space-section: clamp(4rem, 3rem + 5vw, 7rem);
  --measure: 66ch;
  --max: 74rem;
}

html { font-size: calc(100% * var(--text-scale, 1)); }

/* ---- base.css ---- */
/* Type scale, rhythm, and the elements before any component. */

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

/* `hidden` means hidden, whatever a component says about its own display.
   The attribute is only `display: none` in the browser's own stylesheet, so
   any rule of ours that sets display — `.btn { display: inline-flex }` — beats
   it and the element stays on the page. Five components had each written
   their own `X[hidden] { display: none }` to work around that; the share
   button forgot to, and appeared without a script behind it to do anything.
   One rule, for everything, so the sixth cannot forget. */
[hidden] { display: none !important; }

html { -webkit-text-size-adjust: 100%; scroll-behavior: smooth; scroll-padding-top: 5.5rem; }

body {
  margin: 0;
  background: var(--paper);
  color: var(--ink);
  font-family: var(--font-body);
  font-size: var(--step-0);
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
}

img { display: block; max-width: 100%; height: auto; }

h1, h2, h3 {
  margin: 0;
  font-family: var(--font-display);
  font-weight: var(--display-weight);
  font-stretch: var(--display-stretch);
  letter-spacing: var(--display-tracking);
  line-height: 1.1;
  text-wrap: balance;
}

p { margin: 0 0 1em; }

p:last-child { margin-bottom: 0; }

a { color: var(--link); text-underline-offset: 0.2em; }

:focus-visible { outline: 3px solid var(--focus); outline-offset: 3px; }

.icon { width: 1.25em; height: 1.25em; flex: none; }

.wrap { width: min(100% - 2.5rem, var(--max)); margin-inline: auto; }

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

.skip-link {
  position: absolute; left: 1rem; top: -10rem; z-index: 100;
  padding: 0.75rem 1rem; background: var(--ink); color: var(--on-ink); border-radius: var(--radius);
}

.skip-link:focus { top: 1rem; }

/* Motion is a courtesy, never a requirement: nothing on the page moves unless
   the visitor did something, and for those who have asked their system for
   less of it, nothing moves at all. */
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after { transition-duration: 0.01ms !important; animation-duration: 0.01ms !important; animation-iteration-count: 1 !important; scroll-behavior: auto !important; }
}

/* ---- buttons.css ---- */
.btn {
  display: inline-flex; align-items: center; gap: 0.55em;
  padding: 0.85em 1.35em;
  border: 2px solid transparent; border-radius: var(--radius-btn, var(--radius));
  font: 600 var(--step-0)/1.1 var(--font-body);
  text-decoration: none; cursor: pointer;
  transition: background-color 0.15s, color 0.15s, border-color 0.15s;
}

.btn .icon { width: 1.1em; height: 1.1em; }

.btn--accent { background: var(--accent); color: var(--on-accent); border-color: var(--accent-edge, transparent); }

.btn--accent:hover { background: color-mix(in srgb, var(--accent) 86%, var(--ink)); }

.btn--brand { background: var(--brand); color: var(--on-brand); }

.btn--brand:hover { background: color-mix(in srgb, var(--brand) 84%, var(--ink)); }

.btn--ghost { background: transparent; color: inherit; border-color: currentColor; }

.btn--ghost:hover { background: color-mix(in srgb, currentColor 10%, transparent); }

.btn--sm { padding: 0.6em 1em; font-size: 0.95rem; }

.btn:disabled { opacity: 0.6; cursor: progress; }

/* ---- modal.css ---- */
/* A dialog, and the pill that opens it.

   Native <dialog>, so focus, Escape and the backdrop are the platform's job. */

.modal {
  width: min(100% - 2rem, 32rem); padding: 0; border: 0;
  background: none; color: var(--ink);
  /* Centred by the platform; margin:auto is what makes that happen. */
  margin: auto;
}

.modal::backdrop { background: color-mix(in srgb, #000 55%, transparent); }

.modal__box {
  position: relative; padding: 2rem;
  background: var(--paper); border: var(--card-edge);
  border-radius: var(--radius-lg); box-shadow: 0 24px 60px -30px rgb(0 0 0 / 55%);
}

.modal__close {
  position: absolute; top: 0.6rem; right: 0.6rem;
  display: inline-grid; place-items: center; width: 2.75rem; height: 2.75rem;
  padding: 0; background: none; border: 0; color: var(--muted); cursor: pointer;
  /* Round, so the focus ring is round too: a square outline on a round corner
     reads as a box that does not belong to the thing inside it. */
  border-radius: 999px;
}

/* The box is a focus destination rather than a control — it takes focus so a
   screen reader starts at the title, and a ring around the whole dialog would
   say something is selected when nothing is. Keyboard focus on the controls
   inside it still rings normally. */
.modal__box:focus, .modal__box:focus-visible { outline: none; }
.modal__close:hover { color: var(--ink); }
.modal__close .icon { width: 1.25rem; height: 1.25rem; }

.modal__title { margin: 0 2.5rem 0.75rem 0; font-size: var(--step-2); }
.modal__body p { margin: 0 0 0.75rem; color: var(--muted); }
.modal__body p:last-child { margin-bottom: 0; }
.modal__actions { margin-top: 1.5rem; }

/* The pill. Bottom left, because the look switcher is bottom right, and small
   enough to ignore while still being the way to the price. */
.preview-pill {
  position: fixed; left: 1rem; bottom: calc(1rem + env(safe-area-inset-bottom, 0px)); z-index: 35;
  display: inline-flex; align-items: center; gap: 0.45rem;
  padding: 0.6rem 1rem; border: 1px solid var(--on-bar); border-radius: 999px;
  background: var(--bar); color: var(--on-bar);
  font: inherit; font-size: 0.9rem; font-weight: 600; cursor: pointer;
  box-shadow: 0 6px 18px -8px color-mix(in srgb, #000 50%, transparent);
}

.preview-pill .icon { width: 1rem; height: 1rem; }

/* One word on a phone. The full label and the look switcher came to 358px of a
   390px screen and met in the middle with no gap at all. */
.modal-open__short { display: none; }

@media (max-width: 30rem) {
  .modal-open__long { display: none; }
  .modal-open__short { display: inline; }
}

/* ---- header.css ---- */
/* An announcement across the top: one line, the accent colour, a link if there is one. */
.announce { background: var(--accent); color: var(--on-accent); font-size: 0.95rem; text-align: center; }
.announce p { margin: 0; padding: 0.55rem 0; }
.announce a { color: inherit; font-weight: 600; text-underline-offset: 0.15em; }

.site-header { position: sticky; top: 0; z-index: 20; background: var(--paper); border-bottom: 1px solid var(--line); }

.header__inner { display: flex; align-items: center; gap: 1.5rem; min-height: 4.5rem; }

.brand { display: flex; flex-direction: column; margin-right: auto; color: var(--ink); text-decoration: none; line-height: 1; }

.brand img { max-height: 2.75rem; width: auto; }

/* A mark beside the wordmark: the drawing left, the words stacked to its right. */
.brand--marked { flex-direction: row; align-items: center; gap: 0.7rem; }
.brand__icon { width: 2.5rem; height: 2.5rem; flex: 0 0 auto; }

/* An inlined brand drawing, which carries its own proportions — a club crest is
   taller than it is wide, a wordmark much wider. So it is bounded by height and
   left to take whatever width that gives it, rather than squared off like the
   linked .brand__icon above. `currentColor` is the whole reason it is inlined:
   the drawing then follows the palette, including a live change from the
   switcher. */
.brand__svg { height: 2.5rem; width: auto; max-width: 12rem; flex: 0 0 auto; color: inherit; }
.brand__text { display: flex; flex-direction: column; }

.brand__mark {
  font-family: var(--font-display); font-weight: var(--display-weight);
  font-stretch: var(--display-stretch); letter-spacing: var(--display-tracking);
  font-size: 1.55rem;
}

.brand__desc { margin-top: 0.3rem; font-size: 0.8rem; font-weight: 600; color: var(--muted); }

.site-nav ul { display: flex; gap: 1.75rem; margin: 0; padding: 0; list-style: none; }

.site-nav a { padding-block: 0.5rem; color: var(--ink); font-weight: 500; text-decoration: none; }

.site-nav a:hover { color: var(--brand); text-decoration: underline; }

.header__actions { display: flex; align-items: center; gap: 0.5rem; }

.nav-toggle {
  display: none; place-items: center; width: 2.75rem; height: 2.75rem; padding: 0;
  background: none; border: 1px solid var(--line); border-radius: var(--radius); color: var(--ink); cursor: pointer;
}

.nav-toggle .icon { width: 1.4rem; height: 1.4rem; }

.header-call__short { display: none; }
/* theme.headerCall: "icon" — the phone mode at every width: a square button, the receiver alone. */
.header-call--icon { padding-inline: 0.85em; }

@media (max-width: 63.99rem) {
  .nav-toggle { display: inline-grid; }
  /* The header carries one control more than it used to on a phone, now that
     calling lives here instead of in a bar along the bottom. So the button is
     the receiver alone: "Call" beside a picture of a telephone is the picture
     twice, and the word cost 50px that a 44-character business name needed. */
  .brand__mark { font-size: 1.28rem; }
  .brand__desc { margin-top: 0.15rem; font-size: 0.74rem; }
  .header-call { padding-inline: 0.85em; }
  /* Hidden from the eye, not from a screen reader: the link still has to say
     what it does, and an icon says nothing. */
  .header-call__short {
    position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
    overflow: hidden; clip-path: inset(50%); white-space: nowrap; border: 0;
  }
  .site-nav {
    display: none; position: absolute; inset: 100% 0 auto 0;
    background: var(--paper); border-bottom: 1px solid var(--line);
    box-shadow: 0 12px 24px -12px color-mix(in srgb, var(--ink) 30%, transparent);
  }
  .site-nav.is-open { display: block; }
  .site-nav ul { flex-direction: column; gap: 0; width: min(100% - 2.5rem, var(--max)); margin-inline: auto; padding-block: 0.5rem 1rem; }
  .site-nav li + li { border-top: 1px solid var(--line); }
  .site-nav a { display: block; padding-block: 0.9rem; font-size: 1.1rem; }
  .header-call__num { display: none; }
  .header-call__short { display: inline; }
}

/* ---------------------------------------------------------------------------
   Header layouts, from the `header` knob.
   
   Every preset used the same arrangement — name left, nav right — so nine
   looks that differ in type, colour and shape still opened identically. These
   rearrange the same three boxes rather than adding markup, so the layout is
   a choice a prospect can make in the switcher rather than a build of its own.
   
   Each only applies above the mobile breakpoint: under it the nav is already
   a panel behind the menu button, and that is the right answer regardless.
   --------------------------------------------------------------------------- */

@media (min-width: 64rem) {
  /* Centered: the name on its own line, the nav centered underneath it. The
     call button pulls out of the flow so it does not push the name off centre. */
  [data-header="center"] .header__inner {
    display: grid; grid-template-columns: 1fr auto 1fr; grid-template-areas: "left brand right" "nav nav nav";
    justify-items: center; gap: 0.5rem 1rem; padding-block: 1rem;
  }
  [data-header="center"] .brand { grid-area: brand; margin-right: 0; align-items: center; text-align: center; }
  [data-header="center"] .brand--marked { flex-direction: column; }
  [data-header="center"] .brand__text { align-items: center; }
  [data-header="center"] .site-nav { grid-area: nav; }
  [data-header="center"] .header__actions { grid-area: right; justify-self: end; }

  /* Nav underneath: the name stays left, the nav takes a rule-separated second
     row. Closest to the sports-bar and restaurant sites that carry a lot of
     nav items and want them all visible. */
  [data-header="stacked"] .header__inner {
    display: grid; grid-template-columns: auto 1fr; grid-template-areas: "brand actions" "nav nav";
    align-items: center; gap: 0.25rem 1rem; padding-block: 0.9rem 0;
  }
  [data-header="stacked"] .brand { grid-area: brand; margin-right: 0; }
  [data-header="stacked"] .header__actions { grid-area: actions; justify-self: end; }
  [data-header="stacked"] .site-nav { grid-area: nav; border-top: 1px solid var(--line); margin-top: 0.6rem; }
  [data-header="stacked"] .site-nav ul { gap: 1.5rem; padding-block: 0.15rem; }

  /* Menu button only: the nav is behind the button at every width, which is
     the quietest possible header and suits a one-page site whose nav is four
     anchors to sections you would scroll past anyway. */
  [data-header="minimal"] .site-nav {
    display: none; position: absolute; inset: 100% 0 auto 0;
    background: var(--paper); border-bottom: 1px solid var(--line);
    box-shadow: 0 12px 24px -12px color-mix(in srgb, var(--ink) 30%, transparent);
  }
  [data-header="minimal"] .site-nav.is-open { display: block; }
  [data-header="minimal"] .site-nav ul {
    flex-direction: column; gap: 0; width: min(100% - 2.5rem, var(--max));
    margin-inline: auto; padding-block: 0.5rem 1rem;
  }
  [data-header="minimal"] .site-nav a { display: block; padding-block: 0.7rem; }
  [data-header="minimal"] .nav-toggle { display: inline-grid; }
}

/* ---- hero.css ---- */
.hero { position: relative; padding-block: clamp(3rem, 2rem + 5vw, 6.5rem); }

.hero__grid { display: grid; gap: 2.5rem; align-items: center; }

.hero__title { font-size: var(--hero-size); line-height: 1; max-width: var(--hero-measure); }

.hero__text { max-width: 36ch; margin-top: 1.5rem; font-size: var(--step-1); line-height: 1.5; }

.hero__actions { display: flex; flex-wrap: wrap; gap: 0.75rem; margin-top: 2rem; }

/* One per line, at every width. Wrapping badges of uneven length produces a
   ragged block — "Every game on" alone, then "Kitchen and pizza window" with
   "Darts" squeezed in beside it — which reads as a layout accident rather than
   a list. A column reads as a list at any width, and these are short enough
   that the height it costs is not worth the raggedness it buys. */
.hero__badges {
  display: flex; flex-direction: column; align-items: flex-start; gap: 0.7rem;
  margin: 2rem 0 0; padding: 0; list-style: none;
  font-size: 0.95rem; font-weight: 600;
}

.hero__badges li { display: flex; align-items: center; gap: 0.4rem; }


.hero__badges .icon { width: 1.1em; height: 1.1em; color: var(--badge-icon, var(--brand)); stroke-width: 2.5; }

.hero__aside { position: relative; display: grid; gap: 1.25rem; }

.hero__photo { margin: 0; overflow: hidden; border-radius: var(--radius-lg); aspect-ratio: 4 / 3; }

.hero__photo img { width: 100%; height: 100%; object-fit: cover; }

.plate {
  container-type: inline-size;
  display: block; padding: 1.5rem 1.75rem;
  border-radius: var(--radius-lg);
  background: var(--plate-bg, var(--brand)); color: var(--plate-fg, var(--on-brand));
  text-decoration: none;
}

.plate__label { display: block; font-size: 0.95rem; font-weight: 600; }

.plate__value {
  display: block; margin-top: 0.35rem;
  font-family: var(--font-display); font-weight: var(--display-weight);
  font-stretch: var(--display-stretch); letter-spacing: var(--display-tracking);
  font-size: min(var(--plate-size), var(--plate-fit, 15cqi)); line-height: 1;
  /* The cap above is tuned to each preset's own typeface. With another one —
     chosen in the switcher, or set by `theme.font` — the script measures and
     shrinks the line to fit; without the script it wraps at the space rather
     than run off the edge of the plate. */
  white-space: normal;
}

.plate__note { display: block; margin-top: 0.8rem; font-size: 0.95rem; }

a.plate:hover .plate__value { text-decoration: underline; text-decoration-thickness: 0.06em; text-underline-offset: 0.12em; }

@media (min-width: 56rem) {
  .hero__grid { grid-template-columns: minmax(0, 1.2fr) minmax(0, 0.8fr); gap: 4rem; }
  .hero--photo .hero__photo { aspect-ratio: 4 / 5; }
}

/* workshop: dark van-panel hero with a two-tone fleet stripe */

/* storefront: arched shop-window photo, sign-style plate */

/* studio: open page, plate as a quiet card */

/* ---------------------------------------------------------------------------
   Hero arrangements, from the `hero` knob.

   The photo sits to the right of the words on all nine presets, which is a
   large part of why they open the same way. These move it without touching the
   markup: same grid, different instructions.
   --------------------------------------------------------------------------- */

/* No photo. Useful when the pictures are weak, and the fastest way to see
   whether the words are carrying their weight. */
[data-hero="plain"] .hero__photo { display: none; }
[data-hero="plain"] .hero__aside:empty { display: none; }

@media (min-width: 56rem) {
  /* Photo above the words, both full width. Reads as a magazine opener and
     gives a wide interior shot somewhere to breathe. */
  /* `:root` and `.hero--photo` to outrank a preset's own two columns
     (`[data-preset="field"] .hero--photo .hero__grid`), as the photo-behind
     rule below has to: otherwise field put the photograph in the left half. */
  :root[data-hero="above"] .hero--photo .hero__grid,
  [data-hero="above"] .hero__grid { grid-template-columns: minmax(0, 1fr); gap: 2.5rem; }
  [data-hero="above"] .hero__aside { order: -1; }
  /* The plate is a card, and a card stretched to the full width of the page is
     a band. On a preset that draws it as a transparent box with a hairline
     border, that band is two faint arcs and nothing else. */
  /* A width of its own: the plate measures itself to size the number, so
     shrink-to-fit (justify-self: start) left it a sliver with the words
     stacked one to a line. */
  [data-hero="above"] .plate { width: min(32rem, 100%); justify-self: start; }
  [data-hero="above"] .hero--photo .hero__photo,
  [data-hero="above"] .hero__photo { width: 100%; aspect-ratio: 21 / 9; }

}

/* Photo behind the words, at every width.
 *
 * This whole block lived inside the desktop media query, so a phone fell back
 * to the stacked default and the treatment somebody had chosen was simply not
 * there — on the screen most of these are actually read on.
 *
 * The overlay is the paper colour at 78%, so the type keeps its contrast
 * whatever the palette and whatever the photograph: a hero that reads on a dark
 * pub interior and on a white nail salon cannot be a fixed black scrim.
 */
/* The overlay this draws is the paper colour, so the words on it have to be the
   ink colour — whatever the preset would otherwise paint here. `table` and
   `workshop` fill their hero with `--ink` and set light text on it, and
   `dispatch` uses the brand; with a photograph behind instead, all three left
   light text on a light wash and a headline you could barely read.

   The fill goes too. A preset's hero background would sit on top of the
   photograph it is supposed to be behind. */
:root[data-hero="full"] .hero {
  display: grid; isolation: isolate;
  background: none; color: var(--ink);
  --badge-icon: var(--brand); --focus: var(--brand);
}
/* Not only the hero: three presets colour its children from `--on-ink` or
   `--on-brand` as well, which are light on a light palette and so vanish into
   the same wash. Overriding the container was not enough — `table` and
   `workshop` tint the paragraph, `dispatch` tints the paragraph and its ghost
   button, and `lounge` tints the badges. */
/* Halfway between the muted grey and the ink, not the grey itself: the wash
   is 78% paper, so the dark of the photograph still comes through, and grey
   words over it measured 3.9:1 where the picture was darkest. */
:root[data-hero="full"] .hero__text,
:root[data-hero="full"] .hero .s__intro { color: color-mix(in srgb, var(--muted) 50%, var(--ink)); }
:root[data-hero="full"] .hero__badges { color: var(--ink); }
:root[data-hero="full"] .hero .btn--ghost { border-color: var(--ink); color: var(--ink); }

/* `:root` and the `.hero--photo` class are both here to win, not for effect.
   Preset stylesheets load after this one and `[data-preset="field"]
   .hero--photo .hero__grid` sets two equal columns — so on that preset the copy
   got half the width while the container query, measuring the whole grid, still
   split the badges off into what was left. One column means one column: with
   the photograph behind the words there is no second thing to put beside them. */
:root[data-hero="full"] .hero--photo .hero__grid,
:root[data-hero="full"] .hero__grid {
  grid-template-columns: minmax(0, 1fr);
  max-width: min(100% - 3rem, var(--max));
  padding-block: clamp(2.5rem, 1rem + 8vw, 5rem);
}
[data-hero="full"] .hero__aside { position: absolute; inset: 0; z-index: -1; gap: 0; }
/* Matching `.hero--photo .hero__photo` and the preset rules that also carry an
   aspect-ratio, so this wins on specificity rather than on source order. */
[data-hero="full"] .hero--photo .hero__photo,
[data-hero="full"] .hero__photo {
  width: 100%; height: 100%; margin: 0; border-radius: 0; aspect-ratio: auto;
}
[data-hero="full"] .hero__photo::after {
  content: ""; position: absolute; inset: 0;
  background: color-mix(in srgb, var(--paper) 78%, transparent);
}
/* The plate is a card in its own right and would be lost over a photograph. */
[data-hero="full"] .plate { display: none; }

/* The words keep a readable measure and the badges take the room that leaves —
   but only when there is room to take, and that is a question about this box,
   not about the window. A viewport media query got it wrong every time a
   template narrowed the page: Single column gave the copy 46rem and Split gave
   it half the screen, and in both the badge column collapsed to six rems and
   wrapped "Every game on" over three lines. A container query asks the right
   thing once instead of patching each combination as it turns up. */
[data-hero="full"] .hero__grid { container-type: inline-size; }

@container (min-width: 58rem) {
  [data-hero="full"] .hero__copy {
    display: grid; grid-template-columns: minmax(0, 40rem) minmax(0, 1fr);
    column-gap: clamp(2rem, 4vw, 5rem); align-items: start; max-width: none;
  }
  [data-hero="full"] .hero__copy > * { grid-column: 1; }
  [data-hero="full"] .hero__badges {
    grid-column: 2; grid-row: 1 / -1; align-self: end;
    gap: 0.9rem; margin: 0 0 0.4rem;
  }
}

/* ---- sections.css ---- */
/* The sections between the hero and the contact block: services, about,
   areas, reviews, gallery and carousel, FAQ, embeds, and the contact form. */

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

.s--tint { background: var(--tint); }

.s__head { max-width: 42rem; margin-bottom: clamp(2rem, 1.5rem + 2vw, 3.25rem); }

.s__head h2 { font-size: var(--step-4); }

.s__intro { margin-top: 1rem; font-size: var(--step-1); line-height: 1.5; color: var(--muted); }

.s__note, .s__more { margin-top: 2rem; color: var(--muted); }

.s__more a { font-weight: 600; }

/* Services */

.svc-list {
  display: grid; gap: 0 2.5rem; margin: 0; padding: 0; list-style: none;
  grid-template-columns: repeat(auto-fill, minmax(min(100%, 17rem), 1fr));
}

.svc { padding-block: 1.5rem 2rem; border-top: var(--rule); }

.svc__icon {
  display: inline-grid; place-items: center; width: 2.75rem; height: 2.75rem; margin-bottom: 1rem;
  border-radius: var(--radius); background: var(--tint); color: var(--brand);
}

.s--tint .svc__icon { background: var(--paper); }

.svc__icon .icon { width: 1.5rem; height: 1.5rem; }

.svc__title { margin-bottom: 0.5rem; font-size: var(--step-2); }

.svc p { color: var(--muted); }

.svc .svc__price { margin-top: 0.75rem; color: var(--ink); font-weight: 700; }
.svc__more { display: inline-block; margin-top: 0.75rem; font-weight: 600; }

/* About */

.about { display: grid; gap: 3rem; }

.about__text { max-width: var(--measure); }

.about__text .s__head { margin-bottom: 1.5rem; }

.about__sig { margin-top: 1.5rem; font-weight: 700; }

.about__photo { margin: 0 0 1.5rem; overflow: hidden; border-radius: var(--radius-lg); aspect-ratio: 4 / 3; }

.about__photo img { width: 100%; height: 100%; object-fit: cover; }

.checks { padding: 1.75rem; background: var(--paper); border: var(--card-edge); border-radius: var(--radius-lg); box-shadow: var(--card-shadow); }

.s--plain .checks { background: var(--tint); border-color: transparent; }

.checks__h { margin-bottom: 1rem; font-size: var(--step-1); }

.checks ul { display: grid; gap: 1rem; margin: 0; padding: 0; list-style: none; }

.checks li { display: grid; grid-template-columns: 1.5rem 1fr; gap: 0.75rem; font-weight: 500; }

.checks .icon { width: 1.35rem; height: 1.35rem; margin-top: 0.15rem; color: var(--brand); stroke-width: 2.5; }

@media (min-width: 56rem) {
  .about { grid-template-columns: minmax(0, 1.15fr) minmax(0, 0.85fr); gap: 5rem; align-items: start; }
  .about--solo { grid-template-columns: 1fr; }
}

/* Service areas */

.areas { margin: 0; padding: 0; list-style: none; columns: 2 10rem; column-gap: 2.5rem; }

.areas li { padding-block: 0.7rem; border-bottom: 1px solid var(--line); break-inside: avoid; font-weight: 600; }

@media (min-width: 48rem) { .areas { columns: 4 10rem; } }

/* Testimonials */

.quotes { display: grid; gap: 1.5rem; grid-template-columns: repeat(auto-fit, minmax(min(100%, 19rem), 1fr)); }

.quote {
  display: flex; flex-direction: column; margin: 0; padding: 2rem;
  background: var(--paper); border: var(--card-edge); border-radius: var(--radius-lg);
  box-shadow: var(--card-shadow);
}

.s--tint .quote { border-color: transparent; }

.quote blockquote { flex: 1; margin: 0; font-size: 1.15rem; line-height: 1.55; }

.quote blockquote::before {
  content: "\201C"; display: block; height: 2.2rem;
  font-family: var(--font-display); font-size: 4rem; line-height: 1; color: var(--brand);
}

.stars + blockquote::before { display: none; }

.quote figcaption { margin-top: 1.5rem; font-size: 0.95rem; }

.quote__name { display: block; font-weight: 700; }

.quote__detail { color: var(--muted); }

.stars { display: flex; gap: 0.15rem; margin: 0 0 0.75rem; color: color-mix(in srgb, var(--accent) 70%, var(--ink)); }

.stars .icon { width: 1.1rem; height: 1.1rem; fill: var(--accent); }

/* Gallery */

.gallery {
  display: grid; gap: 0.75rem; margin: 0; padding: 0; list-style: none; grid-auto-flow: dense;
  grid-template-columns: repeat(2, minmax(0, 1fr));
}

@media (min-width: 48rem) { .gallery { grid-template-columns: repeat(auto-fill, minmax(15rem, 1fr)); } }

.gallery figure { margin: 0; height: 100%; display: flex; flex-direction: column; }

.gallery img { flex: 1; width: 100%; aspect-ratio: 4 / 3; object-fit: cover; border-radius: var(--radius); }

.gallery figcaption { margin-top: 0.5rem; font-size: 0.9rem; color: var(--muted); }

/* Feature the first photo only when the rest fill the grid with no gaps */

@media (min-width: 48rem) {
  .gallery[data-count="3"] { grid-template-columns: 2fr 1fr; }
  .gallery[data-count="5"] { grid-template-columns: repeat(4, 1fr); }
  .gallery[data-count="3"] li:first-child { grid-row: span 2; }
  .gallery[data-count="5"] li:first-child { grid-column: span 2; grid-row: span 2; }
  .gallery[data-count="3"] li:first-child img, .gallery[data-count="5"] li:first-child img { aspect-ratio: auto; }
}

/* FAQ */

/* ---------- Carousel ----------
   A scroll-snap track, so it works before site.js runs and keeps working if
   the script fails: swipe on a phone, drag or shift-scroll on a desktop, tab
   in and use the arrow keys. The buttons underneath are an enhancement, not
   the mechanism. */

.carousel { position: relative; }

.carousel__track {
  display: flex; gap: 0.9rem; margin: 0; padding: 0 0 0.35rem; list-style: none;
  /* Snapping is only demanded where swiping is the interaction. On a
     desktop, `mandatory` makes Safari treat a two-finger scroll over the row
     as a horizontal gesture and snap, so the page underneath never moves;
     `proximity` lets the gesture's direction decide, and the arrows and dots
     still land exactly because they scroll to measured positions. */
  overflow-x: auto; scroll-snap-type: x proximity; scroll-behavior: smooth;
  overscroll-behavior-x: contain;
  /* The scrollbar is the only affordance without JavaScript, so it stays. */
  scrollbar-width: thin; scrollbar-color: var(--line) transparent;
}

@media (pointer: coarse) { .carousel__track { scroll-snap-type: x mandatory; } }

.carousel__track:focus-visible { outline: 3px solid var(--focus); outline-offset: 3px; }

.carousel__track::-webkit-scrollbar { height: 6px; }

.carousel__track::-webkit-scrollbar-thumb { background: var(--line); border-radius: 999px; }

.carousel__slide {
  flex: 0 0 min(78%, 26rem); scroll-snap-align: start; margin: 0;
}

.carousel[data-per-view="1"] .carousel__slide { flex-basis: 100%; }

.carousel__slide figure { margin: 0; }

.carousel__slide img {
  width: 100%; aspect-ratio: var(--carousel-ratio, 3 / 2); object-fit: cover;
  border-radius: var(--radius-lg); background: var(--tint);
}

.carousel__slide figcaption { margin-top: 0.6rem; font-size: 0.9rem; color: var(--muted); }

.carousel__controls {
  display: flex; flex-wrap: wrap; align-items: center; justify-content: center; gap: 0.75rem; margin-top: 1.1rem;
}

/* "2 of 8 pictures": under the arrows on a gallery, beside them on the examples row. */
.carousel__status { flex-basis: 100%; order: 2; margin: 0; text-align: center; font-size: 0.9rem; color: var(--muted); }

.carousel__nav {
  display: grid; place-items: center; width: 2.6rem; height: 2.6rem; flex: 0 0 auto;
  cursor: pointer; color: var(--ink); background: var(--paper);
  border: var(--rule); border-radius: 999px;
}

.carousel__nav:hover:not(:disabled) { color: var(--on-brand); background: var(--brand); border-color: var(--brand); }

.carousel__nav:disabled { opacity: 0.35; cursor: default; }

.carousel__nav .icon { width: 1.25rem; height: 1.25rem; }

.carousel__dots { display: flex; align-items: center; gap: 0.45rem; }

.carousel__dot {
  width: 0.5rem; height: 0.5rem; padding: 0; cursor: pointer;
  background: var(--line); border: 0; border-radius: 999px;
  transition: width 0.18s ease, background-color 0.18s ease;
}

.carousel__dot[aria-current="true"] { width: 1.5rem; background: var(--brand); }

/* Cards as a swipeable row — the examples on the studio, reviews on any site
   (`layout: "carousel"` on the section). One, two or three whole cards to a
   view by width — never a card cut off at the edge, which reads as a layout
   bug however deliberate it is. That the row continues is said instead by the
   controls, which sit above the row beside the heading where the eye already
   is, with "1–3 of 6 reviews" next to them; a row that already fits shows no
   controls at all. Slides stretch to the tallest and the card fills its slide,
   so a short review does not leave a card hanging above the others' bottom
   edge; a scrollbar under a row of cards reads as a mistake. A fraction here
   (3.2) would show a sliver of the next card, if that were ever wanted. */

.cards-carousel .carousel { display: flex; flex-direction: column; }

.cards-carousel .carousel__controls { order: -1; justify-content: flex-end; margin: 0 0 1.25rem; }

.cards-carousel .carousel__status { flex: 1 1 auto; order: 0; text-align: left; }

.cards-carousel .carousel__track {
  --cards-gap: 1.25rem; --cards-per-view: 1;
  gap: var(--cards-gap); padding-bottom: 0.25rem; scrollbar-width: none;
}

.cards-carousel .carousel__track::-webkit-scrollbar { display: none; }

.cards-carousel .carousel__slide {
  display: flex;
  flex-basis: calc((100% - var(--cards-gap) * (var(--cards-per-view) - 1)) / var(--cards-per-view));
}

.cards-carousel .carousel__slide > * { flex: 1; }

@media (min-width: 48rem) { .cards-carousel .carousel__track { --cards-per-view: 2; } }

@media (min-width: 72rem) { .cards-carousel .carousel__track { --cards-per-view: 3; } }

.carousel__dot:focus-visible { outline: 2px solid var(--focus); outline-offset: 3px; }

@media (min-width: 48rem) {
  .carousel__slide { flex-basis: min(46%, 30rem); }
  .carousel[data-per-view="1"] .carousel__slide { flex-basis: 100%; }
}

/* Somebody who has asked their operating system to stop moving things means
   it: no smooth scrolling, no width transition, and autoplay never starts. */

@media (prefers-reduced-motion: reduce) {
  .carousel__track { scroll-behavior: auto; }
  .carousel__dot { transition: none; }
}

/* Embeds. A third party's iframe or widget arrives sized for somebody else's
   page; these are the two rules that stop it overflowing a phone. */

.s--html iframe { max-width: 100%; border: 0; }

.s--html .wrap > *:not(.s__head) { margin-inline: auto; }

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

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

.faq__item summary {
  position: relative; padding: 1.3rem 2.5rem 1.3rem 0;
  font-size: 1.15rem; font-weight: 600; cursor: pointer; list-style: none;
}

.faq__item summary::-webkit-details-marker { display: none; }

.faq__item summary::after {
  content: ""; position: absolute; right: 0.4rem; top: 50%; width: 0.6rem; height: 0.6rem;
  border-right: 2px solid currentColor; border-bottom: 2px solid currentColor;
  transform: translateY(-70%) rotate(45deg); transition: transform 0.2s;
}

.faq__item[open] summary::after { transform: translateY(-25%) rotate(-135deg); }

.faq__a { max-width: var(--measure); padding-bottom: 1.5rem; color: var(--muted); }

@media (min-width: 56rem) {
  .s--faq > .wrap { display: grid; grid-template-columns: minmax(0, 0.8fr) minmax(0, 1.2fr); gap: 4rem; align-items: start; }
  .s--faq .s__head { margin-bottom: 0; }
}

/* Call-to-action band */

.s--cta { padding-block: clamp(3rem, 2rem + 4vw, 5rem); background: var(--brand); color: var(--on-brand); --focus: var(--on-brand); }

.cta { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 1.5rem 3rem; }

.cta h2 { font-size: var(--step-3); }

.cta p { max-width: 40rem; margin-top: 0.6rem; font-size: 1.1rem; }

.cta__actions { display: flex; flex-wrap: wrap; gap: 0.75rem; }

/* Contact */

.contact { display: grid; gap: 3rem; }

.contact__list { display: grid; gap: 1.25rem; margin: 0 0 2.5rem; }

.contact__list dt { font-size: 0.9rem; font-weight: 500; color: var(--muted); }

.contact__list dd { margin: 0.15rem 0 0; font-size: 1.25rem; font-weight: 600; }

.contact__list address { font-style: normal; }

.contact__list a { color: var(--ink); text-decoration-color: var(--line); }

.contact__list a:hover { text-decoration-color: currentColor; }

.contact__list .contact__dir { display: inline-block; margin-top: 0.4rem; font-size: 1rem; color: var(--link); }

.contact__sub { margin-bottom: 0.75rem; font-size: var(--step-1); }

.hours { width: 100%; max-width: 26rem; border-collapse: collapse; }

.hours th, .hours td { padding: 0.55rem 0; text-align: left; border-bottom: 1px solid var(--line); }

.hours th { padding-right: 1.5rem; font-weight: 600; }

.hours__note { margin-top: 1rem; font-weight: 700; color: var(--brand); }

.contact-form {
  align-self: start; padding: clamp(1.5rem, 1rem + 2vw, 2.5rem);
  background: var(--paper); border: 1px solid var(--line); border-radius: var(--radius-lg);
}

.contact-form h3 { margin-bottom: 1.5rem; font-size: var(--step-2); }

.field { display: grid; gap: 0.4rem; margin-bottom: 1.1rem; }

.field label { font-size: 0.95rem; font-weight: 600; }

.field input, .field select, .field textarea {
  width: 100%; padding: 0.75rem 0.9rem;
  font: inherit; color: inherit; background: var(--paper);
  border: 1px solid color-mix(in srgb, var(--ink) 30%, transparent); border-radius: var(--radius);
}

.field textarea { resize: vertical; min-height: 8rem; }

.field-row { display: grid; gap: 0 1rem; }

.field-row .field { margin-bottom: 0.5rem; }

.field-hint { margin: 0 0 1.1rem; font-size: 0.875rem; color: var(--muted); }

.hp { position: absolute; left: -9999px; width: 1px; height: 1px; overflow: hidden; }

.form-status { margin: 1rem 0 0; font-weight: 600; }

.form-status:empty { display: none; }

.form-status.is-ok { color: #1a7f37; }

.form-status.is-error { color: #b42318; }

.form-note { margin-top: 1rem; font-size: 0.9rem; color: var(--muted); }

.map { margin-top: 3rem; overflow: hidden; aspect-ratio: 16 / 7; border: 1px solid var(--line); border-radius: var(--radius-lg); }

.map iframe { display: block; width: 100%; height: 100%; border: 0; }

@media (min-width: 36rem) { .field-row { grid-template-columns: 1fr 1fr; } }

@media (min-width: 56rem) { .contact { grid-template-columns: minmax(0, 0.9fr) minmax(0, 1.1fr); gap: 5rem; } .contact--solo { grid-template-columns: 1fr; } }

@media (max-width: 47.99rem) { .map { aspect-ratio: 4 / 3; } }

/* ==========================================================================
   Preset character
   The four presets below were added after the original three. Their overrides
   are grouped per preset rather than scattered through each section, because
   each one is a coherent look rather than a tweak. One bold element each.
   ========================================================================== */

/* ---- Before and after: two pictures in one figure, each labelled ---- */
.pair { display: grid; grid-template-columns: 1fr 1fr; gap: 0.5rem; }
.pair__side { position: relative; display: block; }
.pair__side img { width: 100%; height: 100%; aspect-ratio: 4 / 3; object-fit: cover; border-radius: var(--radius); }
.pair__label {
  position: absolute; top: 0.5rem; left: 0.5rem; padding: 0.15rem 0.5rem; border-radius: 999px;
  background: rgb(0 0 0 / 0.55); color: #fff; font-size: 0.72rem; font-weight: 700; letter-spacing: 0.04em;
}
.pair figcaption { grid-column: 1 / -1; }
.gallery .pair, .carousel__slide .pair { height: auto; }

/* ---- Holiday hours: under the regular hours, soonest first ---- */
.hours__special { margin: 0.75rem 0 0; padding: 0; list-style: none; font-size: 0.95rem; }
.hours__special li { display: flex; justify-content: space-between; gap: 1rem; padding: 0.35rem 0; border-top: 1px dashed var(--line); }

/* ---- People: the team, one card each ---- */
/* Under a centred heading the row is centred with it: left where it was, a
   team of three filled three of four columns and the empty fourth hung off
   one side. Whoever centres a section's heading sets --row-tracks and
   --row-justify beside it. On a phone the cards run full width regardless. */
.people {
  display: grid; gap: 1.5rem; margin: 0; padding: 0; list-style: none;
  grid-template-columns: var(--row-tracks, repeat(auto-fill, minmax(min(100%, 14rem), 1fr)));
  justify-content: var(--row-justify, normal);
}
@media (max-width: 47.99rem) { .people { --row-tracks: initial; } }
.person__photo { margin: 0 0 0.9rem; aspect-ratio: 4 / 5; overflow: hidden; border-radius: var(--radius-lg); background: var(--tint); }
.person__photo img { display: block; width: 100%; height: 100%; object-fit: cover; }
.person__name { margin: 0; font-size: var(--step-1); }
/* Two classes deep so this outranks `.person p` below, which would otherwise
   flatten the role against the name — a type selector beats a lone class. */
.person .person__role { margin: 0.15rem 0 0.5rem; color: var(--muted); font-size: 0.95rem; }
.person p { margin: 0; }
.person p + p { margin-top: 0.9rem; }

/* One person is not a row of cards with the row missing.
 *
 * The grid above is built for a team — a salon's stylists, a firm's partners —
 * and with a single card it laid a 14rem column against an empty two thirds of
 * the section. A one-person studio is the common case here, and it is the case
 * where this section matters most: it is the page answering "who am I actually
 * dealing with".
 */
.person:only-child { grid-column: 1 / -1; max-width: 42rem; justify-self: var(--row-justify, normal); }

@media (min-width: 48rem) {
  /* Portrait beside the words, once there is a portrait. Guarded, because the
     same layout with no photo leaves a 14rem hole where the face should be.

     Two columns and two children — the portrait and the block of words — so
     the prose stacks by itself. Placing the paragraphs directly in this grid
     instead made each one a row, which fails twice over: the portrait's height
     sets the first row's, dropping the role an inch below the name, and a
     fourth paragraph wraps back under the portrait into a 15rem column. */
  .person:only-child:has(.person__photo) {
    display: grid; grid-template-columns: 17rem 1fr; column-gap: 2.5rem;
    align-items: start; max-width: 56rem;
  }
  /* A hairline, because this portrait is usually a head against a pale studio
     ground and the section it sits in is tinted — without an edge the two
     washes meet and the picture has no shape. Only here: a team's photographs
     fill their frames and need no help. */
  .person:only-child:has(.person__photo) .person__photo {
    margin: 0; border: 1px solid var(--line);
  }
}

/* A picture shown whole (fit: "contain"): the frame takes the picture's own
   shape instead of imposing one, and nothing is cropped. */
.is-contain { aspect-ratio: auto; }
.is-contain img { height: auto; object-fit: contain; }

/* Four points in a three-wide grid leaves one on a row of its own, which
   reads as a mistake rather than a list. Two columns divides four evenly on a
   desktop and still stacks to one on a phone, where every grid here does. */
.svc-list--pairs { grid-template-columns: repeat(auto-fill, minmax(min(100%, 24rem), 1fr)); }

/* ---------------------------------------------------------------------------
   Styles, from the `style` knob.

   The register the site is pitched in, independent of what kind of business it
   is and what colour it is. A law practice and a taproom can share a layout and
   a palette and should still not look like the same website.

   The first version of this moved radius, border weight and heading weight, and
   the honest verdict was that it did almost nothing. What actually separates
   these is bigger: the size of the headlines, whether words are set in capitals,
   what a button even looks like, how much air a section gets, and what happens
   to a photograph. So each block reaches for those.
   --------------------------------------------------------------------------- */

/* ---- Professional: structured, dense, nothing shouting ------------------- */
:root[data-style="professional"] {
  --radius: 3px; --radius-lg: 4px; --radius-btn: 3px;
  --line: color-mix(in srgb, var(--ink) 22%, transparent);
  --display-weight: 600; --display-tracking: -0.005em;
  --hero-size: clamp(2rem, 1.5rem + 2.4vw, 3rem);
  --step-3: clamp(1.4rem, 1.2rem + 0.9vw, 1.8rem);
  --space-section: clamp(2.75rem, 2.25rem + 3vw, 4.5rem);
  --card-shadow: none;
}
[data-style="professional"] .s + .s { border-top: 1px solid var(--line); }
[data-style="professional"] .s__eyebrow,
[data-style="professional"] .svc__title {
  text-transform: uppercase; letter-spacing: 0.08em; font-size: 0.82rem;
}
[data-style="professional"] .btn { padding: 0.7em 1.1em; font-size: 0.92rem; }
[data-style="professional"] .gallery img, [data-style="professional"] .hero__photo img { filter: saturate(0.9); }

/* ---- Minimalist: air instead of edges ------------------------------------ */
:root[data-style="minimal"] {
  --radius: 0px; --radius-lg: 0px; --radius-btn: 0px;
  --line: color-mix(in srgb, var(--ink) 10%, transparent);
  --display-weight: 300; --display-tracking: 0.005em;
  --hero-size: clamp(2.4rem, 1.6rem + 3.6vw, 4rem);
  --space-section: clamp(6rem, 4.5rem + 8vw, 11rem);
  --card-edge: 0 solid transparent; --card-shadow: none;
  --max: 64rem;
}
[data-style="minimal"] .checks,
[data-style="minimal"] .quote { background: none; padding-inline: 0; }
[data-style="minimal"] .svc__icon { display: none; }
[data-style="minimal"] .svc { border-top: 0; }
/* A button that is a line of text with a rule under it. Nothing else on the
   page is boxed, so a filled rectangle would be the loudest thing on it. */
/* Its colour is its surroundings': a band of the brand, a dark hero or a dark
   header each set a text colour that reads on them, and a line of text that
   insisted on the ink went dark-on-dark on every one of them. */
[data-style="minimal"] .btn {
  padding: 0.35em 0; border: 0; border-bottom: 1px solid currentColor;
  background: none; color: inherit; font-weight: 500; letter-spacing: 0.02em;
}
[data-style="minimal"] .btn:hover { background: none; color: inherit; border-bottom-width: 2px; }

/* ---- Playful: round, heavy, and coloured --------------------------------- */
:root[data-style="playful"] {
  --radius: 16px; --radius-lg: 28px; --radius-btn: 999px;
  --display-weight: 800; --display-tracking: -0.025em;
  --hero-size: clamp(2.8rem, 1.8rem + 5vw, 5rem);
  --card-edge: 2px solid color-mix(in srgb, var(--brand) 25%, transparent);
  --card-shadow: 0 12px 28px -16px color-mix(in srgb, var(--brand) 80%, transparent);
}
[data-style="playful"] .gallery img,
[data-style="playful"] .hero__photo,
[data-style="playful"] .about__photo { border-radius: var(--radius-lg); }
[data-style="playful"] .svc__icon {
  width: 3.5rem; height: 3.5rem; border-radius: 999px;
  background: var(--accent); color: var(--on-accent);
}
[data-style="playful"] .btn { padding: 1em 1.8em; font-weight: 700; }
[data-style="playful"] .s--tint { border-radius: var(--radius-lg); }
/* The gradient that used to be its own knob. A style that rounds everything and
   colours its shadows wants its sections washed too; asking separately produced
   pairs that argued with each other.
   Not the call to action: it is a band of the brand colour with light words on
   it, and a wash of the paper laid over the colour left those words white on
   white. */
[data-style="playful"] .s:not(.s--tint, .s--cta):nth-of-type(odd) {
  background-image: linear-gradient(to bottom, color-mix(in srgb, var(--brand) 16%, var(--paper)), var(--paper));
}
[data-style="playful"] .s:not(.s--tint, .s--cta):nth-of-type(even) {
  background-image: linear-gradient(to top, color-mix(in srgb, var(--accent) 14%, var(--paper)), var(--paper));
}

/* ---- Bold: hard edges, capitals, shadows with no blur -------------------- */
:root[data-style="brutal"] {
  --radius: 0px; --radius-lg: 0px; --radius-btn: 0px;
  --line: var(--ink);
  --display-weight: 800; --display-tracking: -0.035em;
  --hero-size: clamp(3rem, 1.8rem + 6vw, 5.5rem);
  --card-edge: 3px solid var(--ink);
  --card-shadow: 8px 8px 0 var(--ink);
}
[data-style="brutal"] .s__title, [data-style="brutal"] .hero__title { text-transform: uppercase; line-height: 0.92; }
[data-style="brutal"] .gallery img, [data-style="brutal"] .about__photo, [data-style="brutal"] .hero__photo {
  border: 3px solid var(--ink); box-shadow: 8px 8px 0 var(--ink);
}
[data-style="brutal"] .btn {
  border: 3px solid var(--ink); box-shadow: 5px 5px 0 var(--ink);
  text-transform: uppercase; letter-spacing: 0.04em; font-weight: 800;
}
[data-style="brutal"] .btn:active { transform: translate(3px, 3px); box-shadow: 2px 2px 0 var(--ink); }
[data-style="brutal"] .svc__icon { border: 3px solid var(--ink); border-radius: 0; }

/* ---- Editorial: print habits --------------------------------------------- */
:root[data-style="editorial"] {
  --radius: 0px; --radius-lg: 0px; --radius-btn: 0px;
  --display-weight: 700; --display-tracking: -0.025em;
  --hero-size: clamp(3.2rem, 2rem + 6.5vw, 6rem);
  --step-3: clamp(2rem, 1.5rem + 2.2vw, 3rem);
  --card-edge: 0 solid transparent; --card-shadow: none;
  --max: 68rem;
}
[data-style="editorial"] .s__head { border-top: 3px solid var(--ink); padding-top: 1.25rem; }
[data-style="editorial"] .s__title, [data-style="editorial"] .hero__title { line-height: 0.92; }
[data-style="editorial"] .checks, [data-style="editorial"] .quote { background: none; padding-inline: 0; }
[data-style="editorial"] .quote { border-left: 4px solid var(--ink); padding-left: 1.5rem; }
[data-style="editorial"] .svc__icon { display: none; }
[data-style="editorial"] .btn {
  padding: 0.4em 0; border: 0; border-bottom: 3px solid currentColor;
  background: none; color: inherit; font-weight: 700;
  text-transform: uppercase; letter-spacing: 0.06em;
}
[data-style="editorial"] .btn:hover { background: none; color: inherit; border-bottom-color: var(--accent); }
/* Bands, the way a newspaper separates a feature from the page around it. */
[data-style="editorial"] .s:nth-of-type(even) {
  background-color: color-mix(in srgb, var(--brand) 12%, var(--paper));
}

/* ---------------------------------------------------------------------------
   Templates, from the `template` knob.

   How the page is arranged, as opposed to what kind of business it is (preset)
   or what register it is pitched in (style). The three here are the ones that
   are actually a different arrangement of the same content — a full-screen hero
   is already the Hero knob, and F- and Z-patterns describe how eyes move across
   a page somebody has already laid out rather than a setting you can pick.
   --------------------------------------------------------------------------- */

/* One column, narrow, centred. Everything becomes a vertical stream, which is
   what a phone already does — so this is the arrangement that needs no
   rearranging on the way down to one.

   The measure is put on the content, not on `--max`. `.wrap` is shared by the
   header and the footer as well as the sections, so narrowing the token to
   46rem squeezed the navigation: the wordmark broke over three lines and the
   phone number over three more. A reading measure is for reading, not for
   chrome. */
[data-template="single"] .s > .wrap,
[data-template="single"] .hero__grid { max-width: 46rem; }
[data-template="single"] .svc-list,
[data-template="single"] .quotes,
[data-template="single"] .gallery,
[data-template="single"] .menu__cats { grid-template-columns: minmax(0, 1fr); }
@media (min-width: 56rem) {
  [data-template="single"] .hero__grid,
  [data-template="single"] .about__grid { grid-template-columns: minmax(0, 1fr); }
  [data-template="single"] .hero__copy,
  [data-template="single"] .s__head { text-align: center; margin-inline: auto; }
  [data-template="single"] .s { --row-tracks: repeat(auto-fit, minmax(min(100%, 14rem), 17rem)); --row-justify: center; }
  [data-template="single"] .hero__actions,
  [data-template="single"] .hero__badges { align-items: center; justify-content: center; }
  [data-template="single"] .hero__text { margin-inline: auto; }
}

/* Two equal halves, alternating down the page: the picture takes one side and
   the words the other, and the next section swaps them. */
@media (min-width: 56rem) {
  /* Not when the photo is behind the words: `hero=full` takes the picture out
     of the flow, so a second column has nothing to hold — and the copy would
     get half the width while the container query still saw the whole grid and
     split the badges off into six rems of it. */
  [data-template="split"]:not([data-hero="full"]) .hero__grid,
  [data-template="split"] .about__grid { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: 3.5rem; }
  [data-template="split"] .s:nth-of-type(even) .about__grid > :first-child,
  [data-template="split"] .s:nth-of-type(even) .hero__copy { order: 2; }
  [data-template="split"] .svc-list { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  [data-template="split"] .about__photo, [data-template="split"] .hero__photo { aspect-ratio: 1; }
}

/* A bento for the pictures and a card grid for the words.

   The first version gave the first item two columns and two rows in both. That
   works for a photograph, which crops to whatever box it is given, and fails
   for a service, whose three lines of text then sit at the top of a box twice
   their height with a hole underneath. Six items do not tile into four columns
   either, so the last row ended half empty.

   So the pictures get the bento, where an uneven grid is the point and nothing
   can leave a gap, and the services get equal cards that are simply cards. */
@media (min-width: 56rem) {
  [data-template="grid"] .gallery {
    grid-template-columns: repeat(4, minmax(0, 1fr));
    grid-auto-rows: 12rem; gap: 1rem;
  }
  [data-template="grid"] .gallery > :first-child { grid-column: span 2; grid-row: span 2; }
  [data-template="grid"] .gallery > :nth-child(4) { grid-column: span 2; }
  [data-template="grid"] .gallery figure, [data-template="grid"] .gallery img {
    height: 100%; width: 100%; object-fit: cover;
  }

  /* Cards, all the same size, three across. The difference from the default is
     that a service becomes a bounded object rather than a row under a rule. */
  [data-template="grid"] .svc-list { grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 1.25rem; }
  [data-template="grid"] .svc {
    padding: 1.5rem; border-top: 0; border: var(--card-edge);
    border-radius: var(--radius-lg); box-shadow: var(--card-shadow);
  }
}
}

/* ---- beforeafter.css ---- */
/* Before and after: two photographs of the same place, one slider between
   them.

   The layout is chosen by whether the client script ran. Without it the pair
   sits side by side, which is what a magazine would print — a fine way to
   show the work and not a broken version of anything. With it, the two stack
   and a slider wipes between them.

   Shoot both photographs from the same spot: the frame crops them to one
   shape, and a pair taken from two different angles reads as two jobs. */

.ba-grid { display: grid; gap: 1.5rem; grid-template-columns: repeat(auto-fit, minmax(min(100%, 20rem), 1fr)); }

.ba { margin: 0; }

.ba figcaption { margin-top: 0.6rem; font-size: 0.9rem; color: var(--muted); }

.ba__img {
  width: 100%; height: 100%; display: block;
  aspect-ratio: 4 / 3; object-fit: cover;
  border-radius: var(--radius-lg); background: var(--tint);
}

.ba__label {
  position: absolute; bottom: 0.6rem; z-index: 3;
  padding: 0.15rem 0.5rem; border-radius: 999px;
  font-size: 0.75rem; font-weight: 700; letter-spacing: 0.02em;
  /* Said in words, not only by which side it is on: "the left one" is not
     available to somebody looking at one photograph at a time. */
  background: rgb(0 0 0 / 0.66); color: #fff;
}

.ba__label--before { left: 0.6rem; }

.ba__label--after { right: 0.6rem; }

/* ---- Without the script: the pair, side by side ------------------------- */

.ba__frame { display: grid; grid-template-columns: 1fr 1fr; gap: 0.5rem; }

.ba__side { position: relative; display: block; }

.ba__handle, .ba__range { display: none; }

/* ---- With the script: one frame, wiped by the slider -------------------- */

.js .ba__frame {
  position: relative; display: block; gap: 0;
  aspect-ratio: 4 / 3; border-radius: var(--radius-lg); overflow: hidden;
  /* Where the wipe sits. The script writes this; the value here is what the
     range starts at, so the first paint already matches the thumb. */
  --ba: 50%;
}

.js .ba__side { position: absolute; inset: 0; }

.js .ba__img { aspect-ratio: auto; border-radius: 0; }

/* The after photograph is the base and the before is laid over it, clipped
   to everything left of the wipe. */
.js .ba__side--before { clip-path: inset(0 calc(100% - var(--ba)) 0 0); z-index: 2; }

.js .ba__handle {
  display: block; position: absolute; top: 0; bottom: 0; left: var(--ba);
  width: 2px; margin-left: -1px; z-index: 3;
  background: #fff; box-shadow: 0 0 0 1px rgb(0 0 0 / 0.25);
}

/* The range covers the whole frame and shows nothing but its thumb, so the
   photographs are what you see and the native control is what you use. */
.js .ba__range {
  display: block; position: absolute; inset: 0; z-index: 4;
  width: 100%; height: 100%; margin: 0; padding: 0;
  -webkit-appearance: none; appearance: none;
  background: transparent; cursor: ew-resize;
}

.js .ba__range:focus { outline: none; }

.ba__range::-webkit-slider-thumb {
  -webkit-appearance: none; appearance: none;
  width: 2.25rem; height: 2.25rem; border-radius: 999px;
  background: #fff; border: 2px solid var(--brand); box-shadow: 0 1px 6px rgb(0 0 0 / 0.3);
  cursor: ew-resize;
}

.ba__range::-moz-range-thumb {
  width: 2.25rem; height: 2.25rem; border-radius: 999px;
  background: #fff; border: 2px solid var(--brand); box-shadow: 0 1px 6px rgb(0 0 0 / 0.3);
  cursor: ew-resize;
}

.ba__range::-moz-range-track { background: transparent; border: 0; }

/* Focus has to be visible on the thumb itself: an outline around the frame
   would be an outline around a photograph, which says nothing about what the
   keyboard is about to move. */
.ba__range:focus-visible::-webkit-slider-thumb { outline: 3px solid var(--focus); outline-offset: 2px; }

.ba__range:focus-visible::-moz-range-thumb { outline: 3px solid var(--focus); outline-offset: 2px; }

/* ---- actions.css ---- */
/* The rating line, and the row of things a visitor can do besides call:
   pay an invoice, leave a review, save the contact, share the site.

   Both appear in more than one place — the rating in the hero and the
   footer, the actions in the contact section and the footer — so each is
   styled once here and adjusted where it lands. */

/* ---- The rating line ---------------------------------------------------- */

.rating {
  display: inline-flex; align-items: center; gap: 0.4rem;
  margin: 1.25rem 0 0; font-size: 0.95rem; font-weight: 600;
  /* Inherited, never stated: this line sits on a brand-coloured hero on one
     preset and on paper on the next, and the block around it has already had
     its colour checked for contrast. Naming ink here put dark text on a dark
     hero. */
  color: inherit; text-decoration: none;
}

.rating .icon {
  width: 1.1rem; height: 1.1rem;
  /* The one place the accent colour is a fill rather than a stroke: a hollow
     star reads as "not rated" at a glance. */
  fill: var(--accent); color: var(--accent);
}

a.rating:hover { text-decoration: underline; text-underline-offset: 0.2em; }

.rating--hero { margin-top: 1.5rem; }

.site-footer .rating { margin-top: 0.75rem; font-size: 0.9rem; }

/* ---- The row of actions ------------------------------------------------- */

.actions { display: flex; flex-wrap: wrap; gap: 0.6rem; margin-top: 1.75rem; }

/* In the footer they are links in a list, not buttons: four outlined
   rectangles at the bottom of a page is a toolbar, and nothing down there is
   urgent enough to be one. */
.footer__actions { margin-top: 0.75rem; }

/* The share control is a <button> wherever it appears, because there is
   nowhere for it to go. In the footer it has to look like the links beside
   it, which means undoing everything a button brings with it. */
.share--link {
  padding: 0; border: 0; background: none; cursor: pointer;
  font: inherit; color: inherit; text-align: left;
  /* Underlined like the links it sits among: a control that behaves like a
     link and does not look like one is a control nobody presses. */
  text-decoration: underline; text-underline-offset: 0.2em;
}

.share--link:hover { text-decoration-thickness: 2px; }

.share--link:focus-visible { outline: 3px solid var(--focus); outline-offset: 3px; border-radius: 2px; }

/* ---- pages.css ---- */
.simple { padding-block: clamp(5rem, 4rem + 6vw, 9rem); text-align: center; }

.simple h1 { font-size: var(--step-4); }

.simple p { max-width: 36rem; margin: 1rem auto 2rem; font-size: var(--step-1); color: var(--muted); }

/* A page of paragraphs — the accessibility statement — reads left-aligned. */
.simple--text { text-align: left; }
.simple--text p { margin: 1rem 0; max-width: 40rem; color: var(--ink); font-size: var(--step-0); }
.simple--text .simple__meta { color: var(--muted); }
.simple--text .btn { margin-top: 1.5rem; }

/* ---- footer.css ---- */
.site-footer {
  padding-block: 4rem 2rem; background: var(--ink); font-size: 0.95rem;
  color: color-mix(in srgb, var(--on-ink) 80%, var(--ink)); --focus: var(--accent);
}

.site-footer a { color: var(--on-ink); }

.footer__grid { display: grid; gap: 2.5rem; }

.footer__brand {
  margin-bottom: 0.5rem; color: var(--on-ink); line-height: 1.1;
  font-family: var(--font-display); font-weight: var(--display-weight); font-stretch: var(--display-stretch);
  font-size: var(--step-2);
}

.footer__h { margin-bottom: 0.75rem; font: 700 0.95rem/1.3 var(--font-body); color: var(--on-ink); }

.footer__list, .footer__social { display: grid; gap: 0.4rem; margin: 0; padding: 0; list-style: none; }

.footer__social { display: flex; flex-wrap: wrap; align-items: center; gap: 0.4rem 1.25rem; margin-top: 1rem; }

/* A mark instead of a word. Sized to the touch target rather than the glyph:
   the drawing is 22px and the tappable square around it is 44, which is the
   smallest thing a thumb can be asked to hit. The negative margin keeps that
   padding from pushing the row out of line with the columns beside it. */
.footer__mark { display: inline-grid; place-items: center; width: 2.75rem; height: 2.75rem; margin: -0.6rem; padding: 0; }
.footer__mark .icon--brand { width: 1.375rem; height: 1.375rem; }
.footer__mark:hover .icon--brand, .footer__mark:focus-visible .icon--brand { opacity: 1; }
.footer__mark .icon--brand { opacity: 0.8; transition: opacity 0.15s; }

.footer__base {
  display: flex; flex-wrap: wrap; justify-content: space-between; gap: 0.5rem 1rem;
  margin-top: 3rem; padding-top: 1.5rem; font-size: 0.875rem;
  border-top: 1px solid color-mix(in srgb, var(--on-ink) 18%, transparent);
}

.footer__base p { margin: 0; }

@media (min-width: 48rem) { .footer__grid { grid-template-columns: 1.4fr 1fr 1fr; } }

/* Every link in the footer is a target of at least 24px, small type or not. */
.footer__list a, .footer__social a, .footer__base a { display: inline-block; padding-block: 0.2rem; }

/* ---- form.css ---- */
/* The contact form's enquiry-mode chooser and the switcher panel. */

.ask { margin: 0 0 1rem; padding: 0; border: 0; }

.ask legend { padding: 0; font-size: 0.8rem; font-weight: 600; color: var(--muted); margin-bottom: 0.45rem; }

.ask__opt {
  display: inline-flex; align-items: center; gap: 0.45rem; cursor: pointer;
  padding: 0.45rem 0.8rem; margin: 0 0.4rem 0.4rem 0;
  border: 1px solid var(--line); border-radius: 999px; font-size: 0.92rem;
}

.ask__opt:has(input:checked) { border-color: var(--brand); background: var(--tint); font-weight: 600; }

.ask__opt input { accent-color: var(--brand); }

/* ---------- Theme switcher (previews only) ----------
   Deliberately not styled like the site around it: it is our tool sitting on
   the prospect's page, and should read as such rather than as a feature of
   their business's website. */

/* The claim page's second route: pay, or just say yes. Given the same visual
   weight as the plans, because for a lot of owners it is the only one they
   will use. */

/* The "+" between two prices that add up. Hidden from screen readers — the
   words already say "then", and a lone plus read aloud is noise. */

.tiers__join {
  display: grid; place-items: center; padding: 0;
  font-size: var(--step-3); font-weight: 700; color: var(--muted);
}

@media (max-width: 47.99rem) {
  .tiers__join { padding: 0.25rem 0; }
}

.plan__plus {
  display: block; margin-top: 0.35rem; font-size: var(--step-1);
  font-weight: 400; color: var(--muted);
}

/* The plan's name, and its badge if it has one, on one row. Only one card
   carries a badge, so in the flow it pushed that card's price, points and
   button below its opposite number and the two stopped reading as a pair. */
/* The min-height is the point: a badge is taller than a plan name (26px
   against 18px here), so the card carrying one pushed its own price, points
   and button 8px below its neighbour's. Reserving the badge's height on both
   cards lines the two up exactly, whether or not either has one. Generous
   enough to cover every preset's typeface rather than tuned to one. */
.plan__top {
  display: flex; align-items: center; justify-content: space-between;
  gap: 0.75rem; width: 100%; min-height: 2rem;
}

.plan__badge {
  flex: 0 0 auto; margin: 0; padding: 0.2rem 0.6rem;
  font-size: 0.78rem; font-weight: 600; letter-spacing: 0.01em;
  color: var(--on-accent); background: var(--accent); border-radius: 999px;
}

/* The recommended plan. A recoloured hairline was not enough to read as
   picked out — at a glance both cards looked the same weight — so the ring
   doubles the border without moving anything, which a thicker border would. */
.plan--featured {
  border-color: var(--accent);
  box-shadow: 0 0 0 1px var(--accent);
}

.claim__alt {
  margin-top: 2.5rem; padding: 1.75rem; text-align: center;
  background: var(--tint); border: var(--rule); border-radius: var(--radius-lg);
}

.claim__alt h2 { font-size: var(--step-2); margin-bottom: 0.5rem; }

.claim__alt p { max-width: 34rem; margin: 0 auto; color: var(--muted); }

.claim__alt-actions {
  display: flex; flex-wrap: wrap; gap: 0.75rem; justify-content: center; margin-top: 1.25rem;
}

.claim__alt-actions .btn { margin: 0; }

.themer { position: fixed; right: 1rem; bottom: 1rem; z-index: 40; font-size: 0.875rem; }

.themer__toggle {
  display: block; margin-left: auto; padding: 0.55rem 0.9rem; cursor: pointer;
  background: #1b1f23; color: #fff; border: 1px solid #333a41; border-radius: 999px;
  font: inherit; font-weight: 600; box-shadow: 0 6px 20px -8px rgb(0 0 0 / 0.5);
}

.themer__toggle:hover { background: #262b31; }

.themer__panel {
  margin-top: 0.6rem; width: min(20rem, calc(100vw - 2rem)); padding: 1rem;
  box-sizing: border-box;
  /* Five controls plus the code box is a tall panel, and a laptop in a hotel
     room is a short screen. Scroll inside rather than off the bottom. */
  max-height: min(34rem, calc(100vh - 5rem)); overflow-y: auto; overscroll-behavior: contain;
  background: #1b1f23; color: #e8edf1; border: 1px solid #333a41; border-radius: 12px;
  box-shadow: 0 16px 40px -12px rgb(0 0 0 / 0.6);
}

.themer__note { margin: 0 0 0.7rem; font-size: 0.78rem; color: #9fb0bd; line-height: 1.4; }

.themer__row { display: flex; align-items: center; gap: 0.6rem; margin-bottom: 0.55rem; }

.themer__row > span { flex: 0 0 3.6rem; font-size: 0.78rem; color: #9fb0bd; }

.themer__preset {
  /* min-width:0 is what keeps a select inside a flex row: without it the
     intrinsic width of the longest option pushes it out of the panel. */
  flex: 1 1 auto; min-width: 0; max-width: 100%;
  padding: 0.4rem 0.5rem; font: inherit; font-size: 0.85rem; border-radius: 7px;
  background: #111518; color: #e8edf1; border: 1px solid #333a41;
}

.themer__knob {
  flex: 1 1 auto; min-width: 0; max-width: 100%;
  padding: 0.35rem 0.45rem; font: inherit; font-size: 0.82rem;
  background: #0f1317; color: #e8edf1; border: 1px solid #454d55; border-radius: 7px;
}

.themer__sws { display: flex; flex-wrap: wrap; gap: 0.35rem; }

.themer__sw {
  width: 1.6rem; height: 1.6rem; padding: 0; cursor: pointer;
  border: 1px solid #454d55; border-radius: 6px;
}

.themer__sw:hover { outline: 2px solid #7ab6f0; outline-offset: 1px; }

/* The code they send back. Monospaced and letter-spaced because it gets read
   off a screen and typed into a text message, and readonly because a code they
   have edited describes nothing. */

.themer__code {
  position: relative; margin: 0.75rem 0 0.7rem; padding-top: 0.7rem;
  border-top: 1px solid #333a41;
}

.themer__row--code { margin-bottom: 0.5rem; }

.themer__codebox {
  flex: 1 1 auto; min-width: 0; width: 100%; padding: 0.4rem 0.5rem;
  font: 600 0.95rem/1.2 ui-monospace, SFMono-Regular, Menlo, monospace;
  letter-spacing: 0.16em; text-align: center; text-transform: uppercase;
  background: #0f1317; color: #f4d58d; border: 1px solid #454d55; border-radius: 7px;
}

.themer__codebox:focus { outline: 2px solid #7ab6f0; outline-offset: 1px; }

.themer__copy {
  width: 100%; padding: 0.4rem; cursor: pointer; font: inherit; font-size: 0.8rem; font-weight: 600;
  background: #2b333a; color: #e8edf1;
  border: 1px solid #454d55; border-radius: 7px;
}

.themer__copy:hover { border-color: #7ab6f0; }

.themer__help { margin: 0.55rem 0 0; font-size: 0.74rem; line-height: 1.45; color: #9fb0bd; }

.themer__reset {
  width: 100%; padding: 0.4rem; cursor: pointer; font: inherit; font-size: 0.8rem;
  background: none; color: #9fb0bd; border: 1px dashed #454d55; border-radius: 7px;
}

.themer__reset:hover { color: #e8edf1; border-color: #7ab6f0; }

@media (max-width: 47.99rem) {
}

/* ---- portfolio.css ---- */
.works {
  display: grid; gap: 1.25rem; margin: 0; padding: 0; list-style: none;
  grid-template-columns: repeat(auto-fill, minmax(min(100%, 18rem), 1fr));
}

.work {
  /* The card is the list item, so an entry without a link still looks like one. */
  display: flex; flex-direction: column; overflow: hidden;
  border: var(--rule); border-radius: var(--radius-lg); background: var(--paper);
  transition: border-color .15s ease, transform .15s ease;
}

.work:has(.work__link):hover { border-color: var(--brand); transform: translateY(-2px); }

.work:has(.work__link:hover) .work__cta { text-decoration-color: currentColor; }

.work__link { display: flex; flex-direction: column; flex: 1; min-width: 0; text-decoration: none; color: inherit; }

/* The picture is the top of the site's own home page, cropped like a browser
   window: whatever is above the fold is what the card shows. */

.work__shot {
  display: block; aspect-ratio: 16 / 10; overflow: hidden;
  border-bottom: var(--rule); background: var(--tint);
}

.work__shot img { display: block; width: 100%; height: 100%; object-fit: cover; object-position: top; }

.work__shot--mark { display: grid; place-items: center; border-bottom: 0; }

.work__mark {
  font-size: 3rem; font-weight: 700; color: #fff;
  text-shadow: 0 1px 2px rgb(0 0 0 / 0.35);
}

.work__body { display: flex; flex-direction: column; gap: 0.35rem; flex: 1; min-width: 0; padding: 1rem 1.25rem 1.25rem; }

.work__meta { display: flex; align-items: center; justify-content: space-between; gap: 0.5rem; margin-bottom: 0.2rem; }

.work__tag {
  font-size: 0.75rem; font-weight: 700; letter-spacing: 0.02em; padding: 0.2rem 0.6rem; border-radius: 999px;
  background: color-mix(in srgb, var(--brand) 12%, var(--paper)); color: var(--brand);
}

.work__year { font-size: 0.8rem; color: var(--muted); font-variant-numeric: tabular-nums; }

.work__name { font-weight: 650; font-size: var(--step-1); line-height: 1.25; }

.work__detail { font-size: 0.87rem; color: var(--muted); }

.work__text { font-size: 0.95rem; line-height: 1.5; margin-top: 0.15rem; }

.work__cta {
  margin-top: auto; padding-top: 0.85rem; font-weight: 600; color: var(--brand);
  text-decoration: underline; text-decoration-color: transparent; text-underline-offset: 0.2em;
  transition: text-decoration-color .15s ease;
}

/* The same cards as a swipeable row (`layout: "carousel"` on the section).
   The row itself — whole cards, controls above beside the heading — is
   `.cards-carousel` in sections.css, shared with reviews. In a slide the
   screenshot fills the card's top edge to edge. */

.cards-carousel .carousel__slide .work__shot img { aspect-ratio: auto; border-radius: 0; }

/* ---- steps.css ---- */
.steps {
  display: grid; gap: 1.5rem; margin: 0; padding: 0; list-style: none;
  grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr));
  counter-reset: step;
}

.step { display: flex; gap: 1rem; align-items: flex-start; }

.step__n {
  flex: 0 0 auto; width: 2.25rem; height: 2.25rem; border-radius: 999px;
  display: grid; place-items: center; font-weight: 700;
  background: var(--brand); color: var(--on-brand);
}

.step__title { margin: 0 0 0.25rem; font-size: 1.05rem; }

.step p { margin: 0; color: var(--muted); }

/* ---- pricing.css ---- */
.tiers {
  display: grid; gap: 1.25rem; margin: 0; padding: 0; list-style: none;
  grid-template-columns: repeat(auto-fit, minmax(16rem, 1fr));
}

/* The joiner must not claim a column of its own: auto-fit would give the "+"
   the same width as a price card and push the two apart across the page. */

.tiers--joined { grid-template-columns: minmax(16rem, 1fr) auto minmax(16rem, 1fr); }

@media (max-width: 47.99rem) {
  .tiers--joined { grid-template-columns: 1fr; }
}

.tier {
  position: relative; display: flex; flex-direction: column; gap: 0.6rem;
  padding: 1.75rem; border: var(--rule); border-radius: var(--radius-lg); background: var(--paper);
}

.tier--featured { border-color: var(--brand); border-width: 2px; }

.tier__flag {
  position: absolute; top: -0.7rem; left: 1.5rem;
  background: var(--brand); color: var(--on-brand);
  font-size: 0.72rem; font-weight: 700; padding: 0.15rem 0.6rem; border-radius: 999px;
}

.tier__name { margin: 0; font-size: 1rem; }

.tier__price { margin: 0; font-size: 2.1rem; font-weight: 700; line-height: 1.1; }

.tier__per { font-size: 0.95rem; font-weight: 500; color: var(--muted); }

.tier__note { margin: 0; font-size: 0.9rem; color: var(--muted); }

.tier__list { list-style: none; margin: 0.4rem 0 0; padding: 0; display: grid; gap: 0.4rem; font-size: 0.92rem; }

.tier__list li { display: flex; gap: 0.5rem; align-items: flex-start; }

.tier__list .icon { flex: 0 0 auto; width: 1.1em; height: 1.1em; margin-top: 0.2em; color: var(--brand); }

.tier .btn { margin-top: auto; align-self: stretch; justify-content: center; }

/* The terms under the prices, boxed. Everything in here is something a buyer
   weighs — the yearly saving, the notice period, what the price does not
   buy — and as a grey paragraph it read as small print to skip. */
.tiers__note {
  margin: 2rem 0 0; padding: 1.25rem 1.4rem;
  /* Mixed from the accent rather than set to --tint: the pricing section is
     often tinted itself, and a tinted box on a tinted band is just a rule. */
  background: color-mix(in srgb, var(--accent) 9%, var(--paper));
  border-left: 4px solid var(--accent);
  border-radius: var(--radius);
  color: var(--ink); font-size: 0.95rem; line-height: 1.6;
  max-width: 46rem;
}

/* ------------------------------------------------- the monthly/yearly switch
   A segmented control made of two real radio buttons. No script: the checked
   radio reaches the cards through the section they share, so this works with
   JavaScript off and arrives keyboard-operable and announced as a radio group
   without anything being wired up. */
.tiers-switch {
  display: inline-flex; gap: 0.25rem; margin: 0 0 1.75rem; padding: 0.25rem;
  border: 1px solid var(--line); border-radius: 999px; background: var(--paper);
}

.tiers-switch label {
  display: block; padding: 0.45rem 1.1rem; border-radius: 999px;
  font-size: 0.92rem; font-weight: 600; color: var(--muted); cursor: pointer;
  transition: background 120ms ease, color 120ms ease;
}

.tiers-switch input:checked + label { background: var(--brand); color: var(--on-brand); }

/* The radios are visually hidden, so the focus ring has to be drawn on the
   label or keyboard users cannot see where they are. */
.tiers-switch input:focus-visible + label { outline: 2px solid var(--focus, var(--brand)); outline-offset: 2px; }

@media (prefers-reduced-motion: reduce) { .tiers-switch label { transition: none; } }

/* Show the side that is checked. Written as "hide the other one" so that with
   no CSS at all both cards render rather than neither. */
.s--pricing:has([data-plan-input="0"]:checked) .tier[data-plan="1"],
.s--pricing:has([data-plan-input="1"]:checked) .tier[data-plan="0"] { display: none; }

/* Three cards in the markup, two on screen — so the joined grid still wants
   its three columns and the "+" stays between them. */
.tiers--switched.tiers--joined { grid-template-columns: minmax(16rem, 1fr) auto minmax(16rem, 1fr); }

@media (max-width: 47.99rem) {
  .tiers--switched.tiers--joined { grid-template-columns: 1fr; }
}

/* ---- menu.css ---- */
/* The menu section: categories side by side where there is room, each dish on
   a dotted leader with its price, dietary tags as small abbreviations. */
.menu { display: grid; gap: 2.5rem 4rem; grid-template-columns: repeat(auto-fit, minmax(min(100%, 20rem), 1fr)); }
.menu__cat { margin: 0 0 0.25rem; font-size: var(--step-2); }
.menu__catnote { margin: 0 0 0.75rem; color: var(--muted); font-size: 0.95rem; }
.menu__items { margin: 0; padding: 0; list-style: none; }
.menu__item {
  display: grid; grid-template-columns: 1fr auto; gap: 0 1rem; padding: 0.6rem 0;
  border-bottom: 1px dotted color-mix(in srgb, var(--ink) 35%, transparent);
}
.menu__name { font-weight: 600; }
.menu__tags { margin-left: 0.4rem; font-size: 0.72rem; font-weight: 600; letter-spacing: 0.04em; color: var(--muted); }
.menu__tags abbr { text-decoration: none; }
.menu__price { font-family: var(--font-display); font-weight: var(--display-weight); white-space: nowrap; }
.menu__text { grid-column: 1 / -1; margin-top: 0.15rem; color: var(--muted); font-size: 0.95rem; }
.menu__legend { margin-top: 1.25rem; color: var(--muted); font-size: 0.85rem; }
.menu__pdf { margin-top: 1rem; }

/* ---- claim.css ---- */
.claim__includes { margin: 0 0 3rem; max-width: 44rem; }

.claim__gaps {
  max-width: 44rem; margin: 0 0 2.5rem; padding: 1.5rem;
  border-left: 3px solid var(--accent); background: var(--tint); border-radius: var(--radius);
}

.claim__gaps h2 { margin: 0 0 0.75rem; font-size: 1.05rem; }

.claim__gaps ul { margin: 0; padding-left: 1.1rem; display: grid; gap: 0.4rem; }

.claim__gaps-note { margin: 0.9rem 0 0; font-size: 0.85rem; color: var(--muted); }

.plans {
  display: grid; gap: 1.25rem; margin: 0 0 3rem; padding: 0; list-style: none;
  grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr));
}

.plan {
  display: flex; flex-direction: column; align-items: flex-start; gap: 0.5rem;
  padding: 1.75rem; border: var(--rule); border-radius: var(--radius-lg); background: var(--paper);
}

.plan__name { margin: 0; font-size: 1.05rem; }

.plan__price { margin: 0; font-size: 2.25rem; font-weight: 700; line-height: 1.1; }

.plan__per { font-size: 1rem; font-weight: 500; color: var(--muted); }

/* Four facts per plan, one per line. The list needs its own marker removed:
   without this it shows a disc AND the tick icon, which reads as a bug. */
.plan__points {
  display: grid; gap: 0.55rem; width: 100%;
  margin: 0.25rem 0 0.5rem; padding: 0; list-style: none;
  font-size: 0.92rem; color: var(--muted);
}

.plan__points li { display: grid; grid-template-columns: 1.1rem 1fr; gap: 0.6rem; align-items: start; }

.plan__points .icon { width: 1.1rem; height: 1.1rem; margin-top: 0.15rem; color: var(--brand); stroke-width: 2.5; }

/* The aside between the two prices and the email escape hatch. Smaller than
   the body on purpose: it is a reassurance, not one of the facts being sold. */
.claim__most {
  max-width: 34rem; margin: 0 auto 2.25rem;
  font-size: 0.85rem; line-height: 1.5; color: var(--muted); text-align: center;
}

/* What the price does not buy. Boxed like "What you get" so the two read as a
   pair — the second half of one honest answer rather than loose small print. */
.claim__excludes {
  max-width: 44rem; margin: 0 0 2.5rem; padding: 1.75rem;
  background: var(--tint); border: 1px solid var(--line); border-radius: var(--radius-lg);
}

.claim__excludes h2 { margin: 0 0 1rem; font-size: var(--step-1); }

.claim__excludes ul { margin: 0; padding-left: 1.1rem; display: grid; gap: 0.6rem; color: var(--muted); }

.claim__excludes-note { margin: 1.1rem 0 0; font-size: 0.85rem; color: var(--muted); }

.plan .btn { margin-top: auto; align-self: stretch; justify-content: center; }

.claim__foot { max-width: 44rem; margin: 0; }

.claim__back { font-size: 0.9rem; }

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after { transition: none !important; }
}

/* ---------------------------------------------------------------- charts
   Two pictures of numbers already stated in words above them, so a chart is
   never the only place a figure appears. Coloured through the palette tokens
   rather than with literals, because there are twelve palettes. */
.claim__charts { display: grid; gap: 1.75rem; margin: 1.5rem 0 0; }

.chart { margin: 0; }

.chart__svg { display: block; width: 100%; max-width: 26rem; height: auto; overflow: visible; }

.chart__svg--bars { max-width: 30rem; }

/* A business with no website found on its listing, and one with. */
.chart__cell { fill: transparent; stroke: var(--line); stroke-width: 1.5; }

.chart__cell--on { fill: var(--brand); stroke: var(--brand); }

/* Mixed toward transparent, so it takes the page's own ground — but not so
   far that it disappears into a dark one. At 28% the bars were a dim olive on
   the lounge preset's near-black paper; 48% reads on both. */
.chart__bar { fill: color-mix(in srgb, var(--brand) 48%, transparent); }

.chart__bar--peak { fill: var(--brand); }

.chart__tick { font-size: 10px; fill: var(--muted); }

.chart__caption { margin: 0.65rem 0 0; font-size: 0.85rem; line-height: 1.5; color: var(--muted); }

/* The scale behind the demand bars. Faint enough to stay behind them, present
   enough that the numbers have a line to sit on. */
.chart__rule { stroke: var(--line); stroke-width: 1; }

.chart__rule--base { stroke: color-mix(in srgb, var(--ink) 35%, transparent); }

/* What the site has to earn back.
 *
 * A panel rather than a paragraph, because this is the argument and the line
 * under it is a note — as two centred paragraphs of different sizes they read
 * as one ragged block and the number that matters was buried mid-sentence
 * between two other numbers. */
.payback {
  max-width: 34rem; margin: 0 auto 1.75rem; padding: 1.5rem 1.75rem;
  background: var(--tint); border-radius: var(--radius-lg); text-align: center;
}

.payback__figure {
  margin: 0; font-family: var(--font-display); font-weight: var(--display-weight);
  font-stretch: var(--display-stretch); letter-spacing: var(--display-tracking);
  font-size: clamp(2.2rem, 1.4rem + 3vw, 3rem); line-height: 1; color: var(--brand);
}

.payback__figure span { font-size: 0.42em; font-weight: 600; letter-spacing: 0; margin-left: 0.35em; color: var(--ink); }

.payback__what { margin: 0.5rem 0 0; font-size: 1.05rem; font-weight: 600; }

.payback__how {
  max-width: 28rem; margin: 0.85rem auto 0;
  font-size: 0.85rem; line-height: 1.55; color: var(--muted);
}

/* What happens after they say yes. The same numbered steps the studio's own
   /paid/ page uses, sharing `.steps` so the two cannot drift apart — one
   process described twice already, and two stylesheets for it would be a third
   way to disagree. */
.claim__next { margin: 2rem 0 0; padding: 1.75rem; background: var(--tint); border-radius: var(--radius-lg); }
.claim__next h2 { margin: 0 0 1.5rem; font-size: var(--step-1); }

/* ---- presets/workshop.css ---- */
/* workshop: everything this look changes. The token block sets its typography
   and shape; the rules after it override the shared components. */

/* Trades & services: van-door lettering, fleet stripe, big phone number */
[data-preset="workshop"] {
  --font-display: "Archivo", "Arial Narrow", system-ui, sans-serif;
  --font-body: "Archivo", system-ui, sans-serif;
  --display-weight: 800;
  --display-stretch: 72%;
  --display-tracking: 0;
  --radius: 3px;
  --radius-lg: 4px;
  --rule: 3px solid var(--ink);
  --hero-size: clamp(3rem, 1.4rem + 6.4vw, 6rem);
  --hero-measure: 13ch;
  --plate-size: clamp(2.6rem, 0.9rem + 7.2vw, 4.6rem);
  --step-4: clamp(2.4rem, 1.5rem + 3.6vw, 3.6rem);
}
[data-preset="workshop"] .brand__mark { font-size: 1.9rem; }
[data-preset="workshop"] .hero {
  background: var(--ink); color: var(--on-ink);
  --focus: var(--accent); --badge-icon: var(--accent);
  padding-bottom: calc(clamp(3rem, 2rem + 5vw, 6.5rem) + 20px);
}
[data-preset="workshop"] .hero::after {
  content: ""; position: absolute; inset: auto 0 0 0; height: 20px;
  background: linear-gradient(var(--accent) 0 13px, var(--brand) 13px 20px);
}
[data-preset="workshop"] .hero__text { color: color-mix(in srgb, var(--on-ink) 82%, var(--ink)); }
[data-preset="workshop"] .plate { --plate-bg: var(--accent); --plate-fg: var(--on-accent); padding: 1.75rem 2rem 2rem; }
[data-preset="workshop"] .plate__value { font-stretch: 68%; --plate-fit: 17cqi; }
@media (min-width: 56rem) { [data-preset="workshop"] .hero__grid { align-items: end; } }
[data-preset="workshop"] .svc__title { font-size: 1.85rem; }
[data-preset="workshop"] .quote { padding: 1.5rem 0 0; background: none; border: 0; border-top: var(--rule); border-radius: 0; }

/* ---- presets/storefront.css ---- */
/* storefront: everything this look changes. The token block sets its typography
   and shape; the rules after it override the shared components. */

/* Shops, bakeries, cafés, salons: arched windows, serif sign */
[data-preset="storefront"] {
  --font-display: "Young Serif", Georgia, serif;
  --font-body: "Figtree", system-ui, sans-serif;
  --display-weight: 400;
  --display-tracking: -0.01em;
  --radius: 10px;
  --radius-lg: 20px;
  /* Pill buttons are this preset’s signature, said as a token so the
     Corners knob can still square them off if a prospect wants that. */
  --radius-btn: 999px;
  --hero-measure: 14ch;
  --plate-size: clamp(1.8rem, 1.1rem + 2.6vw, 2.6rem);
}

[data-preset="storefront"] .hero { background: var(--tint); }
[data-preset="storefront"] .hero__photo { border-radius: 999px 999px var(--radius-lg) var(--radius-lg); }
[data-preset="storefront"] .plate { text-align: center; }
[data-preset="storefront"] .plate__value { white-space: normal; }
[data-preset="storefront"] .hero--plain .plate {
  display: grid; align-content: center; min-height: 22rem; padding: 4rem 2rem 3rem;
  border-radius: 999px 999px var(--radius-lg) var(--radius-lg);
}
@media (min-width: 56rem) {
  [data-preset="storefront"] .hero--photo .plate {
    position: absolute; left: -2.5rem; bottom: 2.5rem; width: min(19rem, 85%);
    box-shadow: 0 18px 40px -18px color-mix(in srgb, var(--ink) 55%, transparent);
  }
  /* Unless the photo needs its own corner back. Specific enough to beat the
     rule above, which is the only place in the engine a plate covers a photo. */
  [data-preset="storefront"] .hero--photo.hero--plate-below .plate {
    position: static; width: auto; box-shadow: none;
  }
}
[data-preset="storefront"] .about__photo { border-radius: 999px 999px var(--radius-lg) var(--radius-lg); aspect-ratio: 4 / 5; }

/* ---- presets/studio.css ---- */
/* studio: everything this look changes. The token block sets its typography
   and shape; the rules after it override the shared components. */

/* Professional services: quiet, editorial, lots of air */
[data-preset="studio"] {
  --font-display: "Newsreader", Georgia, serif;
  --font-body: "Public Sans", system-ui, sans-serif;
  --display-weight: 500;
  --display-tracking: -0.015em;
  --radius: 2px;
  --radius-lg: 2px;
  --hero-measure: 18ch;
  --plate-size: clamp(1.7rem, 1.1rem + 2.2vw, 2.4rem);
}
[data-preset="studio"] body { line-height: 1.65; }
[data-preset="studio"] .hero { border-bottom: 1px solid var(--line); }
[data-preset="studio"] .plate {
  --plate-bg: transparent; --plate-fg: var(--ink);
  border: 1px solid var(--line); border-left: 3px solid var(--brand);
}
[data-preset="studio"] .plate__label { color: var(--muted); font-weight: 500; }

/* ---- presets/wellness.css ---- */
/* wellness: everything this look changes. The token block sets its typography
   and shape; the rules after it override the shared components. */

/* Health, care and wellbeing: calm, rounded, unhurried */
[data-preset="wellness"] {
  --font-display: "Fraunces", Georgia, serif;
  --font-body: "Nunito Sans", system-ui, sans-serif;
  --display-weight: 600;
  --display-tracking: -0.02em;
  --radius: 14px;
  --radius-lg: 28px;
  --radius-btn: 999px;
  --hero-size: clamp(2.4rem, 1.5rem + 3.8vw, 3.9rem);
  --hero-measure: 16ch;
  --plate-size: clamp(1.7rem, 1.1rem + 2.2vw, 2.4rem);
  --measure: 62ch;
}
[data-preset="wellness"] .hero { background: var(--tint); }
[data-preset="wellness"] .hero__photo { border-radius: var(--radius-lg); }
[data-preset="wellness"] .btn { padding-inline: 1.6rem; }
[data-preset="wellness"] .plate {
  --plate-bg: var(--paper); --plate-fg: var(--ink);
  border: 1px solid color-mix(in srgb, var(--brand) 22%, transparent);
  padding: 1.75rem 2rem;
}
[data-preset="wellness"] .plate__label { color: var(--brand); }
[data-preset="wellness"] .svc { border-top: 0; padding-block: 0 2rem; }
[data-preset="wellness"] .svc__icon { width: 3.25rem; height: 3.25rem; border-radius: 999px; }
[data-preset="wellness"] .quote {
  border: 0; border-radius: var(--radius-lg);
  background: color-mix(in srgb, var(--brand) 7%, var(--paper));
}
[data-preset="wellness"] .s--tint .quote { background: var(--paper); }
[data-preset="wellness"] .checks { border-radius: var(--radius-lg); }
[data-preset="wellness"] .gallery img { border-radius: var(--radius-lg); }
[data-preset="wellness"] .faq__item summary { font-weight: 700; }

/* ---- presets/field.css ---- */
/* field: everything this look changes. The token block sets its typography
   and shape; the rules after it override the shared components. */

/* Outdoor and property work: sturdy slab, photography leads */
[data-preset="field"] {
  --font-display: "Zilla Slab", Georgia, serif;
  --font-body: "Source Sans 3", system-ui, sans-serif;
  --display-weight: 700;
  --display-tracking: -0.01em;
  --radius: 4px;
  --radius-lg: 8px;
  --rule: 2px solid var(--line);
  --hero-size: clamp(2.8rem, 1.6rem + 5vw, 4.8rem);
  --hero-measure: 14ch;
  --plate-size: clamp(2.2rem, 1.2rem + 4vw, 3.2rem);
}
[data-preset="field"] .hero { background: var(--tint); border-bottom: 4px solid var(--brand); }
[data-preset="field"] .hero__photo { aspect-ratio: 3 / 2; }
[data-preset="field"] .plate { --plate-bg: var(--brand); --plate-fg: var(--on-brand); }
[data-preset="field"] .svc {
  border-top: 0; border-left: 3px solid var(--brand);
  padding: 0.25rem 0 1.75rem 1.25rem; margin-bottom: 1.5rem;
}
[data-preset="field"] .svc__icon { background: transparent; width: auto; height: auto; margin-bottom: 0.6rem; }
[data-preset="field"] .svc__title { font-size: var(--step-1); }
[data-preset="field"] .areas li { border-bottom: 2px solid var(--line); }
[data-preset="field"] .gallery { gap: 0.5rem; }
[data-preset="field"] .quote { border-left: 3px solid var(--accent); border-radius: 0; }
@media (min-width: 56rem) {
  [data-preset="field"] .hero--photo .hero__grid { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); }
}

/* ---- presets/table.css ---- */
/* table: everything this look changes. The token block sets its typography
   and shape; the rules after it override the shared components. */

/* Restaurants and food service: the menu is the page */
[data-preset="table"] {
  --font-display: "Playfair Display", Georgia, serif;
  --font-body: "Karla", system-ui, sans-serif;
  --display-weight: 600;
  --display-tracking: -0.015em;
  --radius: 3px;
  --radius-lg: 6px;
  --hero-size: clamp(2.7rem, 1.6rem + 4.6vw, 4.6rem);
  --hero-measure: 15ch;
  --plate-size: clamp(1.9rem, 1.2rem + 2.6vw, 2.7rem);
}
[data-preset="table"] .hero { background: var(--ink); color: var(--on-ink); --badge-icon: var(--accent); --focus: var(--accent); }
[data-preset="table"] .hero__text { color: color-mix(in srgb, var(--on-ink) 82%, var(--ink)); }
[data-preset="table"] .plate { --plate-bg: var(--accent); --plate-fg: var(--on-accent); text-align: center; }
[data-preset="table"] .svc-list { gap: 0 4rem; }
[data-preset="table"] .svc {
  display: grid; grid-template-columns: 1fr auto; gap: 0 1rem; align-items: baseline;
  padding-block: 1rem; border-top: 0;
  border-bottom: 1px dotted color-mix(in srgb, var(--ink) 35%, transparent);
}
[data-preset="table"] .svc__icon { display: none; }
[data-preset="table"] .svc__title { grid-column: 1; grid-row: 1; margin-bottom: 0; font-size: var(--step-1); }
[data-preset="table"] .svc .svc__price { grid-column: 2; grid-row: 1; margin-top: 0; font-family: var(--font-display); }
[data-preset="table"] .svc p { grid-column: 1 / -1; margin-top: 0.3rem; font-size: 0.98rem; }
[data-preset="table"] .s__head { margin-inline: auto; text-align: center; }
[data-preset="table"] .s { --row-tracks: repeat(auto-fit, minmax(min(100%, 14rem), 17rem)); --row-justify: center; }
[data-preset="table"] .quote blockquote { font-style: italic; }

/* ---- presets/atelier.css ---- */
/* atelier: everything this look changes. The token block sets its typography
   and shape; the rules after it override the shared components. */

/* Creative and craft studios: editorial, the portfolio carries it */
[data-preset="atelier"] {
  --font-display: "Cormorant Garamond", Georgia, serif;
  --font-body: "Jost", system-ui, sans-serif;
  --display-weight: 500;
  --display-tracking: -0.02em;
  --radius: 0;
  --radius-lg: 0;
  --hero-size: clamp(3rem, 1.6rem + 6vw, 5.6rem);
  --hero-measure: 15ch;
  --plate-size: clamp(1.8rem, 1.1rem + 2.4vw, 2.5rem);
  --measure: 64ch;
}
[data-preset="atelier"] .site-header { border-bottom: 0; }
[data-preset="atelier"] .hero { padding-block: clamp(4rem, 3rem + 6vw, 8rem); }
/* Cormorant has long descenders; line-height 1 collides at hero size. */
[data-preset="atelier"] .hero__title { font-weight: 400; line-height: 1.06; }
[data-preset="atelier"] .s__head h2, [data-preset="atelier"] .simple h1 { line-height: 1.1; }
[data-preset="atelier"] .hero__photo { aspect-ratio: 1; }
[data-preset="atelier"] .plate {
  --plate-bg: transparent; --plate-fg: var(--ink);
  padding: 0; border-top: 1px solid var(--ink); padding-top: 1rem;
}
[data-preset="atelier"] .plate__label { color: var(--muted); font-weight: 400; }

[data-preset="atelier"] .svc { border-top: 1px solid var(--ink); padding-block: 1.25rem 2rem; }
[data-preset="atelier"] .svc__icon { display: none; }
[data-preset="atelier"] .svc__title { font-size: var(--step-2); font-weight: 400; }
[data-preset="atelier"] .gallery { gap: 0.35rem; }
[data-preset="atelier"] .gallery img { aspect-ratio: 1; border-radius: 0; }
[data-preset="atelier"] .quote { border: 0; background: none; padding: 0; }
[data-preset="atelier"] .quote blockquote { font-family: var(--font-display); font-size: 1.5rem; line-height: 1.35; }
[data-preset="atelier"] .s__head h2 { font-weight: 400; }

/* ---- presets/lounge.css ---- */
/* lounge: everything this look changes. The token block sets its typography
   and shape; the rules after it override the shared components. */

/* Bars, lounges, nightlife: dark paper (from the noir palette), a serif with
   some drama, generous spacing, nothing rounded. The photographs carry it. */
[data-preset="lounge"] {
  --font-display: "DM Serif Display", Georgia, serif;
  --font-body: "DM Sans", system-ui, sans-serif;
  --display-weight: 400;
  --display-tracking: 0;
  --radius: 0;
  --radius-lg: 0;
  --hero-size: clamp(2.8rem, 1.5rem + 5.5vw, 5.2rem);
  --hero-measure: 14ch;
  --plate-size: clamp(2rem, 1.1rem + 3vw, 3rem);
  --measure: 62ch;
  --space-section: clamp(4.5rem, 3.5rem + 5vw, 8rem);
}
/* lounge: the whole page is dark, so the chrome steps back and the metal
   accent does the pointing */
[data-preset="lounge"] .site-header { border-bottom: 1px solid var(--line); }
[data-preset="lounge"] .hero { padding-block: clamp(4rem, 3rem + 7vw, 9rem); }
[data-preset="lounge"] .hero__title { line-height: 1.02; }
[data-preset="lounge"] .hero__photo { aspect-ratio: 4 / 5; filter: saturate(0.85); }
[data-preset="lounge"] .hero__badges { color: var(--brand); text-transform: none; letter-spacing: 0.02em; }
[data-preset="lounge"] .plate {
  --plate-bg: transparent; --plate-fg: var(--ink);
  padding: 1.25rem 0 0; border-top: 1px solid var(--brand);
}
[data-preset="lounge"] .plate__label { color: var(--brand); font-weight: 500; }
[data-preset="lounge"] .btn { letter-spacing: 0.04em; }
[data-preset="lounge"] .btn--ghost { border-color: var(--brand); color: var(--ink); }
[data-preset="lounge"] .s--tint { background: color-mix(in srgb, var(--brand) 5%, var(--paper)); }
[data-preset="lounge"] .svc { border-top: 1px solid var(--line); padding-block: 1.25rem 2rem; }
[data-preset="lounge"] .svc__icon { background: transparent; color: var(--brand); border: 1px solid var(--line); }
[data-preset="lounge"] .svc__title { font-size: var(--step-2); font-weight: 400; }
[data-preset="lounge"] .gallery img, [data-preset="lounge"] .carousel__slide img { border-radius: 0; filter: saturate(0.85); }
[data-preset="lounge"] .quote { background: transparent; border: 1px solid var(--line); }
[data-preset="lounge"] .quote blockquote { font-family: var(--font-display); font-size: 1.4rem; line-height: 1.35; }
/* the footer is normally an ink band; on dark paper that would be a cream
   slab at the bottom of a dark room, so it stays in the room */
[data-preset="lounge"] .site-footer {
  background: color-mix(in srgb, var(--brand) 6%, var(--paper));
  color: color-mix(in srgb, var(--ink) 80%, var(--paper)); border-top: 1px solid var(--brand);
}
[data-preset="lounge"] .site-footer a { color: var(--ink); }
/* The footer headings keep the shared on-ink, which on dark paper is black on black. */
[data-preset="lounge"] .footer__brand, [data-preset="lounge"] .footer__h { color: var(--ink); }

/* ---- presets/dispatch.css ---- */
/* dispatch: everything this look changes. The token block sets its typography
   and shape; the rules after it override the shared components. */

/* Emergency and 24-hour services: condensed heavy type, a high-visibility
   plate, everything sized for a thumb on a phone at a roadside. */
[data-preset="dispatch"] {
  --font-display: "Barlow Condensed", "Arial Narrow", system-ui, sans-serif;
  --font-body: "Barlow", system-ui, sans-serif;
  --display-weight: 800;
  --display-stretch: 100%;
  --display-tracking: -0.005em;
  --radius: 4px;
  --radius-lg: 8px;
  --hero-size: clamp(2.8rem, 1.4rem + 6vw, 5.4rem);
  --hero-measure: 14ch;
  --plate-size: clamp(2.6rem, 1.2rem + 6vw, 4.2rem);
  --measure: 62ch;
}
/* dispatch: brand-coloured hero, an accent plate that is the biggest thing on
   the page, and badges that read as promises rather than decoration */
[data-preset="dispatch"] .hero { background: var(--brand); color: var(--on-brand); }
[data-preset="dispatch"] .hero .s__intro, [data-preset="dispatch"] .hero__text { color: var(--on-brand); }
[data-preset="dispatch"] .hero__title { text-transform: uppercase; letter-spacing: 0.01em; }
[data-preset="dispatch"] .hero__badges { font-size: 1.05rem; --badge-icon: var(--accent); }
[data-preset="dispatch"] .hero .btn--ghost { border-color: var(--on-brand); color: var(--on-brand); }
[data-preset="dispatch"] .plate {
  --plate-bg: var(--accent); --plate-fg: var(--on-accent);
  padding: 1.75rem 2rem; box-shadow: 0 12px 30px -14px rgb(0 0 0 / 0.5);
}
[data-preset="dispatch"] .plate__label { text-transform: uppercase; letter-spacing: 0.06em; font-size: 0.85rem; }
[data-preset="dispatch"] .svc__icon { background: var(--brand); color: var(--on-brand); }
[data-preset="dispatch"] .svc__title { text-transform: uppercase; letter-spacing: 0.02em; }
[data-preset="dispatch"] .s__head h2 { text-transform: uppercase; letter-spacing: 0.02em; }

/* ---- looks/aurora.css ---- */
/* ==========================================================================
   Look: aurora — `"theme": { "look": "aurora" }` in a site.json.

   A layer the build adds after the shared stylesheet for the sites that ask
   (see looks/sleek.css for how a look works). Same markup.

   A clear night with the northern lights over it:

   - the whole page is a deep night sky, faintly starred, and across it hang
     luminous ribbons in the palette's brand and accent — soft, blurred
     curtains of light, fixed behind the page;
   - as the page scrolls, the ribbons drift and turn, as far as the visitor
     scrolls and no further, so the sky seems to move behind the content
     without anything moving on its own;
   - sections are panes of dark frosted glass with a hairline of light at the
     top edge; the headline is set white and large; photographs carry a faint
     halo of the ribbons' colour.

   Like nocturne, this redefines the palette's paper and ink at the root —
   night and starlight — and everything computed from them; the brand and
   accent become the light in the sky. Where a visitor has asked for reduced
   motion the sky stands still. Rules are scoped `:root[data-look="aurora"] …`.
   ========================================================================== */

:root[data-look="aurora"] {
  --night: color-mix(in srgb, var(--brand) 14%, #04060b);
  --paper: var(--night);
  --ink: #eef1f8;
  /* Quieter words sit on glass over the moving light, not on flat night, so
     they keep more of the starlight: at 76% they dipped below 4.5:1 where a
     ribbon passed behind them. */
  --muted: rgb(238 241 248 / 0.84);
  --line: rgb(255 255 255 / 0.12);
  --tint: color-mix(in srgb, var(--brand) 18%, #080b12);
  --on-ink: #04060b;
  --ribbon-a: color-mix(in srgb, var(--accent) 80%, #fff);
  --ribbon-b: color-mix(in srgb, var(--brand) 70%, #fff);
  --ribbon-c: color-mix(in srgb, var(--brand) 50%, var(--accent));
  --lit: color-mix(in srgb, var(--accent) 45%, #fff);
  --link: color-mix(in srgb, var(--brand) 30%, #fff);
  --focus: var(--lit);
  --badge-icon: var(--lit);
  --pane: rgb(10 13 22 / 0.74);
  --pane-edge: rgb(255 255 255 / 0.1);
  --frost: blur(22px) saturate(1.4);
  --radius: 14px;
  --radius-lg: 24px;
  --radius-btn: 999px;
  --display-tracking: -0.03em;
  --hero-size: clamp(2.8rem, 1.2rem + 5.6vw, 5.8rem);
  --hero-measure: 14ch;
  color-scheme: dark;
}

:root[data-look="aurora"] body { background: var(--night); color: var(--ink); }
:root[data-look="aurora"] h1,
:root[data-look="aurora"] h2,
:root[data-look="aurora"] h3 { text-wrap: balance; letter-spacing: var(--display-tracking); }

/* ---- the sky ------------------------------------------------------------ */

/* Stars: three sizes of point, tiled at sizes that do not share a rhythm. */
:root[data-look="aurora"] body::before {
  content: ""; position: fixed; inset: 0; z-index: -2; pointer-events: none;
  background:
    radial-gradient(1px 1px at 12% 18%, rgb(255 255 255 / 0.7), transparent),
    radial-gradient(1px 1px at 68% 42%, rgb(255 255 255 / 0.55), transparent),
    radial-gradient(1.5px 1.5px at 38% 76%, rgb(255 255 255 / 0.6), transparent),
    radial-gradient(1px 1px at 88% 8%, rgb(255 255 255 / 0.5), transparent),
    radial-gradient(1px 1px at 24% 58%, rgb(255 255 255 / 0.45), transparent);
  background-size: 260px 260px, 340px 340px, 410px 410px, 290px 290px, 370px 370px;
}
/* The ribbons: long thin bands of light, combed into curtains by a ripple of
   vertical folds, the way the real thing hangs from the sky. */
:root[data-look="aurora"] body::after {
  content: ""; position: fixed; z-index: -1; pointer-events: none;
  inset: -20% -30%;
  background:
    radial-gradient(46% 7% at 32% 30%, color-mix(in srgb, var(--ribbon-a) 62%, transparent), transparent 72%),
    radial-gradient(52% 6% at 60% 22%, color-mix(in srgb, var(--ribbon-b) 55%, transparent), transparent 72%),
    radial-gradient(40% 9% at 46% 58%, color-mix(in srgb, var(--ribbon-c) 48%, transparent), transparent 74%),
    radial-gradient(36% 5% at 74% 70%, color-mix(in srgb, var(--ribbon-a) 40%, transparent), transparent 74%);
  /* Two ripples of different widths, multiplied, so the folds never repeat
     in step. */
  -webkit-mask:
    repeating-linear-gradient(90deg, rgb(0 0 0 / 0.95) 0, rgb(0 0 0 / 0.4) 9px, rgb(0 0 0 / 0.95) 17px),
    repeating-linear-gradient(90deg, #000 0, rgb(0 0 0 / 0.5) 23px, #000 41px);
  -webkit-mask-composite: source-in;
  mask:
    repeating-linear-gradient(90deg, rgb(0 0 0 / 0.95) 0, rgb(0 0 0 / 0.4) 9px, rgb(0 0 0 / 0.95) 17px),
    repeating-linear-gradient(90deg, #000 0, rgb(0 0 0 / 0.5) 23px, #000 41px);
  mask-composite: intersect;
  filter: blur(16px) saturate(1.4);
  transform: rotate(-12deg) skewX(-14deg);
}

@supports (animation-timeline: scroll()) {
  @media (prefers-reduced-motion: no-preference) {
    :root[data-look="aurora"] body::after {
      animation: aurora-drift linear both; animation-timeline: scroll(root);
    }
  }
}
@keyframes aurora-drift {
  from { transform: rotate(-12deg) skewX(-14deg) translate3d(-6%, 0, 0); }
  to { transform: rotate(6deg) skewX(10deg) translate3d(8%, -10%, 0); }
}

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

:root[data-look="aurora"] .site-header {
  /* Dark enough that a ribbon drifting behind it cannot wash out the name
     and the menu (4.1:1 at 55%). */
  background: rgb(6 8 14 / 0.8); color: var(--ink);
  -webkit-backdrop-filter: blur(18px) saturate(1.4); backdrop-filter: blur(18px) saturate(1.4);
  border-bottom: 1px solid var(--line);
}
:root[data-look="aurora"] .site-header :is(.brand, .site-nav a, .nav-toggle) { color: var(--ink); }
:root[data-look="aurora"] .site-header .brand__desc { color: var(--muted); }
@media (max-width: 63.99rem) {
  :root[data-look="aurora"] .site-nav { background: color-mix(in srgb, var(--night) 96%, transparent); }
}

/* ---- hero: the sky itself ---------------------------------------------- */

:root[data-look="aurora"] .hero {
  background: transparent; color: var(--ink); border: 0;
  padding-block: clamp(4rem, 2.5rem + 6vw, 8rem);
}
:root[data-look="aurora"] .hero::after { content: none; }
:root[data-look="aurora"] .hero :is(.hero__text, .s__intro, .rating) { color: var(--muted); }
:root[data-look="aurora"] .hero .hero__badges { color: var(--ink); }
:root[data-look="aurora"] .hero .btn--ghost { border-color: rgb(255 255 255 / 0.4); color: var(--ink); }
:root[data-look="aurora"] .hero__title { line-height: 0.96; text-shadow: 0 0 60px color-mix(in srgb, var(--ribbon-b) 35%, transparent); }
:root[data-look="aurora"]:not([data-hero="full"]) .hero__photo {
  border-radius: var(--radius-lg);
  box-shadow: 0 0 0 1px rgb(255 255 255 / 0.14), 0 30px 90px -30px color-mix(in srgb, var(--ribbon-a) 60%, transparent);
}
:root[data-look="aurora"][data-hero="full"] .hero__photo::after { background: rgb(4 6 11 / 0.72); }

/* The plate: dark glass, lit along its top edge. */
:root[data-look="aurora"] .plate {
  padding: 1.5rem 1.75rem;
  background: var(--pane); color: var(--ink);
  border: 1px solid var(--pane-edge); border-radius: 22px;
  -webkit-backdrop-filter: var(--frost); backdrop-filter: var(--frost);
  box-shadow: inset 0 1px 0 rgb(255 255 255 / 0.16), 0 30px 60px -30px rgb(0 0 0 / 0.7);
}
:root[data-look="aurora"] .plate__label { color: var(--lit); }
:root[data-look="aurora"] .plate__note { color: var(--muted); }
@media (min-width: 56rem) {
  :root[data-look="aurora"]:not([data-hero]):not([data-template]) .hero--photo .hero__grid { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: 4.5rem; }
  :root[data-look="aurora"]:not([data-hero]):not([data-template]) .hero--photo:not(.hero--plate-below) .plate {
    position: absolute; left: -2.5rem; bottom: 2rem; width: min(21rem, 76%);
  }
}

/* ---- sections: panes of dark frosted glass ------------------------------ */

:root[data-look="aurora"] .s {
  background: transparent;
}
:root[data-look="aurora"] .s--tint {
  width: min(100% - 2 * clamp(0.5rem, 1.5vw, 1.25rem), var(--max) + 8rem); margin: clamp(0.5rem, 1.5vw, 1.25rem) auto;
  border-radius: clamp(22px, 1.4vw + 14px, 36px);
  background: var(--pane); border: 1px solid var(--pane-edge);
  -webkit-backdrop-filter: var(--frost); backdrop-filter: var(--frost);
  box-shadow: inset 0 1px 0 rgb(255 255 255 / 0.1);
}
:root[data-look="aurora"] .s__intro { color: var(--muted); max-width: 46ch; }

/* ---- buttons ------------------------------------------------------------ */

:root[data-look="aurora"] .btn { padding: 0.95em 1.65em; }
:root[data-look="aurora"] .btn--sm { padding: 0.65em 1.15em; }
:root[data-look="aurora"] .btn--accent { box-shadow: 0 0 34px -8px color-mix(in srgb, var(--accent) 70%, transparent); }
:root[data-look="aurora"] .btn--ghost { border-color: rgb(255 255 255 / 0.35); }

/* ---- cards: glass on glass (menu rows left alone) ----------------------- */

:root[data-look="aurora"]:not([data-preset="table"]) .svc-list { gap: 1.1rem; grid-template-columns: repeat(auto-fill, minmax(min(100%, 18.5rem), 1fr)); }
:root[data-look="aurora"]:not([data-preset="table"]) .svc,
:root[data-look="aurora"] .quote,
:root[data-look="aurora"] .step,
:root[data-look="aurora"] .tier,
:root[data-look="aurora"] .checks,
:root[data-look="aurora"] .faq__item,
:root[data-look="aurora"] .contact-form {
  background: rgb(255 255 255 / 0.05); color: var(--ink);
  border: 1px solid var(--pane-edge); border-radius: 20px;
  box-shadow: inset 0 1px 0 rgb(255 255 255 / 0.08);
}
:root[data-look="aurora"]:not([data-preset="table"]) .svc { margin: 0; padding: 1.8rem 1.65rem 2rem; }
:root[data-look="aurora"]:not([data-preset="table"]) .svc__icon {
  width: 3rem; height: 3rem; border-radius: 50%;
  background: color-mix(in srgb, var(--ribbon-a) 18%, transparent); color: var(--lit);
}
:root[data-look="aurora"] :is(.checks .icon, .tier__list .icon, .stars, .quote blockquote::before) { color: var(--lit); }
:root[data-look="aurora"] .quote { padding: 2rem; }
:root[data-look="aurora"] .faq { display: grid; gap: 0.7rem; border-top: 0; }
:root[data-look="aurora"] .faq__item { padding-inline: 1.4rem; }
:root[data-look="aurora"] .tier--featured { border-color: color-mix(in srgb, var(--lit) 55%, transparent); box-shadow: 0 0 60px -20px color-mix(in srgb, var(--ribbon-a) 50%, transparent); }
:root[data-look="aurora"] .field :is(input, select, textarea) {
  background: rgb(0 0 0 / 0.3); color: var(--ink); border: 1px solid rgb(255 255 255 / 0.2); border-radius: 14px;
}
:root[data-look="aurora"] :is(.about__photo, .person__photo, .gallery img, .carousel__slide img) {
  border-radius: 20px; box-shadow: 0 0 0 1px rgb(255 255 255 / 0.1);
}
:root[data-look="aurora"] :is(.hours__note, .payback__figure, .work__tag, .work__cta) { color: var(--lit); }

/* ---- call to action and footer ----------------------------------------- */

:root[data-look="aurora"] .s--cta {
  width: min(100% - 2 * clamp(0.5rem, 1.5vw, 1.25rem), var(--max) + 8rem); margin: clamp(0.5rem, 1.5vw, 1.25rem) auto;
  border-radius: clamp(22px, 1.4vw + 14px, 36px);
  background:
    radial-gradient(40rem 20rem at 85% 0%, color-mix(in srgb, var(--ribbon-a) 20%, transparent), transparent 70%),
    var(--brand);
}
/* On a phone the words run the full width, under the glow; it moves down
   behind the button instead. */
@media (max-width: 40rem) {
  :root[data-look="aurora"] .s--cta {
    background:
      radial-gradient(24rem 14rem at 100% 100%, color-mix(in srgb, var(--ribbon-a) 20%, transparent), transparent 70%),
      var(--brand);
  }
}
:root[data-look="aurora"] .site-footer {
  background: rgb(3 4 8 / 0.82); color: var(--muted); border-top: 1px solid var(--line);
  -webkit-backdrop-filter: blur(18px); backdrop-filter: blur(18px);
}
:root[data-look="aurora"] .site-footer :is(a, .footer__brand, .footer__h) { color: var(--ink); }
:root[data-look="aurora"] .footer__base { border-top-color: var(--line); }

/* A step is drawn as a card here, so it needs a card's padding: without it
   the number sat on the border and the words ran to the edges. */
:root[data-look="aurora"] .step { padding: 1.4rem 1.35rem 1.5rem; }

/* The "how we work" list is a card here, so it keeps its padding even under
   the Minimal and Editorial styles, which take it away to leave the list bare. */
:root[data-look="aurora"] .checks { padding: 1.75rem; }
