/* =====================================================================
   Bedrock OS — implementation canon.
   Spec: bedrock/standards/bedrock-os-web-design.md
   One family (Geist), two weights (500/300), three declared sizes plus the
   key title, tracking and 0.8 leading on ALL CAPS only, 1.2 everywhere else,
   two margins (narrow/wide),
   R0 everywhere, no lines, no shadows, no animation. Blur only.
   Colors are per-brand; everything else is identical across all sites.
   ===================================================================== */

:root {
  /* margins (two tiers; 30 / 100 at 1920px) */
  --m-narrow: clamp(16px, 1.6vw, 30px);
  --m-wide: clamp(24px, 5.2vw, 100px);
  --m-v: clamp(32px, 7vh, 72px);

  /* two faces of one family: Sans is prose, Mono is record */
  --font: "Geist", -apple-system, BlinkMacSystemFont, "Segoe UI", "Helvetica Neue", Arial, sans-serif;
  --font-mono: "Geist Mono", ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;

  /* type scale */
  --fs-body: clamp(20px, 1.85vw, 35px);
  --fs-caption: clamp(12px, 0.95vw, 16px);
  --fs-nav: 13px;                            /* chrome, not content: recessive by size and weight */

  /* tracking: ALL CAPS key titles only. Capitals are drawn to sit beside
     capitals, so they need closing up; lower-case text does not. */
  --track: -0.04em;                          /* key titles only: optical correction for display sizes */

  /* ---- colour, in three layers ----
     1. absolute — raw values with no meaning of their own. Never swap.
     2. role     — what a thing is for. These swap with the mode.
     3. media    — the plate faces. They follow the brightness of the photo
                   underneath, not the mode, so they are built from the
                   absolutes and never swap. A dark photo needs a dark plate
                   in light mode and in dark mode alike. */

  /* 1. absolute */
  --white: #ffffff;
  --black: #000000;
  --gray: #808080;    /* one value serves both modes: 4.0:1 on white, 5.3:1 on black */

  /* 2. role — light mode */
  --bg: var(--white);
  --fg: var(--black);
  --link: #0000ee;                           /* external links only (leaving this site) */
  --chrome: rgba(255, 255, 255, 0.72);       /* nav, dropdown, cookie: glass over the page */
  --veil: rgba(255, 255, 255, 0.22);         /* page dim behind a dropdown */
  --glass-blur: blur(12px) saturate(1.4);

  /* motion: three durations and one curve. The curve never overshoots —
     a bounce is decoration, and decoration is not a reason to move. */
  --t-fast: 90ms;                            /* interaction feedback */
  --t-page: 220ms;                           /* anything that opens, closes, or crosses */
  --t-media: 700ms;                          /* one image giving way to the next */
  --ease: cubic-bezier(0.4, 0, 0.2, 1);      /* feedback */
  --ease-out: cubic-bezier(0.32, 0.72, 0, 1); /* opening: leaves and settles without a snap */

  /* one radius. A corner is rounded only when both edges meeting at it are
     clear of the screen — otherwise it curves against the screen's own corner
     and simply reads as a chip out of the surface. Full-width surfaces (the
     bar, the sheet under it, the cookie notice, the hero) stay square. */
  --r: 12px;
  --tint: color-mix(in srgb, var(--gray) 16%, var(--bg));       /* a control at rest */
  --tint-deep: color-mix(in srgb, var(--gray) 34%, var(--bg));  /* a card under the pointer */

  /* 3. media — fixed in both modes */
  --plate-light: rgba(255, 255, 255, 0.72);
  --plate-dark: rgba(0, 0, 0, 0.30);
  --ink-on-light: var(--black);
  --ink-on-dark: var(--white);
}

/* The mode has three states: an explicit choice stamps data-theme on the root,
   and the default stamps nothing — so the media query has to carry the
   un-stamped case while still losing to an explicit light choice. */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --bg: var(--black);
    --fg: var(--white);
    --link: #8f9cff;                         /* the light-mode blue is 2.2:1 on black */
    --chrome: rgba(0, 0, 0, 0.72);
    --veil: rgba(0, 0, 0, 0.22);
  }
}
:root[data-theme="dark"] {
  --bg: var(--black);
  --fg: var(--white);
  --link: #8f9cff;
  --chrome: rgba(0, 0, 0, 0.72);
  --veil: rgba(0, 0, 0, 0.22);
}

/* selected text is ink on page, the same two colours everything else uses.
   Written with the role tokens, so it flips with the mode without a second
   declaration, and it holds on a dark plate where the text is already white. */
::selection { background: var(--fg); color: var(--bg); }
::-moz-selection { background: var(--fg); color: var(--bg); }

