/* ════════════════════════════════════════════════════════════════════════
   css/firm-pages.css — THE FOUR FIRM PAGES
   service.tnaado.ca · Tier 2 · /firm, /office, /team, /sister

   ONE SHEET FOR FOUR PAGES, because they share a ground, a rail, a type
   scale, a section header idiom and a closing booking band. Four copies of
   those is how the old site's twenty pages drifted apart.

   NOTHING HERE TOUCHES css/chrome.css. That sheet is built, measured and
   verified; it is linked, never overridden. In particular this file does NOT
   set body padding-top for the fixed bar — chrome.css already does that at
   the system level:
       html.chrome-live:not(.sc-live) body { padding-top: var(--chrome-bar-h) }
   with --chrome-bar-h measured at 6.25rem desktop / 5.75rem phone. A second
   padding here would double-count it. (css/work.css still declares its own
   `html.chrome-live body { padding-top: var(--bar) }` from before that landed;
   that is another task's file and is reported, not edited.)

   THE ONE ADDITIVE CHROME RULE, same as css/work.css's and for the same
   reason: with no bypass row under it the fixed bar has no lower edge,
   because chrome.css draws that edge on .bypass. These are not storm pages —
   a five-beat row pointing at #who/#offers/#prices/#examples/#book would be
   five broken anchors — so the edge is drawn here. Nothing is overridden.

   ── COLOUR, LOCKED AND MEASURED ─────────────────────────────────────────
   --red #c8102e is 3.37:1 on ink and is therefore used ONLY for rules,
   fills and display type >= 24px. Never for small text on ink.
       small red on ink   --red-ink  #ef5c6e   6.06:1  (5.69:1 on --plate-2)
       small red on bone  --red-bone #a4132c   6.88:1  (7.31:1 on --ivory)
   Body copy: --bone on ink 17.56:1, --ink on bone 17.56:1, on ivory 18.67:1.
   Secondary: --dim-ink #b9b3a6 on ink 9.49:1; --dim-bone #5c574f on bone
   6.36:1. There is no rgba(bone,.4x) anywhere in this file — that value is
   3.77:1 and is the thing chrome.css was written to stop reproducing.

   ── THE PORTRAIT RULE, AND IT IS NOT NEGOTIABLE ─────────────────────────
   The files in assets/team are 640x640 greyscale cut-outs with REAL ALPHA.
   Their own fill is a pale disc. On #0a0a0a a pale disc reads as a hole and
   the face inside it dies. So every portrait in this file sits on --bone or
   --ivory and NEVER on ink. .fp-disc carries its own pale fill plus a 1px
   ring so the cut-out has an edge even where the subject's hair runs to the
   alpha boundary. If a future band wants portraits on ink, the answer is to
   move the band to bone, not to lighten the disc.

   ── MOTION ──────────────────────────────────────────────────────────────
   None. No storm, no scrub, no pin, no parallax, no per-page observer.
   Ethan: "no scroll animations, not what I asked for." The only transitions
   are hover/focus affordances under 250ms, and they are all inside
   @media (hover:hover) or :focus-visible.

   ── DEGRADATION ─────────────────────────────────────────────────────────
   Everything in this file paints with no JavaScript. The only JS on these
   four pages is chrome.js, plus js/carousel.js on /firm — and that one is a
   pure enhancement: S.04's strip scrolls, snaps and reads in full with the
   file blocked, and its control bar is hidden until the script reveals it. Every figure, every roster row, every
   kit line and the map link are in the served HTML, in flow, in reading
   order. The Google Maps <iframe> on /office is progressive: the address is
   printed in type beside it and an "Open in Google Maps" link sits under it,
   so a blocked frame costs the page nothing but a rectangle.
   ════════════════════════════════════════════════════════════════════════ */

:root {
  --ink:       #0a0a0a;
  --plate:     #0d0d0d;
  --plate-2:   #131313;
  --bone:      #f5f1e8;
  --bone-2:    #ebe5d8;
  --ivory:     #fbf8f1;
  --disc:      #f0ebe0;

  --red:       #c8102e;   /* rules, fills, display >= 24px ONLY */
  --red-ink:   #ef5c6e;   /* 6.06:1 — the only small red on ink  */
  --red-bone:  #a4132c;   /* 6.88:1 — the only small red on bone */

  --dim-ink:   #b9b3a6;   /* 9.49:1 on ink  */
  --dim-bone:  #5c574f;   /* 6.36:1 on bone */

  --hair-ink:  rgba(245, 241, 232, .14);
  --hair-bone: rgba(10, 10, 10, .16);

  --serif: 'Fraunces', 'Times New Roman', Times, serif;
  --sans:  'IBM Plex Sans', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
  --mono:  'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, monospace;

  /* The rail is css/work.css's, to the pixel, so a reader moving between the
     portfolios and the firm pages never sees the column jump. */
  --rail: 1136px;
  --gut:  clamp(1.25rem, 4vw, 3rem);
  --ease: cubic-bezier(.22, 1, .36, 1);
}

*, *::before, *::after { box-sizing: border-box; }

html {
  overflow-x: clip;
  -webkit-text-size-adjust: 100%;
}

body {
  margin: 0;
  background: var(--ink);
  color: var(--bone);
  font-family: var(--sans);
  font-size: 1rem;
  font-weight: 400;
  line-height: 1.62;
  -webkit-font-smoothing: antialiased;
}

img { max-width: 100%; display: block; }

/* Page-local visually-hidden. chrome.css scopes its .u-vh to .chrome and
   .chrome-foot, so a page using the class outside those wrappers gets no rule
   and the text renders on screen. */
.u-vh {
  position: absolute; width: 1px; height: 1px; overflow: hidden;
  clip-path: inset(50%); white-space: nowrap;
}

