/* Beara Joinery design layer, built from mockup E and the seven page mockups (2026-09-23).
   Host Grotesk, white ground, the van blue as the one accent (provisional until a daylight
   photograph of the van arrives), sharp corners, photographs at their natural shape.
   One type scale, stated here (structure audit, 2026-09-28): five sizes. 56 page title, 32 section,
   24 job, tile, phone number and next page, 18 body, 15 caption and label; the lead under a title at
   about 21, between body and job. Nothing under 15px, sentence case, weight 500 for headings.
   The engine's working parts it still needs (rich text, focus rings, social icons) are at the end. */
:root {
  --white: #FFFFFF;
  --ink: #15181B;
  --ink-2: #5C6470;
  --ink-3: #4A515C; /* captions and dates: a little darker than the grey of form notes */
  --line: #E1E4E8;
  --tint: #F1F5FA; /* the pale ground of a shaded band: a tint of the blue */
  --blue: #1F5AA6;
  --blue-d: #184A8A;
  --green: #6CC24A;
  --mark: var(--blue); /* the wordmark, one colour: the blue on white, white on the blue footer and over the hero (T, 2026-10-10: no blue and green split) */
  --pad: 48px;
  --gap: 24px;
  --row: 56px;
  --sans: 'Host Grotesk', system-ui, -apple-system, 'Helvetica Neue', Arial, sans-serif;
  --serif: 'Newsreader', Georgia, 'Times New Roman', serif; /* the small words: captions and the lead line (T, 2026-10-11) */
  --h1: clamp(2.5rem, 3.9vw, 3.5rem); /* 40px on a phone (was 35): the title stays well clear of the wordmark */
  --h2: clamp(1.6rem, 2.3vw, 2rem);
  --h3: clamp(1.25rem, 1.7vw, 1.5rem);
  --lead: var(--h3); /* the lead is the 24px step (20px on a phone): at 20.8px it sat 2.8px off the body and read as a mistake (T, 2026-10-05) */
  --nav: 16px;       /* the menu's links, a step below the body, so the wordmark leads the header */
  --body: 18px;
  --note: 16px;      /* captions and dates under the photographs: they explain the work, so a step above form labels */
  --small: 15px;
  /* engine names, so its partials (social icons, consent nudge, admin previews) pick up the palette */
  --bg: var(--white); --text: var(--ink); --text-2: var(--ink-2); --text-3: var(--ink-2);
  --accent: var(--blue); --accent-l: var(--blue-d); --divider: var(--line);
}
* { box-sizing: border-box; }
html { scroll-behavior: smooth; }
body { margin: 0; background: var(--white); color: var(--ink); font: 400 var(--body)/1.6 var(--sans); -webkit-font-smoothing: antialiased;
  /* Host Grotesk's contextual alternates turn an X between numbers into a multiplication sign
     (the Eircode P75 X560 read "P75 ×560"); off everywhere (2026-09-28). */
  font-feature-settings: "calt" 0; }
img { display: block; max-width: 100%; height: auto; }
a { color: inherit; }
h1, h2 { margin: 0; font-weight: 500; letter-spacing: -0.025em; text-wrap: balance; }
/* line-height: 1.02 put 56px type on a 57px line, so a wrapped title's lines touched (type review, 2026-10-05) */
h1 { font-size: var(--h1); line-height: 1.1; }
h2 { font-size: var(--h2); line-height: 1.16; }
p { margin: 0 0 1.05em; }
p:last-child { margin-bottom: 0; }
figure { margin: 0; }
.mark { font-weight: 600; letter-spacing: -0.035em; text-decoration: none; line-height: 1; }

/* ─── Header: the blue bar on every page; over the hero on the home page until the page moves ─── */
/* T, 2026-10-10: a blue shade for the menu bar. The van blue, the footer's colour, with the wordmark
   and the links in white (6.4:1); the hairline under it is the deeper blue. */
