/* ============================================================
   hingepoint.ai — styles.css
   Binding basis: resources/hingepoint-brand-spec.md
   Direction: Editorial Standard. Serious, precise, management-grade.

   Zero external references. No @import, no CDN, no third-party URL.
   Every url() resolves under /static/. No JavaScript is implied or
   required by anything in this file.

   Two rules from the brand spec that this file exists to enforce:
     - The wordmark's period is ALWAYS Signal red. Never restyled.
     - Red is signal, not decoration. Roughly one red thing per view;
       the dot counts. Everything else lives in Slate, Ink and Mist.
   ============================================================ */

/* ---- Self-hosted fonts --------------------------------------
   No Google Fonts link, ever. Embedding it transmits the visitor's IP
   to Google without a legal basis — LG München I, 3 O 17493/20.
   Both files are variable, latin subset, OFL-licensed. Declared with
   a weight RANGE so the browser never synthesises a weight.
   ------------------------------------------------------------ */
@font-face {
  font-family: 'JetBrains Mono';
  src: url('/static/fonts/jetbrains-mono-var.woff2') format('woff2');
  font-weight: 400 800;      /* actual axis range of the file */
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: 'IBM Plex Sans';
  src: url('/static/fonts/ibm-plex-sans-var.woff2') format('woff2');
  font-weight: 100 700;      /* actual axis range of the file */
  font-style: normal;
  font-display: swap;
}

/* ============================================================
   TOKENS — one edit reaches everywhere
   Contrast ratios below are measured against --paper unless noted.
   ============================================================ */
:root {
  /* Brand colours — do not repurpose Signal as decoration */
  --paper:   #FBFBFC;
  --slate:   #1E2A34;
  --slate-2: #18232C;   /* footer, one step deeper than Slate */
  --ink:     #383838;
  --mist:    #EAEDF0;
  --band:    #F4F6F8;   /* faintest fill, for pills and quiet panels */
  --signal:  #FF2323;

  /* Signal, darkened on the same hue for SMALL RED TEXT only.
     #FF2323 on Paper is 3.82:1 — fine for a 19px bold button label
     (AA large text, 3:1) and fine for dots, fills and borders, which
     carry no text. It fails 4.5:1 for a 13px mono kicker. This tone is
     the same hue derived down in lightness — oklch(0.51 0.20 27.6) —
     and measures 5.16:1. Fills, dots and borders keep --signal. */
  --signal-text: #D6141F;
  --signal-tint: #FFDEDE;   /* selection background only */

  /* Text */
  --body:       #4C555F;   /* 7.40:1 */
  --muted:      #666E76;   /* 5.01:1 on Paper, 4.78:1 on Band */
  --muted-dark: #565E66;   /* 5.60:1 — muted text sitting on Mist */

  /* Lines and edges (non-text; no contrast minimum applies) */
  --hair:      #EAEDF0;
  --border:    #E1E5EA;
  --border-2:  #C9D0D8;   /* brand's secondary-button border */

  /* On dark surfaces */
  --on-dark:      #F3F5F7;  /* 15.7:1 on --slate-2 */
  --on-dark-soft: #B7C0C9;  /*  8.71:1 */
  --on-dark-mute: #8B97A3;  /*  5.39:1 — the floor for text on dark */

  /* Type */
  --mono: 'JetBrains Mono', ui-monospace, Consolas, monospace;
  --sans: 'IBM Plex Sans', system-ui, sans-serif;

  /* Layout */
  --maxw:    1200px;   /* outer container */
  --measure:  760px;   /* reading measure for running text */
  --pad:       40px;
  --radius:     6px;   /* brand spec: buttons, cards, panels */
  --radius-lg: 10px;   /* the large flow panel only */

  /* Nav height, consumed by scroll-padding-top. See §NAV — this is
     load-bearing for the provider anchors, not cosmetic. */
  --nav-h: 76px;
}

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

html {
  scroll-behavior: smooth;
  /* The nav is sticky and translucent, so anything scrolled to y=0 lands
     UNDERNEATH it. That is cosmetic on a heading and serious on a provider
     card: spec-01 §4.9 gives each card a stable id="<slug>" anchor precisely
     so a company can follow a link and verify that provider is certified.
     Without this the link lands on the card behind the nav and the card the
     visitor actually reads is the NEXT one — a false credential display. */
  scroll-padding-top: calc(var(--nav-h) + 28px);
  -webkit-text-size-adjust: 100%;
}

body {
  margin: 0;
  background: var(--paper);
  font-family: var(--sans);
  font-size: 18px;
  line-height: 1.65;
  color: var(--body);
  -webkit-font-smoothing: antialiased;
  /* No element may push the page sideways. Wide content scrolls inside
     its own box (tables, pre, the flow strip) — never the document. */
  overflow-wrap: break-word;
}

img { max-width: 100%; display: block; }
strong { font-weight: 600; color: var(--slate); }
::selection { background: var(--signal-tint); color: var(--slate); }

/* ---- Links -------------------------------------------------
   Decided deliberately, per brief §Brand. The previous draft set every
   hyperlink in Signal. On a prose page that is dozens of red words and
   defeats "roughly one red thing per view" outright — and #FF2323 on
   Paper is 3.82:1, which fails AA for body-sized text anyway. Body links
   are Slate with an underline: unambiguous, 12.6:1, and it costs nothing
   from the red budget. Red is spent on the dot and on calls to action.
   ------------------------------------------------------------ */
a {
  color: var(--slate);
  text-decoration: underline;
  text-decoration-color: var(--border-2);
  text-decoration-thickness: 1px;
  text-underline-offset: 3px;
  transition: text-decoration-color 120ms ease, color 120ms ease;
}
a:hover { text-decoration-color: var(--slate); }

