/* =========================================================================
   CINEMATIC LAYER
   Loaded after site.css. Overrides the section layer of the homepage with a
   darker, full-bleed, editorial treatment.

   Design notes:
   - The palette is unchanged; this file introduces no new hues. It leans on
     the existing forest/paper/stone/orange tokens and simply uses the deep
     end of them far more heavily.
   - Several source photos are small (some as little as 472px wide). Full
     bleed magnifies that, so every full-bleed image is deliberately graded:
     darkened, desaturated toward the forest palette and overlaid with a
     scrim. Grading hides compression that a clean, bright full-bleed would
     expose.
   - Nothing here is load-bearing for content: if this stylesheet fails to
     load, site.css still renders the page correctly.
   ========================================================================= */

:root {
  --cine-field: #07160f;      /* deeper than forest-ink, for the dark fields */
  --cine-rule: rgba(232, 111, 40, 0.9);
  --cine-hair: rgba(244, 241, 234, 0.14);
}

/* The pinned panel is titled for screen readers but shows no visible heading,
   so the section still has an accessible name. */
.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/* -------------------------------------------------------------------------
   Section numerals + hairline rule — the motif that repeats down the page
   and ties the sections together.
   ------------------------------------------------------------------------- */

.cine-index {
  display: flex;
  align-items: center;
  gap: 0.85rem;
  margin-bottom: 1.4rem;
  font-size: 0.78rem;
  font-weight: 700;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  font-variant-numeric: tabular-nums;
  color: var(--stone, #b8b4aa);
}

.cine-index::before {
  content: "";
  width: 2.25rem;
  height: 2px;
  background: var(--cine-rule);
  flex: none;
}

.cine-num {
  font-size: 0.95rem;
  color: var(--paper, #f4f1ea);
}

/* -------------------------------------------------------------------------
   Full-bleed dark field sections.
   ------------------------------------------------------------------------- */

.cine {
  position: relative;
  background: var(--cine-field);
  color: var(--paper, #f4f1ea);
  overflow: clip;
}

.cine-inner {
  position: relative;
  z-index: 2;
  width: min(100% - 2.5rem, 78rem);
  margin-inline: auto;
  padding-block: clamp(5rem, 11vw, 9rem);
}

/* Display type: the main event. Archivo's width axis is pushed wide so the
   headline reads as deliberate rather than "the default bold". */
.cine-display {
  margin: 0 0 1.25rem;
  font-size: clamp(2.5rem, 6.4vw, 5.75rem);
  font-variation-settings: "wdth" 112;
  font-weight: 800;
  line-height: 0.94;
  letter-spacing: -0.03em;
  text-wrap: balance;
}

.cine-lede {
  max-width: 42ch;
  font-size: clamp(1.05rem, 1.6vw, 1.3rem);
  line-height: 1.6;
  color: rgba(244, 241, 234, 0.76);
  text-wrap: pretty;
}

/* -------------------------------------------------------------------------
   SERVICES — numbered cards, photo over copy, two abreast on desktop and
   one on mobile.
   ------------------------------------------------------------------------- */

/* Two-up card grid, not six full-width rows. The alternating photo-left /
   photo-right zig-zag read well but cost 3441px — 27% of the whole home page,
   more than the next four sections combined, and it dragged badly enough that
   readers were unlikely to reach the reviews below it. Photo-on-top cards two
   abreast cut it to roughly half that.

   This is the shape the <56rem tier already produced, so the mobile layout
   below is unchanged: it now simply keeps one column where this keeps two. */
.cine-rows {
  margin-top: clamp(2.5rem, 5vw, 4rem);
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: clamp(2rem, 4vw, 3.5rem) clamp(1.5rem, 3vw, 3rem);
}

.cine-row {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: clamp(1rem, 1.6vw, 1.5rem);
}

/* The old full-width rows carried hairline rules to separate them. Side by
   side those would box each card in, so the cards float on the field instead
   and the grid gap does the separating.

   A service card with no photo yet. Everything hangs off the --no-media
   class, so when the photo arrives the revert is exactly: drop the class,
   add the <img>. Nothing else moves.

   The grid stretches every card to its row's height, so a card with no photo
   was left with its copy floating in the vertical middle of a 620px cell —
   a void above and below that read as a rendering fault. Pin the copy to the
   top instead, level with the neighbouring card's photo, and let the empty
   space fall below where it looks like breathing room. */
.cine-row--no-media {
  justify-content: flex-start;
}

.cine-row--no-media .cine-row-body {
  align-self: start;
  width: 100%;
  padding-top: 0;
}

/* The numeral is an inline span everywhere else; it has to be a block to sit
   centred above the heading. */
.cine-row--no-media .cine-row-num {
  display: block;
  margin-bottom: 0.65rem;
}

/* Was a full-width statement at up to 3.25rem. In a half-width card that
   overpowered the photo cards beside it, so it now matches their h3. */
.cine-row--no-media .cine-row-body h3 {
  text-wrap: balance;
}

.cine-row--no-media .cine-row-body p {
  max-width: none;
}

.cine-row-media {
  position: relative;
  /* 3/2, not the old 16/10. That ratio was picked when the photo was a
     half-width column beside the copy; now it spans the full card width, so
     16/10 made it 398px of a 599px card — the image swamped the words and
     undid much of what the two-column change was for. 3/2 is the shallower
     crop these garden photos take without losing their subject. */
  aspect-ratio: 3 / 2;
  overflow: hidden;
  border-radius: 2px; /* near-sharp: templates default to big radii */
}

.cine-row-media img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  /* Grading: pulls soft, mixed-quality photos into one consistent look. */
  filter: saturate(0.82) contrast(1.12) brightness(0.82);
  transition: transform 0.45s cubic-bezier(0.16, 1, 0.3, 1), filter 0.3s ease;
}

/* The remaining SVG placeholders are pale flat illustrations, which read as
   a bright hole punched in the dark field. Knock them back until a real
   photo replaces them (see PLACEHOLDERS.md item 4). */
.cine-row-media img[src$=".svg"] {
  filter: saturate(0.5) contrast(0.95) brightness(0.45);
}

.cine-row:hover .cine-row-media img[src$=".svg"] {
  filter: saturate(0.6) contrast(0.95) brightness(0.55);
}

.cine-row:hover .cine-row-media img {
  transform: scale(1.04);
  filter: saturate(0.95) contrast(1.1) brightness(0.94);
}

/* A colour wash that ties every photo to the palette regardless of how the
   original was shot (overcast winter vs bright summer). */
.cine-row-media::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(200deg, rgba(7, 22, 15, 0.1) 0%, rgba(7, 22, 15, 0.62) 100%);
  pointer-events: none;
}

/* site.css gives headings a dark ink colour for light backgrounds; on the
   dark field that has to be overridden explicitly rather than inherited. */
.cine-row-body h3,
.cine .cine-display,
.cine-step {
  color: var(--paper, #f4f1ea);
}

/* The copy sits under its own photo now, so the old top padding (which
   aligned it against a tall photo alongside) would just be a gap. */
.cine-row-body {
  align-self: start;
  padding-top: 0;
}

/* Down from a 2.5rem ceiling: at half the width these headings wrapped to
   three lines and swamped the copy under them. */
.cine-row-body h3 {
  margin: 0.4rem 0 0.6rem;
  font-size: clamp(1.5rem, 1.9vw, 1.95rem);
  font-variation-settings: "wdth" 108;
  font-weight: 750;
  line-height: 1.08;
  letter-spacing: -0.02em;
  text-wrap: balance;
}

.cine-row-body p {
  max-width: 46ch;
  color: rgba(244, 241, 234, 0.72);
  line-height: 1.65;
  text-wrap: pretty;
}

.cine-row-num {
  font-size: 0.8rem;
  font-weight: 700;
  letter-spacing: 0.16em;
  font-variant-numeric: tabular-nums;
  /* Stone, not moss: these numerals sit on the dark field. */
  color: var(--stone, #b8b4aa);
}

.cine-row-link {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  margin-top: 1.1rem;
  padding-bottom: 0.2rem;
  font-weight: 650;
  color: var(--paper, #f4f1ea);
  text-decoration: none;
  border-bottom: 1px solid var(--cine-rule);
  transition: gap 0.3s ease, color 0.3s ease;
}

.cine-row-link:hover { gap: 0.9rem; color: var(--orange, #e86f28); }
.cine-row-link .icon { width: 1em; height: 1em; }

/* Keyboard users must see focus as clearly as mouse users see hover. */
.cine-row-link:focus-visible {
  outline: 2px solid var(--orange, #e86f28);
  outline-offset: 4px;
}

/* -------------------------------------------------------------------------
   SERVICES SCROLLER — one horizontal track at every width, with the copy
   sitting ON the photograph rather than beside or beneath it.

   Replaces the two-up grid of six full rows. That grid was 3295px on a phone
   — 23% of the whole home page — because six 3/2 photos plus six blocks of
   copy stacked into a single column. Laying the same six cards along one
   scrolling axis costs the height of ONE card instead of six.

   Phone: swipe, snapping card to card. Desktop: the same track with arrow
   buttons, since there is no swipe gesture on a mouse.

   Built on scroll-snap, so with JavaScript off it is still a working
   horizontal scroller — only the arrows need JS, and they hide without it.
   ------------------------------------------------------------------------- */

.cine-scroller {
  position: relative;
  margin-top: clamp(2.5rem, 5vw, 4rem);
}

.cine-track {
  display: grid;
  grid-auto-flow: column;
  /* Card width: roughly one card on a phone with the next one peeking in to
     signal there is more. The peek is what tells a user to swipe — a track
     that ends flush at the edge reads as a static card.

     A single clamp(), not minmax(): minmax() in grid-auto-columns resolves to
     its MIN whenever the track is free to size itself, which pinned every
     card to 15.5rem and ignored the viewport-relative figure entirely. */
  grid-auto-columns: clamp(15.5rem, 72vw, 24rem);
  gap: 1rem;
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  scroll-behavior: smooth;
  padding-bottom: 0.5rem;
  scrollbar-width: none;
  -ms-overflow-style: none;

  /* Break out of .cine-inner's 78rem column so the track runs to the screen
     edge. Without this the scroller is a narrow inset strip and stops reading
     as something you swipe. The padding is part of the scroll container, so
     scroll-padding has to match it or snapping lands short.

     LEFT is padded to line the first card up with the heading above it.
     RIGHT is only a small gutter, NOT the mirrored figure: padding both sides
     to `50vw - 39rem` pinned the usable width at 1248px no matter how wide
     the display, so a 2560px screen showed the same 3.4 cards as a 1280px one
     and turned the extra 1300px into empty margin. Letting the track run to
     the right edge means a wider screen actually shows more cards, and the
     card cut off at the edge is the clearest possible "scroll me" signal.

     The left inset deliberately still tracks the content column all the way
     up: capping it (min(12rem, …)) was tried and made things worse, because
     the value has to match .cine-inner's own offset exactly or the first card
     no longer lines up under the heading — a 12rem cap left the cards sitting
     192px to the RIGHT of the heading on a 2000px display, and showed fewer
     cards rather than more. Alignment with the heading is what this padding
     is for; the width problem was only ever the right-hand side. */
  margin-inline: calc(50% - 50vw);
  padding-left: max(1.25rem, calc(50vw - 39rem));
  padding-right: 1.25rem;
  scroll-padding-left: max(1.25rem, calc(50vw - 39rem));
  scroll-padding-right: 1.25rem;
}

.cine-track::-webkit-scrollbar { display: none; }

@media (prefers-reduced-motion: reduce) {
  .cine-track { scroll-behavior: auto; }
}

.cine-card {
  position: relative;
  scroll-snap-align: start;
  display: grid;
  /* Media and body occupy the SAME grid cell — this is what puts the copy on
     the photo instead of under it. */
  grid-template-areas: "stack";
  isolation: isolate;
  overflow: hidden;
  border-radius: 2px;
  /* Portrait-ish: a tall card gives the copy room to sit over the lower part
     of the photo without covering the subject. */
  aspect-ratio: 3 / 4;
}

/* Only the media is a grid item now — the body is absolutely positioned over
   the card instead (see .cine-card-body). The copy still sits on the photo;
   it just no longer shares a grid row whose height the photo controls. */
.cine-card-media {
  grid-area: stack;
  position: relative;
  z-index: 0;
}

.cine-card-media img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  filter: saturate(0.82) contrast(1.12) brightness(0.78);
  transition: transform 0.5s cubic-bezier(0.16, 1, 0.3, 1), filter 0.3s ease;
}

/* Portrait sources in a 4/5 card. object-fit: cover crops to the centre,
   which on a tall frame is the middle band — that is what cut the operator
   out of the spider lift photo. Anchor high instead. */
.cine-card-media img.focus-card-top { object-position: 50% 18%; }

/* Scrim. Heavier at the foot, where the type sits, and light at the head so
   the photograph still reads as a photograph. Without this the white type
   fails contrast the moment it lands on a bright patch of sky or gravel. */
.cine-card-media::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(
    to bottom,
    rgba(7, 22, 15, 0.12) 0%,
    rgba(7, 22, 15, 0.30) 42%,
    rgba(7, 22, 15, 0.78) 74%,
    rgba(7, 22, 15, 0.92) 100%
  );
  pointer-events: none;
}

/* Stump grinding still has no photograph (PLACEHOLDERS.md item 4). Rather
   than leave a blank hole in the track, that card gets a flat forest field
   with the same orange rule motif used elsewhere on the page, so it reads as
   a deliberate treatment rather than a failed image. Drop the --none class
   and add a <picture> when the photo arrives; nothing else changes. */
.cine-card-media--none {
  background:
    radial-gradient(120% 90% at 50% 0%, rgba(86, 107, 75, 0.42) 0%, rgba(7, 22, 15, 0) 62%),
    var(--forest-deep, #0c2820);
}

.cine-card-media--none::after {
  content: "";
  position: absolute;
  left: 1.25rem;
  top: 1.5rem;
  width: 2.25rem;
  height: 2px;
  background: var(--cine-rule);
}

/* Stretched to the full card, NOT align-self: end. Bottom-aligning the block
   made it grow upward from a fixed floor, so a two-line heading (Tree Pruning
   & Crown Reduction) pushed its own number and title higher than the one-line
   cards beside it. Filling the card instead and letting one auto margin absorb
   the slack pins the link to the same baseline on every card, whatever the
   heading does.

   The body is positioned against the card rather than left as a grid item.
   As an item it shared the row with .cine-card-media, and the row is sized by
   the media's own aspect-ratio/image — taller than the card's 4/5 box. The
   auto margin then resolved against THAT row (it measured 307px, overflowing
   the body to 469px inside a 440px card), so the link's baseline moved with
   whatever the photo did. inset:0 pins the body to the card's padding box, so
   the free space the auto margin divides is the card's, and every link lands
   on the same line whatever the media above it is doing. */
.cine-card-body {
  position: absolute;
  inset: 0;
  z-index: 1;
  display: flex;
  flex-direction: column;
  /* NO justify-content here. flex-end plus an auto margin fight each other —
     the auto margin eats all the free space and shoves the heading to the TOP
     of the card. The auto margin alone does the job. */
  min-height: 0;
  box-sizing: border-box;
  padding: 1.25rem 1.25rem 1.4rem;
}

/* The number+heading travel together as one block, pushed to the foot of the
   card; this single auto margin collects ALL the slack above them, so the
   elastic gap sits here rather than between heading and link. */
.cine-card-body .cine-row-num {
  display: block;
  margin-top: auto;
  margin-bottom: 0.3rem;
}

.cine-card-body h3 {
  margin: 0 0 0.75rem;
  font-size: clamp(1.35rem, 1.1rem + 1vw, 1.85rem);
  font-variation-settings: "wdth" 108;
  font-weight: 750;
  line-height: 1.08;
  letter-spacing: -0.02em;
  color: var(--paper, #f4f1ea);
  text-wrap: balance;
  /* The scrim does most of the work; this covers the worst case where a
     bright detail sits directly behind a letter. */
  text-shadow: 0 2px 18px rgba(7, 22, 15, 0.8);
}

/* margin-right: auto stops the flex column stretching the link — and its
   bottom border — to the card's full width; a flex child's default
   align-self: stretch overrides inline-flex on its own. margin-top stays at
   the base 1.1rem: the single auto margin lives on .cine-row-num above, and
   a second one here would split the slack between the two instead of
   collecting it all above the heading. */
.cine-card-body .cine-row-link {
  margin-top: 1.1rem;
  margin-right: auto;
}

/* The whole card is the tap target. ::after stretches the link over the card;
   the link keeps its visible label, so no clipping is involved here — see the
   README note about clip-path destroying stretched tap targets. */
.cine-card-body .cine-row-link::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 2;
}

.cine-card:hover .cine-card-media img {
  transform: scale(1.05);
  filter: saturate(0.95) contrast(1.1) brightness(0.9);
}

.cine-card:focus-within {
  outline: 3px solid var(--orange, #e86f28);
  outline-offset: 2px;
}

/* ---- Arrows -------------------------------------------------------------
   Hidden on touch-primary devices, where swiping is the natural gesture and
   a floating button would just cover a card. Shown wherever the pointer is
   fine (mouse/trackpad), which is the case the request was actually about.
   ------------------------------------------------------------------------- */

.cine-scroll-btn {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  z-index: 3;
  display: none;
  place-items: center;
  width: 3rem;
  height: 3rem;
  border: 0;
  border-radius: 50%;
  background: rgba(244, 241, 234, 0.94);
  color: var(--charcoal, #121614);
  box-shadow: 0 6px 22px rgba(7, 22, 15, 0.5);
  cursor: pointer;
  transition: background 0.25s ease, transform 0.25s ease, opacity 0.25s ease;
}

.cine-scroll-btn svg {
  width: 1.35rem;
  height: 1.35rem;
  fill: none;
  stroke: currentColor;
  stroke-width: 2.5;
  stroke-linecap: round;
  stroke-linejoin: round;
}

.cine-scroll-btn:hover { background: #fff; transform: translateY(-50%) scale(1.06); }
.cine-scroll-btn:focus-visible { outline: 3px solid var(--orange, #e86f28); outline-offset: 3px; }
.cine-scroll-btn[disabled] { opacity: 0; pointer-events: none; }
/* Positioned against the VIEWPORT edges, not .cine-scroller's edges. The
   scroller sits inside the 78rem content column while the track bleeds past
   it, so anchoring the buttons to the container left the "next" arrow
   stranded in the middle of the track on a wide screen instead of at the end
   of the visible cards. calc(50% - 50vw) is the container's own offset from
   the viewport edge, so this pins each button a fixed inset from the screen. */
.cine-scroll-btn--prev { left: calc(50% - 50vw + 0.75rem); }
.cine-scroll-btn--next { right: calc(50% - 50vw + 0.75rem); }

@media (hover: hover) and (pointer: fine) {
  /* .js gate: without JavaScript the buttons do nothing, so they must not
     appear at all. The track still scrolls by wheel and keyboard. */
  html.js .cine-scroll-btn { display: grid; }
}

/* Wider screens: smaller cards so more of the track is visible at once.
   These stay fixed widths, NOT 1fr — a fractional unit would divide the
   container between all six cards and remove the overflow the scroller
   depends on, leaving six squashed cards and dead arrows. */
@media (min-width: 48rem) {
  .cine-track { grid-auto-columns: 20rem; }
  .cine-card { aspect-ratio: 4 / 5; }
}

@media (min-width: 75rem) {
  .cine-track { grid-auto-columns: 22rem; }
}

/* -------------------------------------------------------------------------
   PINNED STATEMENT — a full-viewport panel with a graded photo behind it.
   GSAP pins this and steps the lines through; without JS it simply reads as
   a normal tall section, which is why the lines are visible by default.
   ------------------------------------------------------------------------- */

.cine-pin {
  position: relative;
  /* Fallback for browsers without svh/lvh support. */
  min-height: 100vh;
  /* svh is the SMALL viewport height (browser chrome expanded). That is the
     right unit for a section that merely scrolls past, but this one gets
     PINNED: GSAP fixes it against whatever the viewport is at that moment,
     which on a phone is usually the LARGE viewport once the address bar has
     collapsed. Sizing it to svh left the panel ~100px short of the screen for
     the whole pinned scroll, showing a band of page background underneath —
     lvh matches what it is actually pinned against, so it always fills.

     Trade-off, deliberately accepted: before the address bar collapses the
     panel is taller than the visible area, so the type sits marginally low.
     That is a moment during scroll; the white band was permanent. */
  min-height: 100lvh;
  display: grid;
  place-items: center;
  overflow: clip;
}

.cine-pin-media {
  position: absolute;
  inset: 0;
  z-index: 0;
}

/* This backdrop sits behind large display type, so it is graded much harder
   than the row photos — legibility beats showing the photograph off. */
.cine-pin-media img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center 45%;
  filter: saturate(0.75) contrast(1.12) brightness(0.62);
}

/* Darken hardest through the horizontal band where the type sits, and let the
   photograph stay visible above and below it. Contrast where it's needed,
   image where it isn't. */
.cine-pin-media::after {
  content: "";
  position: absolute;
  inset: 0;
  background:
    linear-gradient(
      to bottom,
      rgba(7, 22, 15, 0.42) 0%,
      rgba(7, 22, 15, 0.62) 32%,
      rgba(7, 22, 15, 0.88) 48%,
      rgba(7, 22, 15, 0.62) 66%,
      rgba(7, 22, 15, 0.45) 100%
    );
}

.cine-pin-inner {
  position: relative;
  z-index: 2;
  width: min(100% - 2.5rem, 62rem);
  text-align: center;
}

/* Steps only stack into a single cell once motion.js has confirmed GSAP is
   actually running (it sets .cine-ready). Keying off the .js class instead
   would overlap the lines whenever the libraries fail to load. */
.cine-steps {
  display: grid;
  gap: 1.5rem;
}

.cine-steps.cine-ready { gap: 0; }

/* Only the stacking is conditional — the type styling below always applies. */
.cine-steps.cine-ready .cine-step { grid-area: 1 / 1; }

.cine-step {
  margin: 0;
  font-size: clamp(1.9rem, 4.6vw, 4rem);
  font-variation-settings: "wdth" 108;
  font-weight: 750;
  line-height: 1.1;
  letter-spacing: -0.025em;
  text-wrap: balance;
  /* Insurance against whatever lands behind the type as the image drifts. */
  text-shadow: 0 2px 24px rgba(7, 22, 15, 0.85);
}

.cine-step em {
  font-style: normal;
  color: var(--orange, #e86f28);
}

/* -------------------------------------------------------------------------
   Responsive
   ------------------------------------------------------------------------- */

@media (max-width: 56rem) {
  /* One card per row. The card itself is already photo-over-text at every
     width, so only the track count changes here. */
  .cine-rows {
    grid-template-columns: 1fr;
    gap: 2.5rem;
  }
  .cine-row { gap: 1.25rem; }
  /* .cine-row-media keeps its 3/2 from the base rule — one column and two
     columns want the same crop, so there is nothing to override here. */

  /* Was 88svh, which is what produced the white band below the panel on
     phones: the section came out ~101px shorter than the viewport it was
     pinned against, and the page background showed through the gap for the
     whole pinned scroll. A pinned panel has to be a full viewport or it
     cannot cover the screen — the 88% shortfall had nowhere to hide. */
  .cine-pin {
    min-height: 100vh;
    min-height: 100lvh;
  }
}


/* Small-screen tracking tier for the dark-field eyebrows, mirroring the one
   in site.css. It has to be restated here: the rules above set
   letter-spacing on .page-hero .eyebrow and .cine-field .section-head
   .eyebrow from a later file, so a bare .eyebrow override in site.css cannot
   reach them. */
@media (max-width: 40em) {
  .page-hero .eyebrow,
  .cine-field .section-head .eyebrow {
    gap: 0.5rem;
    letter-spacing: 0.12em;
  }
}

/* -------------------------------------------------------------------------
   Reduced motion: strip every transform and transition. The layout still
   reads correctly because none of it depends on motion.
   ------------------------------------------------------------------------- */

@media (prefers-reduced-motion: reduce) {
  .cine-row-media img,
  .cine-row-link {
    transition: none;
  }
  .cine-row:hover .cine-row-media img { transform: none; }
  /* Steps are never stacked under reduced motion — motion.js bails before
     adding .cine-ready, so they simply read as three lines. */
}

/* =========================================================================
   SITE-WIDE CINEMATIC TREATMENT
   Everything below applies the same language to the shared components used
   across services / our-work / about / quote, so the whole site reads as one
   piece rather than one redesigned page bolted onto five old ones.
   ========================================================================= */

/* -------------------------------------------------------------------------
   Page heroes — the banner at the top of every inner page. Deepened to match
   the homepage field, with the same numeral + rule motif and display type.
   ------------------------------------------------------------------------- */

.page-hero {
  position: relative;
  background: var(--cine-field);
  padding-block: clamp(5rem, 11vw, 8.5rem) clamp(3.5rem, 7vw, 5.5rem);
  overflow: clip;
}

.page-hero h1 {
  max-width: 18ch;
  font-size: clamp(2.5rem, 6vw, 5rem);
  font-variation-settings: "wdth" 112;
  font-weight: 800;
  line-height: 0.96;
  letter-spacing: -0.03em;
  text-wrap: balance;
}

.page-hero .lede { color: rgba(244, 241, 234, 0.76); }

/* Reuse the homepage motif: an orange rule leading into the eyebrow. */
.page-hero .eyebrow {
  display: flex;
  align-items: center;
  gap: 0.85rem;
  letter-spacing: 0.18em;
}

.page-hero .eyebrow::before {
  content: "";
  width: 2.25rem;
  height: 2px;
  background: var(--cine-rule);
  flex: none;
}

/* -------------------------------------------------------------------------
   SERVICES PAGE — .svc rows already alternate, so they only need the dark
   field, graded imagery and sharper type.
   ------------------------------------------------------------------------- */

.section--tight {
  background: var(--cine-field);
  color: var(--paper, #f4f1ea);
}

.svc { border-top-color: var(--cine-hair); }

.section--tight .svc h2 {
  color: var(--paper, #f4f1ea);
  font-size: clamp(1.75rem, 3.2vw, 2.75rem);
  font-variation-settings: "wdth" 108;
  font-weight: 750;
  line-height: 1.04;
  letter-spacing: -0.022em;
}

.section--tight .svc p { color: rgba(244, 241, 234, 0.74); }
/* Stone, not moss: moss is a light-ground colour and only reaches 3.18:1 here. */
.section--tight .svc-kicker { color: var(--stone, #b8b4aa); }
.section--tight .svc-expect span { color: rgba(244, 241, 234, 0.8); }
.section--tight .svc-expect .icon { color: var(--orange, #e86f28); }

/* The one service with no photograph, given the same centred statement
   treatment as its homepage counterpart so the two pages agree.
   site.css already collapses .svc--no-media to one column and caps it at
   46rem; this centres the type inside that.

   Gated at 56.25em to match site.css's own .svc breakpoint — below it the
   row is already single-column and full-width, and centring there would be
   a change nobody asked for. */
@media (min-width: 56.25em) {
  /* Only the kicker and heading centre. The prose, tick-list and buttons keep
     their left edge: this block carries four ticked items and two CTAs, and
     centring body copy of that length costs more in readability than the
     symmetry buys. The centred head over a left-aligned body is the same
     shape the page heroes already use. */
  .section--tight .svc--no-media .svc-kicker,
  .section--tight .svc--no-media h2 { text-align: center; }

  .section--tight .svc--no-media h2 {
    font-size: clamp(1.9rem, 3.6vw, 3rem);
    font-variation-settings: "wdth" 110;
    line-height: 1.0;
    letter-spacing: -0.025em;
    text-wrap: balance;
  }
}

.svc-media {
  position: relative;
  overflow: hidden;
  border-radius: 2px;
}

.svc-media img {
  filter: saturate(0.82) contrast(1.12) brightness(0.82);
  transition: transform 0.45s cubic-bezier(0.16, 1, 0.3, 1), filter 0.3s ease;
}

.svc:hover .svc-media img {
  transform: scale(1.04);
  filter: saturate(0.95) contrast(1.1) brightness(0.94);
}

/* Placeholder illustrations read as bright holes in the dark field. */
.svc-media img[src$=".svg"] { filter: saturate(0.5) contrast(0.95) brightness(0.45); }
.svc:hover .svc-media img[src$=".svg"] { filter: saturate(0.6) contrast(0.95) brightness(0.55); }

.svc-media::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(200deg, rgba(7, 22, 15, 0.08) 0%, rgba(7, 22, 15, 0.55) 100%);
  pointer-events: none;
}

/* -------------------------------------------------------------------------
   OUR WORK — project cards on the dark field, photography leading.
   ------------------------------------------------------------------------- */

.work-grid { --cine-on-dark: 1; }

.work-card {
  background: rgba(244, 241, 234, 0.03);
  border: 1px solid var(--cine-hair);
  border-radius: 2px;
  overflow: hidden;
}

.work-card img {
  filter: saturate(0.85) contrast(1.1) brightness(0.86);
  transition: transform 0.45s cubic-bezier(0.16, 1, 0.3, 1), filter 0.3s ease;
}

.work-card:hover img {
  transform: scale(1.03);
  filter: saturate(0.98) contrast(1.08) brightness(0.98);
}

.work-card img[src$=".svg"] { filter: saturate(0.5) contrast(0.95) brightness(0.5); }

.work-card h3 {
  color: var(--paper, #f4f1ea);
  font-variation-settings: "wdth" 106;
  letter-spacing: -0.015em;
}

.work-card p { color: rgba(244, 241, 234, 0.72); }

/* -------------------------------------------------------------------------
   ABOUT — story block and values list on the dark field.
   ------------------------------------------------------------------------- */

.about-media {
  position: relative;
  overflow: hidden;
  border-radius: 2px;
}

.about-media img {
  filter: saturate(0.85) contrast(1.1) brightness(0.86);
  transition: transform 0.45s cubic-bezier(0.16, 1, 0.3, 1);
}

.about-media:hover img { transform: scale(1.03); }

/* -------------------------------------------------------------------------
   Final conversion banner — lifted to a full-bleed cinematic close.
   ------------------------------------------------------------------------- */

.banner {
  background: var(--cine-field);
  padding-block: clamp(4rem, 9vw, 7rem);
}

.banner h2 {
  font-size: clamp(2rem, 4.6vw, 3.5rem);
  font-variation-settings: "wdth" 110;
  font-weight: 800;
  line-height: 0.98;
  letter-spacing: -0.028em;
  text-wrap: balance;
}

/* -------------------------------------------------------------------------
   .cine-field — converts a stock light .section into the dark field.
   These sections were authored for a light background, so every piece of
   text inside has to be re-stated rather than left to inherit.
   ------------------------------------------------------------------------- */

.cine-field {
  background: var(--cine-field);
  color: rgba(244, 241, 234, 0.78);
}

/* A fractionally lifted variant, so two dark sections in a row still read as
   separate blocks rather than one undifferentiated slab. */
.cine-field--alt { background: #0a1c13; }

.cine-field h2,
.cine-field h3,
.cine-field h4,
.cine-field summary,
.cine-field strong,
.cine-field label,
.cine-field .label {
  color: var(--paper, #f4f1ea);
}

.cine-field h2 {
  font-variation-settings: "wdth" 110;
  font-weight: 800;
  line-height: 0.98;
  letter-spacing: -0.028em;
  text-wrap: balance;
}

.cine-field p,
.cine-field li,
.cine-field .lede { color: rgba(244, 241, 234, 0.74); }

.cine-field .eyebrow { color: var(--stone, #b8b4aa); }

/* Section heads get the same orange rule motif as the homepage. */
.cine-field .section-head .eyebrow {
  display: flex;
  align-items: center;
  gap: 0.85rem;
  letter-spacing: 0.18em;
}

.cine-field .section-head .eyebrow::before {
  content: "";
  width: 2.25rem;
  height: 2px;
  background: var(--cine-rule);
  flex: none;
}

/* Cards, panels and rules that assumed a light ground. */
.cine-field .faq-item,
.cine-field .review-card,
.cine-field .step,
.cine-field .value-list > li {
  border-color: var(--cine-hair);
}

.cine-field .faq-item { border-bottom: 1px solid var(--cine-hair); }

/* Inline links inherit --forest from site.css, which is dark green — barely
   visible on a dark field (1.42:1). Anywhere prose sits on .cine-field, links
   need the light treatment instead. */
.cine-field p a,
.cine-field li a,
.cine-field .faq-body a {
  color: var(--paper, #f4f1ea);
  text-decoration-color: var(--cine-rule);
  text-underline-offset: 0.2em;
}

.cine-field p a:hover,
.cine-field li a:hover,
.cine-field .faq-body a:hover {
  color: var(--orange, #e86f28);
  text-decoration-color: currentColor;
}

.cine-field .review-card,
.cine-field .form-panel {
  background: rgba(244, 241, 234, 0.035);
  border: 1px solid var(--cine-hair);
}

.cine-field .feature-icon {
  background: rgba(232, 111, 40, 0.12);
  color: var(--orange, #e86f28);
}

.cine-field .chip {
  background: rgba(244, 241, 234, 0.07);
  border-color: var(--cine-hair);
  color: rgba(244, 241, 234, 0.86);
}

.cine-field .chip--service {
  background: rgba(232, 111, 40, 0.16);
  border-color: rgba(232, 111, 40, 0.4);
  color: var(--orange, #e86f28);
}

/* Form controls need explicit dark styling or they render as white boxes.
   site.css types the four text inputs with an attribute selector
   (`.field input[type="text"]`, 0-2-1), which outranks a bare
   `.cine-field input` (0-2-0) — so those four kept the light treatment while
   select and textarea, which only reach 0-2-0 there, tied and won on source
   order. Matching that attribute shape below is what lands the dark
   treatment on all six controls.

   Scoped to `.field` deliberately: the honeypot at quote.html is an
   `input[type="text"]` outside any `.field`, and has no business being
   restyled. */
.cine-field .field input[type="text"],
.cine-field .field input[type="tel"],
.cine-field .field input[type="email"],
.cine-field input,
.cine-field textarea,
.cine-field select {
  background: rgba(7, 22, 15, 0.6);
  border-color: var(--cine-hair);
  color: var(--paper, #f4f1ea);
}

.cine-field input::placeholder,
.cine-field textarea::placeholder { color: rgba(244, 241, 234, 0.42); }

/* site.css styles :focus (not :focus-visible) with `border-color: var(--forest)`
   — about 1.3:1 on this field, so clicking into an input looked like the
   border vanished. It also sets a dark-green box-shadow ring that nothing
   here cleared, which muddied the orange outline below. Fix both. */
.cine-field .field input:focus,
.cine-field .field select:focus,
.cine-field .field textarea:focus {
  border-color: var(--orange, #e86f28);
  box-shadow: none;
}

.cine-field input:focus-visible,
.cine-field textarea:focus-visible,
.cine-field select:focus-visible {
  border-color: var(--orange, #e86f28);
  outline: 2px solid rgba(232, 111, 40, 0.45);
  outline-offset: 1px;
}

/* Error states were tuned for the light form panel. On this ground the error
   text measured 2.50:1 and the submit-failure banner worse still — both fail
   WCAG AA for text, on exactly the messages a stuck customer has to read.
   #e8705a is a lightened member of the existing warm-red family (5.60:1
   here), not a new hue. */
.cine-field .field.has-error input,
.cine-field .field.has-error select,
.cine-field .field.has-error textarea {
  border-color: #e8705a;
}

.cine-field .field .error { color: #e8705a; }

.cine-field .form-error {
  background: rgba(232, 112, 90, 0.12);
  border-color: rgba(232, 112, 90, 0.45);
  color: #e8705a;
}

.cine-field .section-note { color: rgba(244, 241, 234, 0.6); }

/* -------------------------------------------------------------------------
   Fixes for components that hardcode a light background or dark ink.
   Found by measuring rendered contrast, not by eye.
   ------------------------------------------------------------------------- */

/* .btn--outline uses dark ink for light grounds — invisible on the field. */
.cine-field .btn--outline,
.section--tight .btn--outline,
.cine .btn--outline {
  border-color: rgba(244, 241, 234, 0.42);
  color: var(--paper, #f4f1ea);
}

.cine-field .btn--outline:hover,
.section--tight .btn--outline:hover,
.cine .btn--outline:hover {
  border-color: var(--paper, #f4f1ea);
  color: var(--paper, #f4f1ea);
  background: rgba(244, 241, 234, 0.08);
}

/* The quote form and confirmation panel hardcode #fffdf8. */
.cine-field .quote-form,
.cine-field .confirmation {
  background: rgba(244, 241, 234, 0.04);
  border: 1px solid var(--cine-hair);
}

/* .label included: the radio-row's group heading is a <span class="label">
   (a span so it doesn't misuse <label> without a control) and must recolour
   with the real labels on this dark ground. */
.cine-field .quote-form label,
.cine-field .quote-form .label,
.cine-field .confirmation h2,
.cine-field .confirmation strong { color: var(--paper, #f4f1ea); }

.cine-field .quote-form .hint,
.cine-field .confirmation p { color: rgba(244, 241, 234, 0.72); }

/* Radio/checkbox pills also assume a light ground. */
.cine-field .choice,
.cine-field .contact-choice label {
  background: rgba(244, 241, 234, 0.05);
  border-color: var(--cine-hair);
  color: rgba(244, 241, 234, 0.86);
}

.cine-field input:checked + label,
.cine-field .choice:has(input:checked) {
  background: rgba(232, 111, 40, 0.16);
  border-color: rgba(232, 111, 40, 0.5);
  color: var(--paper, #f4f1ea);
}

/* Dropzone / file upload area. */
.cine-field .dropzone {
  background: rgba(7, 22, 15, 0.5);
  border-color: var(--cine-hair);
  color: rgba(244, 241, 234, 0.74);
}

.cine-field .urgent {
  background: rgba(232, 111, 40, 0.1);
  border-color: rgba(232, 111, 40, 0.35);
}

.cine-field code {
  background: rgba(244, 241, 234, 0.08);
  color: var(--paper, #f4f1ea);
}

/* FAQ items hardcode the same light panel colour. */
.cine-field .faq-item,
.cine-field details {
  background: rgba(244, 241, 234, 0.04);
  border: 1px solid var(--cine-hair);
}

.cine-field details summary { color: var(--paper, #f4f1ea); }
.cine-field details p { color: rgba(244, 241, 234, 0.74); }
.cine-field details summary::marker,
.cine-field details summary::-webkit-details-marker { color: var(--orange, #e86f28); }

/* Quote-page aside cards. The --dark variant already sits on forest green,
   so it only needs its border tightening; the plain ones need converting. */
.cine-field .aside-card {
  background: rgba(244, 241, 234, 0.04);
  border-color: var(--cine-hair);
  box-shadow: none;
}

.cine-field .aside-card h3 { color: var(--paper, #f4f1ea); }
.cine-field .aside-card p,
.cine-field .aside-card li,
.cine-field .aside-card ol { color: rgba(244, 241, 234, 0.74); }

.cine-field .aside-card--dark {
  background: rgba(18, 55, 42, 0.85);
  border-color: rgba(232, 111, 40, 0.3);
}

/* Remaining panels that hardcode #fffdf8, when they land on a dark field. */
.cine-field .step,
.cine-field .testimonial,
.cine-field .service-card {
  background: rgba(244, 241, 234, 0.04);
  border-color: var(--cine-hair);
  box-shadow: none;
}

.cine-field .step h3,
.cine-field .testimonial h3,
.cine-field .testimonial cite,
.cine-field .service-card h3 { color: var(--paper, #f4f1ea); }

.cine-field .step p,
.cine-field .testimonial p,
.cine-field .service-card p { color: rgba(244, 241, 234, 0.74); }

.cine-field .step-num,
.cine-field .testimonial .stars { color: var(--orange, #e86f28); }

.cine-field .badge-example {
  background: rgba(244, 241, 234, 0.1);
  border-color: var(--cine-hair);
  color: rgba(244, 241, 234, 0.7);
}
