/* ==========================================================================
   OneName Consulting — global stylesheet

   Art direction: editorial magazine, not corporate web. The page is carried
   by type, rule-work and whitespace; there are no filled cards, no drop
   shadows and no radii to speak of. Colour stays the forest/bisque pairing
   with terracotta used only as punctuation.

   Mobile first. Base rules are the phone layout; every media query is
   min-width and only ever adds. Breakpoints:
     base            up to 480px   phone
     min-width 481   481 – 768px   tablet
     min-width 769   769px and up  desktop
   ========================================================================== */

/* --------------------------------------------------------------------------
   1. Design tokens
   The neutral ramp is tinted toward the forest green rather than a flat grey,
   so background, text and primary all read as one family. Sizing tokens hold
   their phone values here and are raised at each breakpoint.
   -------------------------------------------------------------------------- */

:root {
  /* Colour */
  --color-primary: #1B4332;         /* Forest Green */
  --color-primary-hover: #143528;
  --color-on-primary: #F0EAE1;
  --color-background: #F0EAE1;      /* Bisque White */
  --color-surface: #FFFFFF;
  --color-text: #16211C;
  --color-text-muted: #4A574F;
  --color-text-secondary: var(--color-text-muted);   /* alias */
  --color-border: rgba(27, 67, 50, 0.28);

  /* The one accent. Punctuation only — a turned word, a hovered rule, an
     index number. Never body copy, never a background, never meaning-bearing
     on its own. */
  --color-accent: #A55232;          /* muted terracotta — 4.56:1 on bisque */

  /* Reversed-out variants for the forest proof band. The band is the only
     dark surface on the site, and the base accent measures 2.03:1 on forest —
     far below the 4.5:1 floor — so it cannot be used there. These three exist
     for that band alone and are not a second brand palette. */
  --color-on-primary-muted: #B8B5AE;   /* 5.41:1 on forest */
  --color-accent-on-primary: #D8977E;  /* base accent lightened 25%, 4.56:1 */
  --rule-on-primary: rgba(240, 234, 225, 0.24);

  /* Rule-work does most of the structural work now that cards are gone. */
  --rule-hairline: rgba(27, 67, 50, 0.18);
  --rule-strong: rgba(27, 67, 50, 0.42);

  /* Typography */
  --font-display: "Lora", Georgia, "Times New Roman", serif;
  --font-body: "Work Sans", -apple-system, BlinkMacSystemFont, "Segoe UI",
    Helvetica, Arial, sans-serif;

  /* Three sizes, and only three. Hierarchy is carried by family, weight,
     case, tracking and rule-work instead of by a long size ramp, so a
     heading and a card title can share a size and still read as different
     things. Phone values are the base; the two larger sizes grow with the
     viewport and stop at their cap. Anything not listed here — the
     navigation and the footer — keeps its own sizes on purpose.

     --text-s is the floor for the whole page: nothing is set smaller. The
     navigation, footer links and legal line used to sit at 12px and now sit
     here with the rest. */
  --text-s: 0.875rem;                          /* 14px — body, labels, meta */
  --text-m: clamp(1.25rem, 1.4vw + 0.94rem, 1.75rem);  /* 20 → 28px — sub-heads, leads */
  --text-l: clamp(2rem, 3.6vw + 1.1rem, 3.5rem);       /* 32 → 56px — headlines */

  /* Editorial metadata: small, wide, uppercase. One treatment, used
     everywhere that isn't display or body. */
  --tracking-meta: 0.18em;

  /* Spacing */
  --space-xs: 0.25rem;   /*   4px */
  --space-sm: 0.5rem;    /*   8px */
  --space-md: 1rem;      /*  16px */
  --space-lg: 1.5rem;    /*  24px */
  --space-xl: 2.5rem;    /*  40px */
  --space-2xl: 3.75rem;  /*  60px */
  --space-3xl: 5rem;     /*  80px */
  --space-4xl: 7.5rem;   /* 120px — desktop section rhythm */

  /* Layout */
  --container-max: 1240px;
  --container-gutter: var(--space-md);
  --measure: 560px;

  --radius-sm: 0;
  --radius-md: 0;

  /* Kept as tokens because components still reference them, but elevation is
     no longer part of the visual language — both resolve to nothing. */
  --shadow-sm: none;
  --shadow-md: none;

  --transition-fast: 180ms cubic-bezier(0.22, 1, 0.36, 1);

  /* Navbar — phone values */
  --nav-padding-y: var(--space-md);
  --nav-padding-x: var(--space-md);
  --nav-gap: var(--space-md);
  --logo-size: 36px;       /* ON mark HEIGHT; width follows its 1.84:1 ratio */
  --wordmark-size: 23px;   /* wordmark HEIGHT; width follows its 2.94:1 ratio */

  /* Section rhythm — phone values.
     --section-padding-lg is the interior-page rhythm; the landing page
     sets its own padding per section instead of drawing from a token. */
  --section-padding-lg: var(--space-xl);

  /* Pattern accents. Opacity and height are the two things worth tweaking,
     so they live here rather than in the rules below. */
  /* The hero field is meant to be noticed on second glance, not first.
     Phones get the faintest setting so nothing competes with the headline.
     Measured at 0.09: muted copy holds 5.46:1 and the terracotta em 3.92:1
     over the densest dots, so type sits on it cleanly. */
  --pattern-opacity: 0.04;          /* hero field — phone */
  --pattern-opacity-section: 0.12;  /* section accent */
  --pattern-opacity-cta: 0.07;      /* closing CTA field — phone */
  --pattern-divider-h: 56px;        /* full-opacity closing band */
}

/* --------------------------------------------------------------------------
   2. Reset
   -------------------------------------------------------------------------- */

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

body,
h1,
h2,
p {
  margin: 0;
}

ul {
  margin: 0;
  padding: 0;
  list-style: none;
}

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

button {
  font: inherit;
  color: inherit;
  background: none;
  border: 0;
  padding: 0;
}

/* --------------------------------------------------------------------------
   3. Base
   -------------------------------------------------------------------------- */

body {
  background-color: var(--color-background);
  color: var(--color-text);
  font-family: var(--font-body);
  font-size: var(--text-s);
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;

  /* Composition leans on offsets and oversized type; this is the backstop
     that guarantees none of it can push the page sideways. */
  overflow-x: hidden;
}

/* Sections are numbered by CSS so the markup stays clean. */
main {
  counter-reset: section;
}

:focus-visible {
  outline: 2px solid var(--color-primary);
  outline-offset: 3px;
}

/* Off-screen until focused, so keyboard users can jump the navigation
   instead of tabbing the logo and four links on every page. */
.skip-link {
  position: absolute;
  left: var(--space-md);
  top: var(--space-md);
  z-index: 200;
  padding: var(--space-sm) var(--space-md);
  background-color: var(--color-primary);
  color: var(--color-on-primary);
  font-size: var(--text-s);
  font-weight: 500;
  letter-spacing: var(--tracking-meta);
  text-transform: uppercase;
  text-decoration: none;
  transform: translateY(-200%);
}

.skip-link:focus {
  transform: translateY(0);
}

/* --------------------------------------------------------------------------
   4. Layout
   -------------------------------------------------------------------------- */

.container {
  max-width: var(--container-max);
  margin-inline: auto;
  padding-inline: var(--container-gutter);
}

/* --------------------------------------------------------------------------
   5. Header / navigation

   Phone: lockup and hamburger share row one; the menu wraps to row two and
   is collapsed. Tablet up: one row, menu always open, hamburger gone.
   -------------------------------------------------------------------------- */

