/* ==========================================================================
   TTL corporate site - stylesheet
   Version 1.6 - 2026-10-01

   1.6 is the hero image change only (building with BIM overlay and
   drawing panels, 1 October 2026): overlay deepened slightly at the
   mid-point for tablets, phone crop moved onto the building.

   1.5: TTL Virtual structure screenshots side by side above 900px.

   1.4 is the content release from the website review (Katy Barker, first
   and second rounds): company name 22px in the same 82px header, service
   numbers removed, project cards with photographs, and the new TTL Virtual
   page (section 20 at the end of this file).

   1.1 applies TTL Brand and Interface Standards v1.2 (27 September 2026):
   the teal portal identity, the revised action and control tokens, the
   two-colour focus indicator, persistent body-copy link underlines, 44px
   standalone targets, the 560px breakpoint and the guide's type scale.
   1.3 is the hero image change only (night skyline and glass building,
   approved 30 September 2026): lighter overlay, crop anchored right, and a
   phone crop on the building. Nothing else from the pending review is in it.

   1.2 stops the header labels wrapping: the 44px navigation targets are
   made of height, not horizontal padding, nothing in the header may break
   across lines, and the company name now drops out below 1250px instead of
   1100px. Reported from a browser at 150% zoom, where the effective width
   is around 1250px.

   Guide page numbers are cited on the rules they come from. Two inherited
   contrast failures recorded as A03 in the guide's register are corrected:
   supporting text on ink panels, and the white label on solid actions
   inside dark sections.

   Brand tokens are carried over unchanged from the original styles.css
   (--ink, --blue, --blue-dark, --coral, --paper, --line). What changes is the
   system around them: a fluid type scale, a spacing scale, consistent section
   rhythm, real focus states, and layouts that hold together below 900px.

   Contents
   --------
   01  Tokens
   02  Reset and base
   03  Typography
   04  Layout primitives
   05  Buttons and links
   06  Header and navigation
   07  Hero
   08  Section furniture
   09  Platform and roadmap
   10  Golden thread
   11  Framework strip
   12  Services
   13  Capabilities
   14  Process
   15  About
   16  Projects
   17  Contact and form
   18  Footer
   19  Utility, motion and print
   20  TTL Virtual page
   ========================================================================== */

/* -- 01 Tokens ----------------------------------------------------------- */
:root {
  /* Brand palette - unchanged from the original site. */
  --ink: #12202a;
  --muted: #5a6870;
  --blue: #19788d;
  --blue-dark: #0d4f61;
  --coral: #d85f49;
  --coral-dark: #b74736;

  /* ACCESSIBILITY NOTE
     The brand coral #d85f49 is only 3.71:1 against white and 3.41:1 against
     --paper, so it fails WCAG AA (4.5:1) wherever it is used for TEXT. It is
     fine as a decorative rule or bar, where no contrast minimum applies.
     Two derived tokens keep the brand intact while passing:
       --coral-cta  solid button background, white label   -> 5.29:1
       --coral-ink  small coral text on white or paper     -> 6.19 / 5.69:1
     Bare --coral is now used only for non-text decoration. */
  /* BIS v1.2 page 10 names these; the site already used #b74736. The hover
     value moves from #99362a to the published #963a2c (7.15:1 on white).
     --coral-cta / --coral-cta-hover stay as aliases so existing rules and
     any local overrides keep working. */
  --action-coral: #b74736;
  --action-coral-hover: #963a2c;
  --coral-cta: var(--action-coral);
  --coral-cta-hover: var(--action-coral-hover);
  --coral-ink: #a83e2d;
  --paper: #f2f6f7;
  --line: #cdd9dd;
  --white: #ffffff;

  /* Derived tones. Added so dark panels and hover states stop being
     one-off hex values scattered through the file. */
  --ink-soft: #1c2f3b;
  --ink-line: #354a56;
  --on-dark: #cad6da;
  --on-dark-dim: #9fb2ba;
  --accent-light: #9cd9e4;

  --shadow-sm: 0 2px 8px rgba(9, 31, 40, .06);
  --shadow-md: 0 10px 30px rgba(9, 31, 40, .10);
  --shadow-lg: 0 24px 60px rgba(9, 31, 40, .18);

  /* Fluid type scale. clamp() keeps headings readable on a phone without a
     stack of breakpoint overrides. */
  --step--1: clamp(0.83rem, 0.80rem + 0.15vw, 0.90rem);
  --step-0:  clamp(1.00rem, 0.96rem + 0.20vw, 1.10rem);
  --step-1:  clamp(1.15rem, 1.06rem + 0.42vw, 1.40rem);
  --step-2:  clamp(1.35rem, 1.17rem + 0.90vw, 1.85rem);
  --step-3:  clamp(1.70rem, 1.35rem + 1.70vw, 2.60rem);
  --step-4:  clamp(1.95rem, 1.45rem + 2.40vw, 3.10rem);
  /* Capped at 3.4rem: at the original 4.8rem the hero heading ran to four
     lines on a 1440px screen and pushed its own content under the header. */
  --step-5:  clamp(2.30rem, 1.60rem + 3.20vw, 3.40rem);

  /* Spacing scale. */
  --space-1: 0.25rem;
  --space-2: 0.5rem;
  --space-3: 0.75rem;
  --space-4: 1rem;
  --space-5: 1.5rem;
  --space-6: 2rem;
  --space-7: 3rem;
  --space-8: 4rem;
  --space-9: 6rem;

  --radius-sm: 4px;
  --radius-md: 8px;
  --radius-lg: 14px;

  /* TTL Virtual identity (BIS v1.2 page 4). Teal marks portal destinations
     only; everything else stays ink and coral. */
  --portal-teal: #238291;        /* Panel rule and accents. 4.49:1 - never text. */
  --portal-teal-dark: #155d69;   /* Portal actions and links. 7.50:1 on white. */
  --portal-teal-pale: #e8f3f4;   /* TTL Virtual panel surface. */
  --portal-ink: #15282f;         /* Portal hover/pressed, and the focus outer ring. */

  /* Essential control boundaries (BIS v1.2 page 10, register item A04).
     --line is 1.44:1 on white, so it is decorative only; real controls use
     this instead. */
  --control-border: #6d7a7f;

  /* Project target size for standalone controls (guide pages 8 and 12). */
  --target: 44px;

  --measure: 66ch;             /* Comfortable reading width. */
  --container: 1440px;
  --gutter: clamp(22px, 6vw, 90px);
  --header-h: 82px;
}