/* Focus must stay visible — there is no JS to compensate. */
a:focus-visible,
.btn:focus-visible,
:focus-visible {
  outline: 2px solid var(--signal);
  outline-offset: 3px;
  border-radius: 2px;
}

/* ---- The wordmark. The period is the brand. ---- */
.wordmark {
  font-family: var(--mono);
  font-weight: 500;
  color: var(--ink);
  letter-spacing: -0.5px;
  line-height: 1.2;
  text-decoration: none;
  white-space: nowrap;
}
.wordmark .dot { color: var(--signal); }
.wordmark:hover { color: var(--ink); text-decoration: none; }

/* ============================================================
   NAV — sticky, translucent, wordmark left, links right.
   No hamburger: a JS toggle is forbidden outright, and the CSS-only
   checkbox workaround fails the build's no-<input> assertion. Six links
   simply wrap, which needs no interaction at all.

   Below 1000px the nav is STATIC rather than sticky. Once the links wrap
   onto their own row the bar is ~140px tall, and on a 667px phone
   viewport a permanently pinned bar of that height eats a fifth of the
   screen. Static there also makes --nav-h honest: 0, so scroll-padding
   stops reserving space that is no longer occupied and anchors land
   exactly on the card. Nothing is hidden at any width.
   ============================================================ */
.nav {
  position: sticky;
  top: 0;
  z-index: 20;
  background: rgba(251, 251, 252, 0.92);
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
  border-bottom: 1px solid var(--hair);
}
.nav-inner {
  max-width: var(--maxw);
  margin: 0 auto;
  padding: 14px var(--pad);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px 24px;
  flex-wrap: wrap;
}
.nav .wordmark { font-size: 28px; }   /* brand anchor — kept prominent */

.nav-links {
  display: flex;
  align-items: center;
  gap: 4px 10px;
  flex-wrap: wrap;
}
.nav-links a {
  font-size: 16px;
  font-weight: 500;
  color: var(--body);
  text-decoration: none;
  /* 44px minimum touch target, and enough horizontal padding that two
     adjacent links cannot be caught by one thumb. */
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  padding: 0 10px;
  border-radius: 4px;
}
.nav-links a:hover {
  color: var(--slate);
  background: var(--band);
  text-decoration: none;
}
/* Current page marked in Slate, not Signal: the wordmark's period is
   already the red thing in this bar, and a second one halves it. */
.nav-links a[aria-current="page"] {
  color: var(--slate);
  font-weight: 600;
  box-shadow: inset 0 -2px 0 var(--slate);
}

/* ============================================================
   PAGE SHELL
   ============================================================ */
main {
  max-width: var(--maxw);
  margin: 0 auto;
  /* Longhand deliberately. A `padding` shorthand mixing clamp() and var()
     is one invalid token away from the WHOLE declaration being dropped,
     which reads as "the spacing just isn't there". Longhand fails per-side. */
  padding-top: clamp(48px, 7vw, 96px);
  padding-right: var(--pad);
  padding-bottom: 104px;
  padding-left: var(--pad);
}
main > * + * { margin-top: clamp(72px, 9vw, 120px); }

/* Running text sits on a reading measure; grids, panels and the wordmark
   headline are allowed the full container. Capping .prose itself would
   squeeze the card grids to 760px, so the cap is applied per child. */
.prose > * { max-width: var(--measure); }
.prose > h1,
.prose > .panel,
.prose > .card-grid,
.prose > .credentials,
.prose > .point,
.prose > pre { max-width: none; }
/* .authors is deliberately NOT in that list. It was, and the exemption is two
   classes deep — `.prose > .authors` — which silently outranked the one-class
   `.authors { max-width }` below. The grid then ran the full 1120px column, and
   with only two items the 1fr tracks stretched to ~548px each: a 224px portrait
   marooned in a 548px card. */
.prose > table { max-width: 940px; }
.prose > *:first-child { margin-top: 0; }
.prose > *:last-child { margin-bottom: 0; }

/* ============================================================
   TYPOGRAPHY
   ============================================================ */
h1, h2, h3, h4 {
  font-family: var(--sans);
  color: var(--slate);
  letter-spacing: -0.5px;
  line-height: 1.15;
  margin: 0;
  text-wrap: balance;
}
h1 { font-size: clamp(36px, 5vw, 56px); font-weight: 600; margin-bottom: 28px; }
h2 { font-size: clamp(26px, 3.2vw, 32px); font-weight: 600; margin: 56px 0 18px; }
h3 { font-size: clamp(20px, 2.2vw, 23px); font-weight: 600; margin: 40px 0 12px; }
p  { margin: 0 0 20px; text-wrap: pretty; }

/* ---- Wordmark title -----------------------------------------
   Opt-in per page via front matter `title_style: wordmark`.
   The landing headline IS the wordmark, set large: JetBrains Mono
   Medium, Ink, red period. The brand spec fixes its form — never
   another typeface, never a recoloured or removed dot.

   The upper bound is 104px. The lower bound is 40px, not 52px: eleven
   monospace characters at 52px need ~343px and overflow a 320px screen.
   40px fits with the page padding intact and stays far above the 20px
   brand minimum. 11vw carries it between the two.
   ------------------------------------------------------------ */
h1.title--wordmark {
  font-family: var(--mono);
  font-weight: 500;
  font-size: clamp(40px, 11vw, 104px);
  line-height: 1;
  letter-spacing: -0.03em;
  color: var(--ink);
  /* Brand spec: clear space around the wordmark of at least the height
     of the "H". */
  margin-bottom: clamp(28px, 4vw, 44px);
}
h1.title--wordmark .dot { color: var(--signal); }