.header {
  position: sticky;
  top: 0;
  z-index: 100;
  background-color: var(--color-background);
  border-bottom: 1px solid var(--rule-hairline);
}

.nav {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-md);
  padding: var(--nav-padding-y) var(--nav-padding-x);
}

/* Lockup: mark then wordmark, sharing one link to home. */
.logo {
  display: flex;
  align-items: center;
  gap: var(--space-sm);
  flex-shrink: 0;
}

/* Both pieces are wider than tall, so height drives the size and width
   follows. Setting both dimensions would squash them. */
.logo__mark {
  height: var(--logo-size);
  width: auto;
}

.logo__wordmark {
  height: var(--wordmark-size);
  width: auto;
}

/* Hamburger --------------------------------------------------------------- */

/* Hidden by default so that without JavaScript the menu below simply stays
   visible and usable. The .js class, set by main.js, turns the collapse on. */
.nav__toggle {
  display: none;
  width: 44px;              /* 44px minimum touch target */
  height: 44px;
  margin-right: calc(var(--space-xs) * -1);
  cursor: pointer;
}

.js .nav__toggle {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 5px;
}

.nav__toggle-bar {
  display: block;
  width: 22px;
  height: 1px;
  background-color: var(--color-primary);
  transition: transform var(--transition-fast),
    opacity var(--transition-fast);
}

/* Open: top and bottom bars cross, middle bar drops out */
.nav__toggle[aria-expanded="true"] .nav__toggle-bar:nth-child(1) {
  transform: translateY(6px) rotate(45deg);
}

.nav__toggle[aria-expanded="true"] .nav__toggle-bar:nth-child(2) {
  opacity: 0;
}

.nav__toggle[aria-expanded="true"] .nav__toggle-bar:nth-child(3) {
  transform: translateY(-6px) rotate(-45deg);
}

/* Menu -------------------------------------------------------------------- */

.nav__links {
  display: flex;
  flex-direction: column;
  gap: var(--nav-gap);
  flex-basis: 100%;
  padding-bottom: var(--space-sm);
}

/* Collapsed on phones until the hamburger opens it */
.js .nav__links {
  display: none;
}

.js .nav__links--open {
  display: flex;
}

.nav__link {
  position: relative;
  display: inline-block;
  padding-block: var(--space-xs);
  color: var(--color-primary);
  font-family: var(--font-body);
  font-size: var(--text-s);
  font-weight: 500;
  letter-spacing: var(--tracking-meta);
  text-transform: uppercase;
  text-decoration: none;
  transition: color var(--transition-fast);
}

/* Rule grows from the left rather than fading in — reads as drawn, not lit. */
.nav__link::after {
  content: "";
  position: absolute;
  left: 0;
  bottom: 0;
  width: 100%;
  height: 1px;
  background-color: currentColor;
  transform: scaleX(0);
  transform-origin: left;
  transition: transform var(--transition-fast);
}

.nav__link:hover::after {
  transform: scaleX(1);
}

/* Current page: the rule is simply already drawn. Underline, not colour
   alone, carries the meaning. */
.nav__link.active::after {
  transform: scaleX(1);
}

/* --------------------------------------------------------------------------
   6. Hero

   A magazine cover: the headline runs the full measure at display size, the
   supporting copy sits low and right of centre, and the actions sit left
   under it. The imbalance is the composition.
   -------------------------------------------------------------------------- */

/* --------------------------------------------------------------------------
   Pattern accents

   Three placements, no more: a faded field in the hero, a faded field beside
   the approach heading, and a full-opacity band closing the About page.

   The governing rule is that type never sits on the pattern. That isn't left
   to opacity — it is built into the geometry. On phone and tablet the hero
   accent is a band and the hero's top padding is derived from its height, so
   the headline starts below it. On desktop the accent takes the right of the
   frame and the columns below stop short of it. Either way the two cannot
   meet, at any viewport width.

   The artwork is the client's own file with its paper keyed out, so the site's
   bisque shows through rather than the artwork's slightly cooler #F3F1EF. Ink
   is the artwork's measured #0D3720, untouched.

   Paths are relative to this stylesheet, so they resolve identically from the
   root page and from pages/.
   -------------------------------------------------------------------------- */

.pattern-accent {
  position: absolute;
  z-index: 0;
  background-image: url("../assets/images/pattern.webp");
  background-size: cover;
  background-position: center;
  background-repeat: no-repeat;
  opacity: var(--pattern-opacity);
  pointer-events: none;
}

/* A field behind the whole hero, not just the type side. The radial mask
   concentrates it in the top-right corner, which the split layout's
   photograph now covers, and lets it fall away to nothing moving left —
   so it stays faintly present behind the headline without competing with
   it. */
.pattern-accent--hero {
  inset: 0;
  -webkit-mask-image: radial-gradient(115% 95% at 100% 0%,
    #000 0%, rgba(0, 0, 0, 0.5) 40%, transparent 74%);
  mask-image: radial-gradient(115% 95% at 100% 0%,
    #000 0%, rgba(0, 0, 0, 0.5) 40%, transparent 74%);
}

/* Beside the approach heading, which is capped at --measure and sits left.
   Hidden on phones, where there is no clear space to put it. */
.pattern-accent--section {
  display: none;
  opacity: var(--pattern-opacity-section);
}

/* Full opacity — this one is the accent, not a wash. Anchored to the foot of
   the artwork because that is the only region whose dots run unbroken from
   edge to edge; sliced anywhere higher, the band crosses one of the field's
   white voids and reads as two torn-off pieces rather than one rule. */
.pattern-divider {
  height: var(--pattern-divider-h);
  background-image: url("../assets/images/pattern.webp");
  background-size: cover;
  background-position: center bottom;
  background-repeat: no-repeat;
}

.hero {
  position: relative;
  overflow: hidden;
  padding-block: 0 var(--space-3xl);
}

/* Mobile opens on the photograph, not on type. */
.hero__grid {
  position: relative;
  z-index: 1;
  display: grid;
}

.hero__media {
  order: -1;
}

.hero__media img {
  width: 100%;
  height: 100%;
  aspect-ratio: 4 / 3;
  object-fit: cover;
  object-position: center 22%;
}

.hero__body {
  min-width: 0;
  padding-inline: var(--container-gutter);
  padding-block: var(--space-xl) 0;
}

.hero__title {
  font-family: var(--font-display);
  font-size: var(--text-l);
  font-weight: 500;
  line-height: 0.94;
  letter-spacing: -0.035em;
  text-wrap: balance;
}

/* The one flourish: "craft" set in Lora's italic and turned terracotta, so
   the word demonstrates the craft it names. Delete the <em> in index.html to
   remove it. */
.hero__title em {
  font-style: italic;
  font-weight: 400;
  color: var(--color-accent);
}

.hero__description {
  max-width: var(--measure);
  margin-top: var(--space-xl);
  padding-top: var(--space-md);
  border-top: 1px solid var(--rule-strong);
  font-size: var(--text-s);
  line-height: 1.6;
  color: var(--color-text-muted);
  text-wrap: pretty;
}

.hero__cta {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--space-lg);
  margin-top: var(--space-xl);
}

/* --------------------------------------------------------------------------
   7. Buttons

   Editorial labels, not UI chrome: small caps, wide tracking, a hairline
   under them and an arrow that leans on hover. Touch target is held at 44px
   by padding rather than by box size.
   -------------------------------------------------------------------------- */

.btn {
  display: inline-flex;
  align-items: center;
  gap: var(--space-sm);
  max-width: 100%;
  min-height: 44px;
  padding: var(--space-sm) 0;
  border: 0;
  border-bottom: 1px solid currentColor;
  border-radius: var(--radius-sm);
  background: none;
  font-family: var(--font-body);
  font-size: var(--text-s);
  font-weight: 500;
  letter-spacing: var(--tracking-meta);
  line-height: 1.2;
  text-transform: uppercase;
  text-decoration: none;
  cursor: pointer;
  transition: color var(--transition-fast), opacity var(--transition-fast);
}

.btn::after {
  content: "\2197";               /* ↗ */
  font-size: 1em;
  line-height: 1;
  transition: transform var(--transition-fast);
}

.btn:hover::after {
  transform: translate(3px, -3px);
}

/* A filled button, for the one action that must not read as a link. Kept
   square and small-caps so it still belongs to the type system rather than
   arriving as generic UI. */
.btn--solid {
  padding: var(--space-md) var(--space-lg);
  border: 1px solid var(--color-primary);
  background-color: var(--color-primary);
  color: var(--color-on-primary);
}

.btn--solid:hover {
  background-color: var(--color-primary-hover);
  border-color: var(--color-primary-hover);
  color: var(--color-on-primary);
}

.btn--secondary {
  color: var(--color-text-muted);
  border-bottom-color: var(--rule-strong);
}

.btn--secondary:hover {
  color: var(--color-primary);
}

/* --------------------------------------------------------------------------
   8. Shared section furniture
   -------------------------------------------------------------------------- */

.section {
  counter-increment: section;
  padding-block: var(--section-padding-lg);
}

.section__title {
  font-family: var(--font-display);
  font-size: var(--text-l);
  font-weight: 500;
  line-height: 1.05;
  letter-spacing: -0.025em;
  text-wrap: balance;
}

/* Section number as editorial metadata above the heading: 01, 02.
   Generated, so the markup carries no decorative spans. */
.section__title::before {
  content: counter(section, decimal-leading-zero);
  display: block;
  margin-bottom: var(--space-md);
  font-family: var(--font-body);
  font-size: var(--text-s);
  font-weight: 500;
  letter-spacing: var(--tracking-meta);
  line-height: 1;
  color: var(--color-accent);
}

.section__intro {
  max-width: var(--measure);
  margin-top: var(--space-lg);
  font-size: var(--text-s);
  line-height: 1.7;
  color: var(--color-text-secondary);
}

.eyebrow {
  font-size: var(--text-s);
  font-weight: 500;
  letter-spacing: var(--tracking-meta);
  text-transform: uppercase;
  color: var(--color-text-secondary);
}

/* Cards are entries in an index, not objects on a surface: no fill, no
   radius, no elevation. A rule on top and space below. */
.card {
  position: relative;
  padding: var(--space-lg) 0 var(--space-xl);
  background: none;
  border: 0;
  border-top: 1px solid var(--rule-hairline);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-sm);
  transition: border-color var(--transition-fast),
    transform var(--transition-fast);
}