:root { interpolate-size: allow-keywords; }
*, *::before, *::after { box-sizing: border-box; }

html { scroll-behavior: smooth; }  /* anchor jumps; the wheel is smoothed in JS */

body {
  margin: 0;
  background: var(--bg);
  color: var(--fg);
  font-family: var(--font);
  font-weight: 300;
  font-size: var(--fs-body);
  letter-spacing: normal;
  line-height: 1.2;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

/* ---- roles (spec §2.0) ---- */

.display {
  /* key title: CAPS + Medium + 0.8; the text frame spans the full
     width inside the narrow margin and the size fills the frame */
  font-weight: 500;
  line-height: 0.8;
  letter-spacing: var(--track);
  text-transform: uppercase;
  font-size: clamp(38px, 8.6vw, 190px);          /* page title — fills the frame inside the narrow margin */
  margin: 0;
}
.display--xl { font-size: clamp(56px, 12vw, 260px); }    /* a title that is the whole page (404) */

h1, h2, h3, h4, h5, h6 { font-size: inherit; font-weight: inherit; margin: 0; }
.label { font-weight: 500; font-size: var(--fs-body); }

.body { font-weight: 300; }

.caption { font-size: var(--fs-caption); font-weight: 300; letter-spacing: normal; }

/* ---- Mono: the register of record ----
   Not a style but a register. Prose is set in Sans; anything that is a
   recorded fact rather than a sentence is set in Mono — quantities, units,
   dates, identifiers, and the registered company name. Same family, so the
   two faces share proportions and the page still reads as one system.
   Mono is never used for a sentence, a heading, or navigation. */
/* Mono has exactly one weight. It carries no hierarchy — a record is neither
   emphasised nor faint — so it needs none. Regular, not the Light the prose
   uses: at caption size the Light mono goes fragile, and 500 makes a date
   shout. Only 400 is loaded, so no other weight can be reached by accident. */
.num, time, .stamp {
  font-family: var(--font-mono);
  font-weight: 400;
  font-variant-numeric: tabular-nums;
  text-transform: uppercase;
}
/* A unit symbol carries its case as meaning, so it opts out of the transform:
   kW is a kilowatt and KW is not an SI symbol at all, and mW and MW differ by
   a factor of a billion but render identically once uppercased. Wrap every
   unit — the record stays exact where being exact is the whole point. */
.unit { text-transform: none; }

/* ---- interaction: two tiers ----
   PRIMARY carries a surface. At rest a grey plane holding ink; on contact it
   inverts to ink holding the page. Used where an action is the point.
   SECONDARY carries none. Ink at rest, grey on contact. Used for getting
   around — navigation, the footer, links inside prose. A bar of filled pills
   shouts louder than the thing it is a route to, so movement stays quiet. */
.btn {
  display: inline-flex;
  align-items: center;
  border: none;
  border-radius: var(--r);
  background: var(--tint);
  color: var(--fg);
  text-decoration: none;
  cursor: pointer;
  transition: background-color var(--t-fast) linear, color var(--t-fast) linear;
}
.btn:hover, .btn:focus-visible, .btn:active {
  background: var(--fg);
  color: var(--bg);
}

a, .btn-text, .slide-arrow, .mode { color: var(--fg); transition: color var(--t-fast) linear; }
a:hover, a:focus-visible, a:active,
.btn-text:hover, .btn-text:focus-visible, .btn-text:active,
.slide-arrow:hover, .slide-arrow:focus-visible, .slide-arrow:active,
.mode:hover, .mode:focus-visible, .mode:active { color: var(--gray); }
.nav li.is-open > a, .nav li.is-open > .drop, .node.is-open > .big { color: var(--gray); }

/* ---- status mark ----
   Bedrock's mark is a circle, so the system's state glyph is one too. Never
   decoration: a circle appears only where a record actually has a state.
   Fill and weight carry it, within the palette, and blue stays reserved for
   links that leave the site.
     open    ring          not started
     active  half filled   under way
     done    solid         complete, live
     off     solid grey    dropped, retired, not applicable */
.dot {
  display: inline-block; flex: none;
  width: 0.6em; height: 0.6em;
  border: 1px solid currentColor; border-radius: 50%;
  vertical-align: baseline;
}
.dot--active { background: linear-gradient(90deg, currentColor 50%, transparent 50%); }
.dot--done { background: currentColor; }
.dot--off { color: var(--gray); background: currentColor; }

.states { list-style: none; margin: 0; padding: 0; display: grid; gap: 14px; }
.states li { display: grid; grid-template-columns: 0.6em 1fr; gap: 0 14px; align-items: baseline; }

/* ---- layout ---- */

main { padding-top: 48px; }
main.bleed { padding-top: 0; }                 /* a hero starts at the top edge, under the nav */
main.bleed > .hero { min-height: 100svh; }

.hero {
  position: relative;
  min-height: calc(100svh - 48px);
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  padding: var(--m-v) var(--m-narrow);
}

.hero .hero-bg {
  position: absolute; inset: 0; z-index: -1;
  background: color-mix(in srgb, var(--gray) 18%, var(--bg)) center / cover no-repeat;
  display: flex; align-items: center; justify-content: center;
  color: var(--gray); font-size: var(--fs-caption); font-weight: 500;
}
.hero .hero-bg img {
  position: absolute; inset: 0;
  width: 100%; height: 100%; object-fit: cover; display: block;
  opacity: 0;
  transition: opacity 700ms linear;
}
.hero .hero-bg img.is-on { opacity: 1; }

/* slideshow controls live inside the plate, so no second surface is needed */
.slide-ui { display: flex; align-items: center; justify-content: space-between; gap: 24px; margin-top: 1.4em; }
.gauge { display: flex; gap: 6px; }
.gauge span { width: 26px; height: 2px; background: currentColor; opacity: 0.3; }
.gauge span.is-on { opacity: 1; }
.slide-arrows { display: flex; gap: 18px; }
.slide-arrow { border: none; background: none; padding: 0; cursor: pointer;
  font-family: inherit; font-size: var(--fs-nav); font-weight: 500; color: currentColor; }
.hero--dark { color: var(--bg); }  /* only for text placed directly on media, without a plate */
/* long-form display: a full statement carrying the key-title role.
   Sentence case, because ALL CAPS cannot be read at sentence length. */
/* sentence case: it drops the tracking with the capitals, and the leading
   with them too — the 0.8 only works because capitals have no descenders. */
.display--long { text-transform: none; letter-spacing: normal; line-height: 1.2; font-size: clamp(22px, 2.6vw, 52px); }

.page-head { padding: calc(var(--m-v) + 24px) var(--m-narrow) var(--m-v); }

.content { padding: 0 var(--m-wide) var(--m-v); }

.section { padding: var(--m-v) var(--m-wide); }

/* ---- partners: third-party marks, normalised to the palette ----
   Every logo is rendered in one colour at one optical height, so a row of
   marks from different brands still reads as one row. Colour logos are not
   used: they would break the four-colour palette. Supply each as an SVG with
   fills set to currentColor, or as a black-on-transparent PNG. */
.partners { padding: var(--m-v) var(--m-wide); }
.partner-row {
  display: flex; flex-wrap: wrap; align-items: center;
  gap: clamp(32px, 4vw, 72px); margin-top: 1.2em;
}
.partner-row img, .partner-row svg { height: 26px; width: auto; display: block; color: var(--fg); }
.partner-row .ph-logo {
  height: 26px; display: flex; align-items: center;
  font-size: var(--fs-caption); font-weight: 500; color: var(--gray);
}

/* ---- navigation (spec §3: attached, 48px, blur only, immediate) ---- */

.nav {
  position: fixed;
  top: 0; left: 0; right: 0;
  z-index: 10;
  height: 48px;
  padding: 0 var(--m-narrow);
  display: flex;
  align-items: center;
  justify-content: center;
  gap: clamp(22px, 2.4vw, 44px);
  border: none;
  border-radius: 0;
  box-shadow: none;
  background: var(--chrome);
  -webkit-backdrop-filter: var(--glass-blur);
  backdrop-filter: var(--glass-blur);
  transition: background-color var(--t-page) var(--ease-out);
}

/* While a sheet is open the surface stops being glass and becomes the page:
   solid, unblurred. Nothing behind it needs reading, so nothing shows through.
   Only the background colour moves — at full opacity the blur is already
   invisible, so it never has to be switched off mid-motion. */
.nav:has(.has-panel:hover), .nav:has(.has-panel.is-open) { background: var(--bg); }

.nav ul {
  display: flex;
  align-items: stretch;
  height: 100%;
  gap: clamp(22px, 2.4vw, 44px);
  margin: 0;
  padding: 0;
  list-style: none;
}

.nav li {
  /* full-height items keep :hover unbroken from link to panel */
  display: flex;
  align-items: center;
  height: 100%;
}

/* a name that opens a sheet is not a link: the sheet holds the link. It is
   styled as one so the bar reads evenly, and carries a button's semantics. */
.nav .drop { cursor: pointer; }
.nav a, .nav .drop, .nav .lang {
  text-decoration: none;
  font-size: var(--fs-nav);
  font-weight: 500;
  display: flex;
  align-items: center;
  height: 100%;
}


.mark-link { display: flex; align-items: center; height: 100%; }
.nav .mark { height: 13px; width: auto; display: block; color: var(--fg); }

/* language switch: an ordinary nav item, last in the row */
/* inert until a second language ships: shown, but not a link that goes nowhere */
.nav .lang { display: flex; align-items: center; height: 100%; color: var(--fg); cursor: pointer; }
.nav .lang svg { width: 16px; height: 16px; display: block; }
.lang-label { display: none; }   /* the globe carries it on a wide screen */

/* dropdown panel: full-width sheet attached under the bar */
.panel {
  /* The sheet below carries the surface; the panel carries only its contents,
     so moving from one item to the next resizes the open sheet by the
     difference instead of closing it and opening another. */
  position: fixed;
  top: 48px; left: 0; right: 0;
  display: grid;
  visibility: hidden;
  opacity: 0;
  pointer-events: none;
  /* contents wait a beat so the surface is under them before they read */
  transition: opacity 130ms linear 70ms, visibility 0s linear 200ms;
  padding: 44px var(--m-wide) 56px;
}

/* the surface itself: one sheet, always present, its height the animated value */
.sheet {
  position: fixed;
  top: 48px; left: 0; right: 0;
  z-index: 9;
  height: 0;
  overflow: hidden;
  pointer-events: none;
  background: var(--chrome);
  -webkit-backdrop-filter: var(--glass-blur);
  backdrop-filter: var(--glass-blur);
  transition: height var(--t-page) var(--ease-out),
              background-color var(--t-page) var(--ease-out);
}
.nav:has(.has-panel:hover) ~ .sheet,
.nav:has(.has-panel:focus-within) ~ .sheet,
.nav:has(.has-panel.is-open) ~ .sheet { background: var(--bg); }

.nav li:hover > .panel,
.nav li:focus-within > .panel,
.nav li.is-open > .panel {
  visibility: visible;
  opacity: 1;
  pointer-events: auto;
  transition: opacity 130ms linear 70ms, visibility 0s;
}

.panel {
  grid-template-columns: repeat(auto-fill, minmax(210px, 1fr));
  column-gap: clamp(32px, 4vw, 80px);
  row-gap: clamp(32px, 4vh, 56px);
}

.panel .col { display: flex; flex-direction: column; align-items: flex-start; gap: 14px; }

/* the lead column says what the section is, in one line, in the section's own
   words. One track: a wider lead pushes everything after it to the right. */
.panel .col--lead { gap: 16px; }
.panel .col--lead p { margin: 0; font-size: var(--fs-caption); }
.panel .col--lead .lead-name { font-weight: 500; }

/* tree disclosure (hover): a node with children carries a plus; hovering it
   opens that node's own column to the right. Repeatable at any depth.
   The child column's hit area reaches back across the gutter so the pointer
   never falls into a dead zone on its way over. Focus opens it for keyboards. */
.panel .node { display: flex; align-items: center; gap: 12px; }
.node-plus { position: relative; flex: none; width: 13px; height: 13px; }
.node-plus::before, .node-plus::after { content: ""; position: absolute; background: var(--fg); }
.node-plus::before { left: 0; top: 6px; width: 13px; height: 1px; }
.node-plus::after { top: 0; left: 6px; width: 1px; height: 13px; }
.node:hover .node-plus::after,
.node.is-open .node-plus::after,
.node:focus-within .node-plus::after { opacity: 0; }

.panel .col.node-panel {
  display: none;
  opacity: 0;
  transition: opacity var(--t-page) var(--ease-out), display var(--t-page) allow-discrete;
  margin-left: calc(-1 * clamp(32px, 4vw, 80px));
  padding-left: clamp(32px, 4vw, 80px);
}
.panel:has(.node:hover) .col.node-panel,
.panel:has(.node.is-open) .col.node-panel,
.panel:has(.node:focus-within) .col.node-panel,
.panel .col.node-panel:hover,
.panel .col.node-panel:focus-within { display: flex; opacity: 1; }
@starting-style {
  .panel:has(.node:hover) .col.node-panel,
  .panel:has(.node.is-open) .col.node-panel { opacity: 0; }
}

.colhead {
  font-size: var(--fs-caption);
  font-weight: 300;
  letter-spacing: normal;
  color: var(--gray);
  margin: 0 0 4px;
}
.panel .colhead {
  font-size: var(--fs-caption);
  font-weight: 300;
  letter-spacing: normal;
  color: var(--gray);
  margin: 0 0 4px;
}

.panel a.big { font-size: clamp(22px, 1.7vw, 30px); font-weight: 500; height: auto; }

/* language panel: names in their own language, inert until a translation ships */
.panel--lang { grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); row-gap: 18px; }
.panel--lang span { font-size: var(--fs-body); font-weight: 500; color: var(--fg); }
.panel--lang span.is-off { color: var(--gray); }
.panel--lang .now { font-size: var(--fs-caption); font-weight: 300; color: var(--gray); }
.panel a { height: auto; }