/* See the header. Additive: draws the lower edge the missing bypass row would
   have drawn. */
html.chrome-live .menubar { border-bottom: 1px solid var(--hair-ink); }

/* ── FOCUS ────────────────────────────────────────────────────────────────
   One visible ring for the whole sheet, drawn in --red at 2px with a 2px
   offset so it clears the element's own border on both grounds. Never
   `outline: none` anywhere in this file. */
:focus-visible {
  outline: 2px solid var(--red);
  outline-offset: 2px;
  border-radius: 2px;
}
.fp-band--bone :focus-visible,
.fp-band--ivory :focus-visible { outline-color: var(--red-bone); }


/* ══ RAIL AND BANDS ═══════════════════════════════════════════════════════ */
.fp-rail { width: 100%; max-width: var(--rail); margin: 0 auto; padding: 0 var(--gut); }


/* ── THE 2026-08-22 DENSITY PASS ──────────────────────────────────────────
   Ethan: "sizing of sections and images and booking forms all too large ...
   like you're treating the reader as a toddler". The root pass had already
   brought css/reference.css's heading scale down to h1 3.5rem / h2 2.125rem /
   h3 1.4375rem and css/site.css's section padding to ~4.25rem. This sheet is
   brought onto those same two numbers, because five sheets each inventing
   their own display size is how a site ends up shouting on one page and
   whispering on the next. Every value below is the reference scale, not a
   new one. */
.fp-band { position: relative; padding: clamp(2.25rem, 4.6vw, 4rem) 0; }
.fp-band--tight { padding-block: clamp(1.75rem, 3.2vw, 2.75rem); }

.fp-band--ink   { background: var(--ink);   color: var(--bone); }
.fp-band--plate { background: var(--plate-2); color: var(--bone); }
.fp-band--bone  { background: var(--bone);  color: var(--ink); }
.fp-band--ivory { background: var(--ivory); color: var(--ink); }

/* A hairline only between two bands of the SAME ground. Two different grounds
   already have an edge and a rule on top of it reads as a mistake. */
.fp-band--ink   + .fp-band--ink,
.fp-band--plate + .fp-band--plate { border-top: 1px solid var(--hair-ink); }
.fp-band--bone  + .fp-band--bone,
.fp-band--ivory + .fp-band--ivory { border-top: 1px solid var(--hair-bone); }


/* ══ TYPE ═════════════════════════════════════════════════════════════════
   Fraunces is a variable optical-size face; opsz is pinned per step so the
   display sizes get the high-contrast drawing and the small ones do not get
   a hairline serif that disappears. */
h1, h2, h3, h4 { font-family: var(--serif); font-weight: 400; margin: 0; line-height: 1.04; }

h1 {
  font-size: clamp(2.25rem, 5.2vw, 3.5rem);
  font-variation-settings: 'opsz' 144, 'SOFT' 0, 'WONK' 0;
  letter-spacing: -.02em;
}
h2 {
  font-size: clamp(1.625rem, 3.1vw, 2.125rem);
  font-variation-settings: 'opsz' 96;
  letter-spacing: -.012em;
}
h3 {
  font-size: clamp(1.1875rem, 1.9vw, 1.4375rem);
  font-variation-settings: 'opsz' 48;
  letter-spacing: -.008em;
  line-height: 1.16;
}
h4 { font-size: 1.0625rem; font-variation-settings: 'opsz' 24; line-height: 1.24; }

h1 em, h2 em, h3 em { font-style: italic; color: var(--bone-2); }
.fp-band--bone h1 em, .fp-band--bone h2 em, .fp-band--bone h3 em,
.fp-band--ivory h1 em, .fp-band--ivory h2 em, .fp-band--ivory h3 em { color: var(--red-bone); }

.fp-lede {
  margin: 1.1rem 0 0; max-width: 62ch;
  font-size: clamp(1.0625rem, 1.4vw, 1.1875rem); line-height: 1.62;
}
.fp-p { margin: 1.05rem 0 0; max-width: 66ch; }
.fp-note {
  margin: .9rem 0 0; max-width: 68ch;
  font-size: .9375rem; color: var(--dim-ink);
}
.fp-band--bone .fp-note, .fp-band--ivory .fp-note { color: var(--dim-bone); }

.fp-p b, .fp-lede b { font-weight: 600; }

/* Links inside running copy: underlined, because colour alone is not a
   distinction and --red at 16px on ink is 3.37:1. */
.fp-p a, .fp-lede a, .fp-note a, .fp-dl a {
  color: var(--red-ink); text-decoration: underline;
  text-decoration-thickness: 1px; text-underline-offset: .18em;
}
.fp-band--bone .fp-p a, .fp-band--bone .fp-lede a, .fp-band--bone .fp-note a,
.fp-band--bone .fp-dl a,
.fp-band--ivory .fp-p a, .fp-band--ivory .fp-lede a, .fp-band--ivory .fp-note a,
.fp-band--ivory .fp-dl a { color: var(--red-bone); }


/* ══ THE SECTION HEADER ═══════════════════════════════════════════════════
   The site's eyebrow idiom, kept verbatim from the live build and from
   css/work.css: a short red rule, a mono numeral, a label. A reader who
   learns one section header can read every section header on the site. */
.fp-eyebrow {
  display: flex; align-items: center; gap: .75rem;
  margin: 0 0 1.05rem;
  font-family: var(--mono); font-size: .75rem; font-weight: 500;
  letter-spacing: .16em; text-transform: uppercase;
  color: var(--dim-ink);
}
.fp-band--bone .fp-eyebrow, .fp-band--ivory .fp-eyebrow { color: var(--dim-bone); }
.fp-eyebrow__rule { display: block; width: 2.25rem; height: 2px; background: var(--red); flex: 0 0 auto; }
.fp-eyebrow__n { color: var(--red-ink); }
.fp-band--bone .fp-eyebrow__n, .fp-band--ivory .fp-eyebrow__n { color: var(--red-bone); }