.card:hover,
.card:focus-within {
  border-top-color: var(--color-accent);
  transform: translateY(-3px);
}

.card__title {
  font-family: var(--font-display);
  font-size: var(--text-m);
  font-weight: 500;
  line-height: 1.2;
  letter-spacing: -0.015em;
  color: var(--color-primary);
  text-wrap: balance;
}

.card__text {
  margin-top: var(--space-md);
  max-width: 46ch;
  font-size: var(--text-s);
  line-height: 1.7;
  color: var(--color-text-muted);
}

/* --------------------------------------------------------------------------
   9. About — page header
   -------------------------------------------------------------------------- */

.about-header {
  position: relative;
  overflow: hidden;
  padding-block: var(--section-padding-lg) var(--space-3xl);
}

/* Same dot field the landing hero uses, so the two openings rhyme. It sits
   behind the grid, masked into the top-right corner where the portrait is. */
.about-header__grid {
  position: relative;
  z-index: 1;
  display: grid;
  gap: var(--space-xl);
}

.about-header__title {
  margin-top: var(--space-md);
  font-family: var(--font-display);
  font-size: var(--text-l);
  font-weight: 500;
  line-height: 0.98;
  letter-spacing: -0.035em;
  text-wrap: balance;
}

.about-header__subtitle {
  max-width: var(--measure);
  margin-top: var(--space-lg);
  padding-top: var(--space-md);
  border-top: 1px solid var(--rule-strong);
  font-size: var(--text-s);
  line-height: 1.7;
  color: var(--color-text-muted);
}

/* Portrait slot. Holds its 4:5 shape whether or not an image is in it yet,
   so dropping the real headshot in later shifts nothing. Art-directed rather
   than framed: no radius, a hairline, and the image bleeds to the edges. */
.about-header__figure {
  margin: 0;
}

/* The portrait is not a photograph, and says so. */
.about-header__credit {
  margin-top: var(--space-sm);
  font-size: 0.75rem;
  letter-spacing: var(--tracking-meta);
  text-transform: uppercase;
  color: var(--color-text-muted);
}

.about-header__portrait {
  position: relative;
  display: flex;
  align-items: flex-end;
  justify-content: flex-start;
  aspect-ratio: 4 / 5;
  max-width: 400px;
  padding: var(--space-md);
  border: 1px solid var(--rule-hairline);
  border-radius: 0;
  background: none;
  color: var(--color-text-secondary);
  font-size: var(--text-s);
  letter-spacing: var(--tracking-meta);
  text-transform: uppercase;
  overflow: hidden;
}

.about-header__portrait img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  border-radius: 0;
  transition: transform var(--transition-fast);
}

/* A real image removes the placeholder feel entirely; the frame stays. */
.about-header__portrait:hover img {
  transform: scale(1.02);
}

/* --------------------------------------------------------------------------
   10. About — story
   -------------------------------------------------------------------------- */

.story__grid {
  display: grid;
  gap: var(--space-xl);
}

/* Opening paragraph, held apart by colour rather than size — the scale has
   no step between body and sub-head to spend here. */
.story__text p:first-of-type {
  font-size: var(--text-s);
  line-height: 1.75;
  color: var(--color-text);
}

.story__text p {
  max-width: 62ch;
  font-size: var(--text-s);
  line-height: 1.75;
  color: var(--color-text-muted);
}

.story__text p + p {
  margin-top: var(--space-lg);
}

.story__text .section__title {
  margin-bottom: var(--space-xl);
}

.story__aside {
  display: grid;
  gap: var(--space-xl);
  align-content: start;
}

/* The photograph beside the essay. It rides along as the column scrolls,
   so the right-hand side is not empty for the length of a long read. */
.story__figure {
  margin: 0;
}

.story__figure img {
  width: 100%;
  height: auto;
  border: 1px solid var(--rule-hairline);
}


/* --------------------------------------------------------------------------
   11. Expertise, approach and services
   -------------------------------------------------------------------------- */

.services-grid {
  display: grid;
  gap: var(--space-xl);
  margin-top: var(--space-2xl);
}


.services-card__link {
  display: inline-flex;
  align-items: center;
  gap: var(--space-sm);
  margin-top: var(--space-lg);
  color: var(--color-primary);
  font-size: var(--text-s);
  font-weight: 500;
  letter-spacing: var(--tracking-meta);
  text-transform: uppercase;
  text-decoration: none;
  transition: color var(--transition-fast), transform var(--transition-fast);
}

/* The arrow already lives in the markup as → , so the whole label leans
   forward rather than growing a second one. */
.services-card__link:hover {
  color: var(--color-accent);
  transform: translateX(4px);
}

/* --------------------------------------------------------------------------
   12. Credentials
   -------------------------------------------------------------------------- */

