/* ==========================================================================
   SVaaS — technology consultancy
   Six pages, two published languages, generated by build.py.

   The palette is sampled straight from the logo: the cloud-and-infinity mark
   runs #142e77 (deep navy) through #1b5d9d to #1f7cb6 (bright blue), and the
   site uses those three plus tints of them. Nothing else.

   Scale is signalled inside that gradient — bright blue for the small
   practical end of the work, deep navy for platform scale — rather than by
   introducing a second hue.

   Contrast limits worth knowing before reaching for a colour:
     navy   #142e77  12.5:1 on white  — headings, body, anything
     mid    #1b5d9d   6.8:1 on white  — links, small labels
     bright #1f7cb6   4.6:1 on white but 4.1:1 on the tint — graphics and
                                        large type only, never small text on
                                        a tinted band
     On navy, bright blue is 2.7:1. Never use it for text there; use white or
     --tint-strong (10.2:1).

   Mobile-first. Breakpoints at 620px, 900px, 1140px.
   ========================================================================== */

/* --- Fonts ---------------------------------------------------------------- */
/* Newsreader (SIL OFL 1.1, see assets/fonts/Newsreader-OFL.txt), self-hosted.
   Served from our own origin, so there is still no third-party request and
   nothing for a cookie banner to cover — the reason the site had no web font
   before, which this keeps.

   One variable file per subset, weight axis 400-700. The optical-size axis is
   pinned at 24 rather than left variable: carrying opsz 6-72 as well doubles
   the latin subset from 58 KB to 132 KB, and 24 suits a face used almost
   entirely for headings between 16 and 53 px.

   The version sits in the filename because @font-face src cannot carry
   build.py's {{@assetVersion}}, and /assets/fonts/* is served immutable. */