/* The lead after a wordmark title — 104px headline into 18px body reads
   abrupt, so the first paragraph steps down instead of dropping. */
h1.title--wordmark + p {
  font-size: clamp(20px, 2.4vw, 27px);
  line-height: 1.45;
  color: var(--body);
  max-width: 30em;
  margin-bottom: 32px;
}

/* Kicker — mono label preceded by the red section dot */
.kicker {
  display: flex;
  align-items: center;
  gap: 12px;
  font-family: var(--mono);
  font-size: 14px;
  font-weight: 500;
  letter-spacing: 2px;
  text-transform: uppercase;
  color: var(--muted);
  margin-bottom: 28px;
}
.kicker::before {
  content: "";
  flex: none;
  width: 9px;
  height: 9px;
  border-radius: 50%;
  background: var(--signal);
}

/* An intro paragraph under a section heading */
.lede-note {
  font-size: 21px;
  line-height: 1.5;
  color: var(--body);
  max-width: 34em;
}

/* ---- Lists: the red dot replaces the bullet ----------------
   The dot must sit on the VERTICAL MIDDLE OF THE FIRST TEXT LINE, not
   hang on the baseline — the brand spec is explicit, and explicitly
   forbids the align-items:baseline + translateY hack. The marker box
   here is one line-height tall and centres its own content, so it stays
   correct when an item wraps to five lines.
   ------------------------------------------------------------ */
.prose ul { list-style: none; padding: 0; margin: 0 0 20px; }
/* The marker is positioned, NOT a flex item.
   `display: flex` on the li was wrong in a way that only showed on some items:
   in a flex container every inline child becomes its own flex item, so
   <em>independent</em> and <code>training@</code> were pulled out of the text
   flow onto their own lines. Absolute positioning keeps the content in normal
   inline flow while still placing the dot on the vertical middle of the FIRST
   line — which is what the brand spec requires, and what the flex version was
   there to achieve. */
.prose ul li {
  position: relative;
  padding-left: 20px;
  margin-bottom: 10px;
}
.prose ul li::before {
  content: "";
  position: absolute;
  left: 0;
  top: calc((1.65em - 7px) / 2);
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--signal);
}

/* ---- Definition lists --------------------------------------
   For "term — explanation" sequences. Written as a bullet list with an
   em-dash, the terms were ragged (MAY / KNOW / HOW are different widths) and a
   long term such as "Validation — was the right solution built?" wrapped while
   its neighbours did not. A definition list gives the term its own line and
   aligns every explanation on one left edge.
   ------------------------------------------------------------ */
.prose dl { margin: 0 0 24px; }
.prose dt {
  font-family: var(--mono);
  font-size: 15px;
  font-weight: 500;
  letter-spacing: 0.5px;
  color: var(--slate);
  margin-top: 22px;
  padding-left: 20px;
  position: relative;
}
.prose dt::before {
  content: "";
  position: absolute;
  left: 0;
  top: calc((1.5em - 7px) / 2);
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--signal);
}
.prose dd {
  margin: 6px 0 0 20px;
  color: var(--body);
}
.prose dd > p { margin: 0 0 10px; }
.prose dd > p:last-child { margin-bottom: 0; }

.prose ol {
  padding-left: 0;
  margin: 0 0 20px;
  list-style: none;
  counter-reset: hp-ol;
}
.prose ol > li {
  counter-increment: hp-ol;
  position: relative;
  padding-left: 42px;
  margin-bottom: 10px;
}
.prose ol > li::before {
  content: counter(hp-ol) ".";
  position: absolute;
  left: 0;
  top: 0;
  width: 30px;
  font-family: var(--mono);
  font-size: 15px;
  font-weight: 500;
  color: var(--muted);
  line-height: 1.65;
  font-variant-numeric: tabular-nums;
}
.prose li > ul, .prose li > ol { margin-top: 10px; margin-bottom: 0; }

/* ---- Quote, code, rule ------------------------------------- */
blockquote {
  margin: 36px 0;
  padding: 2px 0 2px 26px;
  border-left: 3px solid var(--signal);
  color: var(--slate);
  font-size: 21px;
  line-height: 1.5;
}
blockquote p:last-child { margin-bottom: 0; }

code, pre { font-family: var(--mono); font-size: 0.9em; }
code {
  background: var(--band);
  border: 1px solid var(--hair);
  color: var(--ink);
  padding: 1px 6px;
  border-radius: 4px;
}
pre {
  background: var(--slate);
  color: var(--on-dark);
  padding: 22px 24px;
  border-radius: var(--radius);
  overflow-x: auto;         /* scrolls inside itself, never the page */
  line-height: 1.6;
  font-size: 15px;
  margin: 0 0 24px;
}
pre code { background: none; border: 0; padding: 0; color: inherit; }

hr { border: 0; border-top: 1px solid var(--hair); margin: 56px 0; }

/* ---- Editorial tables --------------------------------------
   `display: block` turns the table itself into the scroll container, so
   a wide table scrolls inside its own box without needing a wrapper div
   the Jinja templates do not emit.
   ------------------------------------------------------------ */
.prose table {
  width: 100%;
  border-collapse: collapse;
  margin: 0 0 24px;
  font-size: 16px;
  display: block;
  overflow-x: auto;
}
.prose th, .prose td {
  text-align: left;
  padding: 14px 18px;
  border-bottom: 1px solid var(--hair);
  vertical-align: top;
}
.prose th {
  font-family: var(--mono);
  font-size: 13px;
  font-weight: 500;
  letter-spacing: 1px;
  text-transform: uppercase;
  color: var(--muted);
  border-bottom: 1px solid var(--border-2);
  white-space: nowrap;
}
.prose td { color: var(--body); }
.prose td:first-child { color: var(--slate); font-weight: 600; }
.prose tbody tr:last-child td { border-bottom: 0; }