.credentials-grid {
  display: grid;
  gap: var(--space-xl);
  margin-top: var(--space-2xl);
}

.credentials-group__title {
  padding-bottom: var(--space-sm);
  border-bottom: 1px solid var(--rule-strong);
}

.credentials-group__list {
  margin-top: var(--space-md);
  display: grid;
  gap: var(--space-sm);
  font-size: var(--text-s);
  line-height: 1.7;
  color: var(--color-text-muted);
}

.credentials-group__list li {
  padding-left: var(--space-lg);
  position: relative;
}

.credentials-group__list li::before {
  content: "";
  position: absolute;
  left: 0;
  top: 0.75em;
  width: 12px;
  height: 1px;
  background-color: var(--color-accent);
}

/* --------------------------------------------------------------------------
   13. CTA

   The last page of the magazine: no filled banner, just a rule, oversized
   type and air. Green stays as the type colour rather than a background.
   -------------------------------------------------------------------------- */

.cta-banner {
  position: relative;
  overflow: hidden;
  padding-block: var(--space-3xl);
  border-top: 1px solid var(--rule-strong);
  background: none;
  color: var(--color-text);
}

.cta-banner > .container {
  position: relative;
  z-index: 1;
}

/* The closing page keeps its air: the dotted field sits in the empty right
   half only, faded out before it reaches the type. Its own artwork, so the
   second piece reads as a companion to the hero rather than a repeat. */
.pattern-accent--cta {
  inset: 0;
  background-image: url("../assets/images/pattern-cta.jpg");
  background-position: right center;
  opacity: var(--pattern-opacity-cta);
  -webkit-mask-image: radial-gradient(52% 115% at 100% 50%,
    #000 0%, rgba(0, 0, 0, 0.4) 42%, transparent 78%);
  mask-image: radial-gradient(52% 115% at 100% 50%,
    #000 0%, rgba(0, 0, 0, 0.4) 42%, transparent 78%);
}

.cta-banner__inner {
  display: flex;
  flex-direction: column;
  flex-wrap: wrap;
  gap: var(--space-xl);
}

/* Every child of the row must be allowed to shrink; the fallback carries an
   email address that has no break opportunity of its own. */
.cta-banner__inner > * {
  min-width: 0;
}

.cta-banner__title {
  max-width: 12ch;
  font-family: var(--font-display);
  font-size: var(--text-l);
  font-weight: 500;
  line-height: 0.98;
  letter-spacing: -0.035em;
  color: var(--color-primary);
  text-wrap: balance;
}

.cta-banner__text {
  max-width: var(--measure);
  margin-top: var(--space-lg);
  font-size: var(--text-s);
  line-height: 1.7;
  color: var(--color-text-muted);
}

/* --------------------------------------------------------------------------
   13c. Booking

   A frame that holds either the scheduler or, until one is connected, the
   note and button that stand in for it. Same hairline as the other framed
   blocks, so an empty-looking area still reads as part of the page.
   -------------------------------------------------------------------------- */

.scheduler {
  margin-top: var(--space-xl);
  border: 1px solid var(--rule-hairline);
}

.scheduler__fallback {
  display: grid;
  justify-items: stretch;
  gap: var(--space-lg);
  padding: var(--space-lg);
}

/* The stand-in picker. It books nothing — a day and a time compose the
   email that asks for them — so it stays quiet: rules and type, no filled
   cells, in keeping with the rest of the page. */
.booking__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-md);
}

.booking__month {
  font-family: var(--font-display);
  font-size: var(--text-m);
  font-weight: 500;
  letter-spacing: -0.015em;
  color: var(--color-primary);
}

.booking__step {
  min-width: 36px;
  min-height: 36px;
  border: 1px solid var(--rule-hairline);
  background: none;
  font-size: var(--text-s);
  line-height: 1;
  color: var(--color-primary);
  cursor: pointer;
  transition: border-color var(--transition-fast);
}

.booking__step:hover:not(:disabled) {
  border-color: var(--color-accent);
}

.booking__step:disabled {
  opacity: 0.35;
  cursor: default;
}

.booking__weekdays,
.booking__grid {
  display: grid;
  grid-template-columns: repeat(7, minmax(0, 1fr));
  gap: var(--space-xs);
}

.booking__weekdays {
  font-size: 0.7rem;
  letter-spacing: var(--tracking-meta);
  text-transform: uppercase;
  color: var(--color-text-muted);
  text-align: center;
}

.booking__day {
  display: flex;
  align-items: center;
  justify-content: center;
  aspect-ratio: 1;
  border: 1px solid transparent;
  background: none;
  font-size: var(--text-s);
  color: var(--color-text);
  cursor: pointer;
  transition: border-color var(--transition-fast),
    background-color var(--transition-fast), color var(--transition-fast);
}

.booking__day:hover:not(.is-closed):not(.is-empty) {
  border-color: var(--color-accent);
}

.booking__day.is-closed,
.booking__day.is-empty {
  color: var(--color-text-muted);
  opacity: 0.4;
  cursor: default;
}

.booking__day.is-chosen {
  background-color: var(--color-primary);
  border-color: var(--color-primary);
  color: var(--color-on-primary);
}

.booking__slots {
  display: grid;
  gap: var(--space-sm);
}

.booking__slots-title {
  padding-top: var(--space-md);
  border-top: 1px solid var(--rule-hairline);
  font-size: var(--text-s);
  color: var(--color-text);
}

.booking__slot-row {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-sm);
}

.booking__slot {
  padding: var(--space-sm) var(--space-md);
  border: 1px solid var(--rule-hairline);
  background: none;
  font-size: var(--text-s);
  color: var(--color-text);
  cursor: pointer;
  transition: border-color var(--transition-fast),
    background-color var(--transition-fast), color var(--transition-fast);
}

.booking__slot:hover {
  border-color: var(--color-accent);
}

.booking__slot.is-chosen {
  background-color: var(--color-primary);
  border-color: var(--color-primary);
  color: var(--color-on-primary);
}

.booking__confirm {
  justify-self: start;
}

/* .btn sets its own display, which would otherwise ignore the hidden
   attribute while no time is chosen. */
.booking__confirm[hidden] {
  display: none;
}

.scheduler__note {
  max-width: var(--measure);
  font-size: var(--text-s);
  line-height: 1.7;
  color: var(--color-text-muted);
}

/* The provider sets its own height inside the frame; this is ours. */
.scheduler__frame {
  display: block;
  width: 100%;
  height: 640px;
  border: 0;
}

/* --------------------------------------------------------------------------
   13b. Legal pages

   Privacy policy and terms: continuous prose with headings, read once rather
   than scanned. Held to the reading measure, with headings that carry no
   section number since these pages sit outside the numbered rhythm.
   -------------------------------------------------------------------------- */

.legal {
  max-width: 68ch;
}

.legal__meta {
  font-size: var(--text-s);
  letter-spacing: var(--tracking-meta);
  text-transform: uppercase;
  color: var(--color-text-muted);
}

.legal h2 {
  margin-top: var(--space-2xl);
  font-family: var(--font-display);
  font-size: var(--text-m);
  font-weight: 500;
  line-height: 1.2;
  letter-spacing: -0.015em;
  color: var(--color-primary);
}

.legal h3 {
  margin-top: var(--space-lg);
  font-size: var(--text-s);
  font-weight: 500;
  color: var(--color-text);
}

.legal p,
.legal li {
  margin-top: var(--space-md);
  font-size: var(--text-s);
  line-height: 1.75;
  color: var(--color-text-muted);
}

