/* ==========================================================================
   Tradie Websites - "editorial" base stylesheet, v1 (2026-09-15)

   The look: off-white ground, near-black ink, one grey. One font weight.
   Hairline-ruled columns with a tiny label top-left and an index numeral
   bottom-left. No cards, no drop shadows, no brand colour anywhere.
   Colour comes from the client's own photographs and nothing else.

   Derived from lightspark.com, measured rather than eyeballed. Type is one
   notch larger than theirs because these sites are read by homeowners on
   phones, not by developers on 27in monitors.

   To reskin for a new client: change nothing in here except --tile-r if
   their photos suit sharper corners. Everything client-specific lives in
   the HTML. Keep the stylesheet self-contained so the client can own their
   files outright.
   ========================================================================== */

:root {
  /* ink and ground */
  --bg:         #f8f8f7;
  --surface:    #f0f0ee;
  --ink:        #1a1a1a;
  --ink-deep:   #020202;
  --grey:       #7c7c7c;
  --grey-mid:   #5f5f5c;
  --rule:       rgba(38, 38, 35, .13);

  /* dark footer */
  --dark:       #1a1a1a;
  --dark-ink:   #ececea;
  --dark-grey:  #8c8c88;
  --dark-rule:  rgba(255, 255, 255, .11);
  --dark-ghost: rgba(255, 255, 255, .038);

  /* THE ONE COLOUR. The page is otherwise monochrome on purpose, so the only
     thing wearing a colour is the thing we want pressed: the phone number.
     Every other call to action stays ink.

     Swap these three per client if they have a colour of their own, and
     MEASURE all three rather than eyeballing them (the house values are
     4.65:1 on ground, 4.95:1 white-on-button, 8.27:1 on the dark footer).
     Robyn Pattison's rose is the worked example: 5.53 / 5.87 / 8.93. */
  --call:       #4a7c3f;
  --call-deep:  #3d6834;
  --call-light: #86c274;

  --wrap: 1120px;
  --gut: 20px;
  --bar: 64px;     /* sticky call bar height, phones only */
  --r: 4px;        /* the only radius on furniture */
  --tile-r: 22px;  /* photo tiles and media panels */
}

* { box-sizing: border-box; }
html { scroll-behavior: smooth; -webkit-text-size-adjust: 100%; }

body {
  margin: 0;
  background: var(--bg);
  color: var(--ink);
  font-family: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
               Helvetica, Arial, sans-serif;
  font-size: 16px;
  font-weight: 400;
  line-height: 1.45;
  letter-spacing: -.005em;
  -webkit-font-smoothing: antialiased;
  padding-bottom: var(--bar);
}

img, video { display: block; max-width: 100%; height: auto; }
a { color: inherit; text-decoration: none; }

/* --------------------------------------------------------------------------
   Type. One weight (400) carries the whole page; size and colour do the
   rest of the work. Tight negative tracking on anything large.
   -------------------------------------------------------------------------- */

h1, h2, h3 { font-weight: 400; margin: 0; }
h1 { font-size: clamp(2.15rem, 5vw, 3.6rem); line-height: 1.08; letter-spacing: -.04em; }
h2 { font-size: clamp(1.75rem, 3.2vw, 2.5rem); line-height: 1.12; letter-spacing: -.035em; }
h3 { font-size: 1.0625rem; line-height: 1.35; letter-spacing: -.015em; }
p  { margin: 0; }

.lede   { font-size: 1.0625rem; line-height: 1.5; color: var(--grey-mid); max-width: 56ch; }
.small  { font-size: .875rem; line-height: 1.45; color: var(--grey-mid); }

/* the tiny uppercase-free label that sits above every block */
.label {
  font-size: .6875rem; line-height: 1.3; color: var(--grey);
  letter-spacing: 0; margin: 0;
}
/* the index numeral that closes every column */
.idx {
  font-size: .625rem; line-height: 1; color: var(--grey);
  font-variant-numeric: tabular-nums; letter-spacing: .02em;
}