/* page veil: blurs the page only while a dropdown-bearing item is hovered */
.veil {
  position: fixed;
  inset: 0;
  z-index: 8;
  display: none;
  opacity: 0;
  transition: opacity var(--t-fast) var(--ease), display var(--t-fast) allow-discrete;
  background: var(--veil);
  -webkit-backdrop-filter: blur(14px) saturate(1.2);
  backdrop-filter: blur(14px) saturate(1.2);
}

.nav:has(.has-panel:hover) ~ .veil,
.nav:has(.has-panel.is-open) ~ .veil { display: block; opacity: 1; }
@starting-style {
  .nav:has(.has-panel:hover) ~ .veil,
  .nav:has(.has-panel.is-open) ~ .veil { opacity: 0; }
}

/* ---- buttons (provisional: outline primary / arrow text link) ---- */

.btn {
  padding: 13px 26px;
  border: 1px solid currentColor;
  border-radius: 0;
  background: var(--bg);
  font-family: inherit;
  font-size: 15px;
  font-weight: 500;
  text-decoration: none;
  cursor: pointer;
}
.btn:hover, .btn:focus-visible, .btn:active { background: var(--gray); border-color: var(--gray); color: var(--bg); }

.btn-text { font-weight: 500; text-decoration: none; }
.btn-text::after { content: " \2192"; }
.btn-text::after { content: " \2192"; }