@font-face {
  font-family: "Newsreader";
  font-style: normal;
  font-weight: 400 700;
  font-display: swap;
  src: url("../fonts/newsreader-latin-v1.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
                 U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122,
                 U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
/* Dutch and French sit entirely inside the latin range above; this subset is
   here for names that reach past it and is only fetched if one appears. */
@font-face {
  font-family: "Newsreader";
  font-style: normal;
  font-weight: 400 700;
  font-display: swap;
  src: url("../fonts/newsreader-latin-ext-v1.woff2") format("woff2");
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7,
                 U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F,
                 U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113,
                 U+2C60-2C7F, U+A720-A7FF;
}

/* --- Tokens --------------------------------------------------------------- */
:root {
  /* Sampled from assets/img/svaas-logo.png. */
  --navy: #142e77;         /* logo dark     — headings, primary surfaces */
  --navy-deep: #0e2158;    /* one step down, for gradient starts and the footer */
  --blue-mid: #1b5d9d;     /* logo mid      — links, small labels */
  --blue: #1f7cb6;         /* logo bright   — rules, marks, large type */
  --blue-light: #2185c3;   /* logo lightest — gradient ends */

  --tint: #eef4fb;         /* tinted bands and page openers */
  --tint-strong: #dfeaf7;  /* chips, hovers, quiet text on navy */

  /* Neutrals carry a blue cast so they belong to the same family. */
  --ink: #10192e;
  --ink-soft: #33405c;
  --muted: #5a6885;
  --line: #cfdcec;
  --line-soft: #e3ecf7;
  --white: #ffffff;

  /* The mark's own gradient, reused as the site's structural accent. */
  --grad: linear-gradient(100deg, var(--navy) 0%, var(--blue-mid) 52%, var(--blue) 100%);
  --grad-panel: linear-gradient(145deg, var(--navy-deep) 0%, var(--navy) 42%, var(--blue-mid) 100%);

  /* Serif headings against a sans body. Newsreader is self-hosted and carries
     the brand; the system families behind it are the swap fallback and what
     renders if the woff2 fails, so the headline never lands in Times. */
  --serif: "Newsreader", "Sitka Text", Charter, "Bitstream Charter", Cambria,
           "Palatino Linotype", Palatino, Georgia, "Times New Roman", serif;
  --sans: -apple-system, BlinkMacSystemFont, "Segoe UI", Inter, Roboto,
          "Helvetica Neue", Arial, sans-serif;

  --shell: 1140px;
  --gutter: clamp(20px, 4vw, 40px);
  --header-h: 64px;
  --band: clamp(56px, 8vw, 92px);
}

/* --- Reset ---------------------------------------------------------------- */
*, *::before, *::after { box-sizing: border-box; }

html {
  scroll-behavior: smooth;
  -webkit-text-size-adjust: 100%;
}

body {
  margin: 0;
  font-family: var(--sans);
  font-size: 1rem;
  line-height: 1.65;
  color: var(--ink-soft);
  background: var(--white);
  overflow-wrap: break-word;
}

img { max-width: 100%; height: auto; }

h1, h2, h3, h4 {
  margin: 0;
  font-family: var(--serif);
  font-weight: 600;
  color: var(--navy);
  line-height: 1.2;
}

p { margin: 0; }
ul, ol { margin: 0; padding: 0; list-style: none; }
dl, dd { margin: 0; }

a { color: var(--blue-mid); text-decoration-thickness: 1px; text-underline-offset: 3px; }
a:hover { color: var(--navy); }

:focus-visible {
  outline: 2px solid var(--blue);
  outline-offset: 3px;
  border-radius: 2px;
}

/* --- Helpers -------------------------------------------------------------- */
.shell {
  width: 100%;
  max-width: var(--shell);
  margin-inline: auto;
  padding-inline: var(--gutter);
}

.sr-only {
  position: absolute;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

.skip-link {
  position: absolute;
  left: 50%;
  top: 0;
  transform: translate(-50%, -110%);
  z-index: 100;
  padding: 10px 18px;
  background: var(--navy);
  color: var(--white);
  font-size: .9rem;
  text-decoration: none;
  border-radius: 0 0 6px 6px;
}
.skip-link:focus { transform: translate(-50%, 0); color: var(--white); }

.lead {
  max-width: 62ch;
  font-size: 1.09rem;
  color: var(--ink-soft);
}

.prose { max-width: 66ch; }
.prose p + p { margin-top: 1.05em; }
.prose strong { color: var(--ink); font-weight: 600; }

.note {
  max-width: 66ch;
  font-size: .9rem;
  color: var(--muted);
}

.sub-heading {
  margin-top: 44px;
  font-size: 1.16rem;
  color: var(--ink);
}
.sub-heading + * { margin-top: 18px; }

/* --- Buttons -------------------------------------------------------------- */
.actions {
  display: flex;
  flex-wrap: wrap;
  gap: 12px;
  margin-top: 30px;
}
.actions-inline { margin-top: 22px; }

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: 48px;
  padding: 0 24px;
  border-radius: 4px;
  font-family: var(--sans);
  font-size: .97rem;
  font-weight: 600;
  text-decoration: none;
  transition: background-color .16s ease, border-color .16s ease, color .16s ease;
}
.btn-compact { min-height: 40px; padding: 0 16px; font-size: .9rem; }

/* The primary button carries the mark's gradient; hover slides the same
   gradient rather than swapping to a different colour. */
.btn-solid {
  background: var(--grad);
  background-size: 190% 100%;
  background-position: 0% 0%;
  color: var(--white);
  transition: background-position .3s ease, color .16s ease;
}
.btn-solid:hover { background-position: 100% 0%; color: var(--white); }

.btn-outline {
  border: 1px solid var(--line);
  color: var(--navy);
  background: var(--white);
}
.btn-outline:hover { border-color: var(--blue-mid); color: var(--navy); background: var(--tint); }

.btn-light { background: var(--white); color: var(--navy); }
.btn-light:hover { background: var(--tint-strong); color: var(--navy); }

.btn-quiet {
  color: var(--white);
  border: 1px solid rgba(255, 255, 255, .3);
}
.btn-quiet:hover { color: var(--white); border-color: var(--white); background: rgba(255, 255, 255, .08); }

/* --- Header --------------------------------------------------------------- */
.site-header {
  position: sticky;
  top: 0;
  z-index: 50;
  /* The logo gradient as a 4px rule across the top of every page. It rides the
     sticky header, so it stays visible while scrolling. */
  background-color: rgba(255, 255, 255, .95);
  background-image: var(--grad);
  background-repeat: no-repeat;
  background-size: 100% 4px;
  background-position: top left;
  backdrop-filter: blur(8px);
  border-bottom: 1px solid var(--line);
  transition: box-shadow .2s ease;
}
.site-header.is-scrolled { box-shadow: 0 1px 3px rgba(20, 46, 119, .10), 0 8px 22px rgba(20, 46, 119, .07); }

.header-inner {
  display: flex;
  align-items: center;
  gap: 12px;
  min-height: var(--header-h);
  padding-top: 4px;   /* clears the gradient rule */
}

.brand {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-right: auto;
  text-decoration: none;
  color: var(--navy);
}

/* The square logo carries the "SVAAS" wordmark below the cloud mark, which is
   illegible at header size. This wrapper crops to the mark only and the
   wordmark is set as text beside it. */
.brand-logo {
  display: block;
  flex: none;
  width: 42px;
  height: 31px;
  overflow: hidden;
}
.brand-mark { display: block; width: 42px; height: 42px; }

.brand-name {
  font-family: var(--serif);
  font-size: 1.28rem;
  font-weight: 600;
  letter-spacing: -0.01em;
}

.nav-toggle {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 44px;
  height: 44px;
  padding: 0;
  border: 1px solid var(--line);
  border-radius: 4px;
  background: var(--white);
  color: var(--navy);
  cursor: pointer;
}
.nav-toggle-bars { display: block; width: 18px; height: 12px; position: relative; }
.nav-toggle-bars span {
  position: absolute;
  left: 0;
  width: 100%;
  height: 2px;
  background: currentColor;
  transition: transform .2s ease, opacity .2s ease;
}
.nav-toggle-bars span:nth-child(1) { top: 0; }
.nav-toggle-bars span:nth-child(2) { top: 5px; }
.nav-toggle-bars span:nth-child(3) { top: 10px; }
.nav-toggle[aria-expanded="true"] .nav-toggle-bars span:nth-child(1) { transform: translateY(5px) rotate(45deg); }
.nav-toggle[aria-expanded="true"] .nav-toggle-bars span:nth-child(2) { opacity: 0; }
.nav-toggle[aria-expanded="true"] .nav-toggle-bars span:nth-child(3) { transform: translateY(-5px) rotate(-45deg); }

.site-nav {
  display: none;
  position: absolute;
  left: 0;
  right: 0;
  top: 100%;
  padding: 6px var(--gutter) 20px;
  background: var(--white);
  border-bottom: 1px solid var(--line);
  box-shadow: 0 10px 26px rgba(20, 46, 119, .13);
}
body.nav-open .site-nav { display: block; }

.nav-list li + li { border-top: 1px solid var(--line-soft); }
.nav-list a,
.nav-list .nav-current {
  display: block;
  padding: 13px 2px;
  font-size: 1rem;
  color: var(--ink-soft);
  text-decoration: none;
}
.nav-list a:hover { color: var(--blue-mid); }
.nav-list .nav-current { color: var(--navy); font-weight: 600; }

.nav-tail {
  display: flex;
  align-items: center;
  gap: 14px;
  margin-top: 18px;
}

.lang-switch {
  display: flex;
  align-items: center;
  gap: 2px;
  margin-left: auto;
}
/* Links, not buttons: each locale is its own URL, so this is navigation.
   inline-flex keeps the 44px hit target, which inline elements would drop. */
.lang-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 40px;
  min-height: 40px;
  padding: 4px 8px;
  font: inherit;
  font-size: .85rem;
  font-weight: 600;
  color: var(--muted);
  text-decoration: none;
  border-radius: 4px;
}
a.lang-btn:hover { color: var(--blue-mid); }
.lang-btn[aria-current="page"] { color: var(--navy); background: var(--tint-strong); }
.lang-sep { color: var(--line); }

/* --- Hero ----------------------------------------------------------------- */
/* The home hero is the one dark opener on the site. Sub-pages keep the light
   .page-head below, so the rule is now "home has dark bookends, sub-pages open
   light and close dark" rather than "light pages, dark endings".

   Same surface family as .cta and the footer — a radial pooled over
   --grad-panel — so the page opens and closes on the same material. The glow
   sits top-right, behind the mark. No border-bottom: the colour change against
   the white band below is already the boundary. */
.hero {
  position: relative;
  overflow: hidden;                /* clips the mark where it runs off the edge */
  padding-top: clamp(44px, 7vw, 76px);
  background:
    radial-gradient(74% 100% at 88% -8%, rgba(33, 133, 195, .38) 0%, rgba(33, 133, 195, 0) 66%),
    var(--grad-panel);
}

.hero-inner { padding-bottom: clamp(28px, 3.5vw, 40px); }

/* Newsreader runs a little wider and lighter than the Sitka Text this was
   drawn for, so the tracking is tighter and the weight a step up from the 600
   the other headings take. */
.hero h1 {
  max-width: 24ch;
  font-size: clamp(2.05rem, 5.6vw, 3.3rem);
  font-weight: 620;
  line-height: 1.08;
  letter-spacing: -0.024em;
  color: var(--white);
}
.hero-lead {
  margin-top: 22px;
  font-size: clamp(1.08rem, 1.6vw, 1.2rem);
  color: var(--tint-strong);       /* 10.2:1 on navy */
}

/* Focus rings are drawn for the light bands further down the page; on navy
   they need to be the light colour instead. */
.hero :focus-visible { outline-color: var(--white); }

/* --- The mark ------------------------------------------------------------- */
/* Decorative and aria-hidden. The artwork is assets/img/svaas-mark-reverse.svg,
   traced from the logo PNG by _trace.py and inlined by build.py.

   It is the one thing on a navy surface allowed to carry the bright end of the
   palette, because it is neither text nor a meaningful graphic. Pinned to the
   right edge so its box never reaches the text column, which the h1 caps at
   24ch. */
.hero-mark {
  position: absolute;
  top: 9%;
  right: 2%;
  width: clamp(240px, 30vw, 430px);
  opacity: .5;
  pointer-events: none;
}
.hero-mark svg { display: block; width: 100%; height: auto; }

/* The mark draws itself in once, along its own silhouette.

   The ribbon tapers, so it cannot be replayed as a uniform stroke. Instead the
   traced outline doubles as a mask spine, stroked wide enough to cover the
   ribbon's full thickness, and revealing that stroke reveals the finished
   artwork underneath. pathLength="1" in the markup normalises the total length
   across all four subpaths, so this one offset drives the whole drawing and
   nothing has to measure the geometry.

   The resting state is fully drawn and the animation only plays the reveal, so
   the mark is complete whenever the animation does not run — including under
   the reduced-motion block further down, which clamps it to nothing. */
#mark-spine {
  stroke-dasharray: 1;
  stroke-dashoffset: 0;
  animation: mark-draw 1.6s cubic-bezier(.4, .05, .35, 1) both;
}
@keyframes mark-draw {
  from { stroke-dashoffset: 1; }
  to { stroke-dashoffset: 0; }
}