/* Breadcrumb. Real links, one per level, current page not a link. */
.fp-crumb {
  display: flex; flex-wrap: wrap; align-items: center; gap: .5rem;
  margin: 0 0 1.5rem; padding: 0; list-style: none;
  font-family: var(--mono); font-size: .75rem; color: var(--dim-ink);
}
.fp-crumb li + li::before { content: '/'; margin-right: .5rem; color: var(--red-ink); }
.fp-crumb a { color: var(--dim-ink); text-decoration: none; }
.fp-crumb [aria-current] { color: var(--bone); }
@media (hover: hover) and (pointer: fine) { .fp-crumb a:hover { color: var(--bone); } }


/* ══ THE HERO ═════════════════════════════════════════════════════════════
   A photograph, a statement, and — on three of the four pages — a strip of
   figures. Nothing to click above the fold except the CTA pair on /office,
   which Ethan asked for by name ("CTA to check our gear, our office as
   CTAs").

   THE SCRIM IS SIDE-WEIGHTED, not a full wash. The old build put a
   .85/.62/.85 three-layer wash over a .50-opacity photograph and measured
   10.7% mean brightness — nothing of the picture survived, on pages whose
   whole argument is the photography. Here the picture keeps its own value
   and the copy gets a gradient that is opaque where the words are and clear
   where they are not. Measured against the actual frames used: the darkest
   text run on any hero is --bone over the >=.86 end of the gradient. */
.fp-hero { position: relative; overflow: hidden; background: var(--ink); }
.fp-hero__shot {
  position: absolute; inset: 0; width: 100%; height: 100%;
  object-fit: cover; object-position: center;
  opacity: .62;
}
.fp-hero__scrim {
  position: absolute; inset: 0;
  background:
    linear-gradient(100deg, rgba(10,10,10,.94) 0%, rgba(10,10,10,.90) 34%,
                            rgba(10,10,10,.62) 62%, rgba(10,10,10,.34) 100%),
    linear-gradient(to top, rgba(10,10,10,.86) 0%, rgba(10,10,10,0) 46%);
}
.fp-hero__inner {
  position: relative;
  padding-block: clamp(2.5rem, 5.4vw, 4.5rem) clamp(2rem, 4.2vw, 3.25rem);
}
.fp-hero__k {
  margin: 0 0 1.1rem;
  font-family: var(--mono); font-size: .75rem; font-weight: 500;
  letter-spacing: .18em; text-transform: uppercase; color: var(--dim-ink);
}
.fp-hero__lede { margin: 1.35rem 0 0; max-width: 56ch; font-size: clamp(1.0625rem, 1.5vw, 1.25rem); }

/* A hero with NO photograph. /sister uses it, deliberately — see that page's
   header. It is not the photo hero with the image left out: it gets its own
   rule, its own vertical rhythm and a red hairline where the frame edge would
   be, so the absence is designed rather than merely empty. */
.fp-hero--blank { background: var(--ink); border-bottom: 1px solid var(--hair-ink); }
.fp-hero--blank .fp-hero__inner { padding-block: clamp(2.5rem, 5.2vw, 4.25rem) clamp(1.9rem, 4vw, 3rem); }
.fp-blank {
  margin: 2.25rem 0 0; padding: 1.5rem 0 0;
  border-top: 2px solid var(--red);
  max-width: 60ch;
}
.fp-blank__k {
  margin: 0 0 .55rem;
  font-family: var(--mono); font-size: .6875rem; font-weight: 500;
  letter-spacing: .16em; text-transform: uppercase; color: var(--red-ink);
}
.fp-blank__p { margin: 0; font-family: var(--mono); font-size: .8125rem; line-height: 1.8; color: var(--dim-ink); }

/* The figure strip. Counts, not statistics — every one of them is a thing the
   reader can then go and look at. */
.fp-figs {
  display: flex; flex-wrap: wrap; gap: clamp(1.25rem, 3.5vw, 2.75rem);
  margin: clamp(1.75rem, 4vw, 2.75rem) 0 0;
  padding: 1.35rem 0 0; border-top: 1px solid var(--hair-ink);
}
.fp-fig-n {
  display: block;
  font-family: var(--serif); font-variation-settings: 'opsz' 72;
  font-size: clamp(1.75rem, 3.4vw, 2.5rem); line-height: 1; color: var(--bone);
}
.fp-fig-t {
  display: block; margin-top: .4rem;
  font-family: var(--mono); font-size: .6875rem; letter-spacing: .12em;
  text-transform: uppercase; color: var(--dim-ink);
}


/* ══ FIGURES ══════════════════════════════════════════════════════════════
   Every <figcaption> on this site is clipped out of view — Ethan: "remove the
   description text on all the photos on the site from visibility." Present in
   the document, because it is the only place the photograph is described in
   words for search and assistive technology; absent from the page. Do not
   restyle them back into view.

   The VISIBLE line under a frame is .fp-slug: a short mono record — subject
   and place — which is what a record carries. A slug NEVER claims a location
   the frame cannot support. */
/* ── THE FRAME WRAPPER, AND WHY IT IS NOT OPTIONAL ───────────────────────
   .fp-fig carries `aspect-ratio`, and a block box with an aspect ratio derives
   its min-content WIDTH from its content HEIGHT. So a .fp-slug placed INSIDE
   the ratio box made the figure taller, which made it wider, which pushed
   .fp-frames--2/--3 past the rail: measured 1458px of laid-out content in a
   1440 viewport, 443px in a 390 one. `html { overflow-x: clip }` hid the
   scrollbar and hid the bug with it.

   So the ratio box holds the image and its clipped <figcaption> and NOTHING
   ELSE, and the visible slug is its SIBLING inside this wrapper. min-width:0
   stops any other min-content contribution from doing the same thing to a
   grid track. Markup:

       <div class="fp-shot">
         <figure class="fp-fig fp-fig--32"><img ...><figcaption>...</figcaption></figure>
         <p class="fp-slug">subject &middot; place</p>
       </div>                                                                  */
