/* SolScapes — design-system tokens and component classes. This file is the source of truth for the system's look; retune it here and see readme.md. */
@import url('https://fonts.googleapis.com/css2?family=Bakbak+One&family=Figtree:wght@400;500;600;700&display=swap');

:root {
  --color-bg: oklch(0.977 0.006 92);            /* warm off-white ground */
  --color-surface: #ffffff;                      /* cards sit brighter than the ground */
  --color-text: oklch(0.255 0.024 158);          /* deep green-black ink */
  --color-accent: oklch(0.550 0.145 152);        /* SolScapes green */
  --color-accent-2: oklch(0.780 0.160 78);       /* sun gold — the "Sol" */
  --color-divider: oklch(0.255 0.024 158 / 0.14);

  /* Tonal ramps — generated in OKLCH on one shared lightness scale, so the
     same step of any role matches the others in visual value. */
  --color-neutral-100: oklch(0.972 0.006 85);
  --color-neutral-200: oklch(0.935 0.008 85);
  --color-neutral-300: oklch(0.875 0.010 85);
  --color-neutral-400: oklch(0.785 0.012 85);
  --color-neutral-500: oklch(0.665 0.012 85);
  --color-neutral-600: oklch(0.560 0.012 85);
  --color-neutral-700: oklch(0.460 0.011 85);
  --color-neutral-800: oklch(0.360 0.010 85);
  --color-neutral-900: oklch(0.270 0.012 85);

  --color-accent-100: oklch(0.972 0.028 152);
  --color-accent-200: oklch(0.935 0.060 152);
  --color-accent-300: oklch(0.875 0.098 152);
  --color-accent-400: oklch(0.785 0.135 152);
  --color-accent-500: oklch(0.665 0.160 152);
  --color-accent-600: oklch(0.560 0.145 152);
  --color-accent-700: oklch(0.460 0.120 152);
  --color-accent-800: oklch(0.360 0.095 152);
  --color-accent-900: oklch(0.270 0.072 152);

  --color-accent-2-100: oklch(0.972 0.032 78);
  --color-accent-2-200: oklch(0.935 0.070 78);
  --color-accent-2-300: oklch(0.875 0.110 78);
  --color-accent-2-400: oklch(0.820 0.140 78);
  --color-accent-2-500: oklch(0.780 0.160 78);
  /* The gold ramp stops at 500 on purpose — the dark steps read brown and are
     not part of this system. Gold is a light fill only; labels on it are ink. */

  /* Bakbak One ships one weight only — 400 IS the heavy weight. Every rule
     that sets the heading face takes its weight from this token, and
     font-synthesis-weight is off so nothing ever fakes a bolder cut. */
  --font-heading: "Bakbak One", system-ui, sans-serif;
  --font-heading-weight: 400;
  --font-body: "Figtree", system-ui, sans-serif;

  --space-1: 4px;
  --space-2: 8px;
  --space-3: 12px;
  --space-4: 16px;
  --space-6: 24px;
  --space-8: 32px;

  --radius-sm: 3px;
  --radius-md: 4px;
  --radius-lg: 10px;

  /* The inset ring — the system's signature edge, taken from the brand tile.
     Every surface that holds content draws a hairline just inside its border. */
  --ring-inset: 4px;
  --ring-color: var(--color-accent-300);

  /* Motion — one duration and one curve for every interactive state change. */
  --motion: 0.18s cubic-bezier(0.2, 0.7, 0.3, 1); /* @kind other */

  /* Elevation — soft, green-tinted ink shadows tuned to the warm ground. */
  --shadow-sm: 0 1px 2px oklch(0.270 0.030 158 / 0.08);
  --shadow-md: 0 4px 14px oklch(0.270 0.030 158 / 0.10);
  --shadow-lg: 0 16px 40px oklch(0.270 0.030 158 / 0.14);

  /* Icon system — one stroke weight across the whole set. */
  --icon-stroke: 2.5;
}

/* ══════════════════════════════════════════════════════════════════════════
   Base
   ══════════════════════════════════════════════════════════════════════ */

*, *::before, *::after { box-sizing: border-box; }
body {
  margin: 0; background: var(--color-bg); color: var(--color-text);
  font-family: var(--font-body); font-size: 16px; line-height: 1.6; font-weight: 400;
  font-synthesis-weight: none; -webkit-font-smoothing: antialiased;
}
h1, h2, h3, h4, h5, h6 {
  font-family: var(--font-heading); font-weight: var(--font-heading-weight);
  line-height: 1.06; letter-spacing: -0.008em; margin: 0 0 var(--space-3);
  text-wrap: balance;
}
h1 { font-size: 50px; }
h2 { font-size: 36px; }
h3 { font-size: 26px; }
h4 { font-size: 20px; }
h5 { font-size: 17px; letter-spacing: 0; }
h6 { font-size: 12px; letter-spacing: 0.1em; text-transform: uppercase; font-weight: var(--font-heading-weight); }
p { margin: 0 0 var(--space-3); text-wrap: pretty; }
a { color: var(--color-accent-700); text-underline-offset: 3px; }
a:hover { color: var(--color-accent); }
img { display: block; max-width: 100%; }
figure { margin: 0; }
figcaption { font-size: 12px; margin-top: var(--space-2); color: var(--color-neutral-700); }
.text-muted { color: var(--color-neutral-700); }
:focus { outline: none; }
:focus-visible { outline: 2px solid var(--color-accent); outline-offset: 2px; }
::selection { background: var(--color-accent-200); }