/* ============================================================
   BUTTONS
   ============================================================ */
.actions {
  display: flex;
  flex-wrap: wrap;
  gap: 14px;
  margin: 0 0 8px;
}
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: 52px;              /* comfortably over the 44px minimum */
  padding: 0 26px;
  border-radius: var(--radius);
  font-family: var(--sans);
  /* 19px at weight 700 is WCAG "large text" (≥18.66px bold), where the
     threshold is 3:1. White on Signal measures 3.82:1 and passes there;
     at body size and weight it would not. The brand spec asks for
     SemiBold — 700 is the smallest weight that makes the label legal
     on this fill, so it is set deliberately, not by preference. */
  font-size: 19px;
  font-weight: 700;
  letter-spacing: -0.2px;
  text-decoration: none;
  white-space: nowrap;   /* labels are short; a wrapped CTA reads broken */
  transition: background-color 140ms ease, border-color 140ms ease, color 140ms ease;
}
.btn--primary {
  background: var(--signal);
  color: #fff;
  border: 1.5px solid var(--signal);
}
.btn--primary:hover { background: #E81212; border-color: #E81212; color: #fff; }
.btn--secondary {
  background: transparent;
  color: var(--slate);
  border: 1.5px solid var(--border-2);
}
.btn--secondary:hover { border-color: var(--slate); color: var(--slate); }

/* ============================================================
   PANEL + FLOW STRIP
   White surface on Paper. Worth being honest about what carries the
   effect: Paper #FBFBFC against white is a 1.4% luminance difference and
   invisible on its own. The separation comes from the 1px border.
   ============================================================ */
.panel {
  background: #fff;
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  padding: 32px 36px 36px;
  margin: 40px 0;
}
.panel-label {
  font-family: var(--mono);
  font-size: 13px;
  font-weight: 500;
  letter-spacing: 2px;
  text-transform: uppercase;
  color: var(--muted);
  margin-bottom: 28px;
}

/* Brand spec, Step-Connector: dots on a line, the critical node in
   Signal. This IS how a Hingepoint is depicted. */
.flow-track { display: flex; align-items: center; }
.flow-node {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 12px;
  flex: none;
}
.flow-node .bead {
  width: 13px;
  height: 13px;
  border-radius: 50%;
  background: var(--slate);
}
.flow-node .cap {
  font-family: var(--mono);
  font-size: 13px;
  color: var(--body);
  white-space: nowrap;
}
.flow-node--hp .bead {
  width: 19px;
  height: 19px;
  background: var(--signal);
  box-shadow: 0 0 0 5px rgba(255, 35, 35, 0.14);
}
/* The caption stays Slate. #FF2323 on white is 3.82:1 and this label is
   13px — the bead is what signals; colouring the word too would fail AA
   and spend the same signal twice. */
.flow-node--hp .cap { color: var(--slate); font-weight: 500; }
.flow-line { height: 2px; flex: 1; background: var(--border); min-width: 10px; }

/* ============================================================
   NOTE CARD — 3px left Signal border, mono kicker in red
   ============================================================ */
.note {
  background: #fff;
  border: 1px solid var(--border);
  border-left: 3px solid var(--signal);
  border-radius: var(--radius);
  padding: 22px 26px;
  margin: 36px 0;
}
.note-label {
  font-family: var(--mono);
  font-size: 13px;
  font-weight: 500;
  letter-spacing: 2px;
  text-transform: uppercase;
  color: var(--signal-text);      /* 5.16:1 — see --signal-text */
  margin-bottom: 8px;
}
.note p:last-child { margin-bottom: 0; }

/* ============================================================
   "AVAILABLE AT LAUNCH" — must read plainly as not-a-link
   Several destinations do not exist yet and must never render as dead
   links. Not underlined, not Slate, not interactive: a stated status.
   ============================================================ */
.pending {
  display: inline-block;
  font-family: var(--mono);
  font-size: 13px;
  font-weight: 500;
  letter-spacing: 1px;
  text-transform: uppercase;
  color: var(--muted-dark);
  background: var(--mist);
  border-radius: 4px;
  padding: 3px 9px;
  cursor: default;
  white-space: nowrap;
}

/* ============================================================
   CARD GRID — 1 / 2 / 3 columns. Explicit breakpoints rather than
   auto-fill: auto-fill stretches a lone card across the full row, and
   the single-provider state is the launch reality, not an edge case.
   ============================================================ */
.card-grid {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: 24px;
  margin: 8px 0 0;
}
@media (min-width: 700px)  { .card-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (min-width: 1000px) { .card-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); } }

/* ---- The single-provider state -----------------------------
   One card in a three-column grid reads as a carousel missing its
   siblings. Held to one column at a deliberate width, with more air
   inside, it reads as the launch partner it is.
   ------------------------------------------------------------ */
.card-grid--single { grid-template-columns: minmax(0, 560px); }
@media (min-width: 700px)  { .card-grid--single { grid-template-columns: minmax(0, 560px); } }
@media (min-width: 1000px) { .card-grid--single { grid-template-columns: minmax(0, 560px); } }
.card-grid--single .provider { padding: 32px; }
.card-grid--single .provider-name { font-size: 26px; }