.top { position: sticky; top: 0; z-index: 50; transition: transform .3s ease-out, background-color .3s, color .3s, border-color .3s; display: flex; align-items: center; justify-content: space-between; padding: 26px var(--pad); border-bottom: 1px solid var(--blue-d); background: var(--blue); color: #fff; }
/* The wordmark is the header's strongest thing and always the size of a section heading, heavier
   (T, 2026-10-05: at 24px it was the smallest heading on the page): 32px on a wide screen, 25.6px
   on a phone, where at 28px it crowded a 35px title. The footer's is the same. */
.top .mark { font-size: var(--h2); color: #fff; }
.top nav ul { list-style: none; margin: 0; padding: 0; display: flex; gap: 28px; }
.top nav a { text-decoration: none; font-size: var(--nav); font-weight: 500; padding: 6px 0; border-bottom: 1.5px solid transparent; }
.top nav a:hover { border-bottom-color: rgba(255,255,255,.7); }
.top nav a[aria-current="page"] { border-bottom-color: #fff; }
/* The menu symbol (T, 2026-10-10): three lines, no box, a 44px target; a cross while the list is open */
.menu-btn { display: none; background: none; border: 0; color: inherit; padding: 0; width: 44px; height: 44px; margin-right: -8px; cursor: pointer; align-items: center; justify-content: center; }
.menu-btn svg { width: 28px; height: 28px; fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round; }
.menu-btn path { transition: transform .2s ease-out, opacity .2s ease-out; transform-origin: 12px 12px; }
.menu-btn.open .l1 { transform: translateY(6px) rotate(45deg); }
.menu-btn.open .l2 { opacity: 0; }
.menu-btn.open .l3 { transform: translateY(-6px) rotate(-45deg); }
/* Interior's dropdown (T, 2026-09-28): opens on hover, on keyboard focus, or by the small arrow
   (main.js). A white panel under the item, whatever the header's colour; the page underlines the
   group's name while one of its areas is open. */
/* every top-level link the same box, so Interior (a flex item beside its arrow) sits on the others' line */
.top nav > ul { align-items: center; }
.top nav > ul > li > a { display: inline-block; }
.top nav .has-sub { position: relative; display: flex; align-items: center; gap: 2px; }
.sub-btn { background: none; border: 0; color: inherit; padding: 10px 4px; margin: -10px 0; cursor: pointer; display: inline-flex; }
.sub-btn svg { width: 12px; height: 12px; fill: none; stroke: currentColor; stroke-width: 1.6; transition: rotate .2s ease-out; }
.top nav .sub { display: none; position: absolute; top: 100%; left: -20px; padding-top: 14px; min-width: 220px; flex-direction: column; gap: 0; }
.top nav .sub::before { content: ""; position: absolute; inset: 14px 0 0; background: var(--white); border: 1px solid var(--line); box-shadow: 0 14px 30px rgba(21, 24, 27, .12); z-index: -1; }
.top nav .sub li:first-child { padding-top: 8px; } .top nav .sub li:last-child { padding-bottom: 8px; }
.top nav .sub a { display: block; padding: 9px 20px; color: var(--ink); border-bottom: 0; white-space: nowrap; }
.top nav .sub a:hover { color: var(--blue); }
.top nav .sub a[aria-current="page"] { color: var(--blue); font-weight: 600; }
.top nav .has-sub.in > a { border-bottom-color: #fff; }
/* a group's dropdown on a wide screen (no collection is a group today; nav-fold.css overrides it when the menu is folded) */
@media (min-width: 861px) {
  .top nav .has-sub:hover .sub, .top nav .has-sub.open .sub { display: flex; }
  .top nav .has-sub.open .sub-btn svg, .top nav .has-sub:hover .sub-btn svg { rotate: 180deg; }
}
/* On the home page the header floats over the hero, transparent, until the page moves */
.top.over { position: absolute; inset: 0 0 auto 0; border-bottom: 1px solid transparent; background: none; color: #fff; }
/* The way around a long page (2026-09-28): the header slides away while the visitor scrolls down
   and comes back as they scroll up (main.js sets hdr-away). On the home page it floats over the
   hero and becomes the blue bar once the page has moved (hdr-solid). */
.has-js .top.over { position: fixed; }
.hdr-away .top { transform: translateY(-100%); }
.hdr-solid .top.over { background: var(--blue); border-bottom-color: var(--blue-d); }
.top.over .mark { color: #fff; }

/* ─── Home: the hero, one photograph, the whole screen ─── */
/* The hero is pinned (position: sticky) and the rest of the page slides up over it: no script,
   nothing moves but the page itself. Under reduced motion it scrolls like any other block. */
.hero { position: sticky; top: 0; z-index: 0; min-height: 100dvh; background: #4a4640; }
.hero ~ main, .hero ~ section, .hero ~ footer { position: relative; z-index: 1; }
.doors { background: var(--white); }
.sr-only { position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0; }
.hero > img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; object-position: 50% 45%; }
.hero::before { content: ""; position: absolute; inset: 0 0 auto 0; height: 200px; z-index: 1; background: linear-gradient(180deg, rgba(0,0,0,.6) 0%, rgba(0,0,0,0) 100%); }
.hero::after { content: ""; position: absolute; inset: auto 0 0 0; height: 60%; background: linear-gradient(180deg, rgba(0,0,0,0) 0%, rgba(0,0,0,.6) 100%); }
.hero .words { position: absolute; left: var(--pad); right: var(--pad); bottom: 56px; z-index: 2; color: #fff; }
.hero h1 { max-width: 30ch; }
/* The line runs as wide as the heading above it and breaks into even lines (T, 2026-10-10: capped at
   30ch it sat 257px narrower than the heading and ended on one word alone). */
.hero .words p { margin: 18px 0 0; font-size: var(--lead); line-height: 1.35; max-width: none; text-wrap: balance; color: rgba(255,255,255,.92); }
.hero .words a { display: inline-block; margin-top: 22px; color: #fff; text-decoration: none; font-weight: 500; font-size: var(--body); border-bottom: 1.5px solid rgba(255,255,255,.8); padding-bottom: 2px; }

/* ─── Home: the doors ─── */
.doors { padding: var(--gap) var(--pad) 0; }
.door img { width: 100%; }
/* Captions in Newsreader at body size, in full ink (T, 2026-10-11: the grotesk at 16px grey read "heartless", and
   grey fails over a textured background). */
.fact { display: block; font-family: var(--serif); color: var(--ink); font-size: var(--body); margin: 0; line-height: 1.45; padding-top: 8px; }
/* Pinned words, scrolling photographs (home "What Marc makes", the Design page): the left column
   stays on screen (position: sticky, released when its row ends) while the right one scrolls. */
.story { display: grid; grid-template-columns: 5fr 7fr; gap: var(--gap); align-items: start; }
.story .pin { position: sticky; top: 32px; align-self: start; transition: top .3s ease-out; }
/* while the header is showing, the pinned column sits below it */
.hdr-solid:not(.hdr-away) .story .pin { top: calc(var(--hh, 80px) + 32px); }
.story .pin figure img { width: 100%; }
.story .pin figure + .say { margin-top: 22px; }
.story .flow > figure + figure { margin-top: var(--row); }
.story .flow img { width: 100%; }
/* Home, "What Marc makes" (design audit 2026-09-28): one screen, five tiles of one height in the
   menu's order, each a photograph and its name, linking to its page. */
.makes { padding-top: 96px; }
.makes-head { display: grid; grid-template-columns: 5fr 7fr; gap: var(--gap); align-items: end; }
.makes-head .say { margin: 0; }
.tiles { list-style: none; margin: 40px 0 0; padding: 0; display: grid; grid-template-columns: repeat(var(--tn, 5), minmax(0, 1fr)); gap: var(--gap); }
.tiles.n4 { --tn: 4; } .tiles.n3 { --tn: 3; } .tiles.n2 { --tn: 2; }
.tiles.n6 { --tn: 3; } .tiles.n7, .tiles.n8 { --tn: 4; }
/* A page with no photographs yet: its tile is plain pale blue (T, 2026-09-28) */
.tiles .ph.none { background: #E9EEF4; }
/* Interior's page of tiles, and the line on a page that has no photographs yet */
.run.group .tiles { margin-top: 0; }
.empty-line { color: var(--ink-2); font-size: var(--body); border-top: 1px solid var(--line); padding-top: 22px; }
.tiles a { display: block; text-decoration: none; color: var(--ink); }
.tiles .ph { display: block; aspect-ratio: 4 / 5; }
.tiles .name { display: block; padding-top: 12px; font-size: var(--h3); font-weight: 500; letter-spacing: -0.02em; line-height: 1.2; }
.tiles a:hover .name { color: var(--blue); }
.say { font-size: var(--body); line-height: 1.55; max-width: 46ch; margin: 14px 0 0; }
.say + .say { margin-top: .8em; }
.say a, .rich-text a { color: var(--blue); text-decoration: none; border-bottom: 1.5px solid var(--blue); }
.say a:hover, .rich-text a:hover { color: var(--blue-d); border-color: var(--blue-d); }
.go { color: var(--blue); text-decoration: none; font-weight: 500; border-bottom: 1.5px solid var(--blue); padding-bottom: 1px; align-self: start; justify-self: start; display: inline-block; }
.go:hover { color: var(--blue-d); border-color: var(--blue-d); }
/* Shaded bands (T, 2026-10-01: "I don't like that it's all white"): "How he works" on the home page
   and every second job on a collection page sit on --tint, edge to edge. The shadow carries the
   ground past the page's side padding; the clip trims it back to the band's own height. */
.how, .run > .job:nth-of-type(even) { background: var(--tint); box-shadow: 0 0 0 100vmax var(--tint); clip-path: inset(0 -100vmax); }
.how { margin-top: 96px; padding: 88px 0 96px; }
.how .words { padding-top: 0; }
.how .go { margin-top: 22px; }
/* With the clip: the page's 5/7 split, words and video aligned at the top */
.how.with-clip { display: grid; grid-template-columns: 5fr 7fr; gap: var(--gap); align-items: start; }
.how .say + .steps { margin-top: 20px; }
/* How he works as three steps (structure audit, 2026-09-28): numbered, each a job-size heading and one line */
.steps { list-style: none; margin: 28px 0 0; padding: 0; counter-reset: step; }
.steps li { counter-increment: step; display: grid; grid-template-columns: 2.2em 1fr; column-gap: 12px; padding: 18px 0; border-top: 1px solid var(--line); }
.steps li:last-child { border-bottom: 1px solid var(--line); }
.steps li::before { content: counter(step); grid-row: 1 / span 2; font-size: var(--h3); font-weight: 500; line-height: 1.2; color: var(--blue); }
.steps h3 { margin: 0; font-size: var(--h3); font-weight: 500; letter-spacing: -0.02em; line-height: 1.2; }
.steps .rich-text { margin-top: 6px; color: var(--ink); }
.steps .rich-text a { color: var(--blue); text-decoration: none; border-bottom: 1.5px solid var(--blue); }
.how .steps + .go { margin-top: 28px; }
.how .clip video { display: block; width: 100%; height: auto; aspect-ratio: 16 / 9; object-fit: cover; background: #e9e4dc; }

/* ─── Collection pages: the name, the line, the rows ─── */
.intro { padding: 56px var(--pad) 40px; }
.intro.tall { padding: 96px var(--pad) 120px; }
/* A short page (404, message sent) fills the screen, so the footer sits at the foot of it, not above white */
body:has(> .intro.tall) { min-height: 100vh; min-height: 100dvh; display: flex; flex-direction: column; }
body:has(> .intro.tall) > footer { margin-top: auto; }
.intro .lead { font-family: var(--serif); font-size: var(--lead); line-height: 1.4; max-width: 58ch; margin: 18px 0 0; text-wrap: balance; }
.intro .say { margin-top: 14px; }
.run { padding: 0 var(--pad); }
.run > * { margin-top: var(--row); }
.run > *:first-child { margin-top: 0; }
.run figure img { width: 100%; }
/* Rows (2026-09-28): each photograph takes a share of the row in proportion to its shape
   (--ar, width over height, from the admin's shape or the photograph's own), so every
   photograph in a row has exactly the same height and the row fills the grid. */
.row { display: flex; gap: var(--gap); align-items: flex-start; }
.row > figure { flex: var(--ar) 1 0; min-width: 0; }
.ph { overflow: hidden; background: #EEF0F2; }
.ph img { width: 100%; height: 100%; object-fit: cover; }
/* Job bands (design audit, 2026-09-28): the page is an index; the viewer is where a photograph
   is seen big. Each job is one band: heading and line in a narrow column beside its photographs
   on desktop, above them on smaller screens. The photographs are packed into lines (bandLayout in
   server.js, at most three to a line, two on a phone); each carries its line's shape total (--s)
   and count (--n), so every photograph in a line has one height, and no line is taller than
   --cap. A line held by the ceiling stops short of the right edge rather than growing. The breaks
   (i.br) close each line; half a gap either side of one makes one gap between lines. */
.run { --cap: min(55vh, 440px); }
.photos { display: flex; flex-wrap: wrap; align-items: flex-start; column-gap: var(--gap); row-gap: calc(var(--gap) / 2); }
.photos > figure { --s: var(--sd); --n: var(--nd); flex: none; min-width: 0;
  width: calc(var(--ar) * min(var(--cap), (100% - (var(--n) - 1) * var(--gap)) / var(--s)) - 0.5px); }
.photos > .br { flex: 0 0 100%; height: 0; }
.photos > .br:not(.d) { display: none; }
/* A landscape alone on its line runs the full width instead of stopping short (T, 2026-09-28) */
/* A line of two or three held by the ceiling may grow taller, so it does not leave a wide empty
   strip beside it (finish review, 2026-10-01). A portrait alone on its line keeps the ceiling (T). */
@media (min-width: 721px) { .photos > figure.multi-d { --cap: min(72vh, 660px); } }
@media (min-width: 721px) { .photos > figure.wide-d { --cap: 100vw; } }
@media (max-width: 720px) { .photos > figure.wide-p { --cap: 100vw; } }
/* The making (How he works, T 2026-10-07): an upright video in a band of its own. It may grow to the
   taller ceiling on desktop (a 440px portrait is too small to watch) and takes the whole column on a
   phone. Its first frame shows with a play disc until it is tapped; then the player's own controls. */
@media (min-width: 721px) { .photos > figure.clip-fig { --cap: min(72vh, 660px); } }
@media (max-width: 720px) { .photos > figure.clip-fig { --cap: 9999px; } } /* no ceiling: the width rules (100vw would hold a portrait to 56% of the column) */
.clip-fig .ph { position: relative; background: #15181B; }
.clip-fig video { display: block; width: 100%; height: 100%; object-fit: cover; }
.clip-fig .play { display: none; position: absolute; inset: 0; margin: auto; width: 76px; height: 76px; padding: 0; border: 0; border-radius: 50%; background: rgba(255,255,255,.94); color: var(--blue); cursor: pointer; box-shadow: 0 6px 24px rgba(0,0,0,.28); transition: transform .2s ease-out, background-color .2s ease-out; }
.clip-fig .play svg { width: 34px; height: 34px; fill: currentColor; margin-left: 4px; }
.clip-fig .play:hover, .clip-fig .play:focus-visible { transform: scale(1.06); background: #fff; }
.clip-fig .play:focus-visible { outline: 3px solid #fff; outline-offset: 3px; }
.has-js .clip-fig:not(.playing) .play { display: grid; place-items: center; }
.job-caps { display: none; }
/* jobs sit edge to edge, so a shaded band meets its neighbours; the space is inside each band */
.run > .job { margin-top: 0; padding: 52px 0; }
.run > .job:first-of-type { padding-top: 28px; }
/* A job's heading is a section heading (--h2), a step above the lead: at the --h3 size it was 20px
   on a phone beside an 18px body and did not read as a heading. Its last two words are held
   together (.tie, collection.ejs) only where the heading's column is wide enough to take them. */
.job-head { container-type: inline-size; }
.job-head h2 { font-size: var(--h2); line-height: 1.16; letter-spacing: -0.025em; }
@container (min-width: 280px) { .job-head .tie { white-space: nowrap; } }
.job-head p { margin: 8px 0 0; color: var(--ink-3); font-size: var(--note); line-height: 1.5; }
.job-head:empty { display: none; }
@media (min-width: 1001px) {
  /* The heading column is never narrower than eleven times the heading's own size (352px at 32px,
     282px at 25.6px): a tied pair of up to 18 letters measures about ten ("Independence Museum" is
     327px at 32px). In the plain 3fr column it cleared by 3px at 1440 and overflowed at 1320. */
  .job { display: grid; grid-template-columns: minmax(calc(var(--h2) * 11), 3fr) 9fr; gap: var(--gap); align-items: start; }
  .job-head:empty { display: block; }
}
/* The header's menu folded behind the Menu button: css/nav-fold.css, loaded under a width the server
   works out from the menu's own links (head.ejs, navFoldAt). */
@media (max-width: 1000px) {
  .job-head + .job-body { margin-top: 20px; }
}
/* A small magnifier on the photograph under the pointer says it opens large */
@media (hover: hover) {
  .run figure[data-full] .ph { position: relative; }
  .run figure[data-full] .ph::after { content: ""; position: absolute; right: 10px; bottom: 10px; width: 36px; height: 36px; background: rgba(255,255,255,.92) url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%2315181B' stroke-width='1.8' stroke-linecap='round'%3E%3Ccircle cx='10.5' cy='10.5' r='6'/%3E%3Cpath d='M15 15l5 5M10.5 8v5M8 10.5h5'/%3E%3C/svg%3E") center / 22px no-repeat; opacity: 0; transition: opacity .2s ease-out; pointer-events: none; }
  .run figure[data-full]:hover .ph::after { opacity: 1; }
}
/* On to the next collection, and back to the one before */
.run > .onward { margin-top: 96px; }
/* One row (T, 2026-10-10): the page before at the left edge, the page after at the right edge, both
   the 24px step in the blue; the line of description under "next" is gone. */
.onward { display: flex; flex-wrap: wrap; justify-content: space-between; align-items: baseline; gap: 4px 32px; border-top: 1px solid var(--line); padding-top: 22px; }
.onward a { text-decoration: none; font-size: var(--h3); font-weight: 500; letter-spacing: -0.02em; line-height: 1.2; color: var(--blue); padding: 8px 0; }
.onward a:hover { color: var(--blue-d); text-decoration: underline; text-decoration-thickness: 1.5px; text-underline-offset: 6px; }
.onward .on { margin-left: auto; text-align: right; }
/* the foot of the page's own content, before the contact band */
.run { padding-bottom: 88px; }
.words { max-width: 62ch; padding-top: 8px; }
.words .say { max-width: none; }
.prose h2 { font-size: var(--h2); margin: 1.5em 0 .35em; }
.prose p { font-size: var(--body); line-height: 1.55; }
/* About (T, 2026-10-10: "can these be equal sections?"): the person and the workshop, built the same
   way. A 32px heading and the words in the wide column, one photograph in the narrow one: a third of
   the page, 4:3, the same for both (they were 546x683 and 770x578 beside two or three lines, on
   opposite splits). Below 1000px the photograph goes under the words, no wider than 560px. */
.about-page { padding: 0 var(--pad) 96px; }
.about-part { display: grid; grid-template-columns: 8fr 4fr; gap: var(--gap); align-items: start; border-top: 1px solid var(--line); padding-top: 40px; margin-top: 64px; }
.about-part:first-of-type { border-top: 0; margin-top: 0; padding-top: 56px; }
.about-part:not(.has-photo) { grid-template-columns: 1fr; }
.about-part .words { padding-top: 0; }
.about-part h2 { font-size: var(--h2); letter-spacing: -0.025em; text-wrap: balance; }
.about-part .say { margin: 14px 0 0; max-width: 62ch; }
.about-part .say + .say { margin-top: .8em; }
.about-part figure { margin: 0; }
.about-part .ph { aspect-ratio: 4 / 3; }
@media (max-width: 1000px) {
  .about-part { grid-template-columns: 1fr; padding-top: 32px; margin-top: 48px; }
  .about-part:first-of-type { padding-top: 40px; }
  .about-part figure { max-width: 560px; }
}

/* ─── Contact (T, 2026-10-10): the form on Home and About, a short band on every other page ───
   On the pale blue ground it reads as a block of its own between the white page and the blue footer
   (the foot of a collection page was photographs, next and previous and the form, white on white on
   white). The home page's sits on white, because the band above it there, "How we work", is the tinted one. */
.contact { padding: 96px var(--pad); background: var(--tint); }
.contact.plain { background: var(--white); padding: 120px var(--pad); }
/* The band: the heading in the narrow column, the number, the email and the button in the wide one,
   on the same 5fr/7fr split as the form's block. */
.contact.band { padding: 64px var(--pad); display: grid; grid-template-columns: 5fr 7fr; gap: var(--gap); align-items: center; }
.contact.band .side { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 20px 32px; }
.contact.band .bigs { margin-top: 0; }
.contact .grid { display: grid; grid-template-columns: 5fr 7fr; gap: var(--gap); align-items: start; }
.contact .bigs { margin-top: 24px; }
.contact .big { display: block; text-decoration: none; font-size: var(--h3); font-weight: 500; letter-spacing: -0.02em; line-height: 1.25; }
.contact .big + .big { margin-top: 6px; }
.contact .addr { margin-top: 24px; color: var(--ink-2); line-height: 1.55; }
.contact .hours { margin-top: 18px; max-width: 34ch; color: var(--ink-2); }
.contact .go { margin-top: 24px; }
form { display: grid; grid-template-columns: 1fr 1fr; gap: 16px 20px; }
form label { display: block; font-size: var(--small); font-weight: 500; margin-bottom: 6px; }
form input, form textarea { width: 100%; border: 1px solid #BEC4CC; background: #fff; padding: 12px 14px; font: 400 var(--body) var(--sans); color: var(--ink); }
form input::placeholder, form textarea::placeholder { color: #8A929C; }
form input:focus, form textarea:focus { outline: 2px solid var(--blue); outline-offset: 0; border-color: var(--blue); }
form .full { grid-column: 1 / -1; }
form textarea { min-height: 150px; resize: vertical; }
.btn { display: inline-block; background: var(--blue); color: #fff; text-decoration: none; font: 500 var(--body) var(--sans); padding: 13px 22px; border: 0; cursor: pointer; }
.btn:hover { background: var(--blue-d); }
.btn:active { transform: translateY(1px); }
.note { font-size: var(--small); color: var(--ink-2); grid-column: 1 / -1; margin: 0; }
/* the spam tripwire's hidden field (partials/tripwire.ejs) must never take a grid cell */
form .hp-field, form [name="website"], form [name="fts"] { position: absolute; left: -9999px; }

/* ─── Footer ─── */
footer { background: var(--blue); color: #F4F7FA; padding: 56px var(--pad); display: grid; grid-template-columns: auto 1fr; gap: 20px 48px; align-items: baseline; }
footer .mark { font-size: var(--h2); color: #fff; }
footer p { margin: 0; font-size: var(--body); line-height: 1.6; }
footer ul { list-style: none; margin: 0; padding: 0; display: flex; flex-wrap: wrap; justify-content: flex-end; gap: 6px 28px; font-size: var(--body); }
footer a { text-decoration: none; }
footer nav a:hover { text-decoration: underline; }
footer .foot-social { margin-top: 18px; }

/* ─── Engine working parts, in this site's colours ─── */
.rich-text > p, .rich-text > div { margin: 0 0 1em; }
.rich-text > p:last-child, .rich-text > div:last-child { margin-bottom: 0; }
.rich-text strong, .rich-text b { font-weight: 600; }
.rich-text em, .rich-text i { font-style: italic; }
.rich-text h1, .rich-text h2, .rich-text h3 { font-weight: 500; letter-spacing: -0.02em; line-height: 1.15; margin: 1.3em 0 .4em; }
/* headings in running text (Privacy, Terms) are section headings, a step above the lead; they were the
   lead's own size (24px), and the third level was body size */
.rich-text h1, .rich-text h2 { font-size: var(--h2); line-height: 1.16; } .rich-text h3 { font-size: var(--h3); }
.rich-text ul, .rich-text ol { margin: 0 0 1em 1.3em; padding: 0; }
.rich-text li { margin-bottom: .35em; }
.rich-text blockquote { border-left: 2px solid var(--blue); padding-left: 1rem; color: var(--ink-2); margin: 0 0 1em; }
.rich-text figure { margin: 1.8em 0; }
.rich-text figure img { width: 100%; }
.rich-text figcaption { font-size: var(--note); color: var(--ink-3); margin-top: .5em; }
a:focus-visible, button:focus-visible, input:focus-visible, textarea:focus-visible, select:focus-visible, [tabindex]:focus-visible { outline: 2px solid var(--blue); outline-offset: 3px; }
.top a:focus-visible, .top button:focus-visible, footer a:focus-visible, footer button:focus-visible { outline-color: #fff; }
.soc-icons { display: flex; gap: 10px; }
.soc-icons a { width: 44px; height: 44px; display: flex; align-items: center; justify-content: center; border: 1.5px solid currentColor; }
.soc-icons svg { width: 20px; height: 20px; fill: currentColor; }
footer .soc-icons a { color: #fff; }
footer .soc-icons a:hover { background: rgba(255,255,255,.12); }
.consent { display: flex; gap: 10px; align-items: flex-start; font-size: 15px; color: var(--ink-2); line-height: 1.5; cursor: pointer; }
.consent input { margin-top: 4px; accent-color: var(--blue); width: 16px; height: 16px; flex: none; }
.consent-nudge { font-size: 15px; background: #EEF3FA; border: 1px solid #C9D6E8; color: var(--ink); padding: 10px 14px; margin: 0; }
.modal-overlay { display: none; position: fixed; inset: 0; z-index: 500; background: rgba(21,24,27,.55); align-items: center; justify-content: center; padding: 1.5rem; }
.modal-overlay.open { display: flex; }
.modal-box { background: #fff; max-width: 540px; width: 100%; padding: 2.4rem; position: relative; max-height: 90vh; overflow-y: auto; }
.modal-close { position: absolute; top: 1rem; right: 1.2rem; background: none; border: 0; cursor: pointer; font-size: 1.2rem; color: var(--ink-2); }
.modal-error { color: #9b1c1c; background: #fdecec; border: 1px solid #F0B8B8; padding: .6rem .8rem; font-size: var(--small); }
.modal-error[hidden], #enquiry-modal [hidden] { display: none; }
.nl-pop { position: fixed; right: 1.1rem; bottom: 1.1rem; z-index: 450; width: min(340px, calc(100vw - 2.2rem)); background: #fff; box-shadow: 0 18px 48px rgba(0,0,0,.25); padding: 1.1rem 1.2rem; transform: translateY(24px); opacity: 0; transition: transform .45s, opacity .45s; }
.nl-pop.show { transform: none; opacity: 1; }
.nl-pop[hidden] { display: none; }

/* ─── Lift (T, 2026-09-28, chosen from a trial of five): under the pointer a photograph that opens
   large, or a home tile, rises 4px with a soft shadow. Pointer devices only, none under reduced
   motion. `translate` (not `transform`) so the framing's own transform stays. ─── */
@media (hover: hover) and (prefers-reduced-motion: no-preference) {
  .run figure[data-full] .ph, .tiles .ph { transition: translate .3s ease-out, box-shadow .3s ease-out; }
  .run figure[data-full]:hover .ph, .tiles a:hover .ph { translate: 0 -4px; box-shadow: 0 14px 30px rgba(21, 24, 27, .18); }
}

/* ─── Every photograph opens large (main.js binds the lightbox to figure[data-full]) ─── */
figure[data-full] img { cursor: zoom-in; }
/* The viewer fills the screen (T, 2026-09-28: it used to shrink the big photographs to leave room
   for the caption). Caption, count and controls sit over the photograph. No zoom (T, same day). */
.lightbox { display: none; position: fixed; inset: 0; z-index: 600; background: #0E1012; overflow: hidden; touch-action: none; user-select: none; -webkit-user-select: none; }
.lightbox.open { display: block; animation: lb-in .2s ease-out; }
.lightbox figure { position: absolute; inset: 0; margin: 0; display: flex; align-items: center; justify-content: center; }
.lightbox img { display: block; max-width: 100vw; max-height: 100vh; max-height: 100dvh; width: auto; height: auto; }
.lightbox figcaption { position: absolute; left: 0; right: 0; bottom: 0; padding: 56px 88px calc(22px + env(safe-area-inset-bottom)); background: linear-gradient(180deg, rgba(14,16,18,0) 0%, rgba(14,16,18,.78) 100%); font-size: var(--small); line-height: 1.5; color: #fff; text-align: center; pointer-events: none; }
.lb-cap { display: block; max-width: 60ch; margin: 0 auto; }
.lb-count { display: block; margin-top: 2px; color: rgba(255,255,255,.7); }
.lightbox button { position: fixed; width: 52px; height: 52px; padding: 0; border: 0; background: rgba(14,16,18,.55); color: #fff; cursor: pointer; display: flex; align-items: center; justify-content: center; transition: background .15s ease-out; }
.lightbox button:hover { background: rgba(14,16,18,.85); }
.lightbox button:focus-visible { outline: 2px solid #fff; outline-offset: 2px; }
.lightbox button svg { width: 28px; height: 28px; fill: none; stroke: currentColor; stroke-width: 1.6; stroke-linecap: round; }
.lb-prev, .lb-next { top: 50%; margin-top: -26px; }
.lb-prev { left: 16px; }
.lb-next { right: 16px; }
.lb-prev[hidden], .lb-next[hidden] { display: none; }
.lb-close { top: 16px; right: 16px; font: 400 22px/1 var(--sans); }
@keyframes lb-in { from { opacity: 0; } to { opacity: 1; } }

/* ─── Reveal: a photograph settles into place as it enters the screen (12px, 500ms) ───
   Scroll-driven where the browser has it; otherwise main.js adds .in as each one arrives.
   The hero and the first photograph of a run are never animated. */
.reveal { opacity: 0; translate: 0 12px; }
.reveal.in { opacity: 1; translate: 0 0; transition: opacity .5s ease-out, translate .5s ease-out; }
@supports (animation-timeline: view()) {
  .reveal, .reveal.in { opacity: 1; translate: none; transition: none; animation: reveal .5s ease-out both; animation-timeline: view(); animation-range: entry 0% entry 30%; }
  @keyframes reveal { from { opacity: 0; translate: 0 12px; } to { opacity: 1; translate: 0 0; } }
}

/* ─── The photograph behind the page (T and Marc, 2026-10-11) ───
   A fixed layer under everything (partials/head.ejs draws it when the page has one): the photograph framed as in
   the site manager, then the white veil at the strength set there. The page's own grounds (header, footer, the
   contact band) sit over it; the shaded bands become a faint blue veil instead of an opaque pale blue, so the
   texture runs on under them. Fixed elements keep their place on iOS, where background-attachment does not. */
body.has-bg { background: transparent; --tint: rgba(31, 90, 166, .07); }
.page-bg { position: fixed; inset: 0; z-index: -1; overflow: hidden; background: var(--white); pointer-events: none; }
.page-bg img { display: block; width: 100%; height: 100%; object-fit: cover; }
.page-veil { position: absolute; inset: 0; background: var(--white); }
/* the card under each caption, where the texture still fights the words: a tick in Rest of Site */
body.cap-cards .photos > figure > .fact, body.cap-cards .grouped .job-caps { background: rgba(241, 245, 250, .72); padding: 10px 12px 12px; margin-top: 0; }
/* the lightbox and the folded menu must stay above the layer: they already carry their own z-index */

/* ─── Cross-fade between pages (Chrome, Safari 18); the header stays put ─── */
@view-transition { navigation: auto; }
::view-transition-old(root), ::view-transition-new(root) { animation-duration: 180ms; }
::view-transition-group(header) { animation-duration: 180ms; }
.top { view-transition-name: header; }

/* ─── Back to the top (2026-09-28): appears after two screens (main.js) ─── */
.to-top { position: fixed; right: var(--pad); bottom: 24px; z-index: 390; width: 48px; height: 48px; display: flex; align-items: center; justify-content: center; background: var(--white); color: var(--blue); border: 1.5px solid var(--blue); transition: opacity .25s ease-out, transform .25s ease-out, visibility .25s, bottom .3s ease-out; }
.to-top svg { width: 22px; height: 22px; fill: none; stroke: currentColor; stroke-width: 2; }
.to-top:hover { background: var(--blue); color: #fff; }
.has-js .to-top { opacity: 0; visibility: hidden; transform: translateY(8px); }
.has-js.to-top-on .to-top { opacity: 1; visibility: visible; transform: none; }

/* ─── Phone: the number along the bottom of the screen (main.js shows it past the top of the
   page and hides it over the contact section and the footer; without the script it is always there) ─── */
.ring { display: none; }

/* ─── Smaller screens ─── */
@media (max-width: 1000px) {
  :root { --pad: 28px; }
  .contact .grid, .contact.band, footer { grid-template-columns: 1fr; }
  .contact.band { gap: 18px; padding: 48px var(--pad); }
  footer ul { justify-content: flex-start; }
}
@media (max-width: 720px) {
  :root { --pad: 16px; --gap: 12px; --row: 32px; }
  .top { padding: 16px var(--pad); }
  .hero > img { object-position: 50% 50%; }
  /* clear of the call bar along the bottom (56px), which shows here without the script */
  .hero .words { bottom: calc(88px + env(safe-area-inset-bottom)); }
  .hero .words p { font-size: var(--lead); }
  .story { grid-template-columns: 1fr; gap: 24px; }
  .story .pin { position: static; }
  .story .flow > figure + figure { margin-top: 24px; }
  .makes { padding-top: 56px; }
  .makes-head { grid-template-columns: 1fr; gap: 12px; }
  .tiles, .tiles[class] { --tn: 2; margin-top: 24px; row-gap: 20px; }
  .tiles .ph { aspect-ratio: 1; }
  /* an odd last tile runs the full width at the same height as the others */
  .tiles li:last-child:nth-child(odd) { grid-column: 1 / -1; }
  .tiles li:last-child:nth-child(odd) .ph { aspect-ratio: auto; height: calc((100vw - 2 * var(--pad) - var(--gap)) / 2); }
  .tiles .name { padding-top: 8px; } /* stays at the 20px step: these are the main ways into the work */
  .how { margin-top: 48px; padding: 44px 0 48px; }
  .how.with-clip { grid-template-columns: 1fr; gap: 24px; }
  .ring { display: flex; align-items: center; justify-content: center; position: fixed; left: 0; right: 0; bottom: 0; z-index: 400; height: calc(56px + env(safe-area-inset-bottom)); padding-bottom: env(safe-area-inset-bottom); background: var(--blue); color: #fff; text-decoration: none; font: 500 var(--body) var(--sans); }
  .has-js .ring { transform: translateY(100%); transition: transform .3s ease-out; }
  .has-js.past-top:not(.at-contact) .ring { transform: none; }
  .intro { padding: 36px var(--pad) 28px; }
  .intro.tall { padding: 64px var(--pad) 80px; }
  .row { flex-direction: column; gap: 24px; }
  .row > figure { flex: none; width: 100%; }
  /* phone: at most two to a line, and what the photographs show is one line under the job */
  .run { --cap: 420px; }
  .photos > figure { --s: var(--sp); --n: var(--np); }
  .photos > .br:not(.d) { display: block; }
  .photos > .br:not(.p) { display: none; }
  .grouped .photos > figure > .fact { display: none; }
  .grouped .job-caps { display: block; font-family: var(--serif); color: var(--ink); font-size: var(--body); line-height: 1.45; margin: 8px 0 0; }
  .run > .job { padding: 32px 0; }
  .run > .job:first-of-type { padding-top: 16px; }
  .job-head + .job-body { margin-top: 16px; }
  .run > .onward { margin-top: 64px; }
  .run { padding-bottom: 56px; }
  .about-page { padding-bottom: 56px; }
  /* phone: the arrows sit at the ends of the caption band, clear of the photograph's middle */
  .lightbox figcaption { padding: 48px 72px calc(18px + env(safe-area-inset-bottom)); }
  .lb-prev, .lb-next { top: auto; margin-top: 0; bottom: calc(12px + env(safe-area-inset-bottom)); }
  .lb-prev { left: 8px; }
  .lb-next { right: 8px; }
  .lb-close { top: calc(8px + env(safe-area-inset-top)); right: 8px; }
  .to-top { right: 16px; bottom: 16px; }
  .has-js.past-top:not(.at-contact) .to-top { bottom: calc(68px + env(safe-area-inset-bottom)); }
  .contact, .contact.plain { padding: 64px var(--pad); }
  .contact.band { padding: 40px var(--pad); }
  .contact.band .side { flex-direction: column; align-items: flex-start; gap: 18px; }
  form { grid-template-columns: 1fr; }
  footer { padding: 40px var(--pad); }
}
/* ─── Reduced motion: every effect off, the page reads the same ─── */
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  .nl-pop, .ring, .top, .to-top, .story .pin { transition: none; }
  .hero { position: static; }
  .reveal, .reveal.in { opacity: 1; translate: none; animation: none; transition: none; }
  .lightbox.open { animation: none; }
  @view-transition { navigation: none; }
}