/* Below the wide breakpoint there is no whitespace for it: the headline wraps
   to the full column and the rail labels reach the right edge, so wherever the
   mark goes it ends up behind white text. Lightening the panel under white text
   costs contrast — measured at 3.55:1 with the mark tucked bottom-right at .32
   opacity, against 6.78:1 for the bare panel. Dropping to .12 would clear the
   bar at 5.33:1, but a mark that faint is not worth the hero's height on a
   phone. So it comes out. */
@media (max-width: 899px) {
  .hero-mark { display: none; }
}

/* --- What modernising changes --------------------------------------------- */
/* Six real before/after pairs. The markup carries all six as text; site.js
   hides that list and types them through .shift-stage one at a time. */
.shift {
  padding-block: 26px 4px;
  border-top: 1px solid rgba(255, 255, 255, .16);
}
.shift-intro {
  max-width: 52ch;
  font-size: .95rem;
  color: var(--tint-strong);
}

.shift-list {
  display: grid;
  gap: 10px;
  margin-top: 18px;
}
.shift-list li,
.shift-stage {
  font-family: var(--serif);
  font-size: clamp(1.05rem, 2.2vw, 1.32rem);
}
/* The arrow lives here rather than in the locale files, so it stays out of the
   translated strings and out of the textContent site.js reads. */