.fp-shot { min-width: 0; }

.fp-fig { margin: 0; }
.fp-fig figcaption {
  position: absolute; width: 1px; height: 1px; overflow: hidden;
  clip-path: inset(50%); white-space: nowrap;
}
.fp-fig img {
  width: 100%; height: 100%; object-fit: cover;
  background: var(--plate-2);
}
.fp-fig--43  { aspect-ratio: 4 / 3; }
.fp-fig--32  { aspect-ratio: 3 / 2; }
.fp-fig--11  { aspect-ratio: 1 / 1; }
.fp-fig--34  { aspect-ratio: 3 / 4; }
.fp-fig--wide { aspect-ratio: 2 / 1; }
.fp-fig--43 img, .fp-fig--32 img, .fp-fig--11 img,
.fp-fig--34 img, .fp-fig--wide img { aspect-ratio: inherit; }

/* THE ONE OFF-CENTRE CROP ON THESE PAGES, and it is deliberate.
   assets/bts/bts-office-glass-doors.jpg is 1300x731. Its right-hand end is a
   hot-desk bench carrying a towel over a chair, a mug and flowers, which
   reads as a coworking floor. In a 4/3 frame the visible window is
   731 * 4/3 = 975px of the 1300, and object-position 10% puts that window at
   x 32..1007 — so the bench cluster (x 1090..1300) is outside the frame
   entirely. Measured, not estimated. Do not change the ratio without
   recomputing the window. */
.fp-fig--doorway img { object-position: 10% 50%; }

.fp-slug {
  margin: .7rem 0 0;
  font-family: var(--mono); font-size: .75rem; line-height: 1.55;
  color: var(--dim-ink);
}
.fp-band--bone .fp-slug, .fp-band--ivory .fp-slug { color: var(--dim-bone); }

/* Two- and three-up frame rows. */
.fp-frames { display: grid; gap: clamp(1rem, 2.4vw, 1.5rem); margin: clamp(1.75rem, 4vw, 2.5rem) 0 0; }
.fp-frames--2 { grid-template-columns: 1fr; }
.fp-frames--3 { grid-template-columns: 1fr; }
@media (min-width: 720px)  { .fp-frames--2 { grid-template-columns: 1fr 1fr; } }
@media (min-width: 900px)  { .fp-frames--3 { grid-template-columns: repeat(3, 1fr); } }


/* ══ TWO-COLUMN READING ROW ═══════════════════════════════════════════════ */
.fp-split { display: grid; gap: clamp(1.75rem, 4vw, 3rem); margin: clamp(1.5rem, 3.5vw, 2.25rem) 0 0; }
@media (min-width: 900px) { .fp-split { grid-template-columns: 1.05fr .95fr; align-items: start; } }
@media (min-width: 900px) { .fp-split--wide-left { grid-template-columns: 1.35fr .65fr; } }


/* ══ THE DEFINITION LEDGER ════════════════════════════════════════════════
   A <dl> is the right element for "term, then what it is", and it is what the
   registry panel, the room facts and the kit list all are. Rows, not cards:
   six cards in a grid would make six equal claims out of a list whose whole
   point is that a reader can walk down it. */
.fp-dl { margin: 0; }
.fp-dl > div { padding: .95rem 0; border-top: 1px solid var(--hair-ink); }
.fp-dl > div:first-child { border-top: 0; padding-top: 0; }
.fp-band--bone .fp-dl > div, .fp-band--ivory .fp-dl > div { border-top-color: var(--hair-bone); }
.fp-dl dt {
  font-family: var(--mono); font-size: .6875rem; font-weight: 500;
  letter-spacing: .14em; text-transform: uppercase; color: var(--red-ink);
}
.fp-band--bone .fp-dl dt, .fp-band--ivory .fp-dl dt { color: var(--red-bone); }
.fp-dl dd { margin: .4rem 0 0; }
.fp-dl dd b { font-weight: 600; }

/* The registry panel: the same ledger, boxed, because it is the thing a reader
   copies into a search box. */
.fp-panel {
  padding: clamp(1.25rem, 3vw, 1.75rem);
  border: 1px solid var(--hair-ink);
  background: var(--plate-2);
}
.fp-band--bone .fp-panel, .fp-band--ivory .fp-panel {
  border-color: var(--hair-bone); background: var(--bone-2); color: var(--ink);
}
.fp-band--bone .fp-panel .fp-dl > div, .fp-band--ivory .fp-panel .fp-dl > div { border-top-color: var(--hair-bone); }
.fp-panel__k {
  margin: 0 0 1rem;
  font-family: var(--mono); font-size: .6875rem; font-weight: 500;
  letter-spacing: .16em; text-transform: uppercase; color: var(--dim-ink);
}
.fp-band--bone .fp-panel__k, .fp-band--ivory .fp-panel__k { color: var(--dim-bone); }
.fp-panel address { font-style: normal; }


/* ══ THE MONO RECORD LINE ═════════════════════════════════════════════════
   Where a fact is deliberately ABSENT, this is what stands in its place. It
   is not a gap and it is not an apology: it is a line of mono with a red rule
   beside it, in the slot the missing fact would have occupied.

   /office uses it for OPENING HOURS. Three contradictory windows are
   published for this firm, so the page states none of them, in copy or in
   markup, and the JSON-LD omits openingHoursSpecification. "By appointment"
   is true whichever of the three turns out to be right, and it is also what
   the booking actually does. */