.legal ul {
  margin-top: var(--space-md);
  display: grid;
  gap: var(--space-sm);
}

.legal li {
  position: relative;
  padding-left: var(--space-lg);
}

.legal li::before {
  content: "";
  position: absolute;
  left: 0;
  top: 0.9em;
  width: 12px;
  height: 1px;
  background-color: var(--color-accent);
}

.legal strong {
  font-weight: 500;
  color: var(--color-text);
}

/* The operator's details: an address block, so the lines stay as set. */
.legal__block {
  margin-top: var(--space-md);
  line-height: 1.9;
}

/* --------------------------------------------------------------------------
   13a. Interior page furniture
   Shared by the service pages, so a second one costs no new CSS.
   -------------------------------------------------------------------------- */

.page-hero {
  position: relative;
  overflow: hidden;
  padding-block: var(--section-padding-lg) var(--space-3xl);
}

/* The hero's own bottom padding already sets the page's first gap, so the
   section under it starts closer than the rhythm between later sections. */
.page-hero + .section {
  padding-block-start: var(--space-2xl);
}

.page-hero > .container {
  position: relative;
  z-index: 1;
}

/* Hand-set line breaks, ignored until there is width enough to keep them. */
.break-wide {
  display: none;
}

/* A title that carries its own breaks must not also be balanced, or the
   browser adds breaks of its own on top of them. */
.page-hero__title--broken {
  max-width: none;
  text-wrap: normal;
}

.page-hero__title {
  max-width: 18ch;
  margin-top: var(--space-md);
  font-family: var(--font-display);
  font-size: var(--text-l);
  font-weight: 500;
  line-height: 1.02;
  letter-spacing: -0.03em;
  text-wrap: balance;
}

.page-hero__subtitle {
  max-width: var(--measure);
  margin-top: var(--space-lg);
  padding-top: var(--space-md);
  border-top: 1px solid var(--rule-strong);
  font-size: var(--text-s);
  line-height: 1.7;
  color: var(--color-text-muted);
  text-wrap: pretty;
}

.page-hero__actions {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--space-lg);
  margin-top: var(--space-xl);
}

/* Opening paragraph of a section. Same size as the body copy that follows;
   it reads as the lead through its darker colour and the space around it. */
.lead {
  max-width: var(--measure);
  margin-top: var(--space-lg);
  font-size: var(--text-s);
  line-height: 1.7;
  color: var(--color-text);
}

.prose {
  max-width: 62ch;
  margin-top: var(--space-lg);
  font-size: var(--text-s);
  line-height: 1.75;
  color: var(--color-text-muted);
}

.prose + .prose {
  margin-top: var(--space-lg);
}

.pullquote {
  max-width: 34ch;
  margin-top: var(--space-2xl);
  padding-left: var(--space-lg);
  border-left: 2px solid var(--color-accent);
  font-family: var(--font-display);
  font-size: var(--text-m);
  font-style: italic;
  font-weight: 400;
  line-height: 1.35;
  color: var(--color-primary);
}

/* A ruled frame with the artwork inside it, used once per page at most. */
.framed {
  position: relative;
  overflow: hidden;
  margin-top: var(--space-2xl);
  padding: var(--space-lg);
  border: 1px solid var(--rule-hairline);
}

.framed > *:not(.pattern-accent) {
  position: relative;
  z-index: 1;
}

/* Carries the same dotted painting as the closing CTA on the home page, so
   the two quiet fields on the site are the one piece of artwork. */