/* ---- long-form article: no width cap. Text runs to the frame;
   the only division allowed is the centered duo gutter. ---- */

.article h2 { margin: 2.2em 0 0.6em; }
.article p { margin: 0 0 1em; }
.article a { text-decoration: none; }

/* ---- forms (R0, 1px, immediate) ---- */

.field { display: block; margin: 0 0 26px; }
.field span { display: block; font-size: var(--fs-caption); font-weight: 500; margin: 0 0 8px; }
.field input, .field textarea {
  width: 100%;
  padding: 13px 14px;
  border: 1px solid currentColor;
  border-radius: 0;
  background: var(--bg);
  font-family: inherit;
  font-size: 16px;
  font-weight: 300;
  letter-spacing: normal;
}
.field input:focus, .field textarea:focus { outline: 1px solid var(--fg); outline-offset: 2px; }
.field textarea { min-height: 180px; resize: vertical; }

/* ---- news ----
   A story is a picture with a date and a headline under it. The newest runs
   wide across the frame; the rest fall into a grid that reflows by width.
   The card carries its surface from the start, which binds the picture to the
   words under it as one object and says it can be pressed before anyone has
   to hover to find out. On contact the surface deepens rather than inverting:
   a card holds a photograph, and a black slab beside one shouts over it. The
   step is about twice the smallest one that reads. */