.fp-record {
  display: grid; grid-template-columns: 2px 1fr; gap: 0 1rem;
  margin: 1.5rem 0 0; padding: .1rem 0;
}
.fp-record::before { content: ''; display: block; background: var(--red); border-radius: 1px; }
.fp-record__k {
  margin: 0 0 .3rem;
  font-family: var(--mono); font-size: .6875rem; font-weight: 500;
  letter-spacing: .16em; text-transform: uppercase; color: var(--red-ink);
}
.fp-band--bone .fp-record__k, .fp-band--ivory .fp-record__k { color: var(--red-bone); }
.fp-record__v { margin: 0; font-family: var(--mono); font-size: .8125rem; line-height: 1.75; color: var(--dim-ink); max-width: 62ch; }
.fp-band--bone .fp-record__v, .fp-band--ivory .fp-record__v { color: var(--dim-bone); }
.fp-record__v b { color: var(--bone); font-weight: 500; }
.fp-band--bone .fp-record__v b, .fp-band--ivory .fp-record__v b { color: var(--ink); }


/* ══ THE ADDRESS ON A MAP — /office ONLY ══════════════════════════════════
   This is AN ADDRESS ON A MAP, not a claimed Google Business Profile. No
   listing for the firm could be verified, so the markup neither links to one
   nor implies one exists: the link goes to a Maps SEARCH for the address,
   which is a search any reader can run for himself.

   /office is the site's SINGLE map surface. The band was deliberately moved
   off /firm so there is one page to correct if the address ever changes, and
   so that one page answers "where are you and can I come".

   The iframe is under a scrim on its non-reading side only; the copy sits in
   its own opaque plate rather than over the map, because text over a live
   third-party frame has no contrast guarantee at all — the map redraws. */
.fp-here { position: relative; margin-top: clamp(1.75rem, 4vw, 2.5rem); background: var(--plate-2); }
.fp-here__map {
  display: block; width: 100%; height: clamp(260px, 42vw, 420px);
  border: 0; filter: grayscale(1) contrast(1.06) brightness(.92);
}
.fp-here__body { padding: clamp(1.5rem, 3.5vw, 2.25rem); border-top: 2px solid var(--red); }
@media (min-width: 960px) {
  .fp-here { display: grid; grid-template-columns: 1.15fr .85fr; align-items: stretch; }
  .fp-here__map { height: 100%; min-height: 380px; }
  .fp-here__body { border-top: 0; border-left: 2px solid var(--red); }
}
.fp-here__k {
  margin: 0 0 .8rem;
  font-family: var(--mono); font-size: .6875rem; font-weight: 500;
  letter-spacing: .16em; text-transform: uppercase; color: var(--dim-ink);
}
.fp-addr {
  margin: 0; font-style: normal;
  font-family: var(--serif); font-variation-settings: 'opsz' 48;
  font-size: clamp(1.35rem, 2.6vw, 1.75rem); line-height: 1.2;
}
.fp-here__note { margin: 1rem 0 0; font-size: .9375rem; color: var(--dim-ink); max-width: 46ch; }


/* ══ THE ROSTER — /team ONLY ═══════════════════════════════════════════════
   Three named disciplines, each a rail beside a static grid of large discs.
   Nothing loops: the previous build was a continuous marquee of 148px circles
   with prev/next/pause controls, and a single undifferentiated loop of
   thirteen faces asserts nothing about structure. Three practices with real
   people under each, readable in one glance, puts the claim IN the page
   rather than next to it.

   ON A BONE GROUND, ALWAYS. See the portrait rule in this file's header. */
.fp-group { display: grid; gap: clamp(1.5rem, 3.5vw, 2.5rem); }
.fp-group + .fp-group {
  margin-top: clamp(2.5rem, 6vw, 4rem);
  padding-top: clamp(2.5rem, 6vw, 4rem);
  border-top: 1px solid var(--hair-bone);
}
@media (min-width: 900px) { .fp-group { grid-template-columns: 15.5rem 1fr; align-items: start; } }

.fp-group__k {
  margin: 0 0 .55rem;
  font-family: var(--mono); font-size: .6875rem; font-weight: 500;
  letter-spacing: .16em; text-transform: uppercase; color: var(--red-bone);
}
.fp-group__p { margin: .7rem 0 0; font-size: .9375rem; color: var(--dim-bone); max-width: 34ch; }
.fp-group__n {
  display: inline-block; margin: .9rem 0 0;
  padding: .28rem .6rem;
  border: 1px solid var(--hair-bone);
  font-family: var(--mono); font-size: .6875rem; letter-spacing: .1em;
  text-transform: uppercase; color: var(--ink);
}