/* — the eyebrow: the small green label above a headline —
   Base is plain; three marks and a centring modifier configure it. */
.eyebrow {
  display: block; font-family: var(--font-heading); font-weight: var(--font-heading-weight);
  font-size: 12px; letter-spacing: 0.14em; text-transform: uppercase;
  color: var(--color-accent-700); margin-bottom: var(--space-2);
}
/* A 20px green rule leads the label — the section-opening default. */
.eyebrow-rule { display: flex; align-items: center; gap: 10px; }
.eyebrow-rule::before {
  content: ''; flex: none; width: 20px; height: 2px;
  background: var(--color-accent); border-radius: 1px;
}
/* A 6px square instead of the rule — quieter, for cards and asides. */
.eyebrow-dot { display: flex; align-items: center; gap: 8px; }
.eyebrow-dot::before {
  content: ''; flex: none; width: 6px; height: 6px;
  background: var(--color-accent); border-radius: 1px;
}
/* A tinted chip — for an eyebrow that carries a number or a claim. */
.eyebrow-boxed {
  display: inline-flex; align-items: center; padding: 5px 10px;
  background: var(--color-accent-100); color: var(--color-accent-800);
  border-radius: var(--radius-sm);
}
/* A 2px rule beneath the label instead of beside it — the mark used on the
   photo card, where a leading rule would collide with the frame edge. */
.eyebrow-ruled {
  display: inline-block; white-space: nowrap;
  padding-bottom: 5px; border-bottom: 2px solid var(--color-accent);
}
.eyebrow-center { justify-content: center; text-align: center; margin-inline: auto; }

/* — rules — */
.hr { height: 1px; border: 0; margin: var(--space-6) 0; background: var(--color-divider); }

/* ══════════════════════════════════════════════════════════════════════════
   The ring — the system's signature edge
   ══════════════════════════════════════════════════════════════════════ */

/* A hairline drawn --ring-inset inside the element's own border, exactly like
   the ring inside the brand tile. Apply to any surface that holds content:
   cards, dialogs, media frames, callouts. */
.ring {
  border: 1px solid var(--color-neutral-300);
  box-shadow: inset 0 0 0 var(--ring-inset) var(--color-surface),
              inset 0 0 0 calc(var(--ring-inset) + 1px) var(--ring-color);
}
.ring-accent { --ring-color: var(--color-accent-400); }
.on-dark .ring, .ring-dark {
  border-color: oklch(1 0 0 / 0.24);
  box-shadow: inset 0 0 0 var(--ring-inset) transparent,
              inset 0 0 0 calc(var(--ring-inset) + 1px) oklch(1 0 0 / 0.24);
}

/* ══════════════════════════════════════════════════════════════════════════
   Brand — the tile lockup
   ══════════════════════════════════════════════════════════════════════ */

/* The mark: a green tile with the ring inside it and a single Phosphor glyph.
   Everything scales from font-size, so one class covers every size. */
.brand-tile {
  display: grid; place-items: center; flex: none;
  width: 1.62em; height: 1.62em; border-radius: 0.18em;
  background: var(--color-accent); color: #fff;
  box-shadow: inset 0 0 0 0.058em var(--color-accent),
              inset 0 0 0 0.103em oklch(1 0 0 / 0.85);
}
.brand-tile > svg { width: 62%; height: 62%; display: block; }

/* The lockup: tile + name in the heading face, caps, wide-tracked. */
.wordmark {
  display: inline-flex; align-items: center; gap: 0.34em;
  font-size: 30px; line-height: 1; white-space: nowrap;
  color: var(--color-text); text-decoration: none;
}
.wordmark b {
  font-family: var(--font-heading); font-weight: var(--font-heading-weight); font-size: 1em;
  text-transform: uppercase; letter-spacing: 0.14em; margin-right: -0.14em;
}
.wordmark:hover { color: var(--color-text); }
/* Stacked lockup — for square avatars, yard signs, shirt backs. */
.wordmark-stack { flex-direction: column; gap: 0.24em; }

/* On any dark ground the tile inverts to white with a green glyph — it never
   stays green on ink. */