.wrap { width: 100%; max-width: var(--wrap); margin: 0 auto; padding: 0 var(--gut); }

/* the header is fixed, so every anchor target has to duck under it */
:target, section[id], article[id], div[id] { scroll-margin-top: 88px; }

/* --------------------------------------------------------------------------
   Buttons. Small, square-ish, two flavours only: solid ink and quiet grey.
   -------------------------------------------------------------------------- */

.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: .45em;
  height: 38px; padding: 0 16px; border-radius: var(--r);
  font-size: .875rem; letter-spacing: -.01em; white-space: nowrap;
  border: 1px solid transparent; cursor: pointer;
  transition: background .18s ease, color .18s ease, border-color .18s ease;
}
.btn--solid { background: var(--ink); color: var(--bg); }
.btn--solid:hover { background: #000; }
.btn--quiet { background: var(--surface); color: var(--ink); }
.btn--quiet:hover { background: #e6e6e3; }
.btn--line  { border-color: var(--rule); color: var(--ink); }
.btn--line:hover { border-color: var(--ink); }
/* the phone number: bigger than everything around it, and the only green */
.btn--call {
  background: var(--call); color: #fff;
  height: 44px; padding: 0 20px; font-size: .95rem; letter-spacing: -.015em;
}
.btn--call:hover { background: var(--call-deep); }
.btn--call svg { width: 15px; height: 15px; stroke: currentColor; fill: none; stroke-width: 2; }
.btn-row { display: flex; flex-wrap: wrap; gap: 8px; }

/* an arrow link, the page's only recurring interactive flourish */
.arrow { display: inline-flex; align-items: baseline; gap: .4em; transition: opacity .18s ease; }
.arrow::after { content: "\2192"; }
.arrow:hover { opacity: .55; }

/* --------------------------------------------------------------------------
   Header. Transparent over the hero, no border until scrolled.
   -------------------------------------------------------------------------- */

.header {
  position: fixed; top: 0; left: 0; right: 0; z-index: 40;
  height: 68px; display: flex; align-items: center;
  /* The bar spans the full width but is mostly empty. Left clickable it eats
     every click in the top 68px of the page, which killed the hero tiles that
     drift up there. Only the furniture inside .wrap takes pointer events. */
  pointer-events: none;
}
/* Lightspark's "edge blur": no bar, no border, just a softening of whatever
   scrolls underneath, faded out at the bottom so it has no visible edge. */
.header::before {
  content: ""; position: absolute; inset: -12px 0 -28px; pointer-events: none;
  backdrop-filter: blur(9px); -webkit-backdrop-filter: blur(9px);
  mask-image: linear-gradient(to bottom, #000 58%, transparent 100%);
  -webkit-mask-image: linear-gradient(to bottom, #000 58%, transparent 100%);
}
.header { height: 68px; }
.header .wrap { position: relative; z-index: 1; display: flex; align-items: center; gap: 20px; pointer-events: auto; }
.brand { font-size: 1.0625rem; letter-spacing: -.035em; margin-right: auto; }
.brand b { font-weight: 400; }
.brand span { color: var(--grey); }
.nav { display: none; gap: 22px; font-size: .875rem; color: var(--grey-mid); }
.nav a:hover, .nav a[aria-current="page"] { color: var(--ink); }
.header .btn { display: none; }
.header__menu {
  font-family: inherit; font-size: .875rem; letter-spacing: -.01em;
  color: var(--ink); background: none; border: 0; padding: 6px 0; cursor: pointer;
}

/* phone navigation panel, opened by .header__menu */
.menu {
  position: fixed; top: 68px; left: 0; right: 0; z-index: 39;
  background: var(--bg); border-bottom: 1px solid var(--rule);
  padding: 4px var(--gut) 20px;
  display: flex; flex-direction: column;
}
.menu[hidden] { display: none; }
.menu a {
  padding: 13px 0; border-top: 1px solid var(--rule);
  font-size: 1.0625rem; letter-spacing: -.02em;
}
.menu a:first-child { border-top: 0; }
.menu .btn { margin-top: 16px; align-self: flex-start; }
.menu .btn:first-child { border-top: 0; }

/* --------------------------------------------------------------------------
   Hero. A field of the client's photographs drifting slowly behind centred
   copy, with the hero film as a sharp tile above the headline.

   The drift is two independent animations per tile: `translate` for X and
   `transform` for Y, so they can run at different periods without one
   clobbering the other. Periods are prime-ish and desynced by delay so the
   field never visibly repeats.
   -------------------------------------------------------------------------- */

.hero { position: relative; overflow: hidden; padding: 78px 0 36px; }
.hero__inner {
  position: relative; z-index: 2;
  max-width: 640px; margin: 0 auto; text-align: center;
  display: flex; flex-direction: column; align-items: center;
  /* The copy column is mostly empty margin sitting on top of the tile field.
     Left solid it blocks clicks on the tiles either side of the film, so the
     container is transparent and only its actual contents take the pointer. */
  pointer-events: none;
}
.hero__inner > * { pointer-events: auto; }
.hero__sub { margin: 14px 0 0; color: var(--grey-mid); font-size: 1rem; max-width: 44ch; }
.hero .btn-row { margin-top: 22px; justify-content: center; }

/* the film: portrait footage, shown far below native size so it stays sharp */
.film {
  position: relative; width: 196px; aspect-ratio: 9 / 16; margin: 0 auto 22px;
  border-radius: var(--tile-r); overflow: hidden; background: var(--surface);
}
.film video, .film img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }

.field { position: absolute; inset: 0; z-index: 1; overflow: hidden; pointer-events: none; }
.tile {
  position: absolute; display: block;
  left: var(--x); top: var(--y); width: var(--w);
  aspect-ratio: 1;
  border-radius: var(--tile-r); overflow: hidden;
  animation: wander-x var(--dx, 13s) ease-in-out infinite alternate,
             wander-y var(--dy, 17s) ease-in-out infinite alternate;
  animation-delay: var(--lx, 0s), var(--ly, 0s);
  will-change: transform, translate;
  /* the field itself is inert so the copy stays selectable; the tiles are not,
     because each one links to the service it is a photograph of */
  pointer-events: auto; cursor: pointer;
}
/* The blur and the fade live on the PHOTO, not on the tile. Putting them on
   the tile blurred its own label with it, so the middle copy went soft on
   every out-of-focus tile and vanished on the faded ones. The photo is scaled
   a touch so the blur has pixels to sample past the edge and the tile's
   rounded corner stays clean instead of haloing. */
.tile img {
  opacity: var(--o, 1);
  filter: blur(var(--b, 0px));
  transform: scale(1.06);
  transition: filter .35s ease, opacity .35s ease, transform .35s ease;
}
.tile:hover img, .tile:focus-visible img { filter: blur(0); opacity: 1; transform: scale(1); }
.tile:focus-visible { outline: 2px solid var(--ink); outline-offset: 3px; }

/* On hover a tile says where it goes. Without this the field is a row of
   pretty photographs that happen to be links, and nobody clicks it. */
.tile__label {
  position: absolute; left: 7px; right: 7px; bottom: 7px;
  padding: 6px 9px; border-radius: 999px;
  background: rgba(248, 248, 247, .95); color: var(--ink);
  font-size: .7rem; line-height: 1.25; letter-spacing: -.015em; text-align: center;
  backdrop-filter: blur(6px); -webkit-backdrop-filter: blur(6px);
  opacity: 0; transform: translateY(5px);
  transition: opacity .2s ease, transform .2s ease;
}
.tile:hover .tile__label,
.tile:focus-visible .tile__label { opacity: 1; transform: none; }

/* At rest every tile names itself in white across the middle of the photo, so
   the field reads as a set of services rather than as decoration. Hovering
   clears the middle and hands the naming over to the pill at the bottom, which
   is why the two never show at once. The middle copy is aria-hidden: the pill
   already carries the accessible name and we do not want it announced twice.
   Sized off the tile's own --w so a 250px tile and an 84px tile both read. */
.tile__mid {
  position: absolute; inset: 0; z-index: 2;
  display: flex; align-items: center; justify-content: center;
  padding: 10px; text-align: center; text-wrap: balance;
  color: #fff;
  font-size: clamp(.58rem, calc(var(--w) * .088), 1.05rem);
  line-height: 1.22; letter-spacing: -.01em;
  text-shadow: 0 1px 2px rgba(0, 0, 0, .62), 0 2px 10px rgba(0, 0, 0, .55);
  opacity: 1; transition: opacity .25s ease;
  pointer-events: none;
}
/* a soft veil so white copy holds on a beach or a white dress, gone on hover */
.tile__mid::before {
  content: ""; position: absolute; inset: 0; z-index: -1;
  background: radial-gradient(ellipse at center, rgba(0, 0, 0, .36), rgba(0, 0, 0, .08) 74%);
}
.tile:hover .tile__mid,
.tile:focus-visible .tile__mid { opacity: 0; }
/* each tile crops its own photo to the subject, never to a patch of sky */
.tile img { width: 100%; height: 100%; object-fit: cover; object-position: var(--op, center); }

@keyframes wander-x {
  from { translate: calc(var(--ax, 26px) * -1) 0; }
  to   { translate: var(--ax, 26px) 0; }
}
@keyframes wander-y {
  from { transform: translateY(calc(var(--ay, 30px) * -1)); }
  to   { transform: translateY(var(--ay, 30px)); }
}

/* --------------------------------------------------------------------------
   Section furniture.
   -------------------------------------------------------------------------- */

.section { padding: 52px 0; }
.section--tight { padding: 32px 0; }
.section__head {
  display: flex; align-items: flex-start; justify-content: space-between;
  gap: 20px; flex-wrap: wrap; margin-bottom: 26px;
}

/* a centred statement paragraph: first sentence ink, the rest grey */
.statement { max-width: 560px; margin: 0 auto; font-size: 1.0625rem; line-height: 1.5; color: var(--grey-mid); }
.statement b { font-weight: 400; color: var(--ink); }

/* --------------------------------------------------------------------------
   The hairline column band. Used for stats, links and the footer. A tiny
   label pins to the top, the payload sits in the middle, the index numeral
   pins to the bottom. Cells are separated by 1px rules, never boxes.
   -------------------------------------------------------------------------- */

.band {
  display: grid; grid-template-columns: repeat(2, 1fr);
  border-right: 1px solid var(--rule);
}
.band--4 { grid-template-columns: repeat(2, 1fr); }
/* Lightspark runs these columns 450px tall with the payload floating in the
   middle, which works when every cell carries a paragraph. Ours carry three
   words, so all that height reads as a mistake. Condensed: label, value and
   index numeral stacked tight, height driven by content. */
.col {
  border-left: 1px solid var(--rule);
  padding: 13px 12px 13px;
  display: flex; flex-direction: column; align-items: flex-start; gap: 3px;
}
.col__mid { display: block; margin: 2px 0 0; padding: 0; }
.col__val { font-size: 1.6rem; line-height: 1.15; letter-spacing: -.035em; display: block; }
.col__cap { font-size: .8125rem; line-height: 1.35; margin-top: 3px; display: block; color: var(--grey-mid); }
/* Cells in a grid row all stretch to the tallest, so pushing the numeral to
   the bottom lines them up across the band. Safe now the columns are
   content-height: on the old 420px cells this is what stranded them. */
.col .idx { margin-top: auto; padding-top: 10px; }

/* --------------------------------------------------------------------------
   Numbered rows. A hairline with the index numeral at its left shoulder,
   then label + copy on the left, media in the centre, links on the right.
   -------------------------------------------------------------------------- */

.rows { margin-top: 20px; }
.rowhead { display: flex; align-items: center; gap: 10px; }
.rowhead .hair { flex: 1; height: 1px; background: var(--rule); }
.row { display: grid; gap: 20px; padding: 20px 0 34px; }
.row__copy { max-width: 42ch; }
.row__copy .small { margin-top: 6px; }
.row__media {
  border-radius: var(--tile-r); overflow: hidden; display: block;
  transition: opacity .25s ease;
  aspect-ratio: 4 / 5; max-width: 280px; margin-inline: auto; width: 100%;
}
.row__media img, .row__media video {
  width: 100%; height: 100%; object-fit: cover; object-position: var(--op, center);
}
a.row__media:hover { opacity: .82; }
a.row__media:focus-visible { outline: 2px solid var(--ink); outline-offset: 3px; }
.row__links { display: flex; flex-direction: column; gap: 4px; font-size: 1rem; letter-spacing: -.02em; }
/* an inner page can put a spec list in the right column instead of links */
.row__checks { align-self: start; padding-top: 3px; }
.row__checks li { font-size: .875rem; }

/* --------------------------------------------------------------------------
   The services rail. A full-bleed horizontal strip of the client's photos,
   one per service, each a link. It drifts slowly on its own and stops the
   moment anyone touches it.

   Position is held in ONE place, the container's scrollLeft, so the auto
   advance and a manual drag can never fight each other. The card list is
   duplicated in the HTML and the script wraps at the halfway mark, which is
   what makes the loop seamless.
   -------------------------------------------------------------------------- */

.rail-head { display: flex; align-items: flex-end; justify-content: space-between; gap: 20px; }
.rail-nav { display: flex; gap: 8px; }
.rail-nav button {
  width: 34px; height: 34px; border-radius: 999px; cursor: pointer;
  border: 1px solid var(--rule); background: none; color: var(--ink);
  font-family: inherit; font-size: .95rem; line-height: 1;
  display: grid; place-items: center; transition: border-color .18s ease, background .18s ease;
}
.rail-nav button:hover { border-color: var(--ink); }
.rail-nav button:disabled { opacity: .35; cursor: default; }

.rail {
  display: flex; gap: 12px; margin-top: 20px;
  overflow-x: auto; overflow-y: hidden;
  padding: 2px var(--gut);
  scrollbar-width: none; -ms-overflow-style: none;
  overscroll-behavior-x: contain;
  -webkit-overflow-scrolling: touch;
}
.rail::-webkit-scrollbar { display: none; }

.railcard {
  flex: 0 0 auto; display: block; position: relative;
  width: 156px; aspect-ratio: 3 / 4;
  border-radius: var(--tile-r); overflow: hidden;
  background: var(--surface);
}
.railcard img { width: 100%; height: 100%; object-fit: cover; object-position: var(--op, center); }
.railcard__label {
  position: absolute; left: 9px; top: 9px; max-width: calc(100% - 18px);
  padding: 5px 11px; border-radius: 999px;
  background: rgba(248, 248, 247, .93); color: var(--ink);
  font-size: .75rem; letter-spacing: -.01em; line-height: 1.25;
  backdrop-filter: blur(6px); -webkit-backdrop-filter: blur(6px);
}
.railcard:focus-visible { outline: 2px solid var(--ink); outline-offset: 3px; }

/* --------------------------------------------------------------------------
   Folding cards. Each service is a card that sticks below the header while
   the next one scrolls up and covers it, so the section deals itself out
   like a hand of cards. Pure CSS: every card is sticky at the same top, and
   a per-card offset (--i) leaves the edge of the one behind showing.
   -------------------------------------------------------------------------- */

.stack { margin-top: 22px; }
.stack__card {
  position: sticky; top: calc(88px + var(--i, 0) * 10px);
  background: var(--surface); border: 1px solid var(--rule);
  border-radius: var(--tile-r); padding: 20px;
  display: grid; align-content: start; gap: 16px;
  /* EVERY CARD MUST BE EXACTLY THE SAME HEIGHT. Each sits 10px lower than
     the last, so an equal card covers the one behind it and only its top
     edge shows; a shorter card lets the previous card's BOTTOM poke out,
     which reads as a broken layout. Below 900px the photo grows with the
     viewport and the copy rewraps, so no min-height can equalise them:
     the card height is fixed instead and the photo (the 1fr row) absorbs
     whatever the copy does not use. */
  height: 560px; grid-template-rows: auto auto 1fr;
}
.stack__card + .stack__card { margin-top: 16px; }
.stack__card--dark { background: var(--dark); color: var(--dark-ink); border-color: var(--dark); }
.stack__card--dark .label, .stack__card--dark .idx { color: var(--dark-grey); }
.stack__card--dark .small { color: #b9b9b5; }
.stack__card--dark .checks li { color: #b9b9b5; }
.stack__card--dark .checks li::before { border-color: var(--dark-ink); }
.stack__card--dark .btn--line { border-color: rgba(255,255,255,.28); color: var(--dark-ink); }
.stack__card--dark .btn--line:hover { border-color: var(--dark-ink); }

.stack__top { display: flex; align-items: baseline; justify-content: space-between; gap: 14px; }
.stack__media { border-radius: 14px; overflow: hidden; background: var(--bg); min-height: 0; }
.stack__media img { width: 100%; height: 100%; object-fit: cover; object-position: var(--op, center); }
.stack__body h3 { font-size: 1.5rem; letter-spacing: -.03em; }
.stack__body .small { margin-top: 8px; max-width: 42ch; }
.stack__body .checks { margin-top: 14px; }
.stack__body .btn-row { margin-top: 18px; }
/* a card with no photograph, used for the closer */
.stack__card--solo { grid-template-columns: 1fr !important; }

/* --------------------------------------------------------------------------
   Media cells: the work reel, before/after pairs, review quotes. Same
   hairline logic as .band, but the payload is a picture or a quote.
   -------------------------------------------------------------------------- */

.cells { display: grid; grid-template-columns: 1fr; border-right: 1px solid var(--rule); }
.cell {
  border-left: 1px solid var(--rule);
  padding: 14px 12px 12px;
  display: flex; flex-direction: column; gap: 12px;
}
.cell__media { border-radius: var(--tile-r); overflow: hidden; background: var(--surface); position: relative; }
.cell__media video, .cell__media img { width: 100%; height: 100%; object-fit: cover; position: absolute; inset: 0; }
.cell .idx { margin-top: 4px; }

/* a before/after pair living inside one cell */
.pairgrid { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; }
.pairgrid figure { margin: 0; }
.pairgrid figcaption { margin-top: 7px; }

.quote { font-size: 1.0625rem; line-height: 1.45; letter-spacing: -.02em; margin: 0; }
.quote__by { font-size: .8125rem; color: var(--grey); }
.stars { font-size: .875rem; letter-spacing: .12em; color: var(--ink); }

/* --------------------------------------------------------------------------
   Footer. Dark, a large call-to-action line, hairline link columns, and a
   giant ghosted wordmark underneath.
   -------------------------------------------------------------------------- */

.footer { background: var(--dark); color: var(--dark-ink); padding: 48px 0 18px; margin-top: 16px; }
.footer .band, .footer .col { border-color: var(--dark-rule); }
.footer .label, .footer .idx { color: var(--dark-grey); }
.footer__cta {
  font-size: clamp(1.75rem, 4.4vw, 2.75rem); letter-spacing: -.04em; line-height: 1.1;
  display: inline-flex; gap: .3em; color: var(--call-light);
}
.footer__cta:hover { color: #9ad489; }
.footer__links a[href^="tel:"] { color: var(--call-light); }
.footer__links a[href^="tel:"]:hover { color: #9ad489; }
/* a phone number anywhere in a hairline column leads its cell */
.col a[href^="tel:"] { color: var(--call); font-size: 1.15rem; letter-spacing: -.02em; }
.col a[href^="tel:"]:hover { color: var(--call-deep); }
.dl dd a[href^="tel:"] { color: var(--call); font-size: 1.05rem; }
.footer__cols { margin-top: 28px; }
.footer__links { display: flex; flex-direction: column; gap: 5px; font-size: .875rem; margin-top: 12px; }
.footer__links span { color: var(--dark-grey); }
.footer__links a { color: var(--dark-ink); }
.footer__links a:hover { color: #fff; }
.footer__mark {
  font-size: clamp(3.2rem, 14.5vw, 11.2rem); line-height: .85; letter-spacing: -.055em;
  color: var(--dark-ghost); margin: 30px 0 20px; user-select: none;
  white-space: nowrap; overflow: hidden;
}
.footer__legal {
  border-top: 1px solid var(--dark-rule); padding-top: 16px;
  display: flex; flex-wrap: wrap; gap: 6px 18px;
  font-size: .75rem; color: var(--dark-grey);
}

/* --------------------------------------------------------------------------
   Sticky call bar, phones only. Everything above it must clear it: check
   the hero CTA with getBoundingClientRect, this has bitten us before.
   -------------------------------------------------------------------------- */

.callbar {
  position: fixed; left: 0; right: 0; bottom: 0; z-index: 60; display: flex;
  height: var(--bar); background: var(--dark); border-top: 1px solid var(--dark-rule);
}
.callbar a {
  flex: 1; display: flex; align-items: center; justify-content: center; gap: .5em;
  color: #fff; font-size: .9375rem;
}
/* the call half is green and takes the larger share of the bar */
.callbar a:first-child { background: var(--call); flex: 1.45; font-size: 1.0625rem; letter-spacing: -.015em; }
.callbar a + a { border-left: 1px solid var(--dark-rule); color: #c9c9c5; }
.callbar svg { width: 16px; height: 16px; stroke: currentColor; fill: none; stroke-width: 2; }

/* --------------------------------------------------------------------------
   Inner pages: breadcrumb, FAQ, definition lists.
   -------------------------------------------------------------------------- */

.crumb { font-size: .8125rem; color: var(--grey); padding-top: 88px; }
.crumb a:hover { color: var(--ink); }

.faq details { border-top: 1px solid var(--rule); padding: 14px 0; }
.faq details:last-of-type { border-bottom: 1px solid var(--rule); }
.faq summary {
  cursor: pointer; list-style: none; display: flex; gap: 16px;
  justify-content: space-between; font-size: 1.0625rem; letter-spacing: -.02em;
}
.faq summary::-webkit-details-marker { display: none; }
.faq summary::after { content: "+"; color: var(--grey); font-size: 1.15rem; line-height: 1; }
.faq details[open] summary::after { content: "\2212"; }
.faq details p { margin-top: 10px; color: var(--grey-mid); font-size: .9375rem; max-width: 62ch; }

.dl { margin: 0; }
.dl > div { display: flex; gap: 14px; padding: 9px 0; border-top: 1px solid var(--rule); }
.dl > div:first-child { border-top: 0; padding-top: 0; }
.dl dt { flex: 0 0 96px; font-size: .875rem; color: var(--grey); }
.dl dd { margin: 0; font-size: .875rem; }

.checks { list-style: none; padding: 0; margin: 0; }
.checks li {
  position: relative; padding-left: 18px; margin-bottom: 7px;
  font-size: .9375rem; color: var(--grey-mid);
}
.checks li::before {
  content: ""; position: absolute; left: 0; top: .5em;
  width: 8px; height: 4px; border-left: 1.5px solid var(--ink); border-bottom: 1.5px solid var(--ink);
  transform: rotate(-45deg);
}

/* --------------------------------------------------------------------------
   Reveal on scroll. Deliberately small: 10px and a fade, nothing that
   reads as a slide deck.
   -------------------------------------------------------------------------- */

.reveal { opacity: 0; transform: translateY(10px); transition: opacity .55s ease, transform .55s ease; }
.reveal.is-in { opacity: 1; transform: none; }

/* --------------------------------------------------------------------------
   Breakpoints.
   -------------------------------------------------------------------------- */

/* On a very narrow phone the copy eats the card and the photo collapses to a
   sliver, so the spec list goes and the picture gets the room back. */
@media (max-width: 374px) {
  .stack__body .checks { display: none; }
}

/* A pinned card needs the viewport height to hold it: 88px of offset, the card
   itself, and the call bar underneath. On a short phone it cannot, and sticky
   would pin a card whose bottom the reader can then never reach. Below that
   the stack becomes an ordinary list, which also frees the height to be auto,
   so the equal-height rule stops mattering here. */
@media (max-width: 899px) and (max-height: 730px) {
  .stack__card { position: static; height: auto; }
}

/* Phones: pen the tile field into a band behind the film so no tile can ever
   sit under the headline. Tiles carry a second position (--xm / --ym) for
   this band; .tile--off drops the ones the narrow field has no room for. */
@media (max-width: 899px) {
  .field { bottom: auto; height: 430px; }
  /* the larger desktop drift would walk a tile under the headline in a 430px
     band, so the amplitude is reined back in here rather than at the source */
  .tile { left: var(--xm, var(--x)); top: var(--ym, var(--y)); --ax: 13px; --ay: 15px; }
  .tile--off { display: none; }
  .tile__mid { font-size: clamp(.56rem, calc(var(--w) * .1), .82rem); }
}

@media (min-width: 620px) {
  .railcard { width: 186px; }
  .band, .band--4 { grid-template-columns: repeat(4, 1fr); }
  .cells { grid-template-columns: repeat(2, 1fr); }
  .col { padding: 15px 14px; }
  .col__val { font-size: 1.75rem; }
}

@media (min-width: 900px) {
  body { padding-bottom: 0; }
  .callbar { display: none; }
  .nav { display: flex; }
  .header .btn { display: inline-flex; }
  /* .section--tight has to be restated here: the plain .section rule below is
     later in the source and the two have equal specificity, so without this
     the "tight" sections quietly take the full padding. They were 59% empty
     space before anyone measured. */
  .section { padding: 72px 0; }
  .section--tight { padding: 40px 0; }
  .crumb { padding-top: 104px; }

  .hero { padding: 112px 0 64px; min-height: 84vh; display: flex; align-items: center; }
  .hero__inner { max-width: 680px; }
  .hero__sub { font-size: 1.0625rem; margin-top: 18px; }
  .film { width: 214px; margin-bottom: 28px; }

  .col { padding: 17px 16px; }
  .col__val { font-size: 2rem; }

  .row { grid-template-columns: 250px 1fr 250px; gap: 40px; align-items: center; padding: 26px 0 44px; }
  .row__media { justify-self: center; width: 100%; max-width: 340px; }
  .row__links { text-align: right; align-items: flex-end; font-size: 1.0625rem; }

  .railcard { width: 210px; }
  .rail { gap: 14px; margin-top: 26px; }

  /* Every card must be the SAME height. Each one sits 10px lower than the
     last, so an equal-height card fully covers the one behind it; a card
     that is shorter than its predecessor leaves that card's bottom edge
     poking out, which reads as a bug. Keep the copy to a similar length and
     measure scrollHeight against clientHeight before shipping. */
  /* Desktop: the photo column is a fixed width, so height stops depending on
     the viewport and a plain min-height equalises the cards cleanly. */
  .stack__card {
    padding: 26px; gap: 30px; align-items: center;
    grid-template-columns: minmax(0, 1fr) 360px;
    grid-template-rows: auto auto; height: auto; min-height: 392px;  /* = the tallest card's natural height; see the note above */
  }
  .stack__card .stack__top { grid-column: 1 / -1; }
  .stack__media { aspect-ratio: 4 / 3; order: 2; grid-row: 2; }
  .stack__body { order: 1; grid-row: 2; }

  .cells--3 { grid-template-columns: repeat(3, 1fr); }
  .footer__cols .band { grid-template-columns: repeat(4, 1fr); }
  .footer { padding-top: 60px; }
  .footer__links { margin-top: 16px; }
  .header__menu, .menu { display: none !important; }
}

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  .reveal { opacity: 1; transform: none; transition: none; }
  .tile { animation: none; }
  /* the rail stops advancing on its own but stays scrollable by hand */
  .stack__card { position: static; }
}