.pattern-accent--frame {
  inset: 0;
  background-image: url("../assets/images/pattern-cta.jpg");
  background-position: right center;
  opacity: var(--pattern-opacity-cta);
  -webkit-mask-image: radial-gradient(100% 110% at 100% 100%,
    #000 0%, rgba(0, 0, 0, 0.45) 45%, transparent 78%);
  mask-image: radial-gradient(100% 110% at 100% 100%,
    #000 0%, rgba(0, 0, 0, 0.45) 45%, transparent 78%);
}

/* An index rather than a card grid: one row per entry, rules running the full
   measure so they stack into a continuous ledger down the page. Titles get the
   whole column width, so they stop wrapping into three ragged lines. */
.index-list {
  counter-reset: idx;
  margin-top: var(--space-2xl);
  border-top: 1px solid var(--rule-strong);
}

.index-list__item {
  counter-increment: idx;
  display: grid;
  gap: var(--space-lg);
  padding-block: var(--space-lg);
  border-bottom: 1px solid var(--rule-hairline);
}

/* Each entry carries a detail of Lisette's own work: the standing clay figure
   and the wall of anatomy studies. The image is a custom property on the
   element, so the markup supplies the source and the CSS keeps the behaviour. */
.index-list__media {
  position: relative;
  aspect-ratio: 5 / 3;
  border: 1px solid var(--rule-hairline);
  overflow: hidden;
  transition: border-color var(--transition-fast);
}

/* On a pseudo-element so it can scale inside the border without moving it. */
.index-list__media::before {
  content: "";
  position: absolute;
  inset: 0;
  background-image: var(--tile);
  background-size: cover;
  background-position: center;
  transition: transform var(--transition-fast);
}

.index-list__item:hover .index-list__media::before {
  transform: scale(1.04);
}

.index-list__item:hover .index-list__media {
  border-color: var(--color-accent);
}

.index-list__body {
  display: grid;
  gap: var(--space-sm);
  align-content: start;
}

/* The gesture: the numeral is set at display size and drawn as an outline,
   so it reads as a printed folio rather than a label. It fills in on hover. */
.index-list__body::before {
  content: counter(idx, decimal-leading-zero);
  font-family: var(--font-display);
  font-size: var(--text-m);
  font-weight: 500;
  line-height: 0.82;
  letter-spacing: -0.04em;
  color: color-mix(in srgb, var(--color-accent) 34%, transparent);
  transition: color var(--transition-fast);
}

/* Where the browser can stroke text, the numeral is hollow. */
@supports (-webkit-text-stroke: 1px red) {
  .index-list__body::before {
    color: transparent;
    -webkit-text-stroke: 1px var(--color-accent);
    transition: color var(--transition-fast),
      -webkit-text-stroke-color var(--transition-fast);
  }

  .index-list__item:hover .index-list__body::before {
    -webkit-text-stroke-color: transparent;
  }
}

.index-list__item:hover .index-list__body::before {
  color: var(--color-accent);
}

.index-list__title {
  font-family: var(--font-display);
  transform-origin: left;
  font-size: var(--text-m);
  font-weight: 500;
  line-height: 1.2;
  letter-spacing: -0.015em;
  color: var(--color-primary);
  text-wrap: balance;
  transition: color var(--transition-fast), transform var(--transition-fast);
}

/* Transform rather than padding, so the row never reflows under the cursor. */
.index-list__item:hover .index-list__title {
  color: var(--color-accent);
  transform: translateX(5px);
}

.index-list__text {
  max-width: 52ch;
  font-size: var(--text-s);
  line-height: 1.7;
  color: var(--color-text-muted);
}

/* Items reading as one line, divided by a terracotta point. Used where a
   list is the content itself and explanatory text would only pad it. */
.sep-list {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
}

.sep-list > li + li::before {
  content: "\2022";
  margin-inline: var(--space-md);
  color: var(--color-accent);
}

/* The same items stacked instead of run together: inline separators wrap
   badly at this size, leaving a bullet stranded at the start of a line.
   One per row, markers hanging in the gutter so every line starts on the
   container's left edge. */
.label-list {
  display: grid;
  gap: var(--space-sm);
  margin-top: var(--space-xl);
  font-family: var(--font-display);
  font-size: var(--text-m);
  line-height: 1.3;
  color: var(--color-primary);
}

.label-list > li {
  position: relative;
  padding-left: 1.1em;
  text-wrap: balance;
}

.label-list > li::before {
  content: "\2022";
  position: absolute;
  left: 0;
  color: var(--color-accent);
}

.credentials-line {
  font-size: var(--text-s);
  line-height: 1.7;
  color: var(--color-text-muted);
}

/* Entrance reveal. Only armed when scripting is present, so without JS the
   content is simply visible rather than stuck at zero opacity. */
.js .reveal {
  opacity: 0;
  transform: translateY(16px);
  transition: opacity 600ms ease, transform 600ms cubic-bezier(0.22, 1, 0.36, 1);
}

.js .reveal.is-visible {
  opacity: 1;
  transform: none;
}

/* Staggered, so rows arrive in sequence rather than as one block. */
.js .reveal:nth-child(2) { transition-delay: 90ms; }
.js .reveal:nth-child(3) { transition-delay: 180ms; }
.js .reveal:nth-child(4) { transition-delay: 270ms; }
.js .reveal:nth-child(5) { transition-delay: 360ms; }
.js .reveal:nth-child(6) { transition-delay: 450ms; }

/* A photograph used as evidence, ruled like everything else on the page. */
.feature-figure {
  margin: var(--space-2xl) 0 0;
}

.feature-figure img {
  width: 100%;
  height: auto;
  border: 1px solid var(--rule-hairline);
}

/* A supporting photograph rather than a plate: held to roughly half the
   column so it sits under the text instead of competing with it. */
.feature-figure--inset {
  max-width: 420px;
}

/* Held to the file's own width, so the photograph is never scaled up past
   the pixels it has. */
.feature-figure--natural {
  max-width: 780px;
}

.feature-figure__caption {
  margin-top: var(--space-md);
  font-size: var(--text-s);
  font-weight: 500;
  letter-spacing: var(--tracking-meta);
  text-transform: uppercase;
  color: var(--color-text-secondary);
}


/* Inline link inside body copy. */
.link {
  color: var(--color-primary);
  text-decoration-thickness: 1px;
  text-underline-offset: 0.2em;
  transition: color var(--transition-fast);
}

.link:hover {
  color: var(--color-accent);
}

/* --------------------------------------------------------------------------
   13c. Proof band — the one dark surface on the site
   Full-bleed forest. Because the base accent measures 2.03:1 here, everything
   inside uses the reversed-out tokens; see the token block for why.
   -------------------------------------------------------------------------- */

.proof {
  padding-block: var(--space-3xl);
  background: var(--color-primary);
  color: var(--color-on-primary);
}

.proof__grid {
  display: grid;
  gap: var(--space-2xl);
}

.proof__eyebrow {
  color: var(--color-accent-on-primary);
}

/* Both columns open on a rule, and those rules have to line up. The eyebrow
   takes a row of its own above them rather than sitting inside the left
   column, where it pushed that column's first rule down by its own height. */
.proof__figures {
  margin-top: 0;
}

.proof__figures {
  display: grid;
  gap: var(--space-xl);
}

/* Figure carries its unit, label sits under a rule. These take the headline
   size — this band is the page's punch. */
.proof__fact {
  padding-top: var(--space-md);
  border-top: 1px solid var(--rule-on-primary);
}

.proof__figure {
  font-family: var(--font-display);
  font-size: var(--text-l);
  font-weight: 500;
  line-height: 1.05;
  letter-spacing: -0.03em;
  overflow-wrap: break-word;
}

.proof__label {
  margin-top: var(--space-sm);
  font-size: var(--text-s);
  line-height: 1.6;
  color: var(--color-on-primary-muted);
}

.proof__role {
  padding-block: var(--space-md);
  border-top: 1px solid var(--rule-on-primary);
  font-size: var(--text-s);
  line-height: 1.5;
  color: var(--color-on-primary-muted);
}

.proof__role:last-child {
  border-bottom: 1px solid var(--rule-on-primary);
}

/* --------------------------------------------------------------------------
   13d. Doors — the two ways into the practice, equal weight
   -------------------------------------------------------------------------- */

.doors {
  display: grid;
  gap: var(--space-2xl);
  margin-top: var(--space-2xl);
}

.door__media {
  display: block;
  position: relative;
  aspect-ratio: 8 / 5;
  border: 1px solid var(--rule-hairline);
  overflow: hidden;
  transition: border-color var(--transition-fast);
}

.door__media img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition: transform var(--transition-fast);
}

.door:hover .door__media img {
  transform: scale(1.04);
}

.door:hover .door__media {
  border-color: var(--color-accent);
}

.door__title {
  margin-top: var(--space-lg);
  font-family: var(--font-display);
  font-size: var(--text-m);
  font-weight: 500;
  line-height: 1.15;
  letter-spacing: -0.02em;
  color: var(--color-primary);
  text-wrap: balance;
}

.door__text {
  max-width: 42ch;
  margin-top: var(--space-md);
  color: var(--color-text-muted);
}

/* --------------------------------------------------------------------------
   13e. Showcase — full-bleed evidence band
   With no testimonials available, this photograph is the page's only proof
   that Lisette works at the level being sold to. Cropped rather than letter-
   boxed so it reads as a band; object-position keeps the faces in frame.
   -------------------------------------------------------------------------- */

.showcase {
  margin-top: var(--section-padding-lg);
}

.showcase img {
  width: 100%;
  height: clamp(220px, 34vw, 440px);
  object-fit: cover;
  object-position: center 32%;
}

/* --------------------------------------------------------------------------
   13f. Contact — enquiry form and details rail

   Fields are ruled, not boxed. The site has no filled surfaces anywhere else
   and a form is the easiest place to accidentally introduce one, so an input
   here is a label over a hairline and nothing more. Only the three text sizes
   are used, as everywhere else.
   -------------------------------------------------------------------------- */

.contact-grid {
  display: grid;
  gap: var(--space-3xl);
}

/* Form on one side, booking on the other: two ways to start, neither
   subordinate to the other. The direct details sit under the booking,
   which is the shorter of the two columns. */
.contact-booking-column {
  display: grid;
  align-content: start;
  gap: var(--space-3xl);
  min-width: 0;
}

.contact-booking__lead {
  max-width: var(--measure);
  margin-top: var(--space-md);
  font-size: var(--text-s);
  line-height: 1.7;
  color: var(--color-text-muted);
}

.contact-form-column,
.form {
  min-width: 0;
}

/* A single section numbered 01 is a count of nothing, and the two headings
   on this page are a pair rather than a sequence. */
.contact-form-column .section__title::before,
.contact-booking-column .section__title::before {
  content: none;
}

.form {
  display: grid;
  gap: var(--space-xl);
  margin-top: var(--space-xl);
  max-width: var(--measure);
}

.form__field {
  display: grid;
  gap: var(--space-sm);
}

.form__label {
  font-size: var(--text-s);
  font-weight: 500;
  letter-spacing: var(--tracking-meta);
  text-transform: uppercase;
  color: var(--color-text-secondary);
}

.form__input,
.form__select,
.form__textarea {
  width: 100%;
  padding: var(--space-sm) 0;
  border: 0;
  border-bottom: 1px solid var(--rule-strong);
  border-radius: var(--radius-sm);
  background: none;
  color: var(--color-text);
  font-family: var(--font-body);
  font-size: var(--text-s);
  line-height: 1.6;
  transition: border-color var(--transition-fast);
}

.form__input:hover,
.form__select:hover,
.form__textarea:hover {
  border-bottom-color: var(--color-primary);
}

.form__input:focus-visible,
.form__select:focus-visible,
.form__textarea:focus-visible {
  outline: 2px solid var(--color-primary);
  outline-offset: 4px;
}

/* The select keeps the native menu — a custom one would need a filled panel,
   which the rest of the page does not have. Only the resting state is
   restyled. */
.form__select {
  appearance: none;
  padding-right: var(--space-lg);
  background-image: linear-gradient(45deg, transparent 50%, currentColor 50%),
                    linear-gradient(135deg, currentColor 50%, transparent 50%);
  background-position: right 8px top 55%, right 3px top 55%;
  background-size: 5px 5px, 5px 5px;
  background-repeat: no-repeat;
}

.form__textarea {
  min-height: 7rem;
  resize: vertical;
}

/* Honeypot. Off the canvas rather than display:none, because some bots skip
   anything that is outright hidden but will still fill a field that is
   technically rendered. aria-hidden and tabindex="-1" in the markup keep it
   away from every real visitor. */
.form__trap {
  position: absolute;
  left: -9999px;
  width: 1px;
  height: 1px;
  overflow: hidden;
}

/* The form is a grid, so a button would otherwise stretch the full column.
   Buttons everywhere else on the site sit at their content width. */
.form .btn {
  justify-self: start;
}

.form__status:empty {
  display: none;
}

.form__status {
  padding-top: var(--space-md);
  border-top: 1px solid var(--rule-hairline);
  font-size: var(--text-s);
  line-height: 1.7;
  color: var(--color-text-muted);
}

.form__status--error {
  color: var(--color-accent);
}

.form__status--success {
  color: var(--color-primary);
}

.form__note {
  margin-top: var(--space-lg);
  font-size: var(--text-s);
  line-height: 1.7;
  color: var(--color-text-muted);
}

.contact-details__title {
  font-family: var(--font-display);
  font-size: var(--text-m);
  font-weight: 500;
  line-height: 1.2;
  letter-spacing: -0.015em;
  color: var(--color-primary);
}

.contact-details__item {
  margin-top: var(--space-lg);
  padding-top: var(--space-md);
  border-top: 1px solid var(--rule-hairline);
}

.contact-details__label {
  font-size: var(--text-s);
  font-weight: 500;
  letter-spacing: var(--tracking-meta);
  text-transform: uppercase;
  color: var(--color-text-secondary);
}

.contact-details__value {
  margin-top: var(--space-sm);
  font-size: var(--text-s);
  line-height: 1.7;
  overflow-wrap: anywhere;
}

/* --------------------------------------------------------------------------
   13b. Footer

   The pattern band is the closing flourish and lives here rather than being
   repeated per page, so every page ends the same way.
   -------------------------------------------------------------------------- */

.footer {
  margin-top: var(--section-padding-lg);
}

/* The CTA already ends the page on air, so its own bottom padding is the
   gap before the footer — the footer's top margin on top of it reads as a
   hole. Pages that end on something else keep the margin. */
main:has(> .cta-banner:last-child) + .footer {
  margin-top: 0;
}

.footer__inner {
  display: grid;
  gap: var(--space-xl);
  padding-block: var(--space-2xl);
}

.footer__wordmark {
  height: 26px;
  width: auto;
}

.footer__tagline {
  max-width: 34ch;
  margin-top: var(--space-md);
  font-size: var(--text-s);
  line-height: 1.7;
  color: var(--color-text-muted);
}

.footer__list {
  display: grid;
  gap: var(--space-sm);
  margin-top: var(--space-md);
}

.footer__link {
  position: relative;
  display: inline-block;
  color: var(--color-primary);
  font-size: var(--text-s);
  text-decoration: none;
  transition: color var(--transition-fast);
}

/* Same drawn rule as the navigation, so the two read as one system. */
.footer__link::after {
  content: "";
  position: absolute;
  left: 0;
  bottom: -2px;
  width: 100%;
  height: 1px;
  background-color: currentColor;
  transform: scaleX(0);
  transform-origin: left;
  transition: transform var(--transition-fast);
}

.footer__link:hover::after {
  transform: scaleX(1);
}

.footer__legal {
  grid-column: 1 / -1;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-md);
  padding-top: var(--space-lg);
  border-top: 1px solid var(--rule-hairline);
  font-size: var(--text-s);
  letter-spacing: 0.02em;
  color: var(--color-text-secondary);
}