.news-lead { display: grid; margin: 1.6em 0 clamp(40px, 4vw, 72px); }
.news-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(320px, 1fr));
  gap: clamp(36px, 3.4vw, 64px) clamp(24px, 2.4vw, 40px);
  margin: 1.6em 0 0;
}

.card {
  display: block;
  padding: 14px;
  margin: -14px;
  border-radius: var(--r);
  background: var(--tint);
  color: var(--fg);
  text-decoration: none;
  /* only the surface moves: the words stay put, which keeps the picture and
     its caption reading as one unchanged object under the pointer */
  transition: background-color var(--t-fast) linear;
}
.card:hover, .card:focus-visible { background: var(--tint-deep); }

.card img, .card .ph {
  display: block; width: 100%;
  aspect-ratio: 16 / 9;
  object-fit: cover;
  border-radius: var(--r);
}
/* where the subject sits in the frame — a fact about the picture, not a
   style, so it travels with the image rather than with the component */
.focus-low { object-position: center 78%; }
.focus-high { object-position: center 12%; }
.news-lead .card img, .news-lead .card .ph { aspect-ratio: 21 / 9; }

.card time {
  display: block;
  font-family: var(--font-mono);
  font-weight: 400;
  font-size: var(--fs-caption);
  color: var(--gray);
  text-transform: uppercase;
  margin: 20px 0 10px;
}
.card .head {
  display: block;
  font-weight: 500;
  font-size: var(--fs-body);
  line-height: 1.2;
  max-width: 34ch;
}
.card .stand {
  display: block;
  font-weight: 300;
  font-size: var(--fs-caption);
  color: var(--gray);
  margin: 10px 0 0;
  max-width: 54ch;
}