/* ============================================================
   PROVIDER DIRECTORY CARD
   The directory IS the register. Each card carries id="<slug>" and a
   link to it is how a company verifies its trainer is licensed.
   ============================================================ */
.provider {
  display: flex;
  flex-direction: column;
  background: #fff;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 26px;
}
.provider-logo {
  display: flex;
  align-items: center;
  height: 54px;
  margin-bottom: 20px;
}
.provider-logo img {
  max-height: 54px;
  width: auto;
  object-fit: contain;
}
.provider-name {
  font-size: 23px;
  font-weight: 600;
  color: var(--slate);
  margin: 0 0 10px;
  letter-spacing: -0.4px;
}
.provider-desc {
  font-size: 16px;
  line-height: 1.6;
  color: var(--body);
  margin: 0 0 20px;
}
/* ---- Diagram -----------------------------------------------
   The published flow and evolution diagrams. They arrive with their
   OWN white card — a rounded rect with a light border baked into the
   SVG — so they must not be nested inside .panel or the card doubles.
   Placed directly on Paper.

   The diagrams carry their own palette (blues, greens, amber, and a
   brick red that is NOT --signal). That is deliberate: the palette was
   widened for legibility, per the architect. Consequence to watch: on a
   page with a red CTA there are then two different reds in view, which
   is the one thing "roughly one red thing per view" is meant to prevent.
   Give a diagram room and do not put a Signal CTA beside it.
   ------------------------------------------------------------ */
.diagram { margin: 44px 0; }
.diagram img {
  width: 100%;
  height: auto;
  display: block;
}
.diagram figcaption {
  font-family: var(--mono);
  font-size: 13px;
  font-weight: 500;
  letter-spacing: 1px;
  color: var(--muted);
  margin-top: 16px;
}

/* ---- Inlined diagram ---------------------------------------
   An <img> is opaque: the SVG asks for Inter, which we do not host, so
   it falls back to whatever the OS provides and the diagram is set in a
   different typeface from the page around it.

   Inlined, the SVG is part of the document, so our self-hosted fonts
   apply and CSS can correct the two things that clash. CSS beats an
   element's presentation attributes, which is what makes this work
   without touching the source file — the published artifact at
   /documents/ stays exactly as the authors exported it.

   ONLY the red is retinted. The blues, greens and amber carry meaning
   through the legend — Human leads / AI generates / Control point /
   Governance — and flattening them would break the diagram. #C0392B is
   different: it is the colour of the word "Hingepoint", so it means what
   --signal means. Two near-identical reds on one page read as a mistake;
   one red reads as the system.
   ------------------------------------------------------------ */