.fp-people {
  display: grid; list-style: none; margin: 0; padding: 0;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: clamp(1.25rem, 3vw, 2rem) clamp(1rem, 2.4vw, 1.75rem);
}
@media (min-width: 560px) { .fp-people { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
@media (min-width: 1080px) { .fp-people { grid-template-columns: repeat(4, minmax(0, 1fr)); } }

/* The disc. Its own pale fill and a 1px ring, so a cut-out whose hair runs to
   the alpha edge still has an edge. */
.fp-disc {
  position: relative; display: block; width: 100%; aspect-ratio: 1;
  border-radius: 50%; overflow: hidden;
  background: var(--disc);
  box-shadow: inset 0 0 0 1px var(--hair-bone);
}
.fp-disc img { width: 100%; height: 100%; object-fit: cover; object-position: 50% 42%; }
.fp-person__n { margin: .85rem 0 0; font-size: 1.0625rem; }
.fp-person__r {
  margin: .3rem 0 0; display: flex; align-items: baseline; gap: .5rem;
  font-family: var(--mono); font-size: .75rem; line-height: 1.5; color: var(--dim-bone);
}
.fp-person__r::before { content: ''; flex: 0 0 auto; width: .75rem; height: 1px; background: var(--red); transform: translateY(-.28em); }


/* ══ THE NAME LIST — retired from /firm, kept for reuse ═══════════════════
   /firm S.04 used to set the eight projects as a two-column list of names.
   Ethan replaced it with a carousel of real captures on 2026-08-21 — see
   css/components/carousel.css. These rules stay because they are the plain
   fallback idiom for any band that has names and no pictures. */
.fp-names { display: grid; list-style: none; margin: clamp(1.5rem, 3.5vw, 2.25rem) 0 0; padding: 0; }
.fp-names li {
  display: flex; align-items: baseline; gap: 1rem;
  padding: .85rem 0; border-top: 1px solid var(--hair-ink);
}
.fp-names li:first-child { border-top: 0; }
.fp-names__i { font-family: var(--mono); font-size: .75rem; color: var(--red-ink); flex: 0 0 2.25rem; }
.fp-names__n { font-family: var(--serif); font-variation-settings: 'opsz' 48; font-size: clamp(1.15rem, 2.2vw, 1.5rem); }
@media (min-width: 720px) { .fp-names { grid-template-columns: 1fr 1fr; column-gap: clamp(2rem, 5vw, 4rem); } }


/* ══ THE STEP LEDGER — /firm's method ═════════════════════════════════════
   Four rows, indented by step, so the page reads DOWN through the job. No
   equal-column grid: an equal grid makes four equal claims out of a sequence. */
.fp-steps { margin: clamp(1.5rem, 3.5vw, 2.25rem) 0 0; }
.fp-step { display: grid; gap: .35rem 1.5rem; padding: 1.35rem 0; border-top: 1px solid var(--hair-ink); }
.fp-step:first-child { border-top: 0; }
@media (min-width: 760px) { .fp-step { grid-template-columns: 11rem 1fr; } }
.fp-step__i {
  font-family: var(--mono); font-size: .75rem; font-weight: 500;
  letter-spacing: .1em; text-transform: uppercase; color: var(--dim-ink);
}
.fp-band--bone .fp-step__i, .fp-band--ivory .fp-step__i { color: var(--dim-bone); }
.fp-step__d { margin: .5rem 0 0; max-width: 62ch; }


/* ══ THE FOUNDER LINE ═════════════════════════════════════════════════════
   NO QUOTE IS EVER INVENTED FOR ETHAN. The one blockquote on these four
   pages is verbatim, was given by him, and has survived every rebuild of the
   firm page unchanged. It is not to be paraphrased, trimmed, re-punctuated
   or "tightened". If a second founder line is ever wanted it has to come
   from him — do not write one.

   On a BONE band, because it carries his portrait and the portrait is a
   cut-out on a pale disc. */
.fp-founder { display: grid; gap: clamp(1.25rem, 3vw, 2rem); margin: clamp(2rem, 5vw, 3rem) 0 0; }
@media (min-width: 640px) { .fp-founder { grid-template-columns: 8.5rem 1fr; align-items: start; } }
.fp-founder blockquote {
  margin: 0; max-width: 46ch;
  font-family: var(--serif); font-variation-settings: 'opsz' 72;
  font-size: clamp(1.2rem, 2.4vw, 1.6rem); line-height: 1.28; font-style: italic;
}
.fp-founder__by {
  margin: 1rem 0 0;
  font-family: var(--mono); font-size: .75rem; letter-spacing: .1em;
  text-transform: uppercase; color: var(--dim-bone);
}


/* ══ THE YEARS LEDGER ═════════════════════════════════════════════════════ */
.fp-years { margin: clamp(2rem, 5vw, 3rem) 0 0; }
.fp-year { display: grid; gap: .3rem 1.5rem; padding: 1.05rem 0; border-top: 1px solid var(--hair-bone); }
.fp-band--ink .fp-year, .fp-band--plate .fp-year { border-top-color: var(--hair-ink); }
@media (min-width: 760px) { .fp-year { grid-template-columns: 7rem 12rem 1fr; align-items: baseline; } }
.fp-year__y { font-family: var(--mono); font-size: .8125rem; color: var(--red-bone); }
.fp-band--ink .fp-year__y, .fp-band--plate .fp-year__y { color: var(--red-ink); }
.fp-year__t { font-weight: 600; }
.fp-year__d { color: var(--dim-bone); font-size: .9375rem; }
.fp-band--ink .fp-year__d, .fp-band--plate .fp-year__d { color: var(--dim-ink); }


/* ══ THE ONWARD LIST ══════════════════════════════════════════════════════
   Progressive disclosure, landed. Every paragraph a firm page did NOT print
   is one row here, pointed at the page that owns it. Not a shorter
   paragraph — a link. */
.fp-onward { display: grid; gap: 0; list-style: none; margin: clamp(1.5rem, 3.5vw, 2.25rem) 0 0; padding: 0; }
@media (min-width: 760px) { .fp-onward { grid-template-columns: 1fr 1fr; column-gap: clamp(1.5rem, 4vw, 3rem); } }
.fp-onward a {
  display: grid; grid-template-columns: 1fr auto; align-items: baseline; gap: .35rem 1rem;
  padding: 1.05rem 0; border-top: 1px solid var(--hair-ink);
  color: inherit; text-decoration: none;
}
.fp-onward__t { font-family: var(--serif); font-variation-settings: 'opsz' 48; font-size: 1.15rem; }
.fp-onward__d { grid-column: 1 / -1; font-size: .9375rem; color: var(--dim-ink); }
.fp-onward__a { color: var(--red-ink); font-family: var(--mono); }
@media (hover: hover) and (pointer: fine) {
  .fp-onward a:hover { background: rgba(245, 241, 232, .045); }
  .fp-onward a:hover .fp-onward__t { color: var(--bone); }
}


/* ══ BUTTONS ══════════════════════════════════════════════════════════════ */
.fp-ctas { display: flex; flex-wrap: wrap; gap: .75rem; margin: clamp(1.5rem, 3.5vw, 2.25rem) 0 0; }
.fp-btn {
  display: inline-flex; align-items: center; gap: .55rem;
  padding: .85rem 1.35rem;
  font-family: var(--sans); font-size: .9375rem; font-weight: 600;
  letter-spacing: .01em; text-decoration: none;
  border: 1px solid var(--hair-ink); color: var(--bone); background: transparent;
  transition: background .18s var(--ease), border-color .18s var(--ease);
}
.fp-btn--red { background: var(--red); border-color: var(--red); color: #fff; }
@media (hover: hover) and (pointer: fine) {
  .fp-btn:hover { background: rgba(245, 241, 232, .08); border-color: rgba(245, 241, 232, .32); }
  .fp-btn--red:hover { background: #a4132c; border-color: #a4132c; }
}
.fp-band--bone .fp-btn, .fp-band--ivory .fp-btn { color: var(--ink); border-color: var(--hair-bone); }
.fp-band--bone .fp-btn--red, .fp-band--ivory .fp-btn--red { color: #fff; border-color: var(--red); }
@media (hover: hover) and (pointer: fine) {
  .fp-band--bone .fp-btn:hover, .fp-band--ivory .fp-btn:hover { background: rgba(10, 10, 10, .06); border-color: rgba(10, 10, 10, .34); }
}

/* An inline link that reads as a way onward rather than a button. */
.fp-go {
  display: inline-flex; align-items: center; gap: .5rem;
  margin: 1.35rem 0 0;
  font-family: var(--sans); font-size: .9375rem; font-weight: 600;
  color: var(--red-ink); text-decoration: none;
  border-bottom: 1px solid currentColor; padding-bottom: 2px;
}
.fp-band--bone .fp-go, .fp-band--ivory .fp-go { color: var(--red-bone); }


/* ══ THE CLOSE — EVERY ONE OF THESE PAGES ENDS IN A BOOKING ═══════════════
   Tier 2's rule: one job, then a booking. /contact is another task's page and
   does not exist in this repo yet, so the primary CTA points at /contact and
   a working mailto:contact@tnaado.ca sits underneath it — a reader who lands
   before /contact ships still has a way to reach the firm, and one who lands
   after gets the form.

   NO BOOKING FORM IS IMPLEMENTED HERE. css/booking-band.css and js/booking.js
   belong to another task; writing a second booking implementation on these
   four pages is how two of them end up disagreeing. When the band lands, the
   swap is this section's markup for .bkx's, and nothing else on the page
   changes. */
.fp-close { position: relative; overflow: hidden; background: var(--ink); border-top: 2px solid var(--red); }
.fp-close__shot { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; opacity: .34; }
.fp-close__scrim {
  position: absolute; inset: 0;
  background: linear-gradient(100deg, rgba(10,10,10,.95) 0%, rgba(10,10,10,.88) 46%, rgba(10,10,10,.58) 100%);
}
.fp-close__inner { position: relative; padding-block: clamp(2.25rem, 4.6vw, 4rem); }
.fp-close__k {
  margin: 0 0 1rem;
  font-family: var(--mono); font-size: .75rem; font-weight: 500;
  letter-spacing: .18em; text-transform: uppercase; color: var(--red-ink);
}
.fp-close__mail {
  margin: 1.35rem 0 0;
  font-family: var(--mono); font-size: .8125rem; color: var(--dim-ink);
}
.fp-close__mail a { color: var(--bone); text-decoration: underline; text-underline-offset: .18em; }

/* /office adds one row: the in-person visit, offered beside the booking
   rather than instead of it. Ethan asked for "an appointment booker for
   in-person office visits" — until the band lands, this is the honest form of
   it: what the visit is, and two ways to ask for one. */
.fp-visit {
  margin: clamp(1.75rem, 4vw, 2.5rem) 0 0;
  padding: clamp(1.25rem, 3vw, 1.75rem);
  border: 1px solid var(--hair-ink); background: rgba(19, 19, 19, .72);
  max-width: 46rem;
}
.fp-visit__k {
  margin: 0 0 .5rem;
  font-family: var(--mono); font-size: .6875rem; font-weight: 500;
  letter-spacing: .16em; text-transform: uppercase; color: var(--red-ink);
}
.fp-visit__h { margin: 0 0 .6rem; }
.fp-visit__p { margin: 0; color: var(--dim-ink); font-size: .9375rem; max-width: 58ch; }


/* ══ THE LABS SCREEN — /sister ═════════════════════════════════════════════
   One real screen, in a plate, with a horizontal scroll of its own so a wide
   table never pushes the page sideways. The hint line is not decoration: on a
   phone the table is genuinely wider than the frame and a reader has to be
   told it moves. */
.fp-screen { margin: clamp(1.75rem, 4vw, 2.5rem) 0 0; max-width: 46rem; }
.fp-screen__frame {
  overflow-x: auto; -webkit-overflow-scrolling: touch;
  border: 1px solid var(--hair-ink); background: var(--plate-2);
}
.fp-screen__frame img { min-width: 46rem; width: 100%; height: auto; }
.fp-screen__hint {
  margin: .7rem 0 0;
  font-family: var(--mono); font-size: .75rem; color: var(--dim-ink);
}
@media (min-width: 800px) { .fp-screen__hint { display: none; } }


/* ══ THE TWO-FIRM PANEL — /sister ═════════════════════════════════════════ */
.fp-pair { display: grid; gap: clamp(1rem, 2.4vw, 1.5rem); margin: clamp(1.75rem, 4vw, 2.5rem) 0 0; }
@media (min-width: 720px) { .fp-pair { grid-template-columns: 1fr 1fr; } }
.fp-pair__card { padding: clamp(1.25rem, 3vw, 1.75rem); border: 1px solid var(--hair-bone); background: var(--bone-2); }
.fp-pair__k {
  margin: 0 0 .6rem;
  font-family: var(--mono); font-size: .6875rem; font-weight: 500;
  letter-spacing: .16em; text-transform: uppercase; color: var(--red-bone);
}
.fp-pair__h { margin: 0 0 .5rem; }
.fp-pair__where {
  margin: 0 0 .8rem;
  font-family: var(--mono); font-size: .8125rem; color: var(--dim-bone);
}
.fp-pair__p { margin: 0; font-size: .9375rem; }


/* ══ REDUCED MOTION ═══════════════════════════════════════════════════════
   There is no motion on these pages to reduce. The declaration is here so
   that anything added later inherits the intent rather than having to
   rediscover it. */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { animation-duration: .01ms !important; transition-duration: .01ms !important; }
}


/* ══ THE PHONE HERO — ADDED AFTER RENDERING IT ════════════════════════════
   MEASURED, NOT GUESSED. The desktop scrim is side-weighted at 100deg, which
   is right at 1440: the copy sits on the >=.86 end and the picture keeps its
   value on the open right-hand third. At 390 there is no right-hand third. The
   gradient line for 100deg in a 390x780 box is 390·sin100° + 780·cos100° ≈
   530px, so the whole visible hero falls inside the .94–.62 stops and the
   photograph renders as a dark texture. On four pages whose argument IS the
   photography, that is a real loss and it was only visible in the render.

   The fix keeps the same two layers and re-weights them for a tall box: a
   BOTTOM-weighted wash, and enough top padding that the top of the hero is a
   band of actual photograph above the first line of type. Nothing above is
   overridden for the desktop case — this is a narrow-viewport block only.   */
@media (max-width: 719px) {
  .fp-hero__shot { opacity: .78; }
  .fp-hero__scrim {
    background:
      linear-gradient(to top, rgba(10,10,10,.95) 0%, rgba(10,10,10,.93) 54%,
                              rgba(10,10,10,.72) 78%, rgba(10,10,10,.34) 100%),
      linear-gradient(100deg, rgba(10,10,10,.42) 0%, rgba(10,10,10,.20) 100%);
  }
  .fp-hero__inner { padding-block: clamp(7.5rem, 30vw, 10rem) 2.75rem; }

  /* The close band is short and its copy starts at the top, so it gets the
     same bottom-weighting without the padding. */
  .fp-close__scrim {
    background: linear-gradient(to top, rgba(10,10,10,.95) 0%, rgba(10,10,10,.90) 70%,
                                        rgba(10,10,10,.70) 100%);
  }
}


/* ══ LINKS IN THE REMAINING COPY CONTAINERS — ADDED, AND IT WAS A REAL BUG ══
   The link rule above lists .fp-p, .fp-lede, .fp-note and .fp-dl. It does NOT
   list .fp-record__v, .fp-here__note or .fp-visit__p, so an <a> inside any of
   those fell through to the user-agent stylesheet and rendered #0000EE — the
   default browser blue — on an ink ground. MEASURED on /office at 1440:
   2.11:1, against a 4.5 requirement, on the one link that sends a reader from
   the opening-hours record to the booking. Colour was the only signal too,
   with no underline.

   Same two colours and the same underline as the rule above, so a reader
   cannot tell which container a link is in.                                  */
.fp-record__v a, .fp-here__note a, .fp-visit__p a, .fp-slug a {
  color: var(--red-ink); text-decoration: underline;
  text-decoration-thickness: 1px; text-underline-offset: .18em;
}
.fp-band--bone .fp-record__v a, .fp-band--bone .fp-here__note a,
.fp-band--bone .fp-visit__p a, .fp-band--bone .fp-slug a,
.fp-band--ivory .fp-record__v a, .fp-band--ivory .fp-here__note a,
.fp-band--ivory .fp-visit__p a, .fp-band--ivory .fp-slug a { color: var(--red-bone); }


/* ══ THE SISTER FIRM'S WORDMARK — /sister ONLY ════════════════════════════
   ADDITIVE. Nothing above is overridden except one grid-template-columns, on
   a modifier class that only /sister uses.

   assets/brand/tnaado-labs-wordmark.png is 822x130 on a transparent ground:
   "TNAADO" in the estate red with the storm spiral for the O, and "Labs"
   beside it in DARK GREY italic under a grey swoosh. The red half survives on
   ink; the grey half does not. So the mark is placed on --bone or --ivory and
   never on --ink — the same rule class, and the same reasoning, as the
   portrait rule at the top of this file.

   IT IS NOT PUT THROUGH .fp-disc OR .fp-founder's PORTRAIT COLUMN. That
   column is 8.5rem, which is right for a 640-square face and wrong for a
   6.3:1 wordmark: at 136px wide the "Labs" half renders about 15px tall and
   stops being a wordmark at all. So the mark gets its own width, and
   .fp-founder--mark stacks the block instead of laying it out in two columns
   — the quote beside it then keeps its full measure at every width. */
.fp-founder--mark { grid-template-columns: 1fr; }
.fp-mark { display: block; margin: 0; }
.fp-mark img { width: clamp(11rem, 22vw, 15.5rem); height: auto; }


/* SAME DEFECT AS .bkx__go: a red-filled button given a red focus ring measures
   1.00:1 and shows nothing. --red-bone is correct for ordinary controls sitting
   ON bone, but not for a control that IS red. Ink clears the red fill at 3.36:1
   and the bone behind the offset gap at near-maximum. */
.fp-btn--red:focus-visible {
  outline: 3px solid var(--ink, #0a0a0a);
  outline-offset: 3px;
}