.shift-to::before {
  content: "\2192";
  margin: 0 .55em;
  color: var(--tint-strong);       /* not the bright blue: 2.7:1 on navy */
}
/* While a pair is mid-sequence the "to" half is still empty, and an arrow
   pointing at nothing reads as a mistake. It appears with the first
   character the animation types. */
.shift-to:empty::before { content: none; }
.shift-from { color: var(--tint-strong); }
.shift-to { color: var(--white); font-weight: 600; }
/* Struck through, never dimmed — reducing the opacity would drop it below
   4.5:1 against the navy. The rule takes the text's own colour. */
.shift-from.is-struck { text-decoration: line-through; text-decoration-thickness: 1px; }

/* Once site.js takes over, the static list is taken out of view but left in
   the accessibility tree, so a screen reader still gets all six pairs as plain
   text. No aria-live: a looping live region would announce forever. */
.shift-live .shift-list {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}
/* Two lines of room are reserved so a wrap at 320px cannot shove the rail
   below it while a pair is typing. */
.shift-stage {
  margin-top: 18px;
  min-height: 2.6em;
}
.shift-cursor {
  display: inline-block;
  width: 2px;
  height: 1.05em;
  margin-left: .12em;
  vertical-align: -.14em;
  background: var(--tint-strong);
  animation: shift-blink 1.1s steps(1, end) infinite;
}
.shift-cursor.is-done { animation: none; opacity: 0; }

@keyframes shift-blink {
  0%, 45% { opacity: 1; }
  50%, 95% { opacity: 0; }
  100% { opacity: 1; }
}

/* The scale rail. One continuum, small on the left, sector-wide on the right —
   the page says "any size" structurally instead of claiming it in a sentence.
   It used to draw its own rule in on load; that animation is gone, because the
   hero now has an orchestrated sequence of its own (the mark assembling, then a
   single typing pass) and a third moving thing only competed with it. */
.scale {
  padding-block: 26px 30px;
  border-top: 1px solid rgba(255, 255, 255, .16);
}
.scale-intro {
  max-width: 52ch;
  font-size: .95rem;
  color: var(--tint-strong);
}

.scale-track {
  position: relative;
  display: grid;
  gap: 18px;
  margin-top: 24px;
}
.scale-node {
  position: relative;
  padding-left: 24px;
  font-family: var(--serif);
  font-size: 1.02rem;
  color: var(--white);
}
.scale-dot {
  position: absolute;
  left: 0;
  top: .52em;
  width: 9px;
  height: 9px;
  border-radius: 50%;
  background: var(--tint-strong);
}
/* The three dots used to run bright blue to navy, which read as small-to-large
   on a light band. Neither that ramp nor its inverse works here: bright blue is
   2.7:1 on navy, and fading the dots to the right makes "a platform a whole
   sector runs on" the faintest thing on the rail, which is backwards. So all
   three are identical and the range is carried by position alone, which is what
   a left-to-right rail is for. Sizing them small-to-large was tried and undone:
   these selectors outrank the >=620px rule that lifts the dots onto the rail,
   so each size would need its own re-centring per breakpoint. */

/* --- Bands ---------------------------------------------------------------- */
.band { padding-block: var(--band); }
.band-tint {
  background: var(--tint);
  border-block: 1px solid var(--line);
}
.band-heading {
  max-width: 30ch;
  font-size: clamp(1.5rem, 3.2vw, 2rem);
  letter-spacing: -0.012em;
}
.band-heading + .lead { margin-top: 16px; }
.band-heading + .prose,
.band-heading + .facts,
.band-heading + .person,
.band-heading + .split,
.band-heading + .ruled { margin-top: 26px; }
.lead + .split,
.lead + .ruled,
.lead + .steps,
.lead + .service-index,
.lead + .namewall,
.lead + .chips,
.lead + .entries,
.lead + .tech-list,
.lead + .prose { margin-top: 38px; }