.diagram--inline svg {
  width: 100%;
  height: auto;
  display: block;
}
.diagram--inline svg text,
.diagram--inline svg tspan {
  font-family: var(--sans);
}
.diagram--inline svg [font-family*="JetBrains"] {
  font-family: var(--mono);
}
.diagram--inline svg [fill="#C0392B"]   { fill: var(--signal); }
.diagram--inline svg [stroke="#C0392B"] { stroke: var(--signal); }
.diagram--inline svg [fill="#FBEAE8"]   { fill: #FFF0F0; }  /* its red tint, matched to Signal */

/* ---- Provider meta: two labelled groups, never one merged row ----
   `languages` and `regions` answer two different questions — spec-01 §4.2
   keeps them apart because the visitor's question is "who can train my team,
   in my language, where I am". A single row cannot answer both halves, and
   the codes collide: language `de` and region `DE` both render as "DE", so
   galaniprojects showed DE twice.

   Merging the two lists and de-duplicating was considered and REJECTED.
   Jinja's `unique` filter is case-insensitive by default, so it drops the
   REGION "DE" and keeps the LANGUAGE "de" — the card silently stops saying
   the provider operates in Germany. Worse, a provider who trains in German
   but works only in AT and CH would render "DE AT CH" and read as operating
   in Germany. On a page that IS the trainer register (§4.9) that is a false
   statement, not a cosmetic glitch.

   The group key carries the meaning, so the pills stay uppercase and the
   design is unchanged. It also fixes screen readers, which previously heard
   "EN DE DE AT CH" as one meaningless string.
   ------------------------------------------------------------ */
.provider-meta {
  display: flex;
  flex-direction: column;
  gap: 9px;
  margin-bottom: 22px;
}
.meta-group {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px;
}
.meta-key {
  flex: none;
  font-family: var(--mono);
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 1px;
  text-transform: uppercase;
  color: var(--muted);
  /* Sits slightly apart from its pills so the grouping is obvious even
     when a group wraps onto two lines. */
  margin-right: 2px;
}
.label {
  font-family: var(--mono);
  font-size: 12px;
  font-weight: 500;
  letter-spacing: 1px;
  text-transform: uppercase;
  color: var(--muted);          /* 4.78:1 on Band */
  background: var(--band);
  border: 1px solid var(--hair);
  border-radius: 3px;
  padding: 3px 8px;
  white-space: nowrap;
}
.provider-links {
  display: flex;
  flex-wrap: wrap;
  gap: 8px 20px;
  margin-top: auto;             /* links align across a ragged row */
  padding-top: 18px;
  border-top: 1px solid var(--hair);
}
.provider-links a {
  display: inline-flex;
  align-items: center;
  min-height: 44px;             /* touch target */
  font-size: 16px;
  font-weight: 600;
  color: var(--slate);
}

/* The verification target. Following a link to #<slug> must make it
   unmistakable WHICH card was addressed — the whole credential check
   rests on the visitor reading the right one. Outline, not border, so
   nothing in the grid shifts. */
.provider:target {
  outline: 2px solid var(--signal);
  outline-offset: -1px;
}

/* ============================================================
   DOCUMENT / DOWNLOAD CARDS
   ============================================================ */
.doc {
  display: flex;
  flex-direction: column;
  background: #fff;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 26px;
}
.doc-kind {
  font-family: var(--mono);
  font-size: 12px;
  font-weight: 500;
  letter-spacing: 1.5px;
  text-transform: uppercase;
  color: var(--muted);
  margin-bottom: 14px;
}
.doc-title {
  font-size: 22px;
  font-weight: 600;
  color: var(--slate);
  margin: 0 0 10px;
  letter-spacing: -0.4px;
}
.doc-desc {
  font-size: 16px;
  line-height: 1.6;
  margin: 0 0 18px;
}
.doc-meta {
  font-family: var(--mono);
  font-size: 12px;
  letter-spacing: 1px;
  color: var(--muted);
  margin-bottom: 18px;
}
.doc-link {
  margin-top: auto;
  display: inline-flex;
  align-items: center;
  align-self: flex-start;
  min-height: 44px;
  font-size: 16px;
  font-weight: 600;
  color: var(--slate);
}

/* The primary artifact of the set. Inverted rather than red-accented —
   the page already spends its red on the section dot, and a Slate card
   distinguishes the Manifesto without competing with it. */
.doc--accent {
  background: var(--slate);
  border-color: var(--slate);
}
.doc--accent .doc-kind,
.doc--accent .doc-meta { color: var(--on-dark-mute); }
.doc--accent .doc-title { color: var(--on-dark); }
.doc--accent .doc-desc  { color: var(--on-dark-soft); }
.doc--accent .doc-link  { color: var(--on-dark); text-decoration-color: rgba(243, 245, 247, 0.45); }
.doc--accent .doc-link:hover { text-decoration-color: var(--on-dark); }

/* ============================================================
   CERTIFICATION — two credentials that must NOT blur together
   They are differentiated by who they are for (the loudest line in each
   card) and by surface, not by colour alone: one sits on white, one on
   Band. Order-independent — no nth-child, so the templates may emit them
   in either order.
   ============================================================ */
.credentials {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: 24px;
  margin-top: 8px;
}
@media (min-width: 760px) { .credentials { grid-template-columns: repeat(2, minmax(0, 1fr)); } }

.credential {
  background: #fff;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 30px;
  display: flex;
  flex-direction: column;
}
.credential + .credential { background: var(--band); }

.credential-for {
  font-family: var(--mono);
  font-size: 13px;
  font-weight: 500;
  letter-spacing: 2px;
  text-transform: uppercase;
  color: var(--muted);
  padding-bottom: 14px;
  margin-bottom: 18px;
  border-bottom: 1px solid var(--border);
}
.credential-name {
  font-size: 26px;
  font-weight: 600;
  color: var(--slate);
  margin: 0 0 14px;
  letter-spacing: -0.5px;
}
.credential p { font-size: 17px; margin-bottom: 18px; }
.credential-route {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-top: auto;
  padding-top: 18px;
  border-top: 1px solid var(--hair);
  font-family: var(--mono);
  font-size: 13px;
  line-height: 1.5;
  color: var(--muted-dark);
}
/* Status dot — brand spec's pill marker, reused as the route marker. */
.credential-route::before {
  content: "";
  flex: none;
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--slate);
  margin-top: calc((1.5em - 7px) / 2);
  align-self: flex-start;
}

/* ============================================================
   MANIFESTO — numbered points, deliberate short lines
   The source breaks its own lines. Nothing here re-wraps or justifies
   them; the setting gives the verse room and gets out of the way.
   ============================================================ */
.manifesto .point {
  display: grid;
  grid-template-columns: 3.5ch minmax(0, 1fr);
  gap: 24px;
  padding: 24px 0;
  border-top: 1px solid var(--hair);
  max-width: 860px;
}
.manifesto .point:first-of-type { border-top: 0; }
.point-no {
  font-family: var(--mono);
  font-size: 17px;
  font-weight: 500;
  color: var(--muted);
  line-height: 1.55;
  font-variant-numeric: tabular-nums;
}
.point-body p {
  font-size: 19px;
  line-height: 1.55;
  color: var(--slate);
  margin: 0;
  text-wrap: initial;    /* honour the authored line breaks */
}
.manifesto-close {
  font-size: 23px;
  line-height: 1.5;
  font-weight: 600;
  color: var(--slate);
  letter-spacing: -0.3px;
  margin-top: 36px;
  padding-top: 28px;
  border-top: 1px solid var(--border);
  max-width: 860px;
}

/* ============================================================
   CALL TO ACTION — prose only. No form, no text box, no copy button.
   ============================================================ */
.cta {
  background: var(--band);
  border: 1px solid var(--hair);
  border-radius: var(--radius-lg);
  padding: clamp(28px, 4vw, 44px);
}
.cta > * { max-width: 62ch; }
.cta h2 { margin-top: 0; }
.cta p:last-child { margin-bottom: 0; }
/* The one red thing in this view: the tag people are asked to post. */
.hashtag {
  font-family: var(--mono);
  font-weight: 500;
  color: var(--signal-text);      /* 5.16:1 */
  white-space: nowrap;
}

/* ============================================================
   AUTHORS — two people, no photographs available
   No avatar placeholders, no monogram circles. A rule and a name.
   ============================================================ */
