/* ==========================================================================
   Herrod Landscapes — site.css
   Reference frame: 1920px. All values measured from artboards (see brief).
   ========================================================================== */

/* -------------------------------------------------------------------------
   0. Reset
   ------------------------------------------------------------------------- */
*, *::before, *::after { box-sizing: border-box; }
html, body, h1, h2, h3, h4, p, figure, ul, ol, li { margin: 0; padding: 0; }
ul, ol { list-style: none; }
a { color: inherit; text-decoration: none; }
img { display: block; max-width: 100%; }
button { font: inherit; color: inherit; background: none; border: 0; padding: 0; margin: 0; cursor: pointer; appearance: none; -webkit-appearance: none; }
html { -webkit-text-size-adjust: 100%; }
body { overflow-x: hidden; }

/* -------------------------------------------------------------------------
   1. Tokens
   ------------------------------------------------------------------------- */
:root{
  /* grid */
  --frame: 1920px;
  --margin: 24px;
  --gutter: 18px;
  --cols: 12;

  /* colour */
  --olive: #898555;
  --paper: #FFFFFF;
  --ink: #000000;

  /* fluid type — clamp(min, vw-at-1920, max). min = value at the 1280px
     floor of the fluid (L) range; below 1280 pages define explicit
     breakpoint overrides where the brief specifies them (e.g. Index row
     pitch, mobile nav size).

     Sizes below are derived from Forma DJR Text's MEASURED cap-height
     ratio (0.70 em — via canvas actualBoundingBoxAscent at 200px, both
     400 and 700 weight, see README "Type scale — VERIFIED"), not the
     brief's assumed 0.72. size = target cap-height (brief §3) / 0.70.
     Line-height values are directly measured pitches (46/26px etc.),
     independent of the cap-height ratio, so they are unchanged from the
     Helvetica-fallback pass except where a min is re-derived from a
     changed font-size min below. */
  --fs-logo:      clamp(16.2px, 1.266vw, 24.3px);  /* cap 17px / 0.70 */
  --fs-nav:       clamp(14.3px, 1.115vw, 21.4px);  /* cap 15px / 0.70 */
  --fs-label:     clamp(14.3px, 1.115vw, 21.4px);  /* cap 15px / 0.70 (same target as Nav — see README) */
  --lh-label:     clamp(17px,   1.354vw, 26px);    /* measured pitch, unchanged */
  --fs-display:   clamp(25.7px, 2.010vw, 38.6px);  /* cap 27px / 0.70 */
  /* client feedback: Practice's intro paragraph (.practice-display .t-display)
     currently rides --fs-display's own floor (25.7px) all the way down to
     mobile — client wants it to keep scaling below that, reaching 22.5px
     at their 500px viewport. Scoped to Practice only (--fs-display itself
     stays untouched — Home also uses .t-display). Anchored to join
     --fs-display's floor exactly at the width where that floor is first
     reached (25.7 / 2.010 × 100 = 1278.6px), then descends linearly to
     22.5px at 500px, floored there for anything narrower. */
  --fs-practice-display: clamp(22.5px, calc(0.41109vw + 20.44455px), 25.7px);
  /* client feedback: leading reads too loose against the smaller 22.5px
     text — reduce 5% off --lh-display's own floor (30.7px), same scope as
     the font override above. Flat, not a clamp: --lh-display's floor is
     already reached at ~1281px, so it's effectively flat across this
     entire ≤1279px range regardless. */
  --lh-practice-display: 29.165px;
  --lh-display:   clamp(30.7px, 2.396vw, 46px);    /* measured pitch 46 unchanged; min re-derived at 2/3 */
  --fs-row:       clamp(15.3px, 1.193vw, 22.9px);  /* cap 16px / 0.70 */
  --fs-small:     clamp(15.7px, 1.224vw, 23.5px);  /* client feedback: +17.5% (mid of the requested 15-20% range) over the cap-height-derived 20.0px */
  --lh-small:     clamp(20.3px, 1.589vw, 30.5px);  /* pitch scaled by the same +17.5% — 26px was too tight for the bigger text (natural line height ~30.2px at the new size, would clip) */
  --fs-footer:    clamp(14.3px, 1.115vw, 21.4px);  /* cap 15px / 0.70 */
  /* client feedback: below 1700px, Index table type slows its scaling so
     Outline's existing ellipsis absorbs more of the narrowing instead.
     Three segments, each anchored to the previous so there's no jump:
     1920–1700: unchanged (matches --fs-label).
     1700–1100: client feedback (follow-up) — fully held at the 1700px
       value (18.955px, = 1.115vw × 1700) through to 1100px, so Project/
       Designer/Scale are fully preserved and Outline alone absorbs all the
       narrowing across this whole range.
     below 1100: resumes at --fs-label's own normal rate (1.115vw),
       anchored to 18.955px at 1100px, reaching the 14.3px floor at
       ~682.5px — a touch tighter than the old ~767px mobile breakpoint,
       intentionally, since this range starts from a larger held value.
     Grid/column widths are untouched throughout — this only slows the
     font, matching "gutter narrows as the browser narrows" behaviour the
     client confirmed works. */
  --fs-index-row-mid: 18.955px;
  --fs-index-row-low: clamp(14.3px, calc(1.115vw + 6.69px), 18.955px);
  /* Corrected again — a flat px line-height was the wrong tool: 23.018px
     was a reasonable ~1.21 ratio at the 18.955px "mid" font-size, but the
     SAME flat value against the 14.3px floor ("low" segment / true mobile)
     gave a 1.61 ratio — genuinely too loose, not just a matter of taste.
     Client's own fix: set leading as a proportion of type size (how
     InDesign's auto-leading works), not a fixed px number, so it scales
     correctly at every font-size automatically. Applied as a unitless
     line-height directly on the rules below instead of a variable here —
     unitless is what makes it recompute against whatever font-size is
     active, rather than needing a separate override per breakpoint. */
  --home-practice-soft-return: 17px; /* measured: "Practice" label's own rendered height at ≤985px (its line-height floor — flat 17px across this whole range, confirmed at 985px and below) */

  /* type */
  --font-sans: "forma-djr-text", "Helvetica Neue", Helvetica, Arial, sans-serif;
}

/* Theme hook — body[data-theme="paper"|"olive"] drives inactive-nav colour
   and page ground (see brief §4, [CONFLICT] resolution). */
body[data-theme="paper"]{
  --ground: var(--paper);
  --nav-dim: #BEBEBE;
}
body[data-theme="olive"]{
  --ground: var(--olive);
  --nav-dim: #BEBEBE; /* client feedback: overrides brief §4's documented white-on-olive reading — inactive nav should match Practice/Landscapes' grey exactly, not stay white on Index */
}

body{
  font-family: var(--font-sans);
  color: var(--ink);
  background: var(--ground);
}

/* -------------------------------------------------------------------------
   2. Grid
   ------------------------------------------------------------------------- */
.grid{
  display: grid;
  grid-template-columns: repeat(var(--cols), 1fr);
  column-gap: var(--gutter);
  padding-inline: var(--margin);
  max-width: var(--frame);
  margin-inline: auto;
}

/* column-span utilities — named by inclusive start-end column numbers */
.col-1      { grid-column: 1 / 2; }
.col-1-3    { grid-column: 1 / 4; }
.col-1-4    { grid-column: 1 / 5; }
.col-1-12   { grid-column: 1 / 13; }
.col-4-5    { grid-column: 4 / 6; }
.col-5-8    { grid-column: 5 / 9; }
.col-5-12   { grid-column: 5 / 13; }
.col-6      { grid-column: 6 / 7; }
.col-7-12   { grid-column: 7 / 13; }
.col-9-12   { grid-column: 9 / 13; }