.on-dark { color: #fff; }
.on-dark .wordmark, .wordmark-reversed { color: #fff; }
.on-dark .brand-tile, .wordmark-reversed .brand-tile {
  background: #fff; color: var(--color-accent);
  box-shadow: inset 0 0 0 0.058em #fff,
              inset 0 0 0 0.103em oklch(0.55 0.145 152 / 0.85);
}
/* The lighter alternative on a green ground: white rule, white glyph. */
.brand-tile-outline {
  background: transparent; color: #fff;
  box-shadow: inset 0 0 0 0.055em oklch(1 0 0 / 0.9);
}

/* ══════════════════════════════════════════════════════════════════════════
   Icons — Lucide for interface, Phosphor for plants and landscape
   ══════════════════════════════════════════════════════════════════════ */

.icon { display: block; width: 20px; height: 20px; flex: none; }
.icon-lg { width: 24px; height: 24px; }

/* The tinted tile an icon sits in when it heads a card or a list row —
   the brand tile's smaller sibling, ring included. */
.icon-tile {
  display: grid; place-items: center; flex: none;
  width: 44px; height: 44px; border-radius: 8px;
  background: var(--color-accent-100); color: var(--color-accent-700);
  box-shadow: inset 0 0 0 3px var(--color-accent-100),
              inset 0 0 0 4px var(--color-accent-300);
}
.icon-tile-solid {
  background: var(--color-accent); color: #fff;
  box-shadow: inset 0 0 0 3px var(--color-accent),
              inset 0 0 0 4px oklch(1 0 0 / 0.75);
}
.icon-tile-gold {
  background: var(--color-accent-2-200); color: var(--color-text);
  box-shadow: none;
}
.icon-tile-lg { width: 56px; height: 56px; border-radius: 10px; }

/* ══════════════════════════════════════════════════════════════════════════
   Buttons — centered labels, heading face, conversion-weight
   ══════════════════════════════════════════════════════════════════════ */

.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 9px;
  cursor: pointer; text-decoration: none; white-space: nowrap;
  font-family: var(--font-heading); font-weight: var(--font-heading-weight); letter-spacing: 0.01em;
  font-size: 15px; line-height: 1.2; color: var(--color-text);
  background: transparent; border: 1.5px solid transparent;
  padding: 12px var(--space-6); border-radius: var(--radius-md);
  transition: background-color var(--motion), color var(--motion), border-color var(--motion);
}
.btn svg { display: block; }
/* One gesture for every button in the system: a trailing icon slides 3px on
   hover. Nothing lifts, scales or grows a shadow. */
.btn svg:last-child { transition: transform var(--motion); }
.btn:hover svg:last-child { transform: translateX(3px); }
.btn:disabled { opacity: 0.45; cursor: not-allowed; }
/* THE WIPE — every button in the system, no exceptions. A field slides in
   from the left over 280ms while the label changes colour. Green fills wipe
   WHITE and the label turns green (the invert); light and outlined buttons
   wipe one step deeper in their own tint. Nothing that was not green at rest
   ever fills green, and nothing lifts, scales or grows a shadow. A variant
   opts in by giving its ::before a background and setting its hover label
   colour — that is the whole contract. */
.btn { position: relative; overflow: hidden; isolation: isolate; }
.btn::before {
  content: ''; position: absolute; inset: 0; z-index: -1; background: var(--color-accent-100);
  transform: scaleX(0); transform-origin: left;
  transition: transform 0.28s cubic-bezier(0.4, 0, 0.2, 1), background-color var(--motion);
}
.btn:hover::before, .btn:active::before { transform: scaleX(1); }
.btn:disabled::before, .btn[aria-disabled='true']::before { transform: scaleX(0); }

.btn-primary { background: var(--color-accent); border-color: var(--color-accent); color: #fff; }
.btn-primary::before { background: #fff; }
.btn-primary:hover { color: var(--color-accent-700); }
.btn-primary:active { color: var(--color-accent-800); }
.btn-primary:active::before { background: var(--color-accent-100); }
/* The second action — all drawn from the green ramp and the neutrals. */
.btn-deep { background: var(--color-accent-800); border-color: var(--color-accent-800); color: #fff; }
.btn-deep::before { background: #fff; }
.btn-deep:hover { color: var(--color-accent-800); }
.btn-deep:active::before { background: var(--color-accent-100); }
.btn-soft { background: var(--color-accent-100); color: var(--color-accent-800); }
.btn-soft::before { background: var(--color-accent-300); }
.btn-soft:active::before { background: var(--color-accent-400); }
.btn-outline-accent { border-color: var(--color-accent); color: var(--color-accent-700); }
.btn-outline-accent::before { background: var(--color-accent-200); }
.btn-outline-accent:hover { color: var(--color-accent-800); }
.btn-outline-accent:active { color: var(--color-accent-800); }
.btn-outline-accent:active::before { background: var(--color-accent-300); }
/* The primary on a green or ink ground. It wipes the deep green of the ramp,
   not a tint — a pale field on a white button is invisible. */
.btn-white { background: #fff; color: var(--color-accent-700); }
.btn-white::before { background: var(--color-accent-800); }
.btn-white:hover, .btn-white:active { color: #fff; }
.btn-white:active::before { background: var(--color-accent-900); }
.btn-secondary { border-color: var(--color-neutral-400); }
.btn-secondary::before { background: var(--color-accent-200); }
.btn-secondary:hover { border-color: var(--color-accent); color: var(--color-accent-800); }
.btn-secondary:active { border-color: var(--color-accent); color: var(--color-accent-800); }
.btn-secondary:active::before { background: var(--color-accent-300); }
.btn-ghost { color: var(--color-accent-700); padding-inline: var(--space-3); }
.btn-ghost::before { background: var(--color-accent-200); }
.btn-ghost:hover { color: var(--color-accent-800); }
.btn-ghost:active { color: var(--color-accent-800); }
.btn-ghost:active::before { background: var(--color-accent-300); }
.btn-icon { width: 42px; height: 42px; padding: 0; }
.btn-icon:hover svg:last-child { transform: none; }
.btn-lg { font-size: 18px; padding: 16px 28px; }
.btn-sm { font-size: 13px; padding: 8px var(--space-4); }
.btn-block { width: 100%; }
/* On a green or ink section the outlined button wipes white, not tinted. */
.on-dark .btn-secondary, .photo-hero-body .btn-secondary { border-color: oklch(1 0 0 / 0.5); color: #fff; }
.on-dark .btn-secondary::before, .photo-hero-body .btn-secondary::before { background: #fff; }
.on-dark .btn-secondary:hover, .photo-hero-body .btn-secondary:hover { border-color: #fff; color: var(--color-accent-800); }
.on-dark .btn-secondary:active, .photo-hero-body .btn-secondary:active { color: var(--color-accent-800); }
.on-dark .btn-secondary:active::before, .photo-hero-body .btn-secondary:active::before { background: var(--color-accent-100); }


/* ── Button motion ── the trailing-icon slide above is the system default;
   the classes below are alternates for one-off emphasis, not for mixing
   several gestures on one page. ────────────────────────────────────── */
.btn-arrow svg { transition: transform var(--motion); }
.btn-arrow:hover svg { transform: translateX(4px); }

.btn-reveal svg {
  width: 0; opacity: 0; transform: translateX(-6px);
  transition: width 0.2s cubic-bezier(0.4, 0, 0.2, 1), opacity 0.2s ease, transform 0.2s cubic-bezier(0.4, 0, 0.2, 1);
}
.btn-reveal:hover svg { width: 20px; opacity: 1; transform: translateX(0); }

.btn-swap .swap { position: relative; display: grid; place-items: center; width: 20px; height: 20px; overflow: hidden; flex: none; }
.btn-swap .swap svg { position: absolute; transition: transform 0.26s cubic-bezier(0.4, 0, 0.2, 1); }
.btn-swap .swap svg:last-child { transform: translateX(-150%); }
.btn-swap:hover .swap svg:first-child { transform: translateX(150%); }
.btn-swap:hover .swap svg:last-child { transform: translateX(0); }

.btn-underline { position: relative; }
.btn-underline::after {
  content: ''; position: absolute; left: var(--space-2); right: var(--space-2); bottom: 7px;
  height: 2px; background: currentColor;
  transform: scaleX(0); transform-origin: left;
  transition: transform 0.22s cubic-bezier(0.4, 0, 0.2, 1);
}
.btn-underline:hover::after { transform: scaleX(1); }

@media (prefers-reduced-motion: reduce) {
  .btn, .btn::before, .btn::after, .btn svg { transition-duration: 0.01s !important; }
  .btn:hover svg:last-child, .btn-arrow:hover svg, .btn-swap:hover .swap svg { transform: none; }
  .btn-reveal svg { width: 20px; opacity: 1; transform: none; }
}

/* ══════════════════════════════════════════════════════════════════════════
   Forms
   ══════════════════════════════════════════════════════════════════════ */

.field > label {
  display: block; font-size: 13px; font-weight: 600; margin-bottom: 6px;
  color: var(--color-neutral-700);
}
.input {
  width: 100%; min-height: 44px; padding: 10px 12px; font: inherit;
  font-size: 15px; color: var(--color-text); caret-color: var(--color-accent);
  background: var(--color-surface);
  border: 1.5px solid var(--color-divider); border-radius: var(--radius-md);
}
.input::placeholder { color: var(--color-neutral-500); }
.input:hover { border-color: var(--color-neutral-400); }
.input:focus-visible { border-color: var(--color-accent); outline-offset: 0; outline-color: var(--color-accent-300); }
textarea.input { min-height: 104px; resize: vertical; }
.radio { display: inline-flex; align-items: center; gap: 8px; cursor: pointer; font-size: 15px; }
.radio input, .seg-opt input { position: absolute; opacity: 0; width: 0; height: 0; pointer-events: none; }
.radio .dot { width: 18px; height: 18px; flex: none; border-radius: 50%; border: 1.5px solid var(--color-neutral-400); }
.radio:hover .dot { border-color: var(--color-accent); }
.radio input:checked + .dot {
  border-color: var(--color-accent); background: var(--color-accent);
  box-shadow: inset 0 0 0 4px var(--color-surface);
}
.radio input:focus-visible + .dot { outline: 2px solid var(--color-accent); outline-offset: 2px; }
.seg {
  display: inline-flex; overflow: hidden; background: var(--color-surface);
  border: 1.5px solid var(--color-divider); border-radius: var(--radius-md);
}
.seg-opt {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 9px 14px; font-size: 14px; font-weight: 600; cursor: pointer;
}
.seg-opt + .seg-opt { border-left: 1.5px solid var(--color-divider); }
.seg-opt:has(input:checked) { background: var(--color-accent); color: #fff; }
.seg-opt:not(:has(input:checked)):hover { background: var(--color-accent-100); }
.seg-opt:has(input:focus-visible) { outline: 2px solid var(--color-accent); outline-offset: -2px; }

/* ══════════════════════════════════════════════════════════════════════════
   Cards — the ring pattern, one clear hierarchy
   ══════════════════════════════════════════════════════════════════════ */

.card {
  display: flex; flex-direction: column;
  gap: var(--space-3); padding: var(--space-6);
  border-radius: var(--radius-lg); background: var(--color-surface);
  border: 1px solid var(--color-neutral-300);
  transition: border-color var(--motion), box-shadow var(--motion);
}
.card > .icon-tile { margin-bottom: var(--space-1); }
/* A photograph at the head of a card, bleeding to its border. */
.card-media {
  overflow: hidden;
  margin: calc(var(--space-6) * -1) calc(var(--space-6) * -1) var(--space-2);
  border-radius: calc(var(--radius-lg) - 1px) calc(var(--radius-lg) - 1px) 0 0;
  background: var(--color-neutral-200);
}
.card-media img { width: 100%; display: block; aspect-ratio: 16 / 9; object-fit: cover; }
.card-media-square img { aspect-ratio: 1; }
.card-kicker {
  font-family: var(--font-heading); font-size: 11px; font-weight: var(--font-heading-weight);
  letter-spacing: 0.12em; text-transform: uppercase;
  color: var(--color-accent-700); margin: 0;
}
.card-title {
  font-family: var(--font-heading); font-weight: var(--font-heading-weight); letter-spacing: -0.005em;
  font-size: 21px; line-height: 1.2; margin: 0; color: var(--color-text);
  text-wrap: pretty;
}
.card-body { margin: 0; font-size: 15px; color: var(--color-neutral-700); }
.card-meta {
  display: flex; align-items: center; gap: var(--space-2);
  width: 100%; margin-top: auto; padding-top: var(--space-3);
  border-top: 1px solid var(--color-neutral-200);
  font-family: var(--font-heading); font-size: 12px; font-weight: var(--font-heading-weight);
  letter-spacing: 0.06em; text-transform: uppercase; color: var(--color-accent-700);
}
/* The services voice: green title, as on the marketing pages. */
.card-service .card-title { color: var(--color-accent-700); }
/* An interactive card lights its ring rather than lifting. */
a.card, .card-link { text-decoration: none; color: inherit; cursor: pointer; }
a.card:hover, .card-link:hover { border-color: var(--color-accent); }
a.card:focus-visible, .card-link:focus-visible { outline: 2px solid var(--color-accent); outline-offset: 2px; }
/* The one card that carries a full green field. */
/* The green card is the one that carries the ring — it is the brand tile at
   card scale, so it keeps the inset edge the white cards give up. */
.card-filled {
  background: var(--color-accent); color: #fff; border-color: transparent;
  padding: var(--space-8) var(--space-6) var(--space-6);
  box-shadow: inset 0 0 0 var(--ring-inset) var(--color-accent),
              inset 0 0 0 calc(var(--ring-inset) + 1px) oklch(1 0 0 / 0.45);
}
.card-filled .card-title, .card-filled .card-body { color: #fff; }
.card-filled .card-kicker { color: var(--color-accent-200); }
.card-filled .card-meta { color: #fff; border-top-color: oklch(1 0 0 / 0.3); }
.elev-sm { box-shadow: var(--shadow-sm); }
.elev-md { box-shadow: var(--shadow-md); }
.elev-lg { box-shadow: var(--shadow-lg); }

/* ══════════════════════════════════════════════════════════════════════════
   Tags
   ══════════════════════════════════════════════════════════════════════ */

.tag {
  display: inline-flex; align-items: center; gap: 5px; font-size: 12px;
  font-weight: 600; letter-spacing: 0.01em; padding: 4px 10px;
  border-radius: var(--radius-sm);
}
.tag-accent { background: var(--color-accent-100); color: var(--color-accent-800); }
.tag-accent-2 { background: var(--color-accent-2-200); color: var(--color-text); }
.tag-neutral { background: var(--color-neutral-200); color: var(--color-neutral-800); }
.tag-outline { border: 1.5px solid var(--color-accent-300); color: var(--color-accent-700); }

/* ══════════════════════════════════════════════════════════════════════════
   Navigation
   ══════════════════════════════════════════════════════════════════════ */

.nav {
  display: flex; align-items: center; gap: var(--space-6);
  padding: var(--space-3) var(--space-6);
  background: var(--color-surface);
  border-bottom: 1px solid var(--color-neutral-200);
}
.nav-brand { margin-right: auto; }
.nav a:not(.btn) { color: inherit; text-decoration: none; font-size: 15px; font-weight: 600; }
.nav a:not(.btn):hover, .nav a:not(.btn)[aria-current='page'] { color: var(--color-accent); }
.on-dark .nav { background: transparent; border-bottom-color: oklch(1 0 0 / 0.2); }

/* ══════════════════════════════════════════════════════════════════════════
   Tables
   ══════════════════════════════════════════════════════════════════════ */

.table { width: 100%; border-collapse: collapse; font-size: 15px; }
.table th {
  text-align: left; font-family: var(--font-heading); font-size: 11px; font-weight: var(--font-heading-weight);
  letter-spacing: 0.1em; text-transform: uppercase; color: var(--color-neutral-700);
  padding: var(--space-3) var(--space-2); border-bottom: 1.5px solid var(--color-divider);
}
.table td { padding: var(--space-3) var(--space-2); border-bottom: 1px solid var(--color-neutral-200); }
.table tbody tr:hover { background: var(--color-accent-100); }

/* ══════════════════════════════════════════════════════════════════════════
   Dialog
   ══════════════════════════════════════════════════════════════════════ */

.dialog-backdrop {
  position: fixed; inset: 0; display: grid; place-items: center;
  padding: var(--space-4);
  background: oklch(0.255 0.024 158 / 0.55);
}
.dialog {
  width: min(460px, 100%); display: flex; flex-direction: column; gap: var(--space-3);
  padding: var(--space-8) var(--space-6) var(--space-6); border-radius: var(--radius-lg);
  background: var(--color-surface); border: 1px solid var(--color-neutral-300);
  box-shadow: var(--shadow-lg);
}
.dialog-title { font-family: var(--font-heading); font-weight: var(--font-heading-weight); letter-spacing: -0.008em; font-size: 23px; }
.dialog-body { font-size: 15px; color: var(--color-neutral-700); }
.dialog-actions { display: flex; justify-content: flex-end; gap: var(--space-2); margin-top: var(--space-2); }

/* ══════════════════════════════════════════════════════════════════════════
   Imagery — full color, ring frame, never tinted
   ══════════════════════════════════════════════════════════════════════ */

.photo {
  display: block; overflow: hidden; position: relative;
  border-radius: var(--radius-lg); background: var(--color-neutral-200);
}
.photo img { width: 100%; height: 100%; object-fit: cover; }
/* The ring reads over a photograph as a white keyline. */
.photo::after {
  content: ''; position: absolute; inset: 0; pointer-events: none;
  border-radius: inherit;
  box-shadow: inset 0 0 0 var(--ring-inset) oklch(1 0 0 / 0.9),
              inset 0 0 0 calc(var(--ring-inset) + 1px) oklch(1 0 0 / 0.55);
}
.photo-plain::after { display: none; }
.photo-square { aspect-ratio: 1; }
.photo-wide { aspect-ratio: 16 / 9; }

/* The hero variant — copy and CTAs sit ON the photograph. The scrim is the
   one overlay the system permits, and only under type: it is neutral ink,
   never a green wash, and it is heaviest at the edge the copy is anchored to
   so the far side of the frame stays a real photograph. */
.photo-hero { display: grid; align-items: end; min-height: 460px; }
.photo-hero > img { position: absolute; inset: 0; }
.photo-hero::before {
  content: ''; position: absolute; inset: 0; z-index: 1; pointer-events: none;
  background: linear-gradient(90deg, oklch(0.19 0.022 158 / 0.88) 0%, oklch(0.19 0.022 158 / 0.72) 38%, oklch(0.19 0.022 158 / 0.18) 100%);
}
.photo-hero-body {
  position: relative; z-index: 2; max-width: 34em;
  padding: clamp(28px, 5%, 56px);
}
.photo-hero-body > h1, .photo-hero-body > h2, .photo-hero-body > h3 { color: #fff; }
.photo-hero-body > p { color: oklch(1 0 0 / 0.88); }
.photo-hero-actions { display: flex; flex-wrap: wrap; gap: var(--space-3); margin-top: var(--space-6); }
/* Anchor the copy to the bottom-centre instead: the scrim flips to vertical. */
.photo-hero-center { justify-items: center; text-align: center; }
.photo-hero-center::before { background: linear-gradient(0deg, oklch(0.19 0.022 158 / 0.9) 0%, oklch(0.19 0.022 158 / 0.55) 45%, oklch(0.19 0.022 158 / 0.15) 100%); }
.photo-hero-center .photo-hero-actions { justify-content: center; }

/* ══════════════════════════════════════════════════════════════════════════
   Dark sections
   ══════════════════════════════════════════════════════════════════════ */

.on-dark { background: var(--color-text); }
.on-dark h1, .on-dark h2, .on-dark h3, .on-dark h4, .on-dark h5, .on-dark h6 { color: #fff; }
.on-dark .text-muted { color: oklch(1 0 0 / 0.7); }
.on-dark .eyebrow { color: var(--color-accent-300); }
.on-dark .eyebrow-rule::before, .on-dark .eyebrow-dot::before { background: var(--color-accent-300); }
.on-dark .eyebrow-boxed { background: oklch(1 0 0 / 0.14); color: #fff; }
.on-dark-green .eyebrow { color: #fff; }
.on-dark-green .eyebrow-rule::before, .on-dark-green .eyebrow-dot::before { background: #fff; }
.on-dark-green .eyebrow-boxed { background: oklch(1 0 0 / 0.18); color: #fff; }
.on-dark a:not(.btn) { color: var(--color-accent-300); }
.on-dark a:not(.btn):hover { color: #fff; }
.on-dark-green { background: var(--color-accent); }

/* ══════════════════════════════════════════════════════════════════════════
   Photo cards — the card-scale version of the photo hero
   A photograph fills the frame, a scrim carries the copy, and the image
   moves on hover: a slow Ken Burns push, never a snap. The image is the
   only thing that scales — the frame, the copy and the button hold still,
   so nothing reflows and text stays crisp.
   ══════════════════════════════════════════════════════════════════════ */

.photo-grid {
  display: grid; gap: var(--space-4);
  grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
}
/* Spans, not ratios: a card that takes two tracks, or the whole row. */
.photo-card-wide { grid-column: span 2; }
.photo-card-full { grid-column: 1 / -1; }
@media (max-width: 720px) { .photo-card-wide { grid-column: span 1; } }

.photo-card {
  position: relative; display: grid; align-items: end; isolation: isolate;
  overflow: hidden; border-radius: var(--radius-lg);
  aspect-ratio: 4 / 3; background: var(--color-neutral-300);
  text-decoration: none; color: inherit;
}
.photo-card > img {
  position: absolute; inset: 0; z-index: 0;
  width: 100%; height: 100%; object-fit: cover;
  transform: scale(1.001); transform-origin: 50% 50%;
  transition: transform 3000ms cubic-bezier(0.22, 0.61, 0.36, 1);
  will-change: transform;
}
/* The scrim is measured in pixels from the edge the copy is anchored to, not
   in percentages: the copy block is a fixed height, so a percentage stop that
   covers it on a 380px card lands well above it on a 660px one. */
.photo-card::before {
  content: ''; position: absolute; inset: 0; z-index: 1; pointer-events: none;
  background: linear-gradient(to top, oklch(0.19 0.022 158 / 0.94) 0, oklch(0.19 0.022 158 / 0.88) 140px, oklch(0.19 0.022 158 / 0.5) 200px, oklch(0.19 0.022 158 / 0) 290px);
  transition: opacity var(--motion);
}
.photo-card:focus-visible { outline: 2px solid var(--color-accent); outline-offset: 3px; }

/* — the hold: how the copy sits on the photograph —
   The split is the default for a service or category card: the label is
   pinned to the top edge and the headline and copy to the bottom, so the
   card reads as a poster rather than a captioned thumbnail. Everything is
   visible at rest — the hover moves the photograph and nothing else. */
.photo-card-split::before { background: linear-gradient(to bottom, oklch(0.19 0.022 158 / 0.84) 0, oklch(0.19 0.022 158 / 0.48) 92px, oklch(0.19 0.022 158 / 0) 180px), linear-gradient(to top, oklch(0.19 0.022 158 / 0.94) 0, oklch(0.19 0.022 158 / 0.88) 140px, oklch(0.19 0.022 158 / 0.5) 200px, oklch(0.19 0.022 158 / 0) 290px); }
.photo-card-split .photo-card-body { height: 100%; align-content: space-between; gap: var(--space-6); }
.photo-card-top, .photo-card-foot { display: grid; gap: var(--space-2); justify-items: start; }

.photo-card-body {
  position: relative; z-index: 2;
  display: grid; gap: var(--space-2); justify-items: start;
  padding: var(--space-6);
}
.photo-card-title {
  font-family: var(--font-heading); font-weight: var(--font-heading-weight);
  letter-spacing: -0.01em; line-height: 1.12;
  font-size: clamp(22px, 2.4vw, 30px); margin: 0; color: #fff; text-wrap: balance;
}
/* Descendant, not direct child — the copy is wrapped in .photo-card-foot on a
   split card, and it has to stay white wherever it sits. */
.photo-card-body p {
  margin: 0; font-size: 14px; line-height: 1.5;
  color: oklch(1 0 0 / 0.86); max-width: 52ch;
}
/* A place and a date under a gallery title — quieter than the label. */
.photo-card-place {
  font-family: var(--font-heading); font-size: 11px; font-weight: var(--font-heading-weight);
  letter-spacing: 0.12em; text-transform: uppercase; color: oklch(1 0 0 / 0.74);
}
.photo-card-body .eyebrow { color: var(--color-accent-300); }
.photo-card-body .eyebrow-rule::before, .photo-card-body .eyebrow-dot::before { background: var(--color-accent-300); }
.photo-card-body .eyebrow-boxed { background: oklch(1 0 0 / 0.16); color: #fff; }
.photo-card-body .eyebrow-ruled { border-bottom-color: var(--color-accent-300); }

/* — the camera moves —
   One of these is the site's move, chosen once and applied everywhere, the
   way the wipe is every button's move. All of them transform the image only,
   on one curve, 1.5s out and 3s back: slower home reads as a settle. The
   only per-card variable is --dir (1 or -1) on a track or a tilt.
   The base .photo-card push is the default; each modifier replaces it. */
.photo-card:hover > img, .photo-card:focus-visible > img {
  transform: scale(1.09); transition-duration: 1500ms;
}
/* Pull back — rests overscaled, settles to true size. Hovering widens the
   view, which is the right instinct for landscape work. */
.photo-card-pull > img { transform: scale(1.12); }
.photo-card-pull:hover > img, .photo-card-pull:focus-visible > img { transform: scale(1.001); transition-duration: 1500ms; }
/* Track — no scale change at all: the frame slides across the photograph. */
.photo-card-track > img { transform: scale(1.1) translateX(calc(-2.6% * var(--dir, 1))); }
.photo-card-track:hover > img, .photo-card-track:focus-visible > img { transform: scale(1.1) translateX(calc(2.6% * var(--dir, 1))); transition-duration: 1500ms; }
/* Tilt — the same slide, vertical. Reads as the eye travelling up a grade. */
.photo-card-tilt > img { transform: scale(1.1) translateY(calc(-2.4% * var(--dir, 1))); }
.photo-card-tilt:hover > img, .photo-card-tilt:focus-visible > img { transform: scale(1.1) translateY(calc(2.4% * var(--dir, 1))); transition-duration: 1500ms; }
/* Focus pull — soft to sharp, with almost no movement. */
.photo-card-focus > img {
  transform: scale(1.06); filter: blur(4px) brightness(0.94);
  transition: transform 3000ms cubic-bezier(0.22, 0.61, 0.36, 1), filter 900ms ease;
}
.photo-card-focus:hover > img, .photo-card-focus:focus-visible > img {
  transform: scale(1.02); filter: blur(0) brightness(1); transition-duration: 1500ms, 700ms;
}
/* Crossfade — a second photograph of the same job takes over. */
.photo-card-fade > img { transform: scale(1.03); }
.photo-card-fade:hover > img, .photo-card-fade:focus-visible > img { transform: scale(1.03); }
.photo-card-swap {
  position: absolute; inset: 0; z-index: 1;
  width: 100%; height: 100%; object-fit: cover;
  opacity: 0; transition: opacity 900ms ease;
}
.photo-card-fade:hover .photo-card-swap, .photo-card-fade:focus-visible .photo-card-swap { opacity: 1; }

/* Aspect presets — the grid reads as a set only when a row shares one ratio. */
.photo-card-tall { aspect-ratio: 3 / 4; }
.photo-card-wide-ratio { aspect-ratio: 16 / 9; }
.photo-card-square { aspect-ratio: 1; }

@media (prefers-reduced-motion: reduce) {
  .photo-card > img { transition: none; }
  .photo-card-focus > img { filter: none; }
  /* Neutralise the resting transform as well as the hover one — overriding only
     the hover leaves a jump from the modifier's rest state with no transition,
     which is worse than the move being suppressed. The crossfade keeps its
     900ms fade: an opacity change is not motion. */
  .photo-card > img, .photo-card:hover > img, .photo-card:focus-visible > img,
  .photo-card-pull > img, .photo-card-pull:hover > img, .photo-card-pull:focus-visible > img,
  .photo-card-track > img, .photo-card-track:hover > img, .photo-card-track:focus-visible > img,
  .photo-card-tilt > img, .photo-card-tilt:hover > img, .photo-card-tilt:focus-visible > img,
  .photo-card-focus > img, .photo-card-focus:hover > img, .photo-card-focus:focus-visible > img,
  .photo-card-fade > img, .photo-card-fade:hover > img { transform: scale(1.03); }
}


/* The hero body is a dark ground without painting one: it borrows the on-dark
   treatments over the scrim, but never the on-dark background. */
.photo-hero-body .eyebrow { color: var(--color-accent-300); }
.photo-hero-body .eyebrow-rule::before, .photo-hero-body .eyebrow-dot::before { background: var(--color-accent-300); }
.photo-hero-body .eyebrow-boxed { background: oklch(1 0 0 / 0.16); color: #fff; }
.photo-hero-body a:not(.btn) { color: var(--color-accent-300); }
.photo-hero-body a:not(.btn):hover { color: #fff; }