/* -- 02 Reset and base --------------------------------------------------- */
*, *::before, *::after { box-sizing: border-box; }

html {
  scroll-behavior: smooth;
  /* Anchor targets must clear the sticky header. */
  scroll-padding-top: calc(var(--header-h) + var(--space-5));
  -webkit-text-size-adjust: 100%;
}

body {
  background: var(--white);
  color: var(--ink);
  font-family: "Segoe UI", -apple-system, BlinkMacSystemFont, Roboto, Arial, sans-serif;
  /* Body 16px, line-height 1.6-1.65 (guide page 3). Fixed rather than fluid
     so the published size is the specified one at every width. */
  font-size: 16px;
  line-height: 1.62;
  margin: 0;
  overflow-x: hidden;               /* Belt and braces against any stray wide child. */
  text-rendering: optimizeLegibility;
}

img, svg { display: block; max-width: 100%; }
img { height: auto; }

/* Skip link - the original page had none. */
.skip-link {
  background: var(--ink);
  color: var(--white);
  left: var(--space-4);
  padding: var(--space-3) var(--space-4);
  position: absolute;
  top: -100px;
  z-index: 100;
  transition: top .15s ease;
}
.skip-link:focus { top: var(--space-4); }

/* Two-colour focus indicator (BIS v1.2 page 10): a 2px white inner ring and
   a 3px ink outer ring, both drawn outside the box so nothing shifts. */
:where(a, button, input, select, textarea, summary):focus-visible {
  border-radius: 2px;
  box-shadow: 0 0 0 2px var(--white);
  outline: 3px solid var(--portal-ink);
  outline-offset: 2px;
}

/* On ink panels the outer ring would vanish, so the two layers swap. The
   requirement is a contrasting layer on every approved surface. */
.section--dark :where(a, button, input, select, textarea, summary):focus-visible,
.hero :where(a, button):focus-visible,
.site-footer :where(a, button):focus-visible {
  box-shadow: 0 0 0 2px var(--portal-ink);
  outline-color: var(--white);
}

/* Forced-colours mode hands the indicator back to the system palette. */
@media (forced-colors: active) {
  :where(a, button, input, select, textarea, summary):focus-visible {
    box-shadow: none;
    outline: 3px solid CanvasText;
  }
}

/* -- 03 Typography ------------------------------------------------------- */
h1, h2, h3, h4 {
  line-height: 1.12;
  margin: 0 0 var(--space-4);
  text-wrap: balance;               /* Avoids a single orphaned word on a heading. */
}
h1 { font-size: var(--step-5); font-weight: 750; letter-spacing: -0.02em; }
/* Section titles 35-62px / 700 / 1.08, allowing wrapping and growth. */
h2 { font-size: clamp(35px, 1.9rem + 1.6vw, 56px); font-weight: 700; letter-spacing: -0.015em; line-height: 1.08; }
h3 { font-size: var(--step-2); font-weight: 700; }
h4 { font-size: var(--step-1); font-weight: 700; }

p { margin: 0 0 var(--space-4); max-width: var(--measure); }
p:last-child { margin-bottom: 0; }

a { color: var(--blue-dark); text-underline-offset: 3px; }
a:hover { color: var(--blue); }

/* Body-copy links carry a permanent underline (guide page 6); navigation,
   buttons and arrow links are identified by shape and position instead. */
p a, li a, dd a, dt a, address a {
  text-decoration: underline;
  text-decoration-thickness: 1px;
}

.eyebrow {
  align-items: center;
  color: var(--blue);
  display: flex;
  font-size: var(--step--1);
  font-weight: 800;
  gap: var(--space-3);
  letter-spacing: .09em;
  margin: 0 0 var(--space-4);
  text-transform: uppercase;
}
/* The short rule before the eyebrow is the one new motif in the design. */
.eyebrow::before {
  background: var(--coral);
  content: "";
  display: block;
  flex: none;
  height: 2px;
  width: 28px;
}

/* Body 16px, introductions 18px, paragraph line-height 1.6-1.65. */
.lead { color: var(--muted); font-size: 18px; line-height: 1.62; }

/* -- 04 Layout primitives ------------------------------------------------ */
.section {
  margin-inline: auto;
  max-width: var(--container);
  padding: clamp(64px, 9vw, 116px) var(--gutter);
}
.section--tight { padding-block: clamp(48px, 6vw, 80px); }

.section--dark {
  background: var(--ink);
  color: var(--white);
  max-width: none;
}
.section--dark .eyebrow { color: var(--accent-light); }
.section--dark p { color: var(--on-dark); }
.section--dark a { color: var(--accent-light); }

.section--paper { background: var(--paper); max-width: none; }

/* Inner wrapper needed when a full-bleed band still wants the content grid. */
.section__inner { margin-inline: auto; max-width: var(--container); }

/* Two-column heading block: title left, supporting paragraph right. */
.section-head {
  display: grid;
  gap: var(--space-5) var(--space-7);
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  margin-bottom: var(--space-8);
}
.section-head .eyebrow { grid-column: 1 / -1; margin-bottom: calc(var(--space-2) * -1); }
.section-head h2 { margin-bottom: 0; }
.section-head__support { align-self: end; }
.section-head--stacked { display: block; max-width: 760px; }