/* Sits at cap height with the legal line rather than shouting: it is a signal,
   not a badge. Slightly held back so the six stripes do not out-saturate a
   palette that is otherwise bisque and green. */
/* The two legal links sit with the copyright line rather than in the page
   list: they are obligations, not part of the site's navigation. */
.footer__legal-links {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-lg);
}

.footer__flag {
  flex: none;
  height: 14px;
  width: auto;
  opacity: 0.85;
  transition: opacity var(--transition-fast);
}

.footer__legal:hover .footer__flag {
  opacity: 1;
}

/* --------------------------------------------------------------------------
   13g. Social marks

   LinkedIn's own bug stands in for the word. The black lockup is used rather
   than LinkedIn blue: the site carries one accent colour and a second brand
   blue would fight it. Black is one of the marks LinkedIn permits, so the
   logo is still used as issued and is never recoloured to the site palette.
   The word "LinkedIn" survives as alt text, which is what a screen reader
   announces and what gives the link its name.
   -------------------------------------------------------------------------- */

.social-bug {
  display: block;
  width: auto;
  height: 1.5rem;
}

/* Sits in the footer list beside the text links. The sliding underline the
   other links carry is dropped — a rule under a logo reads as an artefact,
   not as a hover state — so opacity does that job instead. Padding lifts the
   target past the 24px minimum without opening a gap in the list. */
.footer__link--logo {
  display: inline-flex;
  align-items: center;
  padding-block: var(--space-xs);
  opacity: 0.82;
  transition: opacity var(--transition-fast);
}

.footer__link--logo::after {
  content: none;
}

.footer__link--logo:hover,
.footer__link--logo:focus-visible {
  opacity: 1;
}

/* In the contact rail the mark replaces a label, so it is set to the height
   of the uppercase labels around it rather than to link size. */
.contact-details__label .social-bug {
  height: 1.25rem;
}

/* --------------------------------------------------------------------------
   14. Tablet — 481px and up
   -------------------------------------------------------------------------- */

@media (min-width: 481px) {
  :root {
    --container-gutter: var(--space-xl);
    --nav-gap: var(--space-lg);
    --section-padding-lg: var(--space-2xl);
    --pattern-opacity: 0.07;
    --pattern-opacity-cta: 0.08;
    --pattern-divider-h: 72px;
  }


  .pattern-accent--section {
    display: block;
    top: 0;
    right: 0;
    width: 42%;
    height: 280px;
  }

  /* Full menu, no hamburger */
  .js .nav__toggle {
    display: none;
  }

  /* The logo already goes home, so the link is redundant once the menu is
     open on the page. It stays in the collapsed phone menu, where there is
     no such affordance to hand. */
  .nav__item--home {
    display: none;
  }

  .nav__links,
  .js .nav__links {
    display: flex;
    flex-direction: row;
    flex-wrap: wrap;
    align-items: center;
    justify-content: flex-end;
    flex-basis: auto;
    padding-bottom: 0;
  }

  .hero__cta {
    flex-direction: row;
    align-items: center;
    flex-wrap: wrap;
    gap: var(--space-xl);
  }

  .hero__grid {
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
    align-items: center;
    column-gap: var(--space-xl);
    padding-inline: var(--container-gutter);
  }

  .hero__body {
    order: -1;
    padding-inline: 0;
  }

  .hero__media {
    order: 0;
  }

  .hero__media img {
    aspect-ratio: 4 / 5;
  }

  .services-grid {
    grid-template-columns: repeat(2, minmax(0, 1fr));
    column-gap: var(--space-2xl);
  }

  .credentials-grid {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }

  .footer__inner {
    grid-template-columns: repeat(2, minmax(0, 1fr));
    column-gap: var(--space-2xl);
  }


  .break-wide {
    display: inline;
  }

  .page-hero__actions {
    flex-direction: row;
    align-items: center;
    gap: var(--space-2xl);
  }

  .framed {
    padding: var(--space-xl);
  }

  .btn--solid {
    padding: var(--space-md) var(--space-xl);
  }

  .proof__figures {
    grid-template-columns: repeat(2, minmax(0, 1fr));
    column-gap: var(--space-xl);
  }
}