/* Sub-page opener. Same tint as the home hero, so landing on any page reads as
   the same site, with the gradient closing it off. */
.page-head {
  position: relative;
  padding-block: clamp(40px, 6vw, 68px) clamp(30px, 4vw, 44px);
  background:
    radial-gradient(72% 125% at 88% -12%, rgba(31, 124, 182, .24) 0%, rgba(31, 124, 182, 0) 70%),
    var(--tint);
}
.page-head::after {
  content: "";
  position: absolute;
  inset: auto 0 0 0;
  height: 2px;
  background: var(--grad);
}
.page-head h1 {
  max-width: 26ch;
  font-size: clamp(1.9rem, 4.4vw, 2.65rem);
  letter-spacing: -0.016em;
}
.page-head .lead { margin-top: 18px; }

/* --- Split blocks (hairline-separated, no cards) -------------------------- */
.split {
  display: grid;
  gap: 30px 36px;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 250px), 1fr));
}
.split > li {
  padding-top: 18px;
  border-top: 3px solid transparent;
  border-image: var(--grad) 1;
}
/* Four short blocks: auto-fit lands on 3 + 1 orphan at desktop widths, so pair
   them explicitly instead. */
.split-pairs { grid-template-columns: minmax(0, 1fr); }
.split h3 { font-size: 1.12rem; }
.split p { margin-top: 8px; font-size: .96rem; color: var(--muted); }

/* --- Ruled list (title rail + body) --------------------------------------- */
.ruled > li {
  padding-block: 22px;
  border-top: 1px solid var(--line);
}
.ruled > li:last-child { border-bottom: 1px solid var(--line); }
.ruled h3 { font-size: 1.14rem; }
.ruled p { margin-top: 8px; max-width: 62ch; color: var(--muted); }

/* --- Service index (home) ------------------------------------------------- */
.service-index {
  display: grid;
  gap: 1px;
  background: var(--line);
  border: 1px solid var(--line);
}
.service-index > li { background: var(--white); }
.service-index a {
  display: block;
  height: 100%;
  padding: 20px 22px;
  text-decoration: none;
  border-left: 3px solid transparent;
  transition: background-color .16s ease, border-color .16s ease;
}
.service-index a:hover { background: var(--tint); border-left-color: var(--blue); }
.service-index-marked a:hover { border-left-color: var(--navy); }
.service-index-name {
  display: block;
  font-family: var(--serif);
  font-size: 1.08rem;
  color: var(--ink);
}
.service-index-line {
  display: block;
  margin-top: 6px;
  font-size: .93rem;
  color: var(--muted);
}

/* --- Service detail ------------------------------------------------------- */
.service {
  padding-block: 34px;
  border-top: 1px solid var(--line);
  scroll-margin-top: calc(var(--header-h) + 16px);
}
.service:first-of-type { border-top: 0; padding-top: 6px; }
.service:last-of-type { border-bottom: 1px solid var(--line); }

.service-head h2 {
  font-size: clamp(1.35rem, 2.6vw, 1.7rem);
  letter-spacing: -0.012em;
}
/* A short gradient rule under each service title: the mark repeated eight
   times down the page, which is what keeps this long reading page from going
   flat and grey. */
.service-head h2::after {
  content: "";
  display: block;
  width: 38px;
  height: 3px;
  margin-top: 14px;
  background: var(--grad);
}
.service-summary {
  margin-top: 10px;
  max-width: 34ch;
  color: var(--muted);
}
.service-body { margin-top: 20px; }
.service-body > p { max-width: 64ch; }

/* Who it helps. The bright-blue rule is the same mark used for the small,
   practical end of the work throughout the site. */
.service-who {
  margin-top: 16px;
  padding-left: 16px;
  border-left: 3px solid var(--blue);
  color: var(--ink-soft);
}

.examples-label {
  margin-top: 24px;
  font-size: .9rem;
  font-weight: 600;
  color: var(--ink);
}
.examples { margin-top: 10px; }
.examples li {
  position: relative;
  padding-left: 18px;
  font-size: .96rem;
  color: var(--muted);
}
.examples li + li { margin-top: 7px; }
.examples li::before {
  content: "";
  position: absolute;
  left: 0;
  top: .72em;
  width: 9px;
  height: 2px;
  background: var(--blue);
}

/* The one service the strategy exists to add carries the full-width rule. */
.service-marked .service-head h2::after { width: 100%; }

/* --- Name wall ------------------------------------------------------------ */
.namewall {
  display: grid;
  gap: 0 26px;
  grid-template-columns: repeat(auto-fill, minmax(min(100%, 170px), 1fr));
}
.namewall li {
  padding-block: 13px;
  border-top: 1px solid var(--line);
  font-family: var(--serif);
  font-size: 1.04rem;
  color: var(--ink);
}
.namewall + .note { margin-top: 26px; }

/* --- Chips --------------------------------------------------------------- */
.chips {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
}
.chips li {
  padding: 8px 15px;
  border: 1px solid var(--blue-mid);
  border-radius: 3px;
  background: var(--white);
  font-size: .94rem;
  color: var(--navy);
}
.chips-quiet li {
  background: var(--tint-strong);
  border-color: transparent;
  color: var(--ink-soft);
  font-size: .9rem;
}