.authors {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: 24px;
  margin-top: 8px;
}
@media (min-width: 620px) { .authors { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
.author { border-top: 2px solid var(--slate); padding-top: 18px; }
.author-name {
  font-size: 23px;
  font-weight: 600;
  color: var(--slate);
  margin: 0 0 6px;
  letter-spacing: -0.4px;
}
.author-role {
  font-family: var(--mono);
  font-size: 13px;
  font-weight: 500;
  letter-spacing: 1.5px;
  text-transform: uppercase;
  color: var(--muted);
}

/* ============================================================
   LEGAL PAGES — dense, unglamorous, maximally readable
   Long paragraphs on a slightly tighter measure, headings demoted to
   quiet mono labels so the text itself is the only thing with weight.
   ============================================================ */
.legal-page > * { max-width: 68ch; }
.legal-page p { margin-bottom: 18px; line-height: 1.7; }
.legal-page h3 {
  font-family: var(--mono);
  font-size: 15px;
  font-weight: 500;
  letter-spacing: 1px;
  text-transform: uppercase;
  color: var(--muted);
  margin: 36px 0 10px;
}

/* ============================================================
   FOOTER — dark Slate
   ============================================================ */
.footer { background: var(--slate-2); color: var(--on-dark-mute); }
.footer-inner {
  max-width: var(--maxw);
  margin: 0 auto;
  padding: 64px var(--pad) 48px;
  font-size: 15px;
  line-height: 1.7;
}
.footer .wordmark { font-size: 26px; color: var(--on-dark); display: inline-block; }
.footer-blurb {
  color: var(--on-dark-soft);
  max-width: 42ch;
  margin: 16px 0 0;
}
.footer-links {
  display: flex;
  gap: 8px 28px;
  flex-wrap: wrap;
  margin: 20px 0 28px;
}
.footer-links a {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  color: var(--on-dark-soft);
  font-weight: 500;
  text-decoration-color: rgba(183, 192, 201, 0.4);
}
.footer-links a:hover { color: var(--on-dark); text-decoration-color: var(--on-dark); }
.footer-rule { height: 1px; background: rgba(255, 255, 255, 0.1); margin-bottom: 28px; }
/* Was #5E6B77 — 2.94:1 on the footer, below AA. --on-dark-mute is 5.39:1
   and is the floor for any text on the dark surface. */
.legal {
  max-width: 76ch;
  color: var(--on-dark-mute);
  font-size: 14px;
  margin: 0;
}

/* ============================================================
   RESPONSIVE
   Tested at 320, 375, 768, 1024, 1440 and ultrawide.
   ============================================================ */

/* Below 1000px the links wrap onto their own row; the nav stops being
   sticky there and --nav-h drops to 0 so scroll-padding stays truthful. */
@media (max-width: 999px) {
  :root { --nav-h: 0px; }
  .nav { position: static; }
  .nav-inner { padding: 10px var(--pad); }
  html { scroll-padding-top: 24px; }
}

@media (max-width: 860px) {
  :root { --pad: 24px; }
  body { font-size: 17px; }
  main { padding-bottom: 72px; }
  .nav .wordmark { font-size: 24px; }
  .nav-links a { font-size: 15px; padding: 0 8px; }
  .panel { padding: 24px 22px 28px; }
  .provider, .doc { padding: 22px; }
  .credential { padding: 24px; }
}

/* ---- Flow strip on a phone ---------------------------------
   It becomes a vertical sequence rather than squashing or scrolling: the
   six steps are an ordered chain, and a column keeps that reading with
   no interaction and no hidden nodes. The beads stay optically centred
   on the connector because each small bead carries 3px of side margin,
   giving it the same 19px slot as the Hingepoint node.
   ------------------------------------------------------------ */
@media (max-width: 760px) {
  .flow-track { flex-direction: column; align-items: flex-start; }
  .flow-node { flex-direction: row; align-items: center; gap: 14px; }
  .flow-node .bead { margin: 0 3px; }
  .flow-node--hp .bead { margin: 0; }
  .flow-node .cap { font-size: 14px; }
  .flow-line { width: 2px; height: 18px; flex: none; min-width: 0; margin-left: 8.5px; }
}

@media (max-width: 560px) {
  h1.title--wordmark { letter-spacing: -0.02em; }
  .manifesto .point { grid-template-columns: minmax(0, 1fr); gap: 6px; }
  .point-body p { font-size: 18px; }
  .prose th, .prose td { padding: 12px 14px; }
  .card-grid--single .provider { padding: 22px; }
}

/* At the top end the container stops growing — content stays on a
   readable measure rather than stretching across an ultrawide display. */
@media (min-width: 1600px) {
  :root { --pad: 56px; }
}

/* Respect the visitor's motion preference. */
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  * { transition-duration: 0.01ms !important; animation-duration: 0.01ms !important; }
}

/* ============================================================
   PRINT — the Guide, the Manifesto and the legal pages get printed.
   ============================================================ */
@media print {
  .nav, .footer-links { display: none; }
  body { background: #fff; font-size: 11.5pt; color: #000; }
  main { padding: 0; max-width: none; }
  .panel, .provider, .doc, .credential, .note, .cta { break-inside: avoid; }
  a { text-decoration: none; color: #000; }
}

/* ============================================================
   M4 ADDITIONS
   ============================================================ */

/* ---- End of an embedded document ---------------------------
   /manifesto/ renders the published document whole, then the site adds its own
   words below. Without a hard break the call to action reads as a fifteenth
   point of the Manifesto — which would be the site putting words into a
   CC BY-ND artifact's mouth. This is a boundary, so it is stated, not implied.
   ------------------------------------------------------------ */
.document-end {
  display: flex;
  align-items: center;
  gap: 18px;
  margin: 64px 0 56px;
}
.document-end::before,
.document-end::after {
  content: "";
  flex: 1;
  height: 1px;
  background: var(--hair);
}
.document-end-label {
  flex: none;
  font-family: var(--mono);
  font-size: 12px;
  font-weight: 500;
  letter-spacing: 1.5px;
  text-transform: uppercase;
  /* --signal-text, not --signal: this is 12px text, and #FF2323 is 3.68:1 on
     Paper, below AA. The darkened tone is the same hue at 5.10:1. */
  color: var(--signal-text);
  white-space: nowrap;
}
@media (max-width: 560px) {
  .document-end-label { white-space: normal; text-align: center; }
}

/* ---- Author portraits --------------------------------------
   Photographs are not available yet. Rather than an obviously broken image or
   a stock silhouette, the placeholder is the initials set in the brand's mono
   on Mist — deliberate rather than missing. Replace with real portraits when
   they exist; the layout does not change.
   ------------------------------------------------------------ */
.authors {
  display: grid;
  /* Fixed tracks, not 1fr. With `1fr` two cards divide whatever width is
     available, so the cards grow when the page does and the portrait does not —
     which is how they ended up ~548px wide holding a 224px picture. A fixed
     track makes the card a deliberate size at every viewport.

     The track is half the reading measure less the gap, so the pair aligns
     exactly with the text column above it rather than approximately. Derived
     rather than hardcoded: change --measure and the cards follow.

     The portrait is capped at 156px, so picture and text sit against the left
     edge and the ~168px to their right is intentional, not leftover. That is
     why the track is fixed rather than fitted to content — the whitespace is
     the point.

     minmax(0, …) rather than a bare width: with a fixed track the cards would
     overflow a narrow viewport, and the brief forbids the page scrolling
     sideways at any width. The 0 lets them shrink; the max stops them growing. */
  grid-template-columns: repeat(2, minmax(0, calc((var(--measure) - 24px) / 2)));
  justify-content: start;
  gap: 24px;
  margin: 32px 0 40px;
  max-width: var(--measure);
}
@media (max-width: 620px) {
  .authors { grid-template-columns: minmax(0, calc((var(--measure) - 24px) / 2)); }
}
.author {
  background: #fff;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 24px 22px;
}
/* The portrait was 72px in a ~250px card, which left the card looking mostly
   empty. It now fills the card's content width.

   The cap matches the card's content width: 200px card, 22px padding each
   side, so 156px. The files are cut at 448px, which is 2.9x — comfortably
   retina, and it leaves room to enlarge the cards without recutting. */
.author-portrait {
  width: 100%;
  max-width: 156px;
  aspect-ratio: 1;
  border-radius: 50%;
  background: var(--mist);
  display: flex;
  align-items: center;
  justify-content: center;
  margin-bottom: 20px;
  overflow: hidden;
}
.author-portrait span {
  font-family: var(--mono);
  font-size: 22px;
  font-weight: 500;
  letter-spacing: 1px;
  color: var(--muted-dark);
}
.author-portrait img {
  width: 100%; height: 100%;
  border-radius: 50%;
  object-fit: cover;
}
.author-name { font-size: 19px; margin: 0 0 4px; }
.author-role {
  font-family: var(--mono);
  font-size: 13px;
  letter-spacing: 1px;
  color: var(--muted);
}

/* Each link names its person rather than reading "LinkedIn" twice on one page.
   Two identical link texts pointing at different destinations is the classic
   screen-reader failure: a reader listing the links off a page hears the same
   label twice and cannot tell which is which. */
.author-link {
  display: inline-block;
  margin-top: 12px;
  font-size: 15px;
  font-weight: 600;
}

/* A document card offering two formats. The card is a COLUMN flex container,
   so two .doc-link children stack — and the margin-left this used to carry
   indented the second one. They belong in a row of their own. */
.doc-links {
  margin-top: auto;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px 24px;
}
.doc-links .doc-link { margin-top: 0; }


/* A button whose destination does not exist yet (§7). Plainly not clickable:
   a *button* that goes nowhere is the most misleading form of a dead link. */
.btn--pending {
  background: var(--band);
  border: 1px dashed var(--border-2);
  color: var(--muted);
  cursor: default;
}
.btn--pending:hover { background: var(--band); }


/* ============================================================
   THE RED RULE — used sparingly  (architect, 11 August)
   ============================================================
   Tried on every card and rejected on sight: nine red rules in a grid stopped
   marking anything and just made the page loud. Back to what the rule is for —
   pointing at a specific box.

   It now appears on exactly three things:
     .doc--accent   the Guide, the one document to read first
     .doc--marked   an individual card singled out on its page
     .provider      the register entry — kept for now, under review as the
                    number of providers grows, since ten of them in a grid
                    would reproduce exactly the problem above

   .note keeps its white fill and its own red rule. That was changed to Band
   while every card carried a rule; with the rules gone the note is
   distinguishable again on its own.
   ------------------------------------------------------------ */
.doc--accent,
.doc--marked,
.provider {
  border-left: 3px solid var(--signal);
}

/* ---- The site's own voice, after an embedded document -------
   /manifesto/ renders the published document whole and then the site speaks.
   A rule alone was not enough of a break: the call to action read as a
   fifteenth point of a CC BY-ND artifact. This block changes surface as well,
   so the change of author is visible before it is read.
   ------------------------------------------------------------ */
.site-voice {
  background: var(--band);
  border: 1px solid var(--border);
  border-left: 3px solid var(--signal);
  border-radius: var(--radius);
  padding: 32px 34px 12px;
  margin: 8px 0 40px;
}
.site-voice > h2:first-of-type { margin-top: 12px; }
@media (max-width: 560px) { .site-voice { padding: 24px 22px 8px; } }