/* -- 05 Buttons ---------------------------------------------------------- */
.button {
  align-items: center;
  border: 1px solid transparent;
  border-radius: var(--radius-sm);
  display: inline-flex;
  font-size: 16px;                 /* Action label 16px / 700 / 1.4, guide page 3. */
  font-weight: 700;
  gap: var(--space-2);
  justify-content: center;
  line-height: 1.4;
  min-height: var(--target);       /* 44px standalone target, guide page 12. */
  padding: 14px 19px;
  text-decoration: none;
  transition: background-color .18s ease, color .18s ease, transform .18s ease;
}
.button:hover { transform: translateY(-1px); }

.button--primary { background: var(--coral-cta); border-color: var(--coral-cta); color: var(--white); }
.button--primary:hover { background: var(--coral-cta-hover); border-color: var(--coral-cta-hover); color: var(--white); }

/* Light-surface secondary: ink on white (guide page 6). */
.button--outline { background: transparent; border-color: var(--portal-ink); color: var(--portal-ink); }
.button--outline:hover, .button--outline:focus-visible { background: var(--portal-ink); color: var(--white); }

/* White-text secondary is only used on solid ink surfaces (guide page 15). */
.button--quiet { background: transparent; border-color: var(--white); color: var(--white); }
.button--quiet:hover, .button--quiet:focus-visible { background: var(--white); color: var(--ink); }

/* Portal entry (guide page 6): deep teal, white label, hover portal ink. */
.button--portal { background: var(--portal-teal-dark); border-color: var(--portal-teal-dark); color: var(--white); }
.button--portal:hover, .button--portal:focus-visible {
  background: var(--portal-ink);
  border-color: var(--portal-ink);
  color: var(--white);
}

/* A solid action keeps its white label on dark panels too: .section--dark a
   used to recolour it, leaving 3.39:1 (register item A03). */
.section--dark .button--primary, .hero .button--primary { color: var(--white); }
.section--dark .button--portal, .hero .button--portal { color: var(--white); }

.button-row { display: flex; flex-wrap: wrap; gap: var(--space-3); }

/* Text link with a moving arrow. */
.link-arrow {
  align-items: center;
  display: inline-flex;
  min-height: var(--target);
  font-weight: 700;
  gap: var(--space-2);
  text-decoration: none;
}
.link-arrow::after { content: "\2192"; transition: transform .18s ease; }
.link-arrow:hover::after { transform: translateX(4px); }

/* -- 06 Header ----------------------------------------------------------- */
.site-header {
  align-items: center;
  background: rgba(255, 255, 255, .92);
  backdrop-filter: saturate(160%) blur(10px);
  border-bottom: 1px solid transparent;
  display: flex;
  flex-wrap: nowrap;
  gap: var(--space-4);
  /* 82px is a minimum, not a fixed height: the row grows with the text at
     200% text size rather than clipping it (guide pages 6 and 12). */
  min-height: var(--header-h);
  justify-content: space-between;
  padding-block: 8px;
  left: 0;
  padding-inline: var(--gutter);
  position: fixed;               /* Was absolute: the nav now follows the page. */
  right: 0;
  top: 0;
  transition: border-color .2s ease, box-shadow .2s ease;
  z-index: 50;
}
/* Class toggled by ttl.js once the page scrolls past the hero top. */
.site-header.is-scrolled { border-bottom-color: var(--line); box-shadow: var(--shadow-sm); }

.brand { align-items: center; color: var(--ink); display: flex; flex: 0 0 auto; gap: var(--space-4); text-decoration: none; white-space: nowrap; }
.brand__mark { font-size: 28px; font-weight: 900; letter-spacing: -0.02em; white-space: nowrap; }
/* 22px (was 14px), inside the same 82px bar - review NAV-02/04, and Katy's
   second-round "even bigger, without enlarging the banner" (29 September). */
.brand__name { border-left: 1px solid var(--line); font-size: 22px; line-height: 1.1; font-weight: 700; padding-left: var(--space-4); white-space: nowrap; }

/* Navigation stays at the 14px / 650 baseline (guide page 3). Height gives
   the 44px target; horizontal padding and the gap both shrink as the header
   narrows, and nothing is allowed to wrap - a menu label broken across two
   lines is what this replaced. */
.primary-nav { align-items: center; display: flex; flex-wrap: nowrap; gap: clamp(2px, 0.5vw, 10px); }
.primary-nav a {
  align-items: center;
  color: var(--ink);
  display: inline-flex;
  flex: 0 0 auto;
  font-size: 14px;
  font-weight: 650;
  justify-content: center;
  min-height: var(--target);
  min-width: var(--target);
  padding-inline: clamp(4px, 0.6vw, 10px);
  position: relative;
  text-decoration: none;
  white-space: nowrap;
}
/* Current page is never signalled by colour alone (guide page 10). */
.primary-nav a[aria-current="page"] { text-decoration: underline; text-decoration-thickness: 2px; text-underline-offset: 6px; }
/* Underline grows from the left rather than appearing all at once. */
.primary-nav a:not(.portal-link)::after {
  background: var(--coral);
  bottom: 0;
  content: "";
  height: 2px;
  left: 0;
  position: absolute;
  transform: scaleX(0);
  transform-origin: left;
  transition: transform .18s ease;
  width: 100%;
}
.primary-nav a:not(.portal-link):hover::after { transform: scaleX(1); }
/* Portal entry button: deep teal on white, 4px radius, 44px target. */
.primary-nav .portal-link {
  background: var(--portal-teal-dark);
  border: 1px solid var(--portal-teal-dark);
  border-radius: var(--radius-sm);
  color: var(--white);
  padding: 10px 16px;
  white-space: nowrap;
}
.primary-nav .portal-link:hover, .primary-nav .portal-link:focus-visible {
  background: var(--portal-ink);
  border-color: var(--portal-ink);
  color: var(--white);
}