/* ---- plate: the base that carries text over media ----
   Same glass as every other floating surface. One value used twice:
   the plate sits one narrow margin in from the media edge, and its text
   sits one narrow margin in from the plate edge. Works over any image
   because the blur normalises whatever is behind it. */

.media { position: relative; }  /* the plate positions against the media box */
.plate {
  border-radius: var(--r);
  position: absolute;
  left: var(--m-narrow);
  bottom: var(--m-narrow);
  width: fit-content;
  max-width: calc(100% - var(--m-narrow) * 2);   /* containment, not a measure */
  padding: var(--m-narrow);
  -webkit-backdrop-filter: var(--glass-blur);
  backdrop-filter: var(--glass-blur);
}
/* Two faces, identical geometry. The glass matches the media it sits on, and the
   text opposes the glass — glass is translucent, so it pulls the backdrop toward its
   own tint rather than replacing it. Inverting lands the composite in mid-grey where
   neither text holds. Measured crossover: media luma 90 of 255.
     media luma <  90  ->  .plate--dark  (white text)
     media luma >= 90  ->  .plate--light (black text; never below 6.9:1, so the safe default) */
.plate, .plate--light { background: var(--plate-light); color: var(--ink-on-light); }
.plate--dark { background: var(--plate-dark); color: var(--ink-on-dark); }
.plate > * { margin: 0; }
.plate > * + * { margin-top: 0.6em; }
.plate--tl { top: var(--m-narrow); bottom: auto; }
.plate--tr { top: var(--m-narrow); bottom: auto; left: auto; right: var(--m-narrow); }
.plate--br { left: auto; right: var(--m-narrow); }

/* alignment grid — five lines across any media: both edges, the centre,
   and the midpoint on each side. A plate may end or begin on any line. */
.plate--to-2, .plate--to-3, .plate--to-4,
.plate--from-2, .plate--from-3, .plate--from-4 { width: auto; max-width: none; }
/* A plate keeps one narrow margin from every line it meets, exactly as it keeps
   one from the media edge — the same value, applied everywhere, so a plate that
   stops at the centre and one that starts there leave 60px between them.
   from-* is declared first so a from/to pair spans line to line; on its own it
   runs to the far frame edge, mirroring to-* which starts at the near one. */
.plate--from-2 { left: calc(25% + var(--m-narrow)); right: var(--m-narrow); }
.plate--from-3 { left: calc(50% + var(--m-narrow)); right: var(--m-narrow); }
.plate--from-4 { left: calc(75% + var(--m-narrow)); right: var(--m-narrow); }
.plate--to-2 { right: calc(75% + var(--m-narrow)); }   /* stops short of line 2 (25%) */
.plate--to-3 { right: calc(50% + var(--m-narrow)); }   /* stops short of line 3 (centre) */
.plate--to-4 { right: calc(25% + var(--m-narrow)); }   /* stops short of line 4 (75%) */

/* ---- imagery (two tiers, proposal 2026-08-15) ----
   Full bleed is reserved for the hero background only.
   All other images sit on the text margin system:
   media--n = narrow frame (worldview, same frame as key titles)
   media--w = wide frame (figures, same frame as body text) */

/* auto width, never width:100% — with margin-inline that would overflow the frame */
.media { display: block; margin: 0 0 var(--m-v); }
.media img { display: block; width: 100%; height: auto; }
.media--n { margin-inline: var(--m-narrow); }
.media--w { margin-inline: var(--m-wide); }
.media .ph {
  display: flex; align-items: center; justify-content: center;
  background: color-mix(in srgb, var(--gray) 18%, var(--bg)); color: var(--gray);
  font-size: var(--fs-caption); font-weight: 500;
}
/* the placeholder and the real image share one set of ratios, so dropping an
   image in never changes the layout around it */
.media .ph, .media img { aspect-ratio: 21 / 9; object-fit: cover; border-radius: var(--r); }
.media--w .ph, .media--w img { aspect-ratio: 16 / 9; }
/* scoped to beat `.media img`, which is one specificity point heavier */
.media .ph--flat, .media img.ph--flat { aspect-ratio: 3 / 1; }   /* flat band */
figcaption { font-size: var(--fs-caption); font-weight: 300; letter-spacing: normal; padding: 10px var(--m-wide) 0; }


/* ---- motion (spec §3.1) ----
   Movement is allowed when it explains position or continuity, and at no
   other time. Crossing between documents, scrolling, revealing what a page
   sits on: all of these answer "where did that go". Elements arriving as
   they scroll into view answer nothing, and stay forbidden. */
@view-transition { navigation: auto; }

::view-transition-old(root), ::view-transition-new(root) {
  animation-duration: var(--t-page);
  animation-timing-function: var(--ease);
}

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }
}