/* --- Work entries -------------------------------------------------------- */
.entries > li {
  padding-block: 26px;
  border-top: 1px solid var(--line);
}
.entries > li:last-child { border-bottom: 1px solid var(--line); }
.entries h4 {
  font-size: 1.18rem;
  color: var(--ink);
}
.entry-role {
  margin-top: 4px;
  font-size: .88rem;
  color: var(--blue-mid);
}
.entries > li > p:last-child { margin-top: 14px; max-width: 62ch; }

/* --- Definition lists (facts, technology) -------------------------------- */
.facts, .tech-list {
  display: grid;
  gap: 0;
}
.facts > div, .tech-list > div {
  padding-block: 16px;
  border-top: 1px solid var(--line);
}
.facts > div:last-child, .tech-list > div:last-child { border-bottom: 1px solid var(--line); }
.facts dt, .tech-list dt {
  font-size: .84rem;
  font-weight: 600;
  color: var(--blue-mid);
}
.facts dd, .tech-list dd {
  margin-top: 3px;
  color: var(--ink-soft);
}
.facts + .note { margin-top: 24px; }

/* --- Steps (a real sequence, so it is numbered) -------------------------- */
.steps {
  display: grid;
  gap: 28px;
  counter-reset: step;
}
.steps > li {
  padding-top: 16px;
  border-top: 1px solid var(--line);
  counter-increment: step;
}
.steps > li::before {
  content: counter(step);
  display: block;
  margin-bottom: 8px;
  font-family: var(--serif);
  font-size: 1.9rem;
  line-height: 1;
  color: var(--blue);
}
.steps h3 { font-size: 1.1rem; }
.steps p { margin-top: 8px; max-width: 46ch; color: var(--muted); }

/* --- Statement (about) --------------------------------------------------- */
.statement {
  margin: 0;
  padding-left: clamp(18px, 3vw, 30px);
  border-left: 4px solid transparent;
  /* A vertical gradient here, not --grad: border-image slices the image to the
     border box, so a horizontal gradient on a 4px-wide left border would show
     only its first colour. */
  border-image: linear-gradient(180deg, var(--navy) 0%, var(--blue) 100%) 1;
}
.statement p {
  /* The measure has to be set on the element that carries the large font size:
     `ch` resolves against the element's own font, not its parent's. */
  max-width: 26ch;
  font-family: var(--serif);
  font-size: clamp(1.45rem, 3.4vw, 2.1rem);
  line-height: 1.28;
  letter-spacing: -0.012em;
  color: var(--ink);
}
.statement + .prose { margin-top: 34px; }

/* --- Founder teaser (home) ---------------------------------------------- */
.founder-teaser > div { max-width: 64ch; }
.founder-teaser p { margin-top: 16px; }

/* --- Person (team) ------------------------------------------------------- */
.person { margin-top: 30px; }

.person-photo {
  width: 132px;
  height: 132px;
  border-radius: 4px;
  object-fit: cover;
  background: var(--tint-strong);
}
.person-name {
  margin-top: 16px;
  font-family: var(--serif);
  font-size: 1.2rem;
  color: var(--ink);
}
.person-role { margin-top: 2px; font-size: .92rem; color: var(--muted); }
.person-links { margin-top: 10px; font-size: .92rem; }
.person-body { margin-top: 26px; }

.credentials { margin-top: 4px; }
.credentials li {
  display: grid;
  grid-template-columns: 58px 1fr;
  gap: 12px;
  align-items: baseline;
  padding-block: 13px;
  border-top: 1px solid var(--line);
}
.credentials li:last-child { border-bottom: 1px solid var(--line); }
.cred-year {
  font-variant-numeric: tabular-nums;
  font-size: .88rem;
  font-weight: 600;
  color: var(--blue-mid);
}
.cred-body { display: grid; gap: 1px; }
.cred-body strong { color: var(--ink); font-weight: 600; }
.cred-org { font-size: .9rem; color: var(--muted); }

.quote {
  margin: 40px 0 0;
  padding-top: 28px;
  border-top: 3px solid transparent;
  border-image: var(--grad) 1;
  max-width: 60ch;
}
.quote blockquote { margin: 0; }
.quote blockquote p {
  font-family: var(--serif);
  font-size: 1.2rem;
  line-height: 1.5;
  color: var(--ink);
}
.quote blockquote p + p { margin-top: 14px; }
.quote figcaption { margin-top: 12px; font-size: .88rem; color: var(--muted); }

/* With JS and without prefers-reduced-motion, #quote-carousel keeps every
   quote stacked in the same grid cell and cross-fades between them — see
   the "quote carousel" block in site.js. Without JS, or with reduced motion,
   .quote-carousel never gets .is-live and every quote below simply renders
   as a static, separated list, exactly like .quote on its own. */
.quote-carousel.is-live { display: grid; }
.quote-carousel.is-live .quote {
  grid-area: 1 / 1;
  align-self: start;
  opacity: 0;
  visibility: hidden;
  transition: opacity .6s ease;
}
.quote-carousel.is-live .quote.is-current { opacity: 1; visibility: visible; }