.menu-toggle {
  background: transparent;
  border: 1px solid var(--ink);
  border-radius: var(--radius-sm);
  color: var(--ink);
  cursor: pointer;
  display: none;
  font: inherit;
  font-size: 14px;
  font-weight: 700;
  min-height: var(--target);
  min-width: var(--target);
  padding: 9px 13px;
}

/* -- 07 Hero ------------------------------------------------------------- */
/* Block rather than a centred flex child. A flex item centred inside a
   too-short container overflows BOTH edges, which pushed the eyebrow up
   underneath the fixed header. Padding-top now does the positioning, so the
   content can never collide with the header at any viewport height. */
/* Hero title 48-92px / 750 / 1.08 (guide page 3). Held at 72px: at the top
   of the range the heading runs to four lines at 1440px. */
.hero h1 { font-size: clamp(48px, 2.5rem + 2.2vw, 72px); font-weight: 750; line-height: 1.08; }

.hero {
  background-color: var(--ink);
  /* Universal fallback. The custom properties are set inline on the element
     because only Jinja can resolve a static URL; the @supports block below
     upgrades to WebP and picks a width appropriate to the viewport. */
  background-image: var(--hero-fallback);
  /* The image is supplied at the laptop hero's own 1.44:1, so a 1440px
     screen shows all of it. Anchored right so narrower screens keep the
     building and the drawing panels; wider screens trim sky and paving. */
  background-position: right 45%;
  background-size: cover;
  color: var(--white);
  display: block;
  min-height: min(720px, 86vh);
  padding: calc(var(--header-h) + clamp(48px, 7vw, 96px)) var(--gutter) clamp(190px, 20vw, 250px);
  position: relative;
}
/* Responsive hero artwork.
   The photograph sits under a 48-92% wash, so it is compressed harder than a
   normally-displayed image would be - WebP q55-62 rather than q80 - with no
   visible loss under the overlay. It is NOT blurred: softening the foliage and
   rooflines was visible at 1:1, and that building is project work.
   960w 73 KB / 1440w 159 KB / 1920w 268 KB, against 557 KB before.
   Browsers without image-set() (effectively pre-2020 Safari) keep the JPEG. */
@supports (background-image: image-set(url("h.webp") type("image/webp"))) {
  .hero { background-image: image-set(var(--hero-sm) type("image/webp")); }
  @media (min-width: 48em) {
    .hero { background-image: image-set(var(--hero-md) type("image/webp")); }
  }
  @media (min-width: 75em) {
    .hero { background-image: image-set(var(--hero-lg) type("image/webp")); }
  }
}

/* Gradient rather than a flat 66% wash: keeps the photograph readable while
   still guaranteeing text contrast at the top-left. */
.hero__shade {
  background:
    /* The night sky already sits under the heading, so the overlay only has
       to settle it and the building and drawings on the right show through.
       The mid stop is .66 at 45%: on a tablet (768px) the paragraphs land on
       the building's lit floors, and this keeps white text above 4.5:1 at
       the brightest point under them (measured 1 October 2026). */
    linear-gradient(105deg, rgba(8, 22, 29, .86) 0%, rgba(8, 22, 29, .66) 45%, rgba(8, 22, 29, .2) 100%);
  inset: 0;
  position: absolute;
}
.hero::after {
  background: var(--coral);
  bottom: 0;
  content: "";
  height: 6px;
  left: 0;
  position: absolute;
  width: 34%;
}
.hero__content { max-width: 880px; position: relative; z-index: 1; }
.hero .eyebrow { color: var(--accent-light); }
.hero h1 { margin-bottom: var(--space-5); max-width: 20ch; }
.hero__lead { color: var(--white); font-size: 18px; line-height: 1.62; margin-bottom: var(--space-4); max-width: 58ch; }
.hero__intro { color: var(--on-dark); font-size: 18px; line-height: 1.62; margin-bottom: var(--space-6); max-width: 56ch; }

.hero__stats {
  background: var(--white);
  bottom: 0;
  box-shadow: var(--shadow-lg);
  color: var(--ink);
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  max-width: 980px;
  position: absolute;
  right: 0;
  z-index: 2;
}
.hero__stats div { border-left: 1px solid var(--line); padding: var(--space-5) var(--space-6); }
.hero__stats div:first-child { border-left: 0; }
.hero__stats strong { color: var(--blue-dark); display: block; font-size: var(--step-2); line-height: 1.1; margin-bottom: var(--space-2); }
.hero__stats span { color: var(--muted); display: block; font-size: var(--step--1); line-height: 1.45; }

/* -- 08 Cards ------------------------------------------------------------ */
.card {
  background: var(--white);
  border: 1px solid var(--line);
  border-radius: var(--radius-md);
  padding: var(--space-6);
}
.card h3 { margin-bottom: var(--space-3); }
.card p { color: var(--muted); }

.grid { display: grid; gap: var(--space-5); }
.grid--2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.grid--3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }

/* -- 09 Platform and roadmap --------------------------------------------- */
.platform { display: grid; gap: var(--space-8) var(--space-7); grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); }
.platform__copy h2 { margin-bottom: var(--space-5); }
.platform__features { display: grid; gap: var(--space-4); margin: 0; }
.platform__features > div { border-left: 3px solid var(--blue); padding-left: var(--space-5); }
.platform__features dt { font-size: var(--step-1); font-weight: 700; margin-bottom: var(--space-2); }
/* --muted on ink is 2.88:1 (register item A03), so dark panels use the
   on-dark tone instead. */
.platform__features dd { color: var(--on-dark); margin: 0; }
.platform .button-row { margin-top: var(--space-6); }