/* ---- footer (stacked columns + signature in the narrow frame) ----
   Links stand in vertical columns; the ZERO wordmark closes the page
   inside the narrow frame (full bleed stays reserved for the hero). */

.footer { padding: 0; }
.footer .foot-body {
  display: flex;
  flex-wrap: wrap;
  gap: clamp(48px, 8vw, 140px);
  align-items: flex-start;
  padding: calc(var(--m-v) * 1.5) var(--m-wide) var(--m-v);
}
.footer .foot-col { display: flex; flex-direction: column; gap: 12px; }
.footer .foot-col .colhead {
  font-size: var(--fs-caption); font-weight: 300; letter-spacing: normal;
  color: var(--gray); margin: 0 0 4px;
}
.footer a { font-size: var(--fs-caption); font-weight: 500; text-decoration: none; }
.footer .mode {
  border: none; background: none; padding: 0; margin: 0; cursor: pointer;
  font-family: inherit; font-size: var(--fs-caption); font-weight: 500;
  letter-spacing: normal; text-align: left;
}
.footer .stamp { font-size: var(--fs-caption); letter-spacing: normal; margin: 0 0 0 auto; align-self: flex-end; }
.footer .sig-wrap { padding: 0 var(--m-narrow) var(--m-narrow); }
.footer .sig { display: block; width: 100%; height: auto; aspect-ratio: 79.5 / 14.02;   /* the ink, not the box: the artwork stops short of the viewBox */ color: var(--fg); margin: 0; }

/* ---- cookie notice ---- */

.cookie {
  position: fixed;
  left: 0; right: 0; bottom: 0;
  z-index: 20;
  display: none;
  padding: 16px var(--m-narrow);
  background: var(--chrome);
  -webkit-backdrop-filter: var(--glass-blur);
  backdrop-filter: var(--glass-blur);
  align-items: center;
  justify-content: space-between;
  gap: 20px;
  flex-wrap: wrap;
}
.cookie p { margin: 0; font-size: var(--fs-caption); font-weight: 300; letter-spacing: normal; }
.cookie .btn { padding: 9px 18px; font-size: 13px; }

/* ---- layout patterns (provisional set, 2026-08-14) ----
   Reusable section compositions for web and future apps. */

/* P0 duo: two texts side by side; the gutter is centered, 50px each side
   of the center line (100px total = the wide margin value at 1920). */
.duo {
  display: grid;
  grid-template-columns: 1fr 1fr;
  column-gap: var(--m-wide);
  padding: var(--m-v) var(--m-wide);
}
@media (max-width: 800px) { .duo { grid-template-columns: 1fr; row-gap: var(--m-v); } }

/* P1 split: text one side, media the other (OpenAI-style editorial row) */
.split {
  display: grid;
  grid-template-columns: minmax(0, 5fr) minmax(0, 7fr);
  gap: clamp(32px, 5vw, 96px);
  align-items: center;
  padding: var(--m-v) var(--m-wide);
}
.split--rev { grid-template-columns: minmax(0, 7fr) minmax(0, 5fr); }
.split .media, .split .ph { width: 100%; }
.split .ph { aspect-ratio: 1 / 1; }
@media (max-width: 800px) { .split, .split--rev { grid-template-columns: 1fr; } }


/* P3 stat row: aligned figures (uses tabular-nums) */
.stats { display: flex; flex-wrap: wrap; gap: clamp(32px, 5vw, 96px); padding: var(--m-v) var(--m-wide); }
.stats .stat b { display: block; font-weight: 500; font-size: clamp(34px, 3vw, 60px); font-variant-numeric: tabular-nums; }
.stats .stat span { font-size: var(--fs-caption); font-weight: 300; letter-spacing: normal; }

/* P4 card grid: uniform tiles (news, shop, team) */
.grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
  gap: 48px;
  padding: 0 var(--m-wide) var(--m-v);
}

/* external links (leaving the site) are blue; internal navigation stays ink */
a[href^="http://"], a[href^="https://"] { color: var(--link); }

/* ---- the bar on a phone ----
   Seven items and a globe do not fit in 375px — measured at 438px, so the
   mark fell off one end and the language control off the other. Below the
   breakpoint the bar holds the mark and one control; everything else moves
   into a full-height list. Rows with children stay closed until tapped, and
   they open with the same plus the desktop dropdown uses: one device, both
   platforms, nothing new to learn. */