/* --- Contact ------------------------------------------------------------- */
.contact-layout { display: grid; gap: 46px; }
.facts-contact dd { font-size: 1.04rem; }
.contact-topics > p { margin-top: 16px; max-width: 56ch; }

.ticks { margin-top: 22px; }
.ticks li {
  padding: 12px 0 12px 18px;
  border-left: 3px solid var(--blue);
  background: linear-gradient(90deg, var(--tint) 0%, rgba(238, 244, 251, 0) 62%);
  color: var(--ink-soft);
}
.ticks li + li { margin-top: 4px; }
.ticks + .note { margin-top: 22px; }

/* --- Closing call to action --------------------------------------------- */
/* One full-colour panel per page: the mark's gradient at full strength. */
.cta {
  padding-block: clamp(52px, 7vw, 84px);
  color: var(--white);
  background:
    radial-gradient(70% 130% at 92% 6%, rgba(33, 133, 195, .55) 0%, rgba(33, 133, 195, 0) 62%),
    var(--grad-panel);
}
.cta h2 {
  max-width: 26ch;
  font-size: clamp(1.55rem, 3.4vw, 2.15rem);
  color: var(--white);
  letter-spacing: -0.012em;
}
.cta .lead { margin-top: 16px; color: rgba(255, 255, 255, .89); }

/* --- Footer -------------------------------------------------------------- */
.site-footer {
  padding-top: clamp(40px, 5vw, 60px);
  background-color: var(--navy-deep);
  background-image: var(--grad);
  background-repeat: no-repeat;
  background-size: 100% 4px;
  background-position: top left;
  color: rgba(255, 255, 255, .74);
  font-size: .93rem;
}
.footer-inner { display: grid; gap: 32px; }
.footer-name {
  font-family: var(--serif);
  font-size: 1.24rem;
  color: var(--white);
}
.footer-vat { margin-top: 4px; color: var(--tint-strong); font-size: .85rem; }
.footer-pitch { margin-top: 10px; max-width: 40ch; color: var(--tint-strong); }

.footer-col-title {
  font-size: .88rem;
  font-weight: 600;
  color: var(--white);
}
.footer-col ul { margin-top: 12px; display: grid; gap: 8px; }
.site-footer a { color: rgba(255, 255, 255, .78); text-decoration: none; }
.site-footer a:hover { color: var(--white); text-decoration: underline; }

.footer-legal {
  margin-top: clamp(34px, 5vw, 50px);
  padding-block: 20px;
  border-top: 1px solid rgba(255, 255, 255, .14);
  font-size: .85rem;
  color: rgba(255, 255, 255, .55);
  display: grid;
  gap: 6px;
}
.footer-trust { color: rgba(255, 255, 255, .55); }

/* ==========================================================================
   Breakpoints
   ========================================================================== */