.roadmap { border-top: 1px solid var(--ink-line); margin-top: var(--space-8); padding-top: var(--space-7); }
.badge {
  background: rgba(156, 217, 228, .16);
  border: 1px solid var(--accent-light);
  border-radius: 100px;
  color: var(--accent-light);
  display: inline-block;
  font-size: var(--step--1);
  font-weight: 800;
  letter-spacing: .08em;
  margin-bottom: var(--space-4);
  padding: 5px 14px;
  text-transform: uppercase;
}
.roadmap__note { color: var(--on-dark-dim); font-style: italic; margin-top: var(--space-5); }
.roadmap__grid { display: grid; gap: var(--space-5); grid-template-columns: repeat(3, minmax(0, 1fr)); margin-top: var(--space-6); }
.roadmap__grid article { border: 1px solid var(--ink-line); border-radius: var(--radius-md); padding: var(--space-5); }
.roadmap__grid h4 { color: var(--white); margin-bottom: var(--space-3); }
.roadmap__grid p { color: var(--on-dark-dim); font-size: var(--step--1); }

/* -- 10 Golden thread ---------------------------------------------------- */
.pillars { display: grid; gap: var(--space-5); grid-template-columns: repeat(3, minmax(0, 1fr)); margin-top: var(--space-7); }
.pillar { border-top: 4px solid var(--coral); padding-top: var(--space-5); }
.pillar h3 { font-size: var(--step-1); }
.pillar p { color: var(--muted); }

/* -- 11 Framework strip -------------------------------------------------- */
/* New section: the standards and regulations the copy previously never named. */
.framework__grid { display: grid; gap: var(--space-4); grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); margin-top: var(--space-6); }
.framework__item {
  background: var(--white);
  border: 1px solid var(--line);
  border-left: 4px solid var(--blue);
  border-radius: var(--radius-sm);
  padding: var(--space-5);
}
.framework__item dt { color: var(--blue-dark); font-size: var(--step-0); font-weight: 800; margin-bottom: var(--space-2); }
.framework__item dd { color: var(--muted); font-size: var(--step--1); line-height: 1.6; margin: 0; }
.framework__note { color: var(--muted); font-size: var(--step--1); font-style: italic; margin-top: var(--space-5); max-width: var(--measure); }

/* -- 12 Services --------------------------------------------------------- */
.services { border-block: 1px solid var(--line); display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); }
.services article {
  border-left: 1px solid var(--line);
  display: flex;
  flex-direction: column;
  padding: 32px;                   /* Service cell padding, guide page 6. */
  transition: background-color .2s ease;
}
.services article:first-child { border-left: 0; }
.services article:hover { background: var(--paper); }
.services__number { color: var(--coral-ink); font-size: var(--step--1); font-weight: 800; letter-spacing: .1em; }
.services h3 { margin: var(--space-7) 0 var(--space-3); }
.services p { color: var(--muted); flex: 1; }
.services .link-arrow { color: var(--blue-dark); margin-top: var(--space-5); }

/* TTL Virtual service panel (guide page 6): pale-teal surface, teal rule and
   a deep-teal link. The class is set in partials/services.html from the
   content flag, so the styling never depends on the portal's URL. */
.services .service--portal { background: var(--portal-teal-pale); border-top: 3px solid var(--portal-teal); }
.services .service--portal:hover { background: var(--portal-teal-pale); }
.services .service--portal .services__number { color: var(--portal-teal-dark); }
.services .service--portal .link-arrow { color: var(--portal-teal-dark); }

/* -- 13 Capabilities ----------------------------------------------------- */
.capability-group + .capability-group { border-top: 1px solid var(--line); margin-top: var(--space-7); padding-top: var(--space-7); }
.capability-group__title {
  align-items: baseline;
  color: var(--ink);
  display: flex;
  font-size: var(--step-1);
  gap: var(--space-3);
  margin-bottom: var(--space-5);
}
.capability-group__title::after { background: var(--line); content: ""; flex: 1; height: 1px; }

.capability-grid { display: grid; gap: var(--space-5) var(--space-7); grid-template-columns: repeat(2, minmax(0, 1fr)); margin: 0; }
.capability-grid > div { border-left: 3px solid var(--blue); padding-left: var(--space-5); }
.capability-grid dt { font-size: var(--step-1); font-weight: 700; line-height: 1.3; margin-bottom: var(--space-3); }
.capability-grid dd { color: var(--muted); font-size: var(--step-0); margin: 0; }

/* Standard reference tag, e.g. "CDM 2015". */
.tag {
  background: var(--paper);
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  color: var(--blue-dark);
  display: inline-block;
  font-size: 12px;
  font-weight: 800;
  letter-spacing: .04em;
  margin-left: var(--space-2);
  padding: 2px 8px;
  vertical-align: middle;
}

/* -- 14 Process ---------------------------------------------------------- */
.process { counter-reset: step; display: grid; gap: var(--space-6); grid-template-columns: repeat(4, minmax(0, 1fr)); list-style: none; margin: 0; padding: 0; }
.process li { border-top: 1px solid var(--line); padding-top: var(--space-5); position: relative; }
/* The connector line is decorative and hidden from assistive tech. */
.process li::before {
  background: var(--coral);
  content: "";
  height: 3px;
  left: 0;
  position: absolute;
  top: -2px;
  width: 44px;
}
.process__number { color: var(--coral-ink); display: block; font-size: var(--step--1); font-weight: 800; letter-spacing: .1em; margin-bottom: var(--space-3); }
.process h3 { font-size: var(--step-1); margin-bottom: var(--space-3); }
.process p { color: var(--muted); font-size: var(--step-0); }