/* -------------------------------------------------------------------------
   3. Type roles
   ------------------------------------------------------------------------- */
.t-logo{
  font-size: var(--fs-logo);
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  line-height: 1;
}
.t-nav{
  font-size: var(--fs-nav);
  font-weight: 500; /* Forma Medium — client correction, was 700 */
  line-height: 1;
}
.t-label{
  font-size: var(--fs-label);
  line-height: var(--lh-label);
  font-weight: 500; /* Forma Medium — client correction (was rendering as 700/Bold), shared Section label role across all pages */
}
.t-display{
  font-size: var(--fs-display);
  line-height: var(--lh-display);
  font-weight: 500; /* Forma Medium — client correction, applies to the shared Display/body role (Home + Practice page) */
}
.t-row{
  font-size: var(--fs-small); /* client feedback: index row text set to match Practice's small-text size exactly, not the brief's separate --fs-row token */
  font-weight: 500; /* Forma Medium — client correction, was 400 */
}
.t-row-head{
  font-size: var(--fs-label); /* client feedback: 22.9063px → 21.4063px, matching Practice */
  line-height: var(--lh-label); /* was unset ("normal") — root cause of the Practice/Index flow-line mismatch the client flagged; every other calibrated role has an explicit line-height, this one was missed */
  font-weight: 500; /* Forma Medium — client correction, was 700 */
}
.t-small{
  font-size: var(--fs-small);
  line-height: var(--lh-small);
  font-weight: 400;
}
.t-footer{
  font-size: var(--fs-footer);
  font-weight: 400;
  line-height: 1;
}

/* -------------------------------------------------------------------------
   4. Header
   ------------------------------------------------------------------------- */
.site-header{
  padding-top: 24px;
  padding-bottom: 24px;
  padding-inline: var(--margin);
  max-width: var(--frame);
  margin-inline: auto;
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
}

.site-header__logo{
  display: flex;
  align-items: center;
  flex-shrink: 0;
}
/* Real wordmark replaces the old plain-text logo — scaled off the same
   --fs-logo token the text used to size itself with (so it keeps the same
   responsive curve rather than a hardcoded px height), then client feedback:
   reduce 30% from that. */
.site-header__logo-full{
  display: block;
  height: calc(var(--fs-logo) * 0.7);
  width: auto;
}
/* Roundel is the ≤767px mark (see the breakpoint below) — hidden by default. */
.site-header__logo-roundel{
  display: none;
}

.site-nav{
  text-align: right;
}
.site-nav__item{
  /* Literal value, not var(--nav-dim) — reproducible browser quirk found
     while implementing this fix: <a> elements would not resolve a color
     that depends on a theme-conditional custom property (span separators
     and the unconditional var(--ink) active-state both resolved fine;
     only this combination — anchor + conditionally-defined variable —
     stuck on a stale value after the stylesheet changed). Sidestepped by
     writing the value directly per theme instead of through --nav-dim. */
  color: #BEBEBE;
  transition: color 150ms ease;
}
.site-nav__item.is-active{ color: var(--ink); }
body[data-theme="olive"] .site-nav__item.is-active{ color: var(--ink); }
/* client feedback, screenshot: the comma right after the active/current-page
   item was still dim grey — only the item text itself was picking up --ink.
   --ink is unconditional (not theme-dependent like --nav-dim), so this is
   safe on every page/theme without the anchor+conditional-variable quirk
   noted above. */
.site-nav__item.is-active + .site-nav__sep{ color: var(--ink); }
/* Home: no nav item is active, and nothing dims — all items (including the
   comma separators) render in --ink. Client feedback: separators were
   inheriting the olive theme's white --nav-dim instead of following suit. */
.site-nav--home .site-nav__item,
.site-nav--home .site-nav__sep{ color: var(--ink); }

.site-nav__item:hover,
.site-nav__item:focus-visible{ color: var(--ink); }
/* Olive-specific hover override (was #FFFFFF) removed: now that resting
   inactive items on Index match Practice/Landscapes' grey, hover should
   converge on --ink the same way it does on those pages too — matches the
   already-black active state on Index, rather than introducing a third
   colour just for hover. */

.site-nav__sep{ color: var(--nav-dim); }

/* -------------------------------------------------------------------------
   5. Footer bar
   ------------------------------------------------------------------------- */
.site-footer{
  padding-top: 24px;
  padding-bottom: 24px;
}
.site-footer__item--1,
.site-footer__item--2,
.site-footer__item--3,
.site-footer__item--4{
  justify-self: start;
  white-space: nowrap;
}
/* client feedback: "Partner Studio mouse over state" on Email/Instagram/
   credit (items 2–4) across every page that shares this footer markup
   (Home/Practice/Landscapes/Index all use the same classes, so one rule
   covers all of them) — "Herrod Landscapes" (item 1) deliberately excluded,
   matching the client's own item lists which never named it. Opacity-only,
   no font-size/weight/colour touched, per "don't change text styles".
   Polarity reversed per follow-up feedback: static black at rest, dims to
   the grey tint on hover/focus (was the other way around). */
.site-footer__item--2,
.site-footer__item--3,
.site-footer__item--4{
  opacity: 1;
  transition: opacity 150ms ease;
}
.site-footer__item--2:hover,
.site-footer__item--2:focus-visible,
.site-footer__item--3:hover,
.site-footer__item--3:focus-visible,
.site-footer__item--4:hover,
.site-footer__item--4:focus-visible{
  opacity: 0.6;
}

/* client feedback (Index page only, see §14 below for the breakpoints):
   three text variants for the credit line — "Design & Code: " at full
   screen (default, this rule hidden), no prefix from 1380px down, "Credit:
   " below 767px. Base state here covers every OTHER page too, where
   neither span's display is ever overridden, so they keep reading
   "Design & Code:" at every width, unchanged. */
.footer-credit-prefix--short{
  display: none;
}

/* client feedback: Contact block email address — same static-black/
   hover-grey interaction as the footer's Email/Instagram/Design & Code
   links above. Shared class rather than page-scoped selectors, since the
   email appears inside differently-structured Contact blocks on Home/
   Landscapes/Index (.contact-block__details) and Practice
   (.practice-blocks, no shared class with the others). */
.contact-email-link{
  opacity: 1;
  transition: opacity 150ms ease;
}
.contact-email-link:hover,
.contact-email-link:focus-visible{
  opacity: 0.6;
}

.site-footer__item--1{ grid-column: 1 / 5; }
.site-footer__item--2{ grid-column: 5 / 7; }
.site-footer__item--3{ grid-column: 7 / 11; }
.site-footer__item--4{
  grid-column: 11 / 13;
  justify-self: end; /* client feedback: site-credit copy set flush to the right margin, not left-aligned at col 11 */
}