.nav-toggle {
  display: none;
  position: absolute; right: var(--m-narrow); top: 0;
  width: 22px; height: 48px; padding: 0;
  border: none; background: none; cursor: pointer;
  color: var(--fg);
}
.nav-toggle i, .nav-toggle i::before, .nav-toggle i::after {
  position: absolute; left: 0; width: 22px; height: 1px;
  background: currentColor; content: "";
  transition: transform var(--t-page) var(--ease-out), opacity var(--t-fast) linear;
}
.nav-toggle i { top: 50%; }
.nav-toggle i::before { top: -6px; }
.nav-toggle i::after { top: 6px; }
.nav.is-menu-open .nav-toggle i { transform: rotate(45deg); }
.nav.is-menu-open .nav-toggle i::before { transform: translateY(6px) rotate(-90deg); }
.nav.is-menu-open .nav-toggle i::after { opacity: 0; }

@media (max-width: 760px) {
  .nav-toggle { display: block; }

  .mark-link {
    position: absolute; left: var(--m-narrow); top: 0;
    height: 48px;
  }

  .nav ul {
    /* the bar carries a backdrop-filter, which makes it the containing block
       for its fixed children — so bottom:0 would resolve inside 48px. The
       height is stated instead. */
    position: fixed; top: 48px; left: 0; right: 0;
    display: flex; flex-direction: column; align-items: stretch;
    height: calc(100dvh - 48px);
    gap: 0; padding: 12px var(--m-wide) 48px;
    background: var(--bg);
    overflow-y: auto;
    visibility: hidden;
    clip-path: inset(0 0 100% 0);
    transition: clip-path var(--t-page) var(--ease-out),
                visibility 0s linear var(--t-page);
  }
  .nav.is-menu-open ul {
    visibility: visible;
    clip-path: inset(0 0 0 0);
    transition: clip-path var(--t-page) var(--ease-out), visibility 0s;
  }

  .nav li { display: block; height: auto; }
  .nav > ul > li > a, .nav > ul > li > .drop, .nav > ul > li > .lang {
    height: auto; padding: 16px 0;
    font-size: clamp(24px, 6.4vw, 32px); font-weight: 500;
  }
  .nav > ul > li.has-panel > .drop { display: flex; justify-content: space-between; align-items: center; }

  /* the same plus the desktop uses, at the end of a row that has children */
  .nav > ul > li.has-panel > .drop::after,
  .nav > ul > li.has-panel > .lang::after {
    content: ""; flex: none; width: 14px; height: 14px; margin-left: 16px;
    background:
      linear-gradient(currentColor, currentColor) center/14px 1px no-repeat,
      linear-gradient(currentColor, currentColor) center/1px 14px no-repeat;
    transition: transform var(--t-page) var(--ease-out);
  }
  .nav > ul > li.has-panel.is-open > .drop::after,
  .nav > ul > li.has-panel.is-open > .lang::after { transform: rotate(45deg); }
  .nav > ul > li.has-panel > .lang { display: flex; justify-content: space-between; align-items: center; }
  .nav .lang svg { width: 22px; height: 22px; }
  /* globe and word travel together; the plus is pushed to the far end */
  .lang-label { display: inline; margin-left: 14px; margin-right: auto; }

  /* while the list is open the bar is part of it: solid, unblurred, so the
     top 48px stops showing the page through a sheet that is otherwise opaque */
  .nav.is-menu-open {
    background: var(--bg);
    -webkit-backdrop-filter: none; backdrop-filter: none;
  }

  /* a 3:1 band is a wide-screen shape — on a phone it is a 114px strip with
     no room for the plate that sits in it */
  .media .ph--flat, .media img.ph--flat { aspect-ratio: 4 / 3; }

  /* a panel is no longer a sheet: it is the part of the list under its name */
  .panel {
    position: static;
    display: grid !important;
    grid-template-columns: 1fr;
    visibility: visible;
    background: none;
    -webkit-backdrop-filter: none; backdrop-filter: none;
    padding: 0 0 0 18px;
    row-gap: 0;
    height: 0; overflow: hidden;
    clip-path: none;
    transition: height var(--t-page) var(--ease-out);
  }
  .nav > ul > li.has-panel.is-open > .panel { height: auto; padding-bottom: 10px; }
  .panel .col { gap: 0; }
  .panel .col.node-panel { display: flex; opacity: 1; margin: 0; padding: 0 0 0 18px; }
  .panel a.big { font-size: clamp(18px, 4.6vw, 22px); font-weight: 300; padding: 11px 0; }
  .panel .colhead { display: none; }
  .panel .col--lead { display: none; }
  .node-plus { display: none; }
  .panel--lang .col { padding: 6px 0; }


  /* the alignment lines are a wide-screen device: a quarter of a phone is
     not a column, so every plate takes the whole frame */
  .plate,
  .plate--to-2, .plate--to-3, .plate--to-4,
  .plate--from-2, .plate--from-3, .plate--from-4 {
    left: var(--m-narrow); right: var(--m-narrow);
    width: auto; max-width: none;
  }

  .sheet, .veil { display: none; }
}