/* -- 15 About ------------------------------------------------------------ */
.about { display: grid; gap: var(--space-8) var(--space-7); grid-template-columns: minmax(0, 1.1fr) minmax(0, .9fr); }
.about__points { margin: 0; }
.about__points > div { border-top: 1px solid var(--ink-line); padding: var(--space-5) 0; }
.about__points > div:first-child { border-top: 0; padding-top: 0; }
.about__points dt { font-size: var(--step-1); font-weight: 700; margin-bottom: var(--space-2); }
.about__points dd { color: var(--on-dark-dim); margin: 0; }

/* -- 16 Projects --------------------------------------------------------- */
/* Three columns, 20px gap, 6px radius, 16:9 images (guide page 6). */
.project-grid { display: grid; gap: 20px; grid-template-columns: repeat(3, minmax(0, 1fr)); }
.project-card {
  background: var(--white);
  border: 1px solid var(--line);
  border-radius: 6px;
  display: flex;
  flex-direction: column;
  overflow: hidden;
  transition: box-shadow .22s ease, transform .22s ease;
}
.project-card:hover { box-shadow: var(--shadow-md); transform: translateY(-3px); }
.project-card__media { overflow: hidden; position: relative; }
.project-card img { aspect-ratio: 16 / 9; object-fit: cover; transition: transform .4s ease; width: 100%; }
/* Review copy only: stands in for a photograph still to be supplied. */
.project-card__placeholder {
  align-items: center;
  aspect-ratio: 16 / 9;
  background: repeating-linear-gradient(135deg, var(--paper), var(--paper) 12px, #e6edef 12px, #e6edef 24px);
  color: var(--muted);
  display: flex;
  font-size: 14px;
  font-weight: 700;
  justify-content: center;
  width: 100%;
}
.project-card:hover img { transform: scale(1.04); }
.project-card__sector {
  background: rgba(18, 32, 42, .86);
  border-radius: var(--radius-sm);
  color: var(--white);
  font-size: 11px;
  font-weight: 800;
  left: var(--space-4);
  letter-spacing: .09em;
  padding: 5px 10px;
  position: absolute;
  text-transform: uppercase;
  top: var(--space-4);
}
.project-card__body { display: flex; flex: 1; flex-direction: column; padding: var(--space-5); }
.project-card h3 { font-size: var(--step-1); margin-bottom: var(--space-2); }
.project-card__client { color: var(--blue-dark); font-size: var(--step--1); font-weight: 700; margin-bottom: var(--space-4); }
.project-card__body p { color: var(--muted); font-size: var(--step--1); line-height: 1.65; }
.project-card__scope {
  border-top: 1px solid var(--line);
  color: var(--ink);
  font-size: var(--step--1);
  font-weight: 700;
  margin-top: var(--space-5);
  padding-top: var(--space-4);
}
.project-card__scope span { color: var(--muted); font-weight: 400; }

/* -- 17 Contact and form ------------------------------------------------- */
.contact { display: grid; gap: var(--space-8) var(--space-7); grid-template-columns: minmax(0, .85fr) minmax(0, 1.15fr); }
.contact__aside h2 { margin-bottom: var(--space-5); }
.contact__details { display: grid; gap: var(--space-4); margin-top: var(--space-6); }
.contact__details a { font-size: var(--step-1); font-weight: 700; overflow-wrap: anywhere; text-decoration: none; }
.contact__note { color: var(--muted); font-size: var(--step--1); margin-top: var(--space-5); }

.form { background: var(--white); border: 1px solid var(--line); border-radius: var(--radius-lg); padding: clamp(22px, 3vw, 40px); }
.form__row { display: grid; gap: var(--space-4); grid-template-columns: repeat(2, minmax(0, 1fr)); margin-bottom: var(--space-4); }
.form__field { display: flex; flex-direction: column; gap: var(--space-2); margin-bottom: var(--space-4); }
.form__field--full { grid-column: 1 / -1; }
.form__field label { font-size: var(--step--1); font-weight: 700; }
.form__field .optional { color: var(--muted); font-weight: 400; }

.form input[type="text"],
.form input[type="email"],
.form input[type="tel"],
.form select,
.form textarea {
  background: var(--white);
  /* An essential control boundary needs 3:1 (guide page 10): --line is
     1.44:1 on white, --control-border is 4.07:1. */
  border: 1px solid var(--control-border);
  border-radius: var(--radius-sm);
  color: var(--ink);
  font: inherit;
  min-height: var(--target);
  padding: 12px 14px;
  transition: border-color .15s ease, box-shadow .15s ease;
  width: 100%;
}
.form input:focus, .form select:focus, .form textarea:focus { border-color: var(--portal-teal-dark); }
/* Restated because the rule above would otherwise suppress the shared ring. */
.form input:focus-visible, .form select:focus-visible, .form textarea:focus-visible {
  box-shadow: 0 0 0 2px var(--white);
  outline: 3px solid var(--portal-ink);
  outline-offset: 2px;
}
.form textarea { min-height: 150px; resize: vertical; }
.form select { appearance: none; background-image: linear-gradient(45deg, transparent 50%, var(--muted) 50%), linear-gradient(135deg, var(--muted) 50%, transparent 50%); background-position: calc(100% - 20px) 50%, calc(100% - 14px) 50%; background-repeat: no-repeat; background-size: 6px 6px, 6px 6px; padding-right: 40px; }

/* The checkbox is 24px (the WCAG AA minimum) and its label activates it, so
   the row is the real target and carries the 44px project height. */
.form__consent { align-items: center; display: flex; gap: var(--space-3); margin-bottom: var(--space-5); min-height: var(--target); }
.form__consent input { accent-color: var(--portal-teal-dark); flex: none; height: 24px; width: 24px; }
.form__consent label { cursor: pointer; font-size: var(--step--1); font-weight: 400; line-height: 1.55; padding-block: 10px; }

/* Errors use #b74736 where colour is used, and the boundary thickens so the
   state is not carried by colour alone (guide page 10). */
.field-error { color: var(--action-coral); font-size: var(--step--1); font-weight: 600; }
.form__field--invalid input,
.form__field--invalid select,
.form__field--invalid textarea { border-color: var(--action-coral); border-width: 2px; }

/* Honeypot: off-screen, zero-size, never focusable, invisible to screen readers. */
.hp-field { height: 0; left: -9999px; overflow: hidden; position: absolute; width: 0; }

.alert { border-radius: var(--radius-sm); font-weight: 600; margin-bottom: var(--space-5); padding: var(--space-4) var(--space-5); }
.alert--success { background: rgba(25, 120, 141, .10); border-left: 4px solid var(--blue); color: var(--blue-dark); }
.alert--error { background: rgba(216, 95, 73, .10); border-left: 4px solid var(--coral); color: var(--coral-ink); }

/* -- 18 Footer ----------------------------------------------------------- */
.site-footer { background: var(--ink); color: var(--on-dark-dim); padding: var(--space-6) var(--gutter) var(--space-5); }
.site-footer__inner { margin-inline: auto; max-width: var(--container); }
.site-footer__top { display: grid; gap: var(--space-5) var(--space-7); grid-template-columns: minmax(0, 1.4fr) repeat(2, minmax(0, 1fr)); }
.site-footer__brand .brand__mark { color: var(--white); }
/* Footer lockup: mark + company name. Separate classes from the header's
   .brand__name, which is display:none below 672px. */
.site-footer__lockup { align-items: center; display: flex; gap: var(--space-4); }
.site-footer__name { border-left: 1px solid var(--ink-line); color: var(--white); font-size: 14px; font-weight: 700; line-height: 1.3; padding-left: var(--space-4); }
.site-footer__brand p { color: var(--on-dark-dim); font-size: var(--step--1); margin-top: var(--space-3); }
.site-footer h2 { color: var(--white); font-size: var(--step--1); letter-spacing: .09em; margin-bottom: var(--space-3); text-transform: uppercase; }
.site-footer ul { list-style: none; margin: 0; padding: 0; }
.site-footer li { margin-bottom: var(--space-2); }
.site-footer a { color: var(--on-dark); font-size: var(--step--1); text-decoration: none; }
.site-footer a:hover { color: var(--accent-light); text-decoration: underline; }

/* Statutory trading disclosures - Companies Act 2006 s.82. */
/* Certifications held. Deliberately quieter than the brand block: a
   credential should be findable by a procurement reader, not shouted at
   everybody else. The badge is never restyled - certification marks must be
   displayed as issued - so only its frame is touched here. */
.site-footer__credentials { border-top: 1px solid var(--ink-line); display: flex; flex-direction: column; gap: var(--space-4); margin-top: var(--space-5); padding-top: var(--space-4); }
/* Credentials: each logo and its own text form ONE grid item, so they can
   never separate - side by side on a wide screen, stacked on a phone, always
   together. auto-fit with a 17rem floor gives three columns on desktop, two
   on a tablet and one on a phone, with no breakpoints to maintain.
   Logos share a 64px box so a square badge, a 5:1 pill and a 2:1 panel still
   line up; certification marks are displayed exactly as issued, so only
   their box is styled, never the mark. */
.credentials { align-items: start; display: grid; gap: var(--space-5) var(--space-6); grid-template-columns: repeat(auto-fit, minmax(min(100%, 17rem), 1fr)); list-style: none; margin: 0; padding: 0; }
.site-footer .credentials li { margin: 0; }
.credential__logo { align-items: center; display: flex; height: 64px; margin-bottom: var(--space-3); }
.credential__logo a { border-radius: 6px; display: block; line-height: 0; }
.credential__logo a:focus-visible { outline: 2px solid var(--on-dark); outline-offset: 3px; }
.credential__logo img { display: block; height: 64px; max-width: 100%; object-fit: contain; width: auto; }
/* The smart badge frame. color-scheme is pinned to the embedded page's, or
   Chrome paints an opaque backdrop behind a transparent frame whose scheme
   differs from the host page's. */
.credential__logo iframe { border: 0; color-scheme: normal; display: block; height: 64px; width: 64px; }
/* badge_embed_interactive = False: display-only, so a click cannot reach the
   registry page that loops for first-time visitors. */
.credential__embed--static { pointer-events: none; }
.credential__detail { color: var(--on-dark-dim); font-size: 13px; line-height: 1.7; margin: 0; max-width: 62ch; }
.credential__detail strong { color: var(--on-dark); }
/* The footer's general link rule sets a larger size; a link inside a
   credential must match the text around it. */
.credential__detail a { font-size: inherit; }
/* Footer links are standalone controls: give them height without a box.
   Kept to the whole footer so the legal row matches the approved preview. */
.site-footer a, .credential__detail a { display: inline-block; padding-block: 6px; }
.credential__number { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; font-size: 0.92em; overflow-wrap: anywhere; }

.site-footer__legal { border-top: 1px solid var(--ink-line); font-size: 13px; line-height: 1.7; margin-top: var(--space-5); padding-top: var(--space-4); }
.site-footer__legal p { color: var(--on-dark-dim); max-width: none; }
.site-footer__meta { align-items: center; display: flex; flex-wrap: wrap; gap: var(--space-4); justify-content: space-between; margin-top: var(--space-3); }

/* -- 19 Utility, responsive, motion, print -------------------------------- */
.visually-hidden {
  clip: rect(0 0 0 0); clip-path: inset(50%); height: 1px; overflow: hidden;
  position: absolute; white-space: nowrap; width: 1px;
}

/* Scroll reveal. Elements start hidden ONLY when JS is running and motion is
   allowed - see the .js guard, which ttl.js adds to <html>. Without it the
   content is simply visible, so the page degrades safely. */
.js .reveal { opacity: 0; transform: translateY(18px); transition: opacity .6s ease, transform .6s ease; }
.js .reveal.is-visible { opacity: 1; transform: none; }

@media (max-width: 1250px) {
  /* The name is the widest item in the header and is repeated in the footer,
     so it goes first when space runs short. */
  .brand__name { display: none; }
}

@media (max-width: 1100px) {
  .primary-nav { gap: 6px; }
  .services, .roadmap__grid, .project-grid, .pillars { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .process { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

@media (max-width: 900px) {
  .menu-toggle { display: block; }
  .primary-nav {
    background: var(--white);
    border-bottom: 1px solid var(--line);
    box-shadow: var(--shadow-md);
    display: none;
    flex-direction: column;
    gap: 0;
    left: 0;
    padding: var(--space-3) var(--gutter) var(--space-5);
    position: absolute;
    right: 0;
    top: var(--header-h);
  }
  .primary-nav.is-open { display: flex; }
  .primary-nav a { border-bottom: 1px solid var(--line); justify-content: flex-start; padding: 14px 0; width: 100%; }
  .primary-nav a:not(.portal-link)::after { display: none; }
  .primary-nav .portal-link { border: 0; border-bottom: 1px solid var(--line); border-radius: 0; }

  .hero { min-height: 0; padding-bottom: clamp(230px, 40vw, 300px); }
  .hero__stats { grid-template-columns: 1fr; left: var(--gutter); right: var(--gutter); }
  .hero__stats div { border-left: 0; border-top: 1px solid var(--line); padding: var(--space-4) var(--space-5); }
  .hero__stats div:first-child { border-top: 0; }

  .section-head, .platform, .about, .contact, .capability-grid { grid-template-columns: minmax(0, 1fr); }
  .services { grid-template-columns: minmax(0, 1fr); }
  .services article { border-left: 0; border-top: 1px solid var(--line); }
  .services article:first-child { border-top: 0; }
  .services h3 { margin-top: var(--space-5); }
  .capability-grid > div { padding-left: var(--space-4); }
}

/* The guide's small breakpoint is 560px (page 6): compact wordmark, 48px
   hero title, 72px section padding. */
@media (max-width: 560px) {
  .pillars, .roadmap__grid, .project-grid, .process, .form__row { grid-template-columns: minmax(0, 1fr); }
  .brand__name { display: none; }
  .hero h1 { font-size: 48px; max-width: none; }
  .section { padding-block: 72px; }
  .site-footer__top { grid-template-columns: minmax(0, 1fr); }
  .site-footer__meta { flex-direction: column; align-items: flex-start; gap: var(--space-2); }
}

/* Respect the OS "reduce motion" setting. The original site had no such
   handling; smooth scrolling alone can trigger vestibular symptoms. */
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after {
    animation-duration: .01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .01ms !important;
    scroll-behavior: auto !important;
  }
  .js .reveal { opacity: 1; transform: none; }
}

@media print {
  .site-header, .menu-toggle, .hero__stats, .form, .button-row { display: none !important; }
  body { color: #000; font-size: 11pt; }
  .section { padding: 12pt 0; }
  a::after { content: " (" attr(href) ")"; font-size: 9pt; }
}


/* -- Hero on phones (1 October 2026) -------------------------------------- */
/* A phone shows a narrow slice of a wide image: centre it on the building
   rather than the drawing panels (which are busy behind text), and even the
   overlay out so white text stays well above 4.5:1 over the lit floors. */
@media (max-width: 47.99em) {
  .hero { background-position: 58% center; }
  .hero__shade { background: rgba(8, 22, 29, .72); }
}


/* -- 20 TTL Virtual page (/ttl-virtual, 30 September 2026) --------------- */
/* Screenshots sit in a light frame with a visible caption. They are UI
   captures, so they are never cropped (no object-fit): height follows width
   from the width/height attributes, and nothing shifts as they load. */
.tv-intro { padding-top: calc(var(--header-h) + clamp(40px, 6vw, 80px)); }
.tv-intro h1 { font-size: clamp(40px, 2rem + 2.4vw, 64px); }
.tv-intro .section-head { margin-bottom: var(--space-7); }

.tv-screen { margin: 0; }
.tv-screen img {
  background: var(--white);
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  box-shadow: var(--shadow-md);
  width: 100%;
}
.tv-screen__zoom { cursor: zoom-in; display: block; }
.tv-screen__zoom:hover img { box-shadow: var(--shadow-md), 0 0 0 2px var(--portal-teal); }
.tv-screen figcaption {
  color: var(--muted);
  font-size: var(--step--1);
  line-height: 1.5;
  margin-top: var(--space-3);
  max-width: var(--measure);
}

.tv-structure__text { font-size: 18px; margin-bottom: var(--space-7); max-width: 760px; }
.tv-structure__screens { display: grid; gap: var(--space-7); grid-template-columns: repeat(2, minmax(0, 1fr)); }

/* Each capability: text beside its screenshot on wide screens, stacked on
   narrow ones. The screenshot gets the larger share of the width. */
.tv-features { display: grid; gap: clamp(56px, 7vw, 96px); }
.tv-feature {
  align-items: center;
  display: grid;
  gap: var(--space-6) var(--space-7);
  grid-template-columns: minmax(0, .8fr) minmax(0, 1.6fr);
}
.tv-feature h2 { font-size: var(--step-3); margin-bottom: var(--space-4); }
.tv-feature p { color: var(--muted); font-size: 18px; }
.tv-feature--text { grid-template-columns: minmax(0, 1fr); max-width: 760px; }

.tv-cta .button-row { margin-top: var(--space-6); }

@media (max-width: 900px) {
  .tv-feature, .tv-structure__screens { grid-template-columns: minmax(0, 1fr); }
}