@media (min-width: 620px) {
  .scale-track {
    grid-template-columns: repeat(3, 1fr);
    gap: 0 22px;
    padding-top: 26px;
  }
  /* The rail itself: one rule behind three nodes. It no longer draws itself in
     — see the note on .scale. The gradient used to end in navy, which is
     invisible on the navy hero, so it now fades out in the light instead and
     still reads left-to-right as small to sector-wide. */
  .scale-track::before {
    content: "";
    position: absolute;
    left: 4px;
    right: 4px;
    top: 4px;
    height: 1px;
    background: linear-gradient(90deg,
      var(--tint-strong) 0%,
      rgba(223, 234, 247, .55) 50%,
      rgba(223, 234, 247, .28) 100%);
  }
  .scale-node { padding-left: 0; }
  .scale-dot { top: -26px; left: 0; }
  .scale-large { text-align: left; }

  .facts, .tech-list { grid-template-columns: repeat(2, 1fr); column-gap: 36px; }
  .facts > div:nth-child(2), .tech-list > div:nth-child(2) { border-top: 1px solid var(--line); }
  .facts > div:nth-last-child(2), .tech-list > div:nth-last-child(2) { border-bottom: 1px solid var(--line); }

  .service-index { grid-template-columns: repeat(2, 1fr); }
  .split-pairs { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .steps { grid-template-columns: repeat(3, 1fr); gap: 26px; }
  .person-body { margin-top: 0; }
}

@media (min-width: 900px) {
  :root { --header-h: 72px; }

  .nav-toggle { display: none; }

  .site-nav {
    display: flex !important;
    align-items: center;
    gap: 30px;
    position: static;
    padding: 0;
    background: none;
    border: 0;
    box-shadow: none;
  }
  .nav-list { display: flex; gap: 28px; }
  .nav-list li + li { border-top: 0; }
  .nav-list a,
  .nav-list .nav-current {
    padding: 6px 0;
    font-size: .96rem;
    border-bottom: 2px solid transparent;
  }
  .nav-list .nav-current { border-bottom-color: var(--blue); }

  .nav-tail { margin-top: 0; gap: 18px; }
  .lang-switch { padding-left: 18px; border-left: 1px solid var(--line); }
  .lang-btn { min-width: 34px; min-height: 32px; }

  /* Editorial two-column blocks: a narrow naming rail, a wider body. */
  .ruled > li,
  .entries > li {
    display: grid;
    grid-template-columns: minmax(0, 260px) minmax(0, 1fr);
    column-gap: 48px;
  }
  .ruled > li > p,
  .entries > li > p:last-child { margin-top: 0; }
  .entries > li > .entry-role { grid-column: 1; margin-top: 6px; }
  .entries h4 { grid-column: 1; }
  .entries > li > p:last-child { grid-row: 1 / span 2; grid-column: 2; }

  .service {
    display: grid;
    grid-template-columns: minmax(0, 300px) minmax(0, 1fr);
    column-gap: 56px;
    padding-block: 44px;
  }
  .service-body { margin-top: 0; }

  .namewall { grid-template-columns: repeat(4, 1fr); }

  .person {
    display: grid;
    grid-template-columns: 180px minmax(0, 1fr);
    column-gap: 52px;
  }

  .contact-layout { grid-template-columns: minmax(0, 5fr) minmax(0, 6fr); gap: 60px; }

  .footer-inner { grid-template-columns: minmax(0, 2fr) minmax(0, 1fr) minmax(0, 1fr); gap: 48px; }

}

@media (min-width: 1140px) {
  .service-index { grid-template-columns: repeat(4, 1fr); }
  .split { gap: 34px 44px; }
}

/* ==========================================================================
   Thai (th)
   ==========================================================================
   Thai needs different typography from the Latin defaults, not just different
   words. Applies page-wide because build.py emits <html lang="th">.

   1. The system stack has no Thai coverage, and the serif heading stack has
      none at all, so a Thai-capable family has to come first everywhere.
   2. Thai stacks marks: a vowel above the consonant, a tone mark above that,
      and vowels below. Tight Latin line-heights clip or collide them.
   3. Negative letter-spacing pulls those marks off their base consonant.
   4. Thai has no spaces between words — spaces separate clauses — and relies
      on the browser's dictionary line-breaking, which `overflow-wrap:
      break-word` would override mid-syllable.

   Written but not visually verified: there is no Thai text yet. Check it
   properly when content/th.json is translated. */

html[lang="th"] body {
  font-family: "Leelawadee UI", "Noto Sans Thai", "Sukhumvit Set", Thonburi,
               "IBM Plex Sans Thai", var(--sans);
  line-height: 1.9;
  overflow-wrap: normal;
  word-break: normal;
  line-break: loose;
}

html[lang="th"] h1,
html[lang="th"] h2,
html[lang="th"] h3,
html[lang="th"] h4,
html[lang="th"] .brand-name,
html[lang="th"] .footer-name,
html[lang="th"] .person-name,
html[lang="th"] .scale-node,
html[lang="th"] .service-index-name,
html[lang="th"] .namewall li,
html[lang="th"] .statement p,
html[lang="th"] .quote blockquote p {
  font-family: "Leelawadee UI", "Noto Sans Thai", "Sukhumvit Set", Thonburi,
               "IBM Plex Sans Thai", var(--sans);
  line-height: 1.55;
  letter-spacing: normal;
}

html[lang="th"] .hero h1 { line-height: 1.5; }
html[lang="th"] .statement p { line-height: 1.6; }
html[lang="th"] .steps > li::before { font-family: var(--sans); }

/* Runs of Latin text inside a Thai page — the verbatim English pull-quote, the
   course titles — keep the Latin stack. */
html[lang="th"] [lang="en"] {
  font-family: var(--sans);
  line-height: 1.65;
}
html[lang="th"] .quote blockquote p[lang="en"] { font-family: var(--serif); line-height: 1.5; }

/* ==========================================================================
   Motion & print
   ========================================================================== */

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after {
    transition-duration: .01ms !important;
    animation-duration: .01ms !important;
    animation-iteration-count: 1 !important;
  }
}

@media print {
  .site-header, .skip-link, .actions, .footer-col { display: none !important; }
  body { color: #000; }
  .cta, .site-footer, .band-tint, .hero, .page-head {
    color: #000;
    background: none !important;
    border: 0;
  }
  .page-head::after { display: none; }
  .cta h2, .cta .lead, .footer-name, .footer-pitch { color: #000; }
  /* The hero prints on white, so everything drawn for the navy has to come
     back to black, and the decorative mark comes out entirely. */
  .hero h1, .hero-lead, .shift-intro, .shift-from, .shift-to,
  .scale-intro, .scale-node { color: #000; }
  .hero-mark, .shift-cursor { display: none !important; }
  /* With JavaScript on, the six pairs live in the clipped list; unclip it so
     the printed page carries them instead of a half-typed line. */
  .shift-stage { display: none; }
  .shift-live .shift-list {
    position: static;
    width: auto;
    height: auto;
    clip-path: none;
    white-space: normal;
  }
  .shift, .scale { border-top: 1px solid #000; }
  .band, .page-head, .cta { padding-block: 16px; }
  .service, .entries > li, .split > li { break-inside: avoid; }
  a { text-decoration: none; color: #000; }
}