/* --------------------------------------------------------------------------
   15. Desktop — 769px and up

   Where the asymmetry lives. Columns are deliberately unequal and a few
   elements are stepped down the page; everything stays inside the grid, so
   nothing can overflow.
   -------------------------------------------------------------------------- */

@media (min-width: 769px) {
  :root {
    --container-gutter: var(--space-2xl);
    --nav-padding-y: 20px;
    --nav-padding-x: var(--space-2xl);
    --nav-gap: var(--space-xl);
    --logo-size: 44px;
    --wordmark-size: 28px;
    --section-padding-lg: var(--space-3xl);
    --pattern-opacity: 0.09;
    --pattern-opacity-cta: 0.09;
    --pattern-divider-h: 88px;
  }

  .pattern-accent--section {
    width: 38%;
    height: 340px;
  }

  /* Hero ------------------------------------------------------------------ */

  /* Three columns: a left gutter that lines .hero__body up with the site
     container, the type, then the photograph running to the right edge.
     The gutter is computed from 100% of the section box, never 100vw, so a
     scrollbar cannot push the page into horizontal overflow. */
  .hero__grid {
    grid-template-columns:
      minmax(
        var(--container-gutter),
        calc((100% - var(--container-max)) / 2 + var(--container-gutter))
      )
      minmax(0, 1fr)
      minmax(0, 0.56fr);
    align-items: stretch;
    min-height: 62vh;

    /* The tablet rule above sets both of these on .hero__grid. Left
       uncleared they'd cascade in here too: padding-inline shrinks the
       box col1's calc measures its 100% against, so the gutter comes out
       too narrow to line .hero__body up with .container and the media
       column falls short of the right edge; column-gap then displaces
       col2 by a further 50px on top of that. */
    padding-inline: 0;
    column-gap: 0;
  }

  .hero__body {
    grid-column: 2;
    align-self: center;
    padding-block: var(--space-2xl);
    padding-inline: 0 var(--space-2xl);
  }

  .hero__media {
    order: 0;
    grid-column: 3;
    display: flex;
    align-items: center;
    justify-content: flex-start;
    padding-inline-end: var(--container-gutter);
  }

  /* The same frame the about page gives its portrait, so the two read as
     one pair rather than two different treatments. */
  .hero__media img {
    width: 100%;
    max-width: 400px;
    height: auto;
    aspect-ratio: 4 / 5;
    object-position: center 20%;
  }

  /* About header ---------------------------------------------------------- */

  .about-header__grid {
    grid-template-columns: 1.45fr 0.9fr;
    align-items: end;
    column-gap: var(--space-3xl);
  }

  /* Portrait drops below the baseline of the type beside it. The offset sits
     on the figure, not the frame: on the frame it pulled the credit line up
     underneath the picture, which paints over it. */
  .about-header__figure {
    margin-bottom: calc(var(--space-xl) * -1);
  }

  /* Story ----------------------------------------------------------------- */

  .story__grid {
    grid-template-columns: 3fr 2fr;
    column-gap: var(--space-3xl);
  }

  /* Facts start well down the column, level with the second paragraph. */
  .story__aside {
    margin-top: var(--space-4xl);
  }

  .story__figure {
    position: sticky;
    top: calc(80px + var(--space-xl));
  }

  /* Index and grids ------------------------------------------------------- */

  .services-grid {
    grid-template-columns: 1.15fr 1fr 0.85fr;
    column-gap: var(--space-3xl);
  }


  .credentials-grid {
    grid-template-columns: 1.1fr 0.8fr 1.2fr;
    column-gap: var(--space-3xl);
  }

  /* CTA ------------------------------------------------------------------- */

  .cta-banner {
    padding-block: var(--space-3xl) var(--space-2xl);
  }

  .cta-banner__inner {
    flex-direction: row;
    align-items: flex-end;
    justify-content: space-between;
    gap: var(--space-3xl);
  }

  .cta-banner__actions {
    flex-shrink: 0;
    padding-bottom: var(--space-sm);
  }

  /* Contact ----------------------------------------------------------------- */

  .contact-grid {
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
    align-items: start;
  }

  /* Proof band -------------------------------------------------------------- */

  .proof {
    padding-block: var(--space-3xl);
  }

  .proof__grid {
    grid-template-columns: minmax(0, 1fr) minmax(0, 1.4fr);
    column-gap: var(--space-3xl);
    row-gap: var(--space-xl);
    align-items: start;
  }

  .proof__eyebrow {
    grid-column: 1 / -1;
  }

  /* The stats carry phrases now, not just numbers. Two tracks inside this
     column left no room for a long word, so they stack here and each fact
     runs the full width of the column. */
  .proof__figures {
    grid-template-columns: minmax(0, 1fr);
    row-gap: var(--space-xl);
  }


  /* Brand block takes the wider column; the two link lists sit beside it. */
  .footer__inner {
    grid-template-columns: 1.6fr 1fr 1fr;
    column-gap: var(--space-3xl);
  }

  .index-list__item {
    grid-template-columns: minmax(0, 0.42fr) minmax(0, 1fr);
    align-items: start;
    column-gap: var(--space-2xl);
    padding-block: var(--space-xl);
  }

  /* Alternate rows step in. The rule stays on the item's padding box, so it
     still spans the full measure — the content weaves, the ledger does not. */
  .index-list__item:nth-child(even) {
    padding-left: var(--space-3xl);
  }


  .framed {
    padding: var(--space-2xl);
  }

  .doors {
    grid-template-columns: repeat(2, minmax(0, 1fr));
    column-gap: var(--space-2xl);
  }
}

/* --------------------------------------------------------------------------
   16. Reduced motion
   Movement is decoration here, never information — so it all goes.
   -------------------------------------------------------------------------- */

@media (prefers-reduced-motion: reduce) {
  .btn,
  .btn::after,
  .nav__link,
  .nav__link::after,
  .footer__link,
  .footer__link::after,
  .nav__toggle-bar,
  .card,
  .index-list__body::before,
  .index-list__title,
  .services-card__link,
  .about-header__portrait img {
    transition: none;
  }

  .btn:hover::after,
  .index-list__item:hover .index-list__title,
  .index-list__item:hover .index-list__media::before,
  .door:hover .door__media img,
  .js .reveal,
  .card:hover,
  .card:focus-within,
  .services-card__link:hover,
  .about-header__portrait:hover img {
    transform: none;
  }

  /* Never withhold content from anyone who asked for less motion. */
  .js .reveal {
    opacity: 1;
    transform: none;
    transition: none;
  }

  /* The active-page rule must still be drawn without a transition. */
  .nav__link.active::after {
    transform: scaleX(1);
  }
}