.site-footer[data-on="ground"]{ color: var(--ink); }
.site-footer[data-on="image"]{ color: #FFFFFF; }

/* -------------------------------------------------------------------------
   6. Practice page
   ------------------------------------------------------------------------- */
.practice-label{
  padding-top: 160.7px; /* client feedback: label cap-top should sit at 240px from viewport top */
}

.practice-display{
  padding-top: 14.5px; /* recalibrated for Forma DJR Text — target cap-top delta 45px (y305-y260) */
}
.practice-display p{ margin: 0; }
.practice-display p + p{ text-indent: 3ch; }

.practice-blocks{
  padding-top: 120px; /* client feedback: 120px gap from paragraph base to the Contact/Services/Sustainability row */
}
.practice-blocks__label{
  /* client feedback (latest): reverted — Contact/Services/Sustainability
     back to the plain --fs-label size (21.4063px at 1920, matches Home's
     headings, which the client confirmed they like), inherited from
     .t-label's own defaults. The whole block (labels AND body copy) now
     reads at this one size instead of --fs-small. */
  /* client feedback (follow-up): tightened to a flat value, uniformly
     across Contact/Services/Sustainability — "we don't need the padding"
     (i.e. no cap-top-delta calibration here, just the literal value).
     17px, then updated to 18px next round (client confirmed Sustainability
     should match too). Supersedes the earlier Contact-only 33%-reduction
     (18.09px). */
  padding-bottom: 18px;
}
.practice-blocks .t-small{
  /* client feedback: the Contact/Services/Sustainability body copy (Tom
     Herrod details, Consultancy/Delivery/Stewardship/Design + their
     paragraphs, Sustainability paragraphs) scaled down from --fs-small
     (23.5px) to --fs-label (21.4063px) — scoped to this block only, other
     pages' .t-small (Home/Index/Landscapes contact blocks) are unaffected. */
  font-size: var(--fs-label);
  line-height: var(--lh-label);
}

.service-group{ margin-bottom: 24px; } /* client feedback: space above Delivery/Stewardship/Design set to 24px */
.service-group:last-child{ margin-bottom: 0; }
.service-group__title{ font-weight: 500; } /* Forma Medium — client correction, was 700/Bold */

.sustain-para + .sustain-para{ margin-top: 14.2969px; } /* client feedback: treat like a soft return, not a paragraph break */

.practice-foot-spacer{ padding-top: 240px; } /* client feedback: 240px gap from the tallest column's base to the footer */

/* -------------------------------------------------------------------------
   7. Contact block (shared — Home, Practice-global-pattern, Index)
   ------------------------------------------------------------------------- */
.contact-block__label{ padding-bottom: 12px; }
.contact-block__intro{ margin: 0; }
.contact-block__details{ margin: 0; }

/* client feedback: Landscapes' and Index's Contact block — body copy resized
   to match Practice/Index's 21.4063px (was inheriting the 23.5px --fs-small
   default), and the label→intro gap removed entirely. Scoped to these two
   pages' Contact block only — Practice's own Contact block wasn't mentioned
   and is left as it is. Home's label→intro gap is handled by the same rule
   below (also requested, separately, once Home's Contact block was up for
   review) — font-size there stays on the dedicated 24px rule instead. */
.gallery-contact .contact-block__label,
.index-contact .contact-block__label,
.home-contact .contact-block__label{ padding-bottom: 0; }
.gallery-contact .t-small,
.index-contact .t-small{
  font-size: var(--fs-label);
  line-height: var(--lh-label);
}

/* -------------------------------------------------------------------------
   8. Index page (project table)
   ------------------------------------------------------------------------- */
:root{ --row-pitch: 33px; } /* client feedback: +10% over the 30px round-three value, and held CONSTANT across viewports now (see §14 below) — client explicitly okay with "tight at full screen" rather than fluid */

.index-header{
  padding-top: 160.7px; /* client feedback: header row cap-top to sit at 240px from viewport top, matching Practice */
  padding-bottom: 0;
}
.index-rows{
  padding-top: 14.7px; /* recalibrated for Forma DJR Text — target cap-top delta 52px (y312-y260) */
}
.index-row{
  width: 100%;
  text-align: left;
  line-height: var(--row-pitch);
}
.index-row span{ line-height: var(--row-pitch); }
/* client feedback: Designer/Scale/Outline sized to match Home's heading
   token exactly (reusing --fs-label, not a hardcoded px, so it stays in
   sync with Home) — Project stays on --fs-small ("index projects" at
   18.5px, per the client's own measurement). Data cells only; the header
   row (Project/Designer/Scale/Outline labels) is unchanged pending the
   same header-vs-data scope question flagged on Practice/Index before. */
/* client feedback (latest): Designer/Scale/Outline must also be Forma
   Regular (400), not the 500 they were still inheriting from .t-row —
   all four data columns (Project included) now read 21.4063px / 400. */
.index-row .col-4-5,
.index-row .col-6,
.index-row .col-7-12,
.index-row .col-1-3{ font-size: var(--fs-label); font-weight: 400; }

/* client feedback: Designer names are hard-truncated to 21 characters in
   JS (site.js, truncateChars). At narrow widths the column can still be
   too tight for that 21-char string, and plain overflow:hidden was
   hard-clipping mid-letter with no ellipsis — client flagged the crop on
   "Ursula Chandler Arc[hi…]". Adding text-overflow:ellipsis here means any
   further clipping past the JS truncation also gets a clean "…" instead of
   a raw cut. */
.index-row .col-4-5{
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* client feedback: Project ("Paradise Dr, St Andrews Beach") was wrapping
   to a second line at narrow-but-not-yet-mobile widths — same ellipsis
   treatment as Outline/Designer instead. */
.index-row .col-1-3{
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.index-outline{
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  padding-right: 24px;
}

.index-rows.is-hovering .index-row{ opacity: .5; transition: opacity 300ms ease; }
.index-rows.is-hovering .index-row.is-active{ opacity: 1; }
.index-row{ transition: opacity 300ms ease; }

.index-contact{ padding-top: 260px; } /* eyeballed against the artboard — brief gives no explicit y-coordinate for this block */
.index-foot-spacer{ padding-top: 240px; } /* client feedback: 240px gap from the Contact block's details to the footer bar, matching Practice */

/* -------------------------------------------------------------------------
   9. Landscapes page (photo grid) — brief §6.3, §2.1
   Own 7-col grid, independent of the page's 12-col grid (§2.1 [CONFLICT]:
   7 tiles doesn't divide into 12 columns).
   ------------------------------------------------------------------------- */
.gallery{
  display: grid;
  grid-template-columns: repeat(7, 1fr);
  column-gap: 18px;
  row-gap: 60px;
  padding-inline: 24px;
  padding-top: 160.7px; /* client feedback: first caption cap-top to sit at 240px from viewport top, matching Practice/Index */
  max-width: var(--frame);
  margin-inline: auto;
  align-items: start; /* top-align tiles; row height = tallest tile — no stretch, no masonry */
}

.tile{
  margin: 0;
  display: flex;
  flex-direction: column;
  transition: opacity 300ms ease;
}
.tile-caption{
  /* client feedback: 23.5px → 21.4063px, matching Practice/Index. Also
     giving it an explicit line-height (was inheriting .t-row's unset
     "normal") pre-emptively — this exact gap (missing line-height on a
     text role) was the root cause of the Practice/Index flow-line
     mismatch caught last round, so fixing it here before it becomes the
     same bug a second time. */
  /* client feedback (latest): project names now longer ("Balaclava Rd,
     Balaclava" vs the old plain "Balaclava"), so scaled 10% off the shared
     --fs-label/--lh-label pair — scoped to this tile role only, not the
     shared tokens themselves, so Index/Practice/Home headings are unaffected. */
  font-size: calc(var(--fs-label) * 0.9);
  line-height: calc(var(--lh-label) * 0.9);
  margin: 0 0 11px; /* target: 11px clear between caption and image top, calibrated below */
}
.tile-btn{
  display: block;
  width: 100%;
  padding: 0;
}
.tile-placeholder{
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  background: rgba(137, 133, 85, 0.2); /* --olive at 20% */
  color: rgba(0, 0, 0, 0.4);
  font-size: 11px;
  line-height: 1.3;
  text-align: center;
  padding: 8px;
  overflow: hidden;
}
.tile-image{
  display: block;
  width: 100%;
  height: auto;
  object-fit: cover;
}

@media (hover: hover){
  .gallery.is-hovering .tile{ opacity: .5; }
  .gallery.is-hovering .tile.is-active{ opacity: 1; }
}

.gallery-contact{ padding-top: 180px; }
.gallery-foot-spacer{ padding-top: 172px; }

/* -------------------------------------------------------------------------
   10. Lightbox — brief §7. One overlay shared by Index rows and Landscapes
   tiles (assets/js/site.js Lightbox module). Always an olive veil regardless
   of the underlying page's theme.
   ------------------------------------------------------------------------- */
.lightbox{
  position: fixed;
  inset: 0;
  z-index: 1000;
}
.lightbox[hidden]{ display: none; }

.lightbox__veil{
  position: fixed;
  inset: 0;
  background: rgba(137, 133, 85, 0.92); /* --olive at 92% */
  backdrop-filter: blur(6px); /* [ASSUMPTION] exact blur radius not given — brief only says the page beneath is "blurred out" */
  -webkit-backdrop-filter: blur(6px);
}

.lightbox__close{
  position: fixed;
  top: 24px;
  right: 24px;
  z-index: 2;
  color: var(--ink);
} /* [ASSUMPTION] position not given in brief — placed at the frame's top-right margin, matching header nav's right edge */

/* client feedback: dots removed entirely (were .lightbox__prev/__next) —
   the whole Lightbox (veil, frame, caption, close — not just the image)
   now shows a directional arrow cursor instead, toggled by the mousemove
   handler on the lightbox root in site.js. Clicking left/right still only
   navigates over the image itself — that hit-target didn't change, only
   cursor visibility did. The dark-image variant additionally requires the
   cursor to actually be over the photo (see the mousemove handler) since
   there's no pixel data to sample anywhere else. */
.lightbox.is-hover-prev,
.lightbox.is-hover-prev *{
  cursor: url("../img/cursor-arrow-left.svg") 20 6, auto;
}
.lightbox.is-hover-next,
.lightbox.is-hover-next *{
  cursor: url("../img/cursor-arrow-right.svg") 20 6, auto;
}
.lightbox.is-hover-prev.is-dark-under-cursor,
.lightbox.is-hover-prev.is-dark-under-cursor *{
  cursor: url("../img/cursor-arrow-left-white.svg") 20 6, auto;
}
.lightbox.is-hover-next.is-dark-under-cursor,
.lightbox.is-hover-next.is-dark-under-cursor *{
  cursor: url("../img/cursor-arrow-right-white.svg") 20 6, auto;
}

.lightbox__frame{
  position: fixed;
  inset: 0;
  z-index: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  pointer-events: none; /* let clicks in the dead space fall through to the veil */
}
.lightbox__frame > *{ pointer-events: auto; }

/* client feedback: image now centred on both axes of the full viewport, not
   just within the leftover space above the caption — the caption is pulled
   out of the flex flow (position:absolute, pinned to the bottom) so it no
   longer competes with the image for vertical space. */
.lightbox__image-area{
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 24px 120px;
}
.lightbox__image-btn{
  display: block;
  padding: 0;
  max-height: 100%;
}
.lightbox__placeholder{
  display: flex;
  align-items: center;
  justify-content: center;
  max-height: calc(100vh - 190px); /* brief §7 — tall portraits are height-constrained, never cropped */
  max-width: calc(100vw - 200px);
  width: auto;
  height: auto;
  background: rgba(0, 0, 0, 0.15);
  color: #FFFFFF;
  font-size: 12px;
  text-align: center;
  padding: 8px;
}
.lightbox__image{
  display: block;
  max-height: calc(100vh - 190px); /* brief §7 — tall portraits are height-constrained, never cropped */
  max-width: calc(100vw - 200px);
  width: auto;
  height: auto;
}
/* real bug, caught while verifying "centred on both axes": both rules above
   set their own `display`, which — being an author-stylesheet rule — beats
   the browser's default `[hidden]{display:none}` UA rule. So whichever of
   the two was `.hidden = true` was still rendering as an empty flex box
   (16px tall from its own padding alone), silently adding to imageBtn's
   total height and throwing off the vertical centring math by half of that.
   Only became visible now that the caption no longer masks a few px of
   drift — explicit [hidden] overrides fix it at the root. */
.lightbox__placeholder[hidden],
.lightbox__image[hidden]{
  display: none;
}

/* client feedback: caption simplified to just the project title, centred
   below the image — Designer/Scale/Outline removed entirely (that
   information now lives only on the Index/Practice/Landscapes Contact
   pages, not the Lightbox). position:absolute (not a flex sibling of
   image-area) so it doesn't affect the image's centring above. */
.lightbox__caption{
  position: absolute;
  left: 0;
  right: 0;
  bottom: 24px;
  text-align: center;
  color: var(--ink);
  pointer-events: none; /* text only, no controls — clicks fall through to the veil, same as any other dead space */
}
/* client feedback: sized to match the Landscapes tile caption exactly
   (--fs-label/--lh-label × 0.9) rather than the old 4-column grid's --fs-label. */
.lightbox__caption-title{
  font-size: calc(var(--fs-label) * 0.9);
  line-height: calc(var(--lh-label) * 0.9);
}

/* client feedback: mobile Lightbox felt "meh" — the image sat tiny in a sea
   of blurred veil (120px side padding + a separate 100px-per-side max-width
   cap, both sized for desktop). Image now runs near edge-to-edge, an 18px
   margin either side. */
@media (max-width: 767px){
  .lightbox__image-area{
    padding: 24px 18px;
  }
  .lightbox__image,
  .lightbox__placeholder{
    max-width: calc(100vw - 36px);
  }
  /* client feedback, screenshot: a visible box around "Close" — the global
     button:focus-visible outline (site-wide accessibility rule, correct and
     left alone everywhere else), triggered here by open()'s own
     closeBtn.focus() call on every single open, not by an actual keyboard
     Tab press. Scoped to mobile only: touch-primary devices have no
     keyboard user to serve, so this outline is pure visual noise here and
     removing it doesn't cost real keyboard accessibility anywhere — desktop
     keeps the ring for genuine Tab navigation. */
  .lightbox__close:focus-visible{
    outline: none;
  }
}

/* -------------------------------------------------------------------------
   11. Home page — brief §6.1. Three full-bleed bands, zero seams between
   them (margin:0 throughout, backgrounds meet exactly — see .home/.band).
   ------------------------------------------------------------------------- */
/* overflow-x:clip on .home would establish a clip container that also clips
   the position:fixed reveal footer below — scope the parallax overflow fix
   to the hero band itself instead. */
.band-hero{ overflow-x: clip; }
.band{ margin: 0; }

/* Band C reveal, client feedback: "reveal on scroll like zorankonjarski.com.au,
   not a module that follows" — i.e. not position:sticky (which travels WITH
   the scroll once caught), but a fixed panel sitting BEHIND the scrolling
   page that only becomes visible once the opaque content above scrolls past
   it. Technique (confirmed by inspecting that site's DOM): Bands A+B stay in
   normal flow with solid backgrounds and a higher stacking order; Band C is
   position:fixed, full-viewport, z-index below them; a spacer the height of
   one viewport takes Band C's place in the document flow so the page's
   scrollable length is unchanged (nothing here shortens or hijacks scroll —
   see brief §6.1 point 5). */
.band-hero{ background: var(--olive); position: relative; z-index: 1; }
.band-content{
  background: var(--paper);
  position: relative;
  z-index: 1;
  padding-bottom: 70px; /* client feedback: breathing room after the services list before the Band C reveal boundary — the fixed-panel reveal technique means Band C has no in-flow "gap" of its own, this has to live in Band B */
}

.home-footer-spacer{ height: 100vh; }

.band-image{
  /* real footer sweep photograph — #7d8a70 (the old plain-colour placeholder)
     stays as the background-color fallback beneath it: shows instantly
     before the (large, 7MB) animated webp decodes, and covers any gap if
     the source ever fails to load. cover/center scales the real 960×540
     frame proportionately to fill the fixed full-viewport band, cropping
     rather than distorting. */
  background: #7d8a70 url("../img/footer-sweep.webp") center / cover no-repeat;
  position: fixed;
  inset: 0;
  z-index: 0;
  display: flex;
  flex-direction: column;
}
.band-image > .site-footer{ margin-top: auto; } /* Contact sits at top of the band, footer pins to the bottom */
/* Real layout bug, client-reported: .home-contact and .site-footer are flex
   items of this column flex container, and a flex item's cross-axis "auto"
   width shrinks to its content's preferred size rather than stretching to
   fill, even under the default align-items:stretch, once the item also
   carries its own max-width. Result: both were rendering under 1920px and
   centering (via their own margin-inline:auto) within that smaller box,
   throwing their grid columns out of alignment with Band A/B's true
   12-column grid. Forcing the cross-axis to fill explicitly fixes it. */
.band-image > .grid{ width: 100%; }

/* Hero (Band A) */
.hero{
  display: flex;
  justify-content: center;
  padding-top: 71px;    /* calibrated: image top y143, header renders ~72px tall */
  padding-bottom: 142px; /* calibrated to close Band A at y975 total */
}
.hero__image{
  /* height-driven, not width-driven: the 7 scrub images are different real
     ratios (client feedback — see README), so width is left to vary with
     each image's own intrinsic ratio while height (and therefore the
     module's vertical footprint) stays exactly what the old fixed
     552×690 box was — 35.9375vw = 28.75vw (old width) × 690/552. */
  height: 35.9375vw;
  max-height: 690px;
  width: auto;
  display: block;
}

/* "Beside content, cap-tops align" (brief §3 corrected rule) — label and
   content are grid siblings placed in the same row (auto-placement, since
   their column spans don't overlap), top-aligned by the grid, then the
   label gets a calibrated translateY to bring its cap-top to exactly match
   the content's — necessary because the two sit at different font sizes,
   so box-top alignment alone does not produce cap-top alignment. */
.home-cap-row{ align-items: start; }
.home-cap-row .home-label{ transform: translateY(0px); } /* calibrated per instance below once measured */

/* Practice (Band B, label beside content — cap-tops align) */
.home-practice{
  padding-top: 39px; /* first pass: label cap-top target y1015, relative to band start y976 — recalibrated after measuring */
}
.home-practice .t-display p{ margin: 0; }
.home-practice .t-display p + p{ text-indent: 3ch; }
.home-practice .home-label{ transform: translateY(5.47px); } /* recalibrated for label weight 500 (was 700) — target cap-top delta 0 */

/* Landscapes (Band B, label above content — fixed 45px gap) */
.home-landscapes{
  padding-top: 70px; /* client feedback: fixed 70px gap from paragraph base to Landscapes label (was 250px) */
}
.home-landscapes__label{
  padding-inline: var(--margin);
  max-width: var(--frame);
  margin-inline: auto;
}
.home-landscapes__images{
  display: grid;
  grid-template-columns: repeat(12, 1fr);
  column-gap: var(--gutter);
  padding-inline: var(--margin);
  max-width: var(--frame);
  margin-inline: auto;
  padding-top: 14.8px; /* client feedback: reduce label→image gap by 25%, 45px → 33.75px */
}
.home-landscapes__tile{
  display: block;
  width: 100%;
  position: relative;
}
.home-landscapes__image{
  /* real photo ratio (1224×820) — wider than the old 612/519 placeholder guess */
  aspect-ratio: 1224 / 820;
  display: block;
  width: 100%;
  height: auto;
  object-fit: cover;
  transition: filter 250ms ease;
}
/* client feedback: rollover — greyscale the photo, reveal the project name
   centred in white; click opens that project's Lightbox (same shared
   component the Landscapes page tiles use — see site.js). */
.home-landscapes__tile:hover .home-landscapes__image,
.home-landscapes__tile:focus-visible .home-landscapes__image{
  filter: grayscale(100%);
}
.home-landscapes__tile-label{
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  text-align: center;
  padding: 8px;
  color: #FFFFFF;
  font-weight: 400; /* Forma Regular, client feedback — not the 500 .t-label uses */
  font-size: calc(var(--fs-label) * 0.85); /* client feedback: 15% smaller than the Services/Landscapes/Practice heading scale */
  line-height: 1;
  opacity: 0;
  transition: opacity 250ms ease;
  pointer-events: none;
}
.home-landscapes__tile:hover .home-landscapes__tile-label,
.home-landscapes__tile:focus-visible .home-landscapes__tile-label{
  opacity: 1;
}

/* Services (Band B, label beside content — cap-tops align) */
.home-services{
  padding-top: 50.5px; /* client feedback: fixed 50px gap from images to Services label */
}
.home-services .home-label{ transform: translateY(6.67px); } /* recalibrated for label weight 500 (was 700) — target cap-top delta 0 */
.home-services__list{
  margin: 0;
  /* Client correction: InDesign has this list at 36px against the display
     paragraph's 40px (0.9 ratio) — not the shared --fs-small role. Scaled
     against the *corrected* display size (38.6px, cap-height-derived), not
     InDesign's nominal 40: 38.6 * 0.9 = 34.74px. Line-height reuses the
     display pitch (--lh-display) — the shared --lh-small (26px) is too
     tight for a font this size (would clip); no pitch was specified, so
     tying it to the paragraph above keeps one consistent vertical rhythm. */
  font-size: clamp(23.2px, 1.809vw, 34.74px);
  line-height: var(--lh-display);
  font-weight: 500; /* Forma Medium — client correction */
}

/* client feedback: same hover-dim mechanic as the Index rows/Landscapes
   tiles — hovering one item keeps it at full opacity, every other item in
   the list (both columns, treated as one flat 13-item group) dims to 50%.
   Wired up via the same shared attachHoverDim() utility in site.js. */
@media (hover: hover){
  .home-services.is-hovering li{ opacity: .5; transition: opacity 300ms ease; }
  .home-services.is-hovering li.is-active{ opacity: 1; }
}
.home-services li{ transition: opacity 300ms ease; }

/* Contact + footer (Band C, white on the image ground) */
.home-contact{
  padding-top: 35px; /* client feedback: reduced 50% (was 70px, before that 130px) — margin above "Contact" read too deep on both mobile and desktop */
  color: #FFFFFF;
}
.home-contact .contact-block__intro,
.home-contact .contact-block__details{ color: #FFFFFF; }
/* client feedback: Home's Contact block (label, intro, and details all
   together — "Contact and Tom Herrod details") set to 24px at their 1920
   viewport, distinct from the 21.4063px (--fs-label) used everywhere else.
   New dedicated clamp, not a reuse of an existing token — 24px doesn't
   match any other role. Footer bar (.t-footer) deliberately NOT touched,
   client confirmed it should stay at --fs-footer (21.4063px). */
.home-contact .contact-block__label,
.home-contact .contact-block__intro,
.home-contact .contact-block__details{
  font-size: clamp(16px, 1.25vw, 24px);
  line-height: clamp(20px, 1.5625vw, 30px);
}

/* -------------------------------------------------------------------------
   12. Responsive — Landscapes gallery column progression (brief §8.2)
   Column count only here; the 2×2 mobile truncation/padding logic is
   deferred to the full responsive pass (build step 8).
   ------------------------------------------------------------------------- */
@media (max-width: 1439px){
  .gallery{ grid-template-columns: repeat(5, 1fr); }
}
@media (max-width: 1279px){
  .gallery{ grid-template-columns: repeat(4, 1fr); }
}
@media (max-width: 1023px){
  .gallery{ grid-template-columns: repeat(3, 1fr); }
}
@media (max-width: 767px){
  .gallery{ grid-template-columns: repeat(2, 1fr); column-gap: 12px; padding-inline: 16px; }

  /* client feedback: at the 2-column mobile view only — desktop/tablet
     keep every image — cap each project to its first 4 tiles so the
     gallery scans better, and force each project's first tile to always
     start the left column (never the right), so the title stays scannable. */
  .tile--extra{ display: none; }
  .tile--first{ grid-column-start: 1; }
}

/* -------------------------------------------------------------------------
   13. Responsive — Practice page
   ------------------------------------------------------------------------- */
@media (max-width: 1279px){
  .grid{ column-gap: 16px; }
  .practice-blocks.grid{
    row-gap: 40px;
  }
  /* client feedback: full single-column stack here (Contact, then Services,
     then Sustainability, each full-width) wasted the right half of the
     viewport at this width — there's room for two columns. Contact keeps
     its own desktop span (.col-1-4's plain 1/5, left unoverridden here);
     Services and Sustainability now share the same 5/13 span, which stacks
     them in that one right-hand column in DOM order rather than each
     taking a full-width row of its own. True mobile (≤767px) still forces
     all three to the single full-width column below — that override is
     more specific in source order and wins at that width regardless of
     this rule. */
  .practice-blocks .col-5-8,
  .practice-blocks .col-9-12{
    grid-column: 5 / 13;
  }

  /* client feedback: intro paragraph keeps scaling down below --fs-display's
     own floor, reaching 22.5px at 500px instead of flatlining at 25.7px.
     Leading tightened 5% to match (see --lh-practice-display above). */
  .practice-display .t-display{
    font-size: var(--fs-practice-display);
    line-height: var(--lh-practice-display);
  }
}

@media (max-width: 500px){
  /* client feedback, tested at 375px: intro paragraph text reduced a
     further 7.5% off its 22.5px floor (20.8125px), leading scaled by the
     same 7.5% to keep the ratio constant (26.9776px, was 29.165px). Scoped
     here rather than recalculating the 1279–500px clamp above, since nothing
     in that already-verified range was asked to change — only the flat
     floor reached at 500px and below. */
  .practice-display .t-display{
    font-size: 20.8125px;
    line-height: 26.9776px;
  }

  /* client feedback: the roundel was coming in too early at ≤767px — with
     the wordmark now 30% smaller (see the logo-sizing round above) there's
     room for it further down. Moved this swap from ≤767px to ≤500px;
     wordmark now shows all the way down to 501px. */
  .site-header__logo-full{
    display: none;
  }
  .site-header__logo-roundel{
    display: block;
    width: 30px;
    height: 30px;
  }
}

@media (max-width: 767px){
  .grid{
    grid-template-columns: repeat(6, 1fr);
    padding-inline: 16px;
    column-gap: 12px;
  }
  .site-nav{ grid-column: 3 / 7; }
  /* client feedback: at narrow widths, Practice's flow line no longer
     matched Index/Landscapes (which share one fixed 160.7px padding-top at
     every width, no ≤767px override) — traced to this orphaned override,
     undocumented and never propagated to the other two pages when the
     desktop-wide 240px cap-top sync was established. Removed so Practice
     falls back to the same 160.7px base value the other two already use,
     restoring one shared flow line at every width, not just desktop. */
  .practice-display{ padding-top: 12px; }
  .practice-blocks{ padding-top: 60px; }
  .practice-blocks .col-1-4,
  .practice-blocks .col-5-8,
  .practice-blocks .col-9-12{
    grid-column: 1 / 7;
  }
  /* client feedback: at 500px the three blocks stack (each full-width, one
     per grid row) — "Services" sitting 40px below "0417 542 983" (the
     ≤1279px row-gap) should be 30px instead. */
  .practice-blocks.grid{
    row-gap: 30px;
  }

  /* client feedback: once Contact/Services/Sustainability stack to one
     column, tighten further. Contact and Sustainability lose their
     label→body gap entirely; Services' label→body gap and every
     Consultancy→Delivery→Stewardship→Design service-group transition
     become "a soft return" — reusing the same 14.2969px established for
     that exact phrase on the Sustainability paragraphs above. */
  .practice-blocks .col-1-4 .practice-blocks__label,
  .practice-blocks .col-9-12 .practice-blocks__label{
    padding-bottom: 0;
  }
  .practice-blocks .col-5-8 .practice-blocks__label{
    padding-bottom: 14.2969px;
  }
  .service-group{
    margin-bottom: 14.2969px;
  }
  .site-footer .grid{
    row-gap: 8px;
  }
  .site-footer__item--1{ grid-column: 1 / 4; }
  .site-footer__item--2{ grid-column: 4 / 7; }
  .site-footer__item--3{ grid-column: 1 / 4; }
  .site-footer__item--4{ grid-column: 4 / 7; }

  /* client feedback, Index only: column 1 reads Herrod Landscapes / Credit,
     column 2 reads Email / Instagram — swaps items 3 and 4's column
     assignment from the site-wide default above (higher specificity via
     the extra .index-footer class wins regardless of source order). Also
     fixes the same "row-gap never applies" bug as .practice-blocks.grid
     (site-footer and grid are the same element, not ancestor/descendant),
     scoped to Index for now rather than changed site-wide unprompted.
     Follow-up: client wants the gap between "Herrod Landscapes" and
     "Credit: ..." (now stacked in column 1) removed entirely — back to 0,
     which also closes the Email→Instagram gap in column 2 (not flagged
     separately, so left as a shared side effect for now). */
  .index-footer.grid{
    row-gap: 0;
  }
  /* grid-row pinned explicitly on both — without it, the auto-placement
     cursor (advanced past column 4 by item3's own column-4/7 assignment)
     skipped item4 into a third row instead of sharing row 2 with item3,
     which is what produced the stray 14.3px gap the client flagged
     between "Herrod Landscapes" and "Credit: ...". */
  .index-footer .site-footer__item--3{ grid-column: 4 / 7; grid-row: 2; }
  .index-footer .site-footer__item--4{
    grid-column: 1 / 4;
    grid-row: 2;
    /* client feedback: still inheriting justify-self:end from the desktop
       rule (site-credit flush to the right margin there) — moving it into
       column 1 needs it flush left instead, under "Herrod Landscapes". */
    justify-self: start;
  }
  /* client feedback: at narrow mobile widths the long credit line
     ("Credit: The Office of Zoran Konjarski") can overflow its own column
     1 cell (white-space:nowrap) far enough to visually collide with
     "Instagram" in column 2 — measured a genuine 30px overlap at 441px.
     Pushing Email/Instagram flush to the right margin (same treatment
     desktop's credit line already gets) maximises the buffer instead of
     them sitting at the left edge of their own cell, right where column
     1's overflow reaches. */
  .index-footer .site-footer__item--2,
  .index-footer .site-footer__item--3{
    justify-self: end;
  }

  /* client feedback: footer text reads "too tight" at this width — the
     base .t-footer rule sets line-height:1 (zero leading at all), which
     combined with the 0px row-gap above (client's own request, previous
     round) left column 1 with no breathing room whatsoever between
     "Herrod Landscapes" and "Credit: ...". Fixed with leading, not by
     reopening the row-gap — InDesign-style proportional line-height,
     scoped to Index/mobile only so the other three pages' footers (and
     Index's own already-verified desktop footer) are untouched. */
  .index-footer .t-footer{
    line-height: 1.2;
  }

  /* client feedback: Landscapes "uses the same footer system" as Index —
     replicate the same footer-level fixes (not the Contact-block font-sync
     work above, which was specific to syncing against Index's table; there
     is no equivalent table on Landscapes to sync against). Same column
     reorder, same grid-row pin (same auto-placement cursor issue applies
     here identically, since the markup is the same), same justify-self,
     same row-gap-never-applied bug, same line-height fix. */
  .gallery-footer.grid{
    row-gap: 0;
  }
  .gallery-footer .site-footer__item--3{ grid-column: 4 / 7; grid-row: 2; }
  .gallery-footer .site-footer__item--4{
    grid-column: 1 / 4;
    grid-row: 2;
    justify-self: start;
  }
  /* client feedback: same overflow/collision fix as Index — see the note
     on .index-footer's version above. */
  .gallery-footer .site-footer__item--2,
  .gallery-footer .site-footer__item--3{
    justify-self: end;
  }
  .gallery-footer .t-footer{
    line-height: 1.2;
  }

  /* client feedback: Practice's footer bar (Herrod Landscapes / Email /
     Instagram / Credit) mirrors Index and Landscapes too — footer-bar
     content only, explicitly not the Contact block (Practice's Contact
     block is its own established layout from earlier rounds, untouched
     here). Same reorder/pin/justify-self/row-gap/line-height set. */
  .practice-footer.grid{
    row-gap: 0;
  }
  .practice-footer .site-footer__item--3{ grid-column: 4 / 7; grid-row: 2; }
  .practice-footer .site-footer__item--4{
    grid-column: 1 / 4;
    grid-row: 2;
    justify-self: start;
  }
  .practice-footer .site-footer__item--2,
  .practice-footer .site-footer__item--3{
    justify-self: end;
  }
  .practice-footer .t-footer{
    line-height: 1.2;
  }

  /* client feedback: Home's footer bar mirrors the other three pages too —
     same full set (Home's Contact block already has its own established
     mobile treatment from the earlier ≤985px Home round, untouched here). */
  .home-footer.grid{
    row-gap: 0;
  }
  .home-footer .site-footer__item--3{ grid-column: 4 / 7; grid-row: 2; }
  .home-footer .site-footer__item--4{
    grid-column: 1 / 4;
    grid-row: 2;
    justify-self: start;
  }
  .home-footer .site-footer__item--2,
  .home-footer .site-footer__item--3{
    justify-self: end;
  }
  .home-footer .t-footer{
    line-height: 1.2;
  }
}

/* -------------------------------------------------------------------------
   14. Responsive — Index page (brief §8.1)
   step 0a: 1700–1100px, table type holds flat (--fs-index-row-mid) —
   Project/Designer/Scale are fully preserved, Outline's existing ellipsis
   absorbs all of the narrowing across this range. The footer (.index-footer
   only — scoped so Home/Practice/Landscapes' footers are untouched) tracks
   the same curve: client feedback was that table text and footer text
   should move together, since both start at the same 21.4063px at full
   screen but had drifted apart (18.9688px vs 15.4219px measured at
   1380px) — the footer was still on the site-wide --fs-footer clamp,
   unaffected by the table's own held-flat/slower-scaling curve.
   step 0b: below 1100px, type resumes scaling down, more slowly than the
   rest of the site (--fs-index-row-low). Footer follows here too.
   step 1: Outline truncates with an ellipsis, from ~1280px down.
   step 2: below ~768px, Outline is dropped entirely.
   ------------------------------------------------------------------------- */
@media (max-width: 1700px){
  .index-header,
  .index-row .col-1-3,
  .index-row .col-4-5,
  .index-row .col-6,
  .index-row .col-7-12{
    font-size: var(--fs-index-row-mid);
  }
  .index-footer .t-footer{
    font-size: var(--fs-index-row-mid);
  }
  /* client feedback: Contact block ("Contact" label, intro, details) was
     "the odd one out" — still on the site-wide --fs-label, unsynced with
     the table/footer's held-flat curve. Now tracks the same curve. */
  .index-contact .t-label,
  .index-contact .t-small{
    font-size: var(--fs-index-row-mid);
  }
  /* client feedback: unitless line-height, not a flat px value — this is
     what makes it recompute correctly against whichever font-size is
     active (this "mid" segment or the "low" one below) instead of needing
     a separate override per breakpoint. 1.21 matches --fs-label/--lh-label's
     own ratio, so it reads the same as the rest of the site at this size. */
  .index-contact .t-small{
    line-height: 1.21;
  }

  /* client feedback: "mirrored footers, using Index as the goal" — Landscapes'
     Contact block gets the identical treatment. No table exists on this
     page to justify a sync against, but that was never the point — the
     point is the two pages' Contact blocks should look and behave the
     same, so it reuses the exact same values directly rather than a
     table-relative derivation. */
  .gallery-contact .t-label,
  .gallery-contact .t-small{
    font-size: var(--fs-index-row-mid);
  }
  .gallery-contact .t-small{
    line-height: 1.21;
  }
}

@media (max-width: 1100px){
  .index-header,
  .index-row .col-1-3,
  .index-row .col-4-5,
  .index-row .col-6,
  .index-row .col-7-12{
    font-size: var(--fs-index-row-low);
  }
  .index-footer .t-footer{
    font-size: var(--fs-index-row-low);
  }
  .index-contact .t-label,
  .index-contact .t-small{
    font-size: var(--fs-index-row-low);
  }
  .gallery-contact .t-label,
  .gallery-contact .t-small{
    font-size: var(--fs-index-row-low);
  }
}

@media (max-width: 1279px){
  /* row-pitch no longer overridden here — client wants it held constant
     across viewports (fixed the "opens up when the browser is tightened"
     bug this override was causing: 32px > the round-three 30px base,
     so pitch was inconsistently growing below 1280px instead of just
     staying put). */
  .col-7-12.index-outline{ grid-column: 7 / 13; }
}

@media (max-width: 1380px){
  /* client feedback: Contact block stacks — intro sentence dropped, the
     details paragraph (Tom Herrod... Melbourne, Australia) sits directly
     below "Contact" instead of beside it in a separate column. */
  .index-contact .col-1-4,
  .index-contact .col-5-8{
    grid-column: 1 / -1;
  }
  .index-contact .contact-block__intro{
    display: none;
  }

  /* client feedback: identical stacking on Landscapes' Contact block —
     this, not the font-size sync above, is most of what was visibly wrong
     in the client's screenshot (still two-column, still showing the intro
     sentence, "tom@herrodlandscape…" clipped mid-word). */
  .gallery-contact .col-1-4,
  .gallery-contact .col-5-8{
    grid-column: 1 / -1;
  }
  .gallery-contact .contact-block__intro{
    display: none;
  }

  /* client feedback: drop the "Design & Code: " prefix here to open up
     space, now that the footer text is the same larger size as the table
     (see the 1700/1100px rules above). */
  .index-footer .footer-credit-prefix{
    display: none;
  }

  /* client feedback: same 3-variant credit line on Landscapes' footer, same
     breakpoint, for consistency between the two pages' "same footer
     system" — even though there's no table-sync reason driving it here. */
  .gallery-footer .footer-credit-prefix{
    display: none;
  }

  /* client feedback: same on Practice's footer bar. */
  .practice-footer .footer-credit-prefix{
    display: none;
  }

  /* client feedback: same on Home's footer bar. */
  .home-footer .footer-credit-prefix{
    display: none;
  }
}

@media (max-width: 767px){
  .index-outline{ display: none; }

  /* client feedback: at their 500px mobile viewport, the constant 33px
     row-pitch reads proportionately looser than desktop against the now-
     much-smaller text — reduced 10%, then 10% again, then a further 5%
     (33 × 0.9 × 0.9 × 0.95). */
  :root{ --row-pitch: 25.39px; }

  /* client feedback: at 500px the footer credit reads "Credit: The Office
     of Zoran Konjarski" — a third variant, shorter than the desktop
     "Design & Code:" prefix but not blank like the 1380–768px range. */
  .index-footer .footer-credit-prefix--short{
    display: inline;
  }

  /* client feedback, measured at 500px: space from the last project row
     to "Contact" should be exactly 200px (was 260px, a 1:1 match with this
     padding — no other offset in between). */
  .index-contact{
    padding-top: 200px;
  }
  /* client feedback: space from the Contact details' last line to the
     footer's first row should also be exactly 200px. Measured 264px at
     the old 240px spacer value — the extra 24px is .site-footer's own
     padding-top, so the spacer needed to drop to 200 − 24 = 176px, not
     200, to hit the target end-to-end measurement. */
  .index-foot-spacer{
    padding-top: 176px;
  }

  /* client feedback: same "same footer system" replication on Landscapes
     — credit-prefix variant and the Contact→footer 200px gap (not the
     gallery→Contact gap, which is a different relationship with its own
     existing 180px value, left untouched since it wasn't part of "the
     footer system" the client called out). */
  .gallery-footer .footer-credit-prefix--short{
    display: inline;
  }
  /* client feedback: same 200px Contact→footer target as Index. Measured
     196px at the existing 172px spacer value (24px offset from
     .site-footer's own padding-top, same relationship as Index) — 176px
     lands the measured gap on exactly 200px. */
  .gallery-foot-spacer{
    padding-top: 176px;
  }

  /* client feedback: Practice — footer-bar content only ("Herrod
     Landscapes, Email, Instagram and Credit:"), explicitly not the
     Contact→footer spacing (that's a Contact-block relationship, out of
     scope for this request). */
  .practice-footer .footer-credit-prefix--short{
    display: inline;
  }

  /* client feedback: same on Home's footer bar. */
  .home-footer .footer-credit-prefix--short{
    display: inline;
  }
}

/* -------------------------------------------------------------------------
   15. Responsive — Home page
   Client-specified breakpoint: 985px, coming down from full screen.
   Practice and Services both go from label-beside-content to a single
   stacked column, matching each other. Landscapes goes straight to the
   single-column carousel at the same breakpoint — client tried a 2-column
   intermediate step first, didn't like the wrapped/uneven look, and asked
   for the carousel to never show a stacked grid state at all. */
@media (max-width: 985px){
  /* Practice: label above content, full width. The side-by-side cap-top
     alignment transform no longer applies once stacked. */
  .home-practice .col-1-4,
  .home-practice .t-display{
    grid-column: 1 / -1;
  }
  .home-practice .home-label{
    transform: none;
  }
  /* client feedback: gap between "Practice" and the paragraph should read
     as a soft return, sized off "Practice"'s own rendered height rather
     than a value borrowed from another page's different type size. */
  .home-practice .t-display{
    margin-top: var(--home-practice-soft-return);
  }

  /* Services: same treatment as Practice — label above content, full
     width; both list columns stack as one. Gap above the list matches
     Practice's, per client feedback (only the label→first-list gap, not
     the gap between the two stacked lists). */
  .home-services .col-1-4,
  .home-services__list{
    grid-column: 1 / -1;
  }
  .home-services .home-label{
    transform: none;
  }
  .home-services .col-5-8{
    margin-top: var(--home-practice-soft-return);
  }

  /* client feedback: Contact block stacks too, same as the other pages —
     intro sentence dropped, details sit directly below "Contact". Type
     scale explicitly untouched, per "don't change type scales" — layout
     only, using Home's own 985px breakpoint for consistency with the
     Practice/Services stacking above rather than importing Index's 1380px. */
  .home-contact .col-1-4,
  .home-contact .col-5-8{
    grid-column: 1 / -1;
  }
  .home-contact .contact-block__intro{
    display: none;
  }

  /* Landscapes: horizontal scroll-snap carousel — client feedback: a single
     full-width tile looked "HUGE"/ridiculous at ~979px; wants two tiles
     viewable at once, third reached by scrolling (not wrapped to a new
     row, which was the other thing rejected last round). column-gap is
     inherited from the base rule above and still applies under flex. */
  .home-landscapes__images{
    grid-template-columns: none;
    display: flex;
    overflow-x: auto;
    scroll-snap-type: x mandatory;
    -webkit-overflow-scrolling: touch;
  }
  .home-landscapes__tile{
    flex: 0 0 calc((100% - var(--gutter)) / 2);
    scroll-snap-align: start;
  }
}

@media (max-width: 767px){
  /* client feedback: two-up is too cramped at true mobile widths (500px)
     — back to one tile visible, matching the site's standard mobile
     breakpoint used everywhere else. */
  .home-landscapes__tile{
    flex: 0 0 100%;
  }
}

@media (max-width: 500px){
  /* client feedback, tested at 375px: Home's Practice intro paragraph
     ("Herrod Landscapes works across…character.") reduced 7.5% off its
     --fs-display floor (25.7px → 23.7725px), leading scaled by the same
     7.5% (--lh-display floor 30.7px → 28.3975px). Home's version was never
     given its own Practice-page-style sync, so this is the first override
     at any width — starting from the plain --fs-display/--lh-display
     floors, not the already-reduced Practice-page numbers. */
  .home-practice .t-display{
    font-size: 23.7725px;
    line-height: 28.3975px;
  }
}

/* -------------------------------------------------------------------------
   16. Accessibility
   ------------------------------------------------------------------------- */
a:focus-visible,
button:focus-visible{
  outline: 2px solid var(--ink);
  outline-offset: 2px;
}
body[data-theme="olive"] a:focus-visible,
body[data-theme="olive"] button:focus-visible{
  outline-color: #FFFFFF;
}

@media (prefers-reduced-motion: reduce){
  *{ animation-duration: 0.01ms !important; animation-iteration-count: 1 !important; transition-duration: 0.01ms !important; scroll-behavior: auto !important; }
}
