/* Sapphire component library.
   GENERATED. Built from tokens/tokens.json and src/. Do not edit by hand.
   Foundation tokens are implementation values. Brand decisions live outside the component system. */

/* === src/styles/tokens.css === */
/* GENERATED FILE. Edit tokens/tokens.json and run npm run tokens. */
/* Sapphire. Implementation draft. Brand decisions remain external. */

:root {

  /* colour */
  --ksf-colour-bone: #FBF9F4;
  --ksf-colour-walnut: #26231D;
  --ksf-colour-stone: #5F584A;
  --ksf-colour-linen: #D8CFBD;
  --ksf-colour-brass: #A08A54;
  --ksf-colour-oxide: #9A4A22;
  --ksf-colour-evergreen: #143B22;
  --ksf-colour-white: #FFFFFF;

  /* semantic */
  --ksf-surface-page: var(--ksf-colour-bone);
  --ksf-surface-raised: var(--ksf-colour-white);
  --ksf-surface-tint: var(--ksf-colour-linen);
  --ksf-surface-ceremonial-brass: var(--ksf-colour-brass);
  --ksf-surface-ceremonial-evergreen: var(--ksf-colour-evergreen);
  --ksf-surface-closing: var(--ksf-colour-walnut);
  --ksf-ink-primary: var(--ksf-colour-walnut);
  --ksf-ink-secondary: var(--ksf-colour-stone);
  --ksf-ink-on-dark: var(--ksf-colour-bone);
  --ksf-ink-on-brass: var(--ksf-colour-walnut);
  --ksf-accent-institution: var(--ksf-colour-brass);
  --ksf-accent-human: var(--ksf-colour-oxide);
  --ksf-provenance-quotation: var(--ksf-colour-evergreen);
  --ksf-provenance-invention: var(--ksf-colour-oxide);
  --ksf-rule-hairline: rgba(38, 35, 29, 0.16);
  --ksf-rule-emphasis: var(--ksf-colour-brass);
  --ksf-action-fill: var(--ksf-colour-walnut);
  --ksf-action-fill-hover: var(--ksf-colour-oxide);
  --ksf-action-label: var(--ksf-colour-bone);

  /* space */
  --ksf-space-4: 4px;
  --ksf-space-8: 8px;
  --ksf-space-12: 12px;
  --ksf-space-16: 16px;
  --ksf-space-22: 22px;
  --ksf-space-32: 32px;
  --ksf-space-48: 48px;
  --ksf-space-76: 76px;

  /* size */
  --ksf-size-radius: 0;
  --ksf-size-hairline: 1px;
  --ksf-size-rule: 2px;
  --ksf-size-measure: 38rem;
  --ksf-size-spine: 180px;

  /* font */
  --ksf-font-display: 'EB Garamond', Georgia, serif;
  --ksf-font-reading: 'EB Garamond', Georgia, serif;
  --ksf-font-meta: 'Inter', system-ui, sans-serif;
  --ksf-font-catalogue: 'Silkscreen', monospace;
  --ksf-font-ku-display: 'Amiri', serif;
  --ksf-font-ku-reading: 'Vazirmatn', sans-serif;
  --ksf-font-ku-meta: 'Noto Kufi Arabic', sans-serif;
  --ksf-font-ar-display: var(--ksf-font-ku-display);
  --ksf-font-ar-reading: var(--ksf-font-ku-reading);
  --ksf-font-ar-meta: var(--ksf-font-ku-meta);
}


/* === src/styles/base.css === */
/* Base: element defaults and the type ramp.
   Every rule here is a system rule, not a reset for its own sake. */

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

body,
.ksf {
  background: var(--ksf-surface-page);
  color: var(--ksf-ink-primary);
  font-family: var(--ksf-font-reading);
  font-size: 19px;
  line-height: 1.68;
  -webkit-font-smoothing: antialiased;
}

/* --- Latin ramp --- */
.ksf-display        { font-family: var(--ksf-font-display); font-style: italic; font-weight: 400; font-size: 60px; line-height: 1.02; letter-spacing: -0.01em; margin: 0; }
.ksf-display--large { font-size: 40px; line-height: 1.18; letter-spacing: 0; }
.ksf-display--title { font-size: 28px; line-height: 1.2; letter-spacing: 0; }
.ksf-heading        { font-family: var(--ksf-font-display); font-style: normal; font-weight: 400; font-size: 35px; line-height: 1.16; margin: 0; }
.ksf-lede           { font-family: var(--ksf-font-reading); font-style: italic; font-size: 25px; line-height: 1.45; color: rgba(38, 35, 29, 0.86); margin: 0; }
.ksf-body           { font-family: var(--ksf-font-reading); font-size: 19px; line-height: 1.68; max-width: var(--ksf-size-measure); margin: 0; }
.ksf-body + .ksf-body { margin-top: var(--ksf-space-16); }

.ksf-label { font-family: var(--ksf-font-meta); font-size: 12px; font-weight: 500; letter-spacing: 0.2em; text-transform: uppercase; color: var(--ksf-ink-secondary); margin: 0; }
.ksf-small { font-family: var(--ksf-font-meta); font-size: 10px; font-weight: 600; letter-spacing: 0.18em; text-transform: uppercase; color: var(--ksf-ink-secondary); margin: 0; }
.ksf-note  { font-family: var(--ksf-font-meta); font-size: 13px; font-weight: 400; line-height: 1.65; color: var(--ksf-ink-secondary); margin: 0; }

/* Catalogue signal. Three permitted uses: archive identifiers, section
   numbers, the provenance stamp. If it is not a number that came out of a
   catalogue, it is set in Inter. */
.ksf-code { font-family: var(--ksf-font-catalogue); font-size: 11px; letter-spacing: 0.14em; color: var(--ksf-ink-primary); margin: 0; }

/* --- Sorani ramp ---
   Not a translation of the Latin. Set larger with a taller line height,
   because naskh at a matched point size reads smaller and lighter.
   Parity is judged optically, on the page. */
.ksf-ku            { direction: rtl; text-align: right; }
.ksf-ku-display    { font-family: var(--ksf-font-ku-display); font-size: 33px; line-height: 1.65; margin: 0; }
.ksf-ku-title      { font-family: var(--ksf-font-ku-display); font-size: 22px; line-height: 1.6; margin: 0; }
.ksf-ku-body       { font-family: var(--ksf-font-ku-reading); font-size: 19px; line-height: 1.95; margin: 0; }
.ksf-ku-meta       { font-family: var(--ksf-font-ku-meta); font-size: 14px; line-height: 1.9; letter-spacing: 0; color: var(--ksf-ink-secondary); margin: 0; }

/* --- grounds --- */
/* ksf-on-brass is DEPRECATED (decision 31): Brass is an accent, never a
   ground. Kept, not deleted, until v1 pages are migrated; remove it then
   if nothing uses it. */
.ksf-on-brass     { background: var(--ksf-surface-ceremonial-brass); color: var(--ksf-ink-on-brass); }
.ksf-on-evergreen { background: var(--ksf-surface-ceremonial-evergreen); color: var(--ksf-ink-on-dark); }
.ksf-on-closing   { background: var(--ksf-surface-closing); color: var(--ksf-ink-on-dark); }
.ksf-on-brass .ksf-label,
.ksf-on-brass .ksf-small,
.ksf-on-brass .ksf-note,
.ksf-on-brass .ksf-code,
.ksf-on-brass .ksf-lede { color: var(--ksf-ink-on-brass); }
.ksf-on-evergreen .ksf-label,
.ksf-on-evergreen .ksf-small,
.ksf-on-evergreen .ksf-note,
.ksf-on-evergreen .ksf-code,
.ksf-on-evergreen .ksf-lede,
.ksf-on-closing .ksf-label,
.ksf-on-closing .ksf-small,
.ksf-on-closing .ksf-note,
.ksf-on-closing .ksf-code,
.ksf-on-closing .ksf-lede { color: rgba(251, 249, 244, 0.72); }

/* --- shared --- */
.ksf-rule { border: 0; border-top: var(--ksf-size-hairline) solid var(--ksf-rule-hairline); margin: 0; }
.ksf-plate { background: var(--ksf-surface-raised); border: var(--ksf-size-hairline) solid var(--ksf-rule-hairline); border-radius: var(--ksf-size-radius); }

:where(a, button, [tabindex]):focus-visible {
  outline: 2px solid var(--ksf-accent-human);
  outline-offset: 3px;
}

@media (prefers-reduced-motion: reduce) {
  * { transition: none !important; animation: none !important; }
}


/* === src/components/action.css === */
/* Action. Square, flat, no radius and no shadow.
   Primary is a Walnut field hovering to Oxide. Secondary is a Walnut rule
   that fills on hover. The state changes, the page does not move.
   One primary per view. Reserved for actions on a file or a record:
   navigation is typographic. */
.ksf-action {
  display: inline-block;
  font-family: var(--ksf-font-meta);
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  text-decoration: none;
  padding: 14px 24px;
  border: var(--ksf-size-hairline) solid var(--ksf-ink-primary);
  border-radius: var(--ksf-size-radius);
  background: transparent;
  color: var(--ksf-ink-primary);
  cursor: pointer;
  transition: background-color 0.15s linear, color 0.15s linear, border-color 0.15s linear;
}
.ksf-action:hover { background: var(--ksf-action-fill); color: var(--ksf-action-label); }

.ksf-action--primary {
  background: var(--ksf-action-fill);
  border-color: var(--ksf-action-fill);
  color: var(--ksf-action-label);
}
.ksf-action--primary:hover {
  background: var(--ksf-action-fill-hover);
  border-color: var(--ksf-action-fill-hover);
}

/* Secondary, ground-adaptive (decision 7 asked this to be checked before
   the masthead could use it). The base .ksf-action's border and text are
   pinned to Walnut, which only reads correctly on a light ground: on
   Evergreen it is close to invisible, dark on dark. This variant swaps
   the fixed ink for currentColor, so it inherits Bone wherever an
   ancestor sets it, the way .ksf-on-evergreen already does, and renders
   identically to the base look on a light ground, since the page's own
   default colour is the same Walnut. The hover fill is unchanged: it is
   already a self-contained Walnut box with Bone text, so it already
   reads correctly on any ground. A behavioural difference, not a
   cosmetic one, so it qualifies as a variant under the component
   contract rather than a one-off appearance change. */
.ksf-action--secondary {
  border-color: currentColor;
  color: currentColor;
}

/* Sorani actions are never letterspaced and never uppercased. */
.ksf-action--ku {
  font-family: var(--ksf-font-ku-meta);
  font-size: 13px;
  font-weight: 500;
  letter-spacing: 0;
  text-transform: none;
  direction: rtl;
}


/* === src/components/link.css === */
/* Links.
   Body links carry a Brass underline and turn Oxide on hover. Brass never
   colours the word itself: it fails contrast at reading size.
   Titles have no underline at rest, so a page of headlines does not look
   like a page of links. */
.ksf-link {
  color: var(--ksf-ink-primary);
  text-decoration: none;
  border-bottom: 1.5px solid var(--ksf-accent-institution);
  padding-bottom: 1px;
  transition: color 0.15s linear, border-color 0.15s linear;
}
.ksf-link:hover { color: var(--ksf-accent-human); border-bottom-color: var(--ksf-accent-human); }

.ksf-link--title {
  border-bottom: var(--ksf-size-rule) solid transparent;
  padding-bottom: 0;
}
.ksf-link--title:hover { color: inherit; border-bottom-color: var(--ksf-accent-institution); }

/* The quiet call to action. The default inside the Archive intensity, where a
   button would shout. Formerly set in Silkscreen, which made a catalogue
   signal out of a link. */
.ksf-quiet {
  display: flex;
  align-items: center;
  gap: 0.9rem;
  max-width: 340px;
  font-family: var(--ksf-font-meta);
  font-size: 10px;
  font-weight: 600;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--ksf-ink-secondary);
  text-decoration: none;
}
.ksf-quiet::before,
.ksf-quiet::after { content: ""; flex: 1; height: 1px; background: var(--ksf-colour-linen); }
.ksf-quiet:hover { color: var(--ksf-accent-human); }

/* ksf-quiet's rest colour (--ksf-ink-secondary, Stone) and its connecting
   hairlines (--ksf-colour-linen) are both tuned for a light ground; on a
   dark one they read at the wrong contrast. Generalised from a
   footer-only fix (this used to live in chrome.css, scoped to
   .ksf-footer alone) once the homepage build needed a quiet link inside
   a plain Walnut/closing band too, the same technique as devices.css's
   mark and action.css's secondary variant: ksf-footer is still listed,
   since its Walnut background is not applied through the ksf-on-closing
   utility class. */
.ksf-on-evergreen .ksf-quiet,
.ksf-on-closing .ksf-quiet,
.ksf-footer .ksf-quiet { color: color-mix(in srgb, var(--ksf-ink-on-dark) 72%, transparent); }
.ksf-on-evergreen .ksf-quiet::before,
.ksf-on-evergreen .ksf-quiet::after,
.ksf-on-closing .ksf-quiet::before,
.ksf-on-closing .ksf-quiet::after,
.ksf-footer .ksf-quiet::before,
.ksf-footer .ksf-quiet::after { background: color-mix(in srgb, var(--ksf-ink-on-dark) 30%, transparent); }
.ksf-on-evergreen .ksf-quiet:hover,
.ksf-on-closing .ksf-quiet:hover,
.ksf-footer .ksf-quiet:hover { color: var(--ksf-ink-on-dark); }


/* === src/components/table.css === */
/* Table. Two columns, key and value.
   Header sets a label over a single Brass rule, carried by rule-emphasis.
   Brass appears here as a line, never as a field: the header is marked, not
   filled. Body sets the key in Stone and the value in Walnut over a hairline.
   No zebra striping, no Silkscreen: a table is a publication device, not a
   dashboard. */
.ksf-table { width: 100%; border-collapse: collapse; font-family: var(--ksf-font-meta); font-size: 13px; }
.ksf-table th {
  text-align: left;
  font-size: 10px;
  font-weight: 600;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--ksf-ink-primary);
  padding: 0 14px 10px 0;
  vertical-align: bottom;
  border-bottom: var(--ksf-size-hairline) solid var(--ksf-rule-emphasis);
}
.ksf-table td {
  padding: 12px 14px 12px 0;
  border-bottom: var(--ksf-size-hairline) solid var(--ksf-rule-hairline);
  vertical-align: top;
  line-height: 1.55;
  color: var(--ksf-ink-primary);
}
.ksf-table td.ksf-table__key { color: var(--ksf-ink-secondary); width: 26%; }
.ksf-table td.ksf-table__ku { direction: rtl; text-align: right; font-family: var(--ksf-font-ku-reading); font-size: 15px; }


/* === src/components/list.css === */
/* Do and Don't.
   Evergreen plus, Oxide cross, both at metadata size so the marker never
   competes with the line it marks. Set in reading type, not in a UI face:
   these are sentences, not interface labels. */
.ksf-list { list-style: none; margin: 0; padding: 0; }
.ksf-list__item {
  position: relative;
  padding: 10px 0 10px 22px;
  border-bottom: var(--ksf-size-hairline) solid var(--ksf-rule-hairline);
  font-family: var(--ksf-font-reading);
  font-size: 16.5px;
  line-height: 1.5;
}
.ksf-list__item::before {
  position: absolute;
  left: 0;
  top: 12px;
  font-family: var(--ksf-font-meta);
  font-size: 12px;
  line-height: 1;
}
.ksf-list--do .ksf-list__item::before    { content: "+"; color: var(--ksf-provenance-quotation); }
.ksf-list--dont .ksf-list__item::before  { content: "\00D7"; color: var(--ksf-provenance-invention); }
.ksf-list__heading { margin-bottom: var(--ksf-space-4); }
.ksf-list__heading--do   { color: var(--ksf-provenance-quotation); }
.ksf-list__heading--dont { color: var(--ksf-provenance-invention); }


/* === src/components/callout.css === */
/* Status callout.
   For anything the reader must not mistake for settled: a status read off
   Family Decisions, an open question, a flag on a record. One per view.
   If a page needs three, the page is a decision register and belongs in
   Notion instead. */
.ksf-callout {
  background: var(--ksf-surface-raised);
  border: var(--ksf-size-hairline) solid var(--ksf-rule-hairline);
  border-inline-start: 3px solid var(--ksf-accent-human);
  padding: 22px 26px;
}
.ksf-callout__label {
  font-family: var(--ksf-font-meta);
  font-size: 10px;
  font-weight: 600;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--ksf-accent-human);
  margin: 0 0 12px;
}
.ksf-callout p {
  font-family: var(--ksf-font-meta);
  font-size: 13.5px;
  line-height: 1.65;
  color: var(--ksf-ink-secondary);
  margin: 0 0 9px;
}
.ksf-callout p:last-child { margin-bottom: 0; }
.ksf-callout b { color: var(--ksf-ink-primary); font-weight: 600; }


/* === src/components/provenance.css === */
/* Provenance.
   Evergreen marks what is in the object. Oxide marks what is ours.
   Every device, caption and catalogue entry that states provenance uses this
   pairing. An invention is never captioned as something he made. */
.ksf-provenance {
  font-family: var(--ksf-font-meta);
  font-size: 9.5px;
  font-weight: 600;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  margin: 0;
}
.ksf-provenance--quotation { color: var(--ksf-provenance-quotation); }
.ksf-provenance--invention { color: var(--ksf-provenance-invention); }

/* The stamp. A publishing convention with no relationship to the needlework.
   Our invention. The one place Silkscreen earns its keep. */
.ksf-stamp {
  display: inline-block;
  font-family: var(--ksf-font-catalogue);
  font-size: 11px;
  letter-spacing: 0.12em;
  color: var(--ksf-provenance-invention);
  border: 2px solid var(--ksf-provenance-invention);
  padding: 9px 14px;
  transform: rotate(-2deg);
}

/* Metadata row. Catalogue data sits beside the thing it describes, at the
   same optical distance every time. Provenance is part of the design, never
   footnote clutter. */
.ksf-metarow {
  border-top: var(--ksf-size-hairline) solid var(--ksf-rule-hairline);
  padding-top: var(--ksf-space-12);
  font-family: var(--ksf-font-meta);
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--ksf-ink-secondary);
}


/* === src/components/record.css === */
/* Record with spine. THE SIGNATURE.
   A permanent catalogue spine at the left edge, in every register and both
   languages. Reading order is fixed: evidence, object, person, context,
   provenance. The publish flag is visible in the layout, not buried in the
   catalogue. Field names match the Archive Inventory schema exactly, and any
   field with no data reads Unknown: an empty row is a gap in the record, not
   a design problem to hide. */
.ksf-record {
  display: grid;
  grid-template-columns: var(--ksf-size-spine) 1fr;
  background: var(--ksf-surface-raised);
  border: var(--ksf-size-hairline) solid var(--ksf-rule-hairline);
}
@media (max-width: 640px) {
  .ksf-record { grid-template-columns: 1fr; }
  .ksf-record__spine { border-right: 0; border-bottom: var(--ksf-size-hairline) solid var(--ksf-rule-hairline); }
}
.ksf-record__spine {
  border-right: var(--ksf-size-hairline) solid var(--ksf-rule-hairline);
  padding: 26px 20px;
  font-family: var(--ksf-font-meta);
  font-size: 11px;
  line-height: 1.5;
  color: var(--ksf-ink-secondary);
}
.ksf-record__code { font-family: var(--ksf-font-catalogue); font-size: 11px; letter-spacing: 0.1em; color: var(--ksf-ink-primary); margin: 0 0 18px; line-height: 1.7; }
.ksf-record__fields { margin: 0; }
.ksf-record__fields dt { font-size: 9.5px; font-weight: 600; letter-spacing: 0.16em; text-transform: uppercase; color: var(--ksf-ink-secondary); margin-top: 14px; }
.ksf-record__fields dd { color: var(--ksf-ink-primary); margin: 3px 0 0; }
.ksf-record__fields dd.is-unknown { color: var(--ksf-ink-secondary); }
.ksf-record__fields dd.is-flag { color: var(--ksf-accent-human); font-weight: 600; }
.ksf-record__main { padding: 30px 28px; }
.ksf-record__main .ksf-body { color: var(--ksf-ink-secondary); }

/* Where sources disagree, show the disagreement. The archive gains
   credibility by revealing the limits of what is known. */
.ksf-record__notes { border-top: var(--ksf-size-hairline) solid var(--ksf-rule-hairline); margin-top: 20px; padding-top: 16px; font-family: var(--ksf-font-meta); font-size: 12.5px; line-height: 1.6; color: var(--ksf-ink-secondary); }
.ksf-record__notes h4 { font-size: 10px; font-weight: 600; letter-spacing: 0.16em; text-transform: uppercase; color: var(--ksf-accent-human); margin: 0 0 10px; }
.ksf-record__notes ul { list-style: none; margin: 0 0 10px; padding: 0; }
.ksf-record__notes li { position: relative; padding: 5px 0 5px 16px; }
.ksf-record__notes li::before { content: ""; position: absolute; left: 0; top: 13px; width: 7px; height: 1px; background: var(--ksf-accent-human); }


/* === src/components/rail.css === */
/* Section rail. The document-level expression of the spine: every section
   declares its number, its owner and whether it is settled. The hairline runs
   the full height of the section, which is what makes the spine read as one
   continuous element down a page rather than a repeated decoration. */
.ksf-section { display: grid; grid-template-columns: 136px minmax(0, 660px); max-width: 900px; margin: 0 auto; padding: 0 28px; }
.ksf-rail { border-right: var(--ksf-size-hairline) solid var(--ksf-rule-hairline); padding-right: 22px; text-align: right; }
.ksf-rail__number { font-family: var(--ksf-font-catalogue); font-size: 11px; letter-spacing: 0.14em; color: var(--ksf-ink-primary); margin: 0; }
.ksf-rail__label { font-family: var(--ksf-font-meta); font-size: 10px; font-weight: 500; letter-spacing: 0.2em; text-transform: uppercase; color: var(--ksf-ink-secondary); margin: 8px 0 0; line-height: 1.5; }
.ksf-rail__tag { font-family: var(--ksf-font-meta); font-size: 9.5px; font-weight: 500; letter-spacing: 0.16em; text-transform: uppercase; color: var(--ksf-ink-secondary); margin: 14px 0 0; line-height: 1.7; }
.ksf-rail__tag b { display: block; font-weight: 600; color: var(--ksf-ink-primary); }
.ksf-rail__tag b.is-family { color: var(--ksf-accent-human); }
.ksf-section__body { padding-left: 34px; min-width: 0; }
@media (max-width: 840px) {
  .ksf-section { grid-template-columns: 1fr; }
  .ksf-rail { border-right: 0; border-bottom: var(--ksf-size-hairline) solid var(--ksf-rule-hairline); padding: 0 0 14px; text-align: left; margin-bottom: 26px; display: flex; flex-wrap: wrap; gap: 18px; align-items: baseline; }
  .ksf-rail__label, .ksf-rail__tag { margin-top: 0; }
  .ksf-section__body { padding-left: 0; }
}

/* Beat. ADOPTED (decision 38): in production use since Phase 3 step 7's
   chapter-page migration. A repeatable narrative marker (Phase 2 mapping, section 3 and 6):
   a year or place label with its own short top rule, paired with a prose
   column. Different in kind from the spine above, not a variant of it:
   ksf-rail/ksf-section is page-level metadata (a number, an owner, a
   status), used once per page, with one hairline running its full
   height. A beat repeats three or four times down a single page to mark
   a narrative sequence instead, each with its own top rule rather than
   one shared hairline, and carries a year or place rather than a
   design-system status tag. See rail.stories.js for when to reach for
   which. Reuses ksf-section's 136px marker column and 900px content
   measure so the two patterns still line up if a page ever needed both.

   Rule: 4px solid Oxide, the same weight and colour the staging site
   already used for this exact pattern (site/assets/style.css, .story
   .marker). A flat rule needs no adaptation to fit decision 6: it was
   already a plain border, nothing to flatten.

   Year: Inter (ksf-font-meta), not Silkscreen. Silkscreen (ksf-code) is
   reserved for archive identifiers, section numbers and the provenance
   stamp (base.css); a birth year or a place name is narrative content,
   not a catalogue number, so it does not qualify.

   Oxide is a fixed accent here, not ground ink, the same way the band
   ornament and the focus ring stay Oxide regardless of ground (base.css
   never overrides --ksf-accent-human per ground); the rule and the year
   are not given a ground-adaptive treatment either. The place label
   reuses ksf-label (already ground-adaptive, base.css) and the prose
   column is plain ksf-body (inherits currentColor, already correct on
   any ground) rather than a third repeat of either's colour logic.

   Grid tracks are inline-direction aware by default, so the 136px
   marker column sits at the reading start on its own under dir="rtl"
   without an explicit override; nothing here uses a physical left/right
   property. */
.ksf-beat {
  display: grid;
  gap: 24px;
  margin: 32px 0;
}
@media (min-width: 841px) {
  .ksf-beat { grid-template-columns: 136px minmax(0, 1fr); }
}
.ksf-beat__marker {
  border-top: 4px solid var(--ksf-accent-human);
  padding-top: 10px;
}
.ksf-beat__year {
  font-family: var(--ksf-font-meta);
  font-size: 22px;
  font-weight: 600;
  line-height: 1;
  color: var(--ksf-accent-human);
  margin: 0;
}
.ksf-beat__marker .ksf-label { margin-top: 8px; }
.ksf-beat__body { min-width: 0; }
.ksf-beat__body .ksf-body + .ksf-body { margin-top: 16px; }


/* === src/components/chrome.css === */
/* Header, utility bar and footer.
   PLACEHOLDER CONTENT. The nav labels and the footer lines are lorem until
   the family confirms the sitemap, and the Sorani needs a native reader
   before anything is built on it. The structure is the deliverable here,
   not the words. */

/* Utility bar. ADOPTED (decision 38): in production use since Phase 3
   step 6's homepage pilot. Decision 16. Full-width strip above the
   header, Walnut ground through the existing ksf-on-closing utility
   (composed by the render function, not redefined here), Bone text.
   The reserved start slot always exists in the markup, even empty, so a
   notice can be dropped in later without a structural change.
   Language items sit at the logical end via margin-inline-start: auto,
   the same technique the site's own .nav already uses for exactly this
   job. They are plain links, typographic, never buttons or pills: no
   background, no border, no radius. */
.ksf-utility-bar {
  display: flex;
  align-items: center;
  min-height: var(--ksf-space-32);
  padding-inline: var(--ksf-space-32);
  gap: var(--ksf-space-22);
}
.ksf-utility-bar__notice {
  font-family: var(--ksf-font-meta);
  font-size: 11px;
  line-height: 1.4;
}
.ksf-utility-bar__notice:empty { display: none; }
.ksf-utility-bar__languages {
  display: flex;
  align-items: center;
  gap: var(--ksf-space-16);
  margin-inline-start: auto;
}
.ksf-utility-bar__lang {
  font-family: var(--ksf-font-meta);
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  text-decoration: none;
  color: var(--ksf-colour-linen);
  padding-bottom: 4px;
  border-bottom: var(--ksf-size-rule) solid transparent;
  transition: color 0.15s linear;
}
.ksf-utility-bar__lang:hover { color: var(--ksf-ink-on-dark); }
.ksf-utility-bar__lang[aria-current="page"] {
  color: var(--ksf-ink-on-dark);
  border-bottom-color: var(--ksf-accent-institution);
}
/* Brass focus, not the system default Oxide (base.css's
   :where(a,button,[tabindex]):focus-visible rule): decision 16 asks
   specifically for a Brass ring here, so this overrides it for the
   language items only. */
.ksf-utility-bar__lang:focus-visible {
  outline: 2px solid var(--ksf-accent-institution);
  outline-offset: 2px;
}
/* Sorani and Arabic labels are never letterspaced or uppercased, the
   same rule ksf-action--ku and ksf-testimony already follow. Arabic
   reads through the ar-* aliases (decision 21), which point at the
   same font values as ku-* until Arabic gets its own. */
.ksf-utility-bar__lang[lang="ckb"] {
  font-family: var(--ksf-font-ku-meta);
  font-size: 13px;
  font-weight: 500;
  letter-spacing: 0;
  text-transform: none;
}
.ksf-utility-bar__lang[lang="ar"] {
  font-family: var(--ksf-font-ar-meta);
  font-size: 13px;
  font-weight: 500;
  letter-spacing: 0;
  text-transform: none;
}

.ksf-header {
  position: relative; /* positioning root for the dropdown nav: .ksf-header__menu only sizes to fit the "Menu" word, so anchoring there would leave the open panel that narrow instead of spanning the header */
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--ksf-space-32);
  padding: var(--ksf-space-22) var(--ksf-space-32);
  border-bottom: var(--ksf-size-hairline) solid var(--ksf-rule-hairline);
  background: var(--ksf-surface-page);
}
.ksf-header__mark { display: flex; align-items: center; gap: var(--ksf-space-12); text-decoration: none; color: inherit; }
.ksf-header__wordmark { font-family: var(--ksf-font-display); font-style: italic; font-size: 21px; line-height: 1; }
.ksf-header__ku { font-family: var(--ksf-font-ku-display); font-size: 18px; line-height: 1; color: var(--ksf-ink-secondary); }
.ksf-nav { display: flex; align-items: center; gap: var(--ksf-space-22); }
.ksf-nav a {
  font-family: var(--ksf-font-meta);
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--ksf-ink-secondary);
  text-decoration: none;
  border-bottom: var(--ksf-size-hairline) solid transparent;
  padding-bottom: 2px;
}
.ksf-nav a:hover,
.ksf-nav a[aria-current="page"] { color: var(--ksf-ink-primary); border-bottom-color: var(--ksf-accent-institution); }
.ksf-nav__ku { font-family: var(--ksf-font-ku-meta); font-size: 13px; letter-spacing: 0; text-transform: none; }

/* Header responsive behaviour. EXPERIMENTAL: chrome.css previously had no
   @media rules at all, so this is new design work, not a port.
   Mobile-first: the inline row is hidden and the Menu disclosure shown by
   default; above the breakpoint that flips. Two separate <nav> regions
   render the same links rather than one nav whose visibility is forced
   open by CSS overriding <details>'s native state, so a screen reader
   never sees a "closed" disclosure that is actually fully visible: only
   one of the two navs is ever in the accessibility tree at a time,
   because display: none removes an element from it.
   1024px chosen by testing seven realistic-length items
   (NAV_PLACEHOLDER_SEVEN) across a range of widths, not just the three
   the brief named: a single item's own label starts wrapping onto two
   lines (not yet the whole row breaking, the individual link text
   itself) somewhere between 960px and 1000px, which is the real
   failure point for this content. 1024px sits just above that with a
   deliberate margin, rather than sitting exactly on the measured edge
   where slightly different real nav labels could tip it back over. */
.ksf-nav--inline { display: none; }
.ksf-header__menu-toggle {
  cursor: pointer;
  list-style: none;
  font-family: var(--ksf-font-meta);
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--ksf-ink-secondary);
  border-bottom: var(--ksf-size-hairline) solid transparent;
  padding-bottom: 2px;
}
.ksf-header__menu-toggle::-webkit-details-marker { display: none; }
.ksf-header__menu-toggle::marker { content: ''; }
/* base.css's global focus rule only covers a, button and [tabindex], so a
   bare <summary> (natively focusable, but none of those three) gets no
   ring at all without this. Brass, not the system-default Oxide, because
   this control asks specifically for Brass. */
.ksf-header__menu-toggle:focus-visible {
  outline: 2px solid var(--ksf-accent-institution);
  outline-offset: 2px;
}
.ksf-header__menu-toggle:hover,
.ksf-header__menu[open] .ksf-header__menu-toggle { color: var(--ksf-ink-primary); border-bottom-color: var(--ksf-accent-institution); }
.ksf-nav--dropdown {
  position: absolute;
  inset-inline: 0;
  top: 100%;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--ksf-space-16);
  /* Sits flush under the header's own border-bottom: no margin-top and no
     border-top of its own, so there is one rule, not a gap followed by a
     second rule. Inline padding matches the header's own horizontal
     padding, so the list lines up with the mark and the wordmark above it. */
  padding: var(--ksf-space-16) var(--ksf-space-32);
  background: var(--ksf-surface-page);
  /* Soft fade only, no shadow, no slide/transform, per decision 6. Respects
     prefers-reduced-motion through base.css's blanket rule, which zeroes
     every transition and animation in the system, not just this one. */
  animation: ksf-header-menu-fade 0.15s ease;
}
@keyframes ksf-header-menu-fade {
  from { opacity: 0; }
  to { opacity: 1; }
}

@media (min-width: 1024px) {
  .ksf-nav--inline { display: flex; }
  .ksf-header__menu { display: none; }
}

.ksf-footer { background: var(--ksf-surface-closing); color: var(--ksf-ink-on-dark); padding: var(--ksf-space-76) var(--ksf-space-32); }
.ksf-footer__inner { max-width: 900px; margin: 0 auto; display: grid; grid-template-columns: 1fr 1fr; gap: var(--ksf-space-48); }
/* Mark zone: a third column, sized to the mark itself (auto) rather than
   sharing the fr track with text, so the mark's own width governs it
   regardless of the mark preset used. Documented exception, decision 23:
   the only place ksf-footer renders real Foundation artwork rather than
   placeholder content, same reasoning as the Mark story in
   devices.stories.js. The mark zone is optional: when footer() is called
   without one, no ksf-footer__mark--with-mark class is added and the
   grid falls back to its existing two columns, unchanged. */
.ksf-footer__inner--with-mark { grid-template-columns: auto 1fr 1fr; }
.ksf-footer__mark { align-self: start; }
@media (max-width: 640px) {
  .ksf-footer__inner,
  .ksf-footer__inner--with-mark { grid-template-columns: 1fr; }
}
.ksf-footer__motto { font-family: var(--ksf-font-ku-display); font-size: 24px; direction: rtl; text-align: right; margin: 0; }
.ksf-footer__motto-en { font-family: var(--ksf-font-meta); font-size: 11px; font-weight: 500; letter-spacing: 0.14em; text-transform: uppercase; color: color-mix(in srgb, var(--ksf-ink-on-dark) 72%, transparent); margin: var(--ksf-space-8) 0 0; }
.ksf-footer__rule { width: 46px; height: 1px; background: var(--ksf-accent-institution); border: 0; margin: 14px 0 0 auto; }
.ksf-footer__note { margin-top: var(--ksf-space-12); }
.ksf-footer__cta { margin-top: var(--ksf-space-16); }
/* This block's translucent-Bone colour is written as color-mix() against
   --ksf-ink-on-dark rather than a raw rgba(): every colour has to come
   through a var(). The pre-existing ksf-footer/ksf-footer__code rules
   below used to write the same figure as a literal rgba(251, 249, 244,
   0.72); converted here too while touching this block, so the file does
   not mix both techniques for the identical value. */
.ksf-footer h3 { font-family: var(--ksf-font-meta); font-size: 10px; font-weight: 600; letter-spacing: 0.18em; text-transform: uppercase; color: var(--ksf-accent-institution); margin: 0 0 var(--ksf-space-12); }
.ksf-footer p { font-family: var(--ksf-font-meta); font-size: 12.5px; line-height: 1.8; color: color-mix(in srgb, var(--ksf-ink-on-dark) 72%, transparent); margin: 0 0 var(--ksf-space-12); }
.ksf-footer a { color: color-mix(in srgb, var(--ksf-ink-on-dark) 72%, transparent); }
.ksf-footer__code { font-family: var(--ksf-font-catalogue); font-size: 10px; letter-spacing: 0.14em; color: color-mix(in srgb, var(--ksf-ink-on-dark) 72%, transparent); }


/* === src/components/devices.css === */
/* Devices. Two quotations, two inventions.
   How many devices a composition may carry is not yet decided. The portrait
   is not counted either way: it is the identifier, not an ornament. */
.ksf-device { display: block; width: 100%; }
.ksf-device--stitch { opacity: 1; }

/* The mark. One asset, tinted by the page, using the silhouette as a mask so
   a single file serves Brass on Bone, Walnut on Brass and Brass on Evergreen.
   Runtime source is a PNG alpha mask rasterised from the official artwork,
   mark-mono.svg, which is kept in assets/ as the source of truth. See the
   Mark story in devices.stories.js for why real Foundation artwork lives
   here rather than a placeholder.
   Used at every size, including the 62px chrome preset (decision 30): no
   simplified small-mark artwork is used, so there is only ever one mask
   source, --ksf-mark-src. */
.ksf-mark {
  display: block;
  position: relative;
  aspect-ratio: 1 / 1;
  background-color: var(--ksf-accent-institution);
  -webkit-mask-image: var(--ksf-mark-src, url("/assets/mark-mono.png"));
  mask-image: var(--ksf-mark-src, url("/assets/mark-mono.png"));
  -webkit-mask-size: contain;
  mask-size: contain;
  -webkit-mask-repeat: no-repeat;
  mask-repeat: no-repeat;
  -webkit-mask-position: center;
  mask-position: center;
}
/* chrome: sized for the header at 62px, filling ksf-header__mark's own
   height (decision 30). See the "Mark, chrome preset" story. */
.ksf-mark--chrome { width: 62px; }
.ksf-mark--sm { width: 96px; }
.ksf-mark--md { width: 154px; }
.ksf-mark--lg { width: 260px; }
.ksf-on-brass .ksf-mark { background-color: var(--ksf-ink-primary); }
.ksf-on-evergreen .ksf-mark { background-color: var(--ksf-accent-institution); }

/* Dark grounds (decision 34, retiring decision 29's CSS disc composition):
   the mark renders supplied on-dark artwork directly instead of the
   masked composition. That artwork already bakes in the Linen disc, the
   Walnut portrait and its own ring, so the mask is switched off entirely
   rather than tinted: it is a fixed, single-purpose asset, unlike the
   silhouette mask which has to serve several different fixed colours on
   other grounds. Source is context/assets/inbox/portrait_ondark.svg; the
   optimised SVG (546 KB) is kept as source at assets/mark-ondark.svg, and
   the runtime asset is a 520x520 alpha PNG rasterised from it (2x the
   260px lg preset, the same margin the mark-mono mask uses), because the
   optimised SVG is still over the 200 KB threshold. The switch keys off
   the ground selectors themselves (plus ksf-footer, whose Walnut
   background is not applied through the ksf-on-closing utility class),
   not a modifier on the mark, so nothing using ksf-mark has to know
   which ground it is on. Applies at every preset, same as the mask. */
.ksf-on-evergreen .ksf-mark,
.ksf-on-closing .ksf-mark,
.ksf-footer .ksf-mark {
  background-color: transparent;
  -webkit-mask-image: none;
  mask-image: none;
  background-image: var(--ksf-mark-src-ondark, url("/assets/mark-ondark.png"));
  background-size: contain;
  background-repeat: no-repeat;
  background-position: center;
}


/* === src/components/testimony.css === */
/* Testimony. ADOPTED (decision 38): in production use since Phase 3
   step 6's homepage pilot.
   A quotation with attribution: comrades and family remembering him
   directly. No variants, no inverse. Ink comes from the ground it sits on,
   the same way ksf-label, ksf-small and ksf-note already do in base.css:
   the quotation itself carries no colour of its own (it inherits, exactly
   like ksf-body), and the two attribution parts are added to base.css's
   existing .ksf-on-brass / .ksf-on-evergreen / .ksf-on-closing override
   list rather than getting a second, duplicate colour rule here. */

.ksf-testimony { margin: 0; }

.ksf-testimony__quote {
  margin: 0;
  font-family: var(--ksf-font-reading);
  font-style: italic;
  font-size: 21px;
  line-height: 1.6;
}
.ksf-testimony[lang="ckb"] .ksf-testimony__quote,
.ksf-testimony[lang="ar"] .ksf-testimony__quote {
  /* Arabic has no dedicated reading face yet (decision 21, tokens/tokens.json
     not yet updated): the planned --ksf-font-ar-reading will point at this
     same value, so using the Sorani token here now already renders Arabic
     correctly and needs no change once the alias exists. */
  font-family: var(--ksf-font-ku-reading);
  font-style: normal;
  font-size: 20px;
  line-height: 1.9;
}
.ksf-testimony__quote p { margin: 0; }

/* Quotation marks via the `quotes` property and open-quote/close-quote,
   not hard-coded glyphs, so direction handles itself: in an RTL instance
   the browser mirrors which glyph renders on which visual side, rather
   than this stylesheet guessing. Guillemets match the existing Sorani
   motto lockup and the site's own prior convention; English gets its own
   curly pair since it never shared that convention with Sorani to begin
   with. Coloured with the human accent because a testimony is exactly
   that: the human-moment token, applied to punctuation, not body ink, so
   it works unchanged on every ground without a ground-specific override. */
.ksf-testimony__quote p::before,
.ksf-testimony__quote p::after {
  color: var(--ksf-accent-human);
}
.ksf-testimony__quote p::before { content: open-quote; }
.ksf-testimony__quote p::after { content: close-quote; }
.ksf-testimony__quote p { quotes: "\201C" "\201D"; }
.ksf-testimony[lang="ckb"] .ksf-testimony__quote p,
.ksf-testimony[lang="ar"] .ksf-testimony__quote p {
  quotes: "\00AB" "\00BB";
}

.ksf-testimony__attribution {
  margin: var(--ksf-space-16) 0 0;
  padding-top: var(--ksf-space-12);
  /* Ground-aware hairline: same 16% ratio as --ksf-rule-hairline (which is
     Walnut at 16% over the page), but mixed from currentColor so it reads
     correctly on Bone, Brass, Evergreen and Walnut/closing alike without a
     second rule per ground. */
  border-top: var(--ksf-size-hairline) solid color-mix(in srgb, currentColor 16%, transparent);
}
.ksf-testimony__attribution > * + * { margin-top: var(--ksf-space-4); }

.ksf-testimony__speaker {
  margin: 0;
  font-family: var(--ksf-font-meta);
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ksf-ink-secondary);
}
.ksf-testimony[lang="ckb"] .ksf-testimony__speaker,
.ksf-testimony[lang="ar"] .ksf-testimony__speaker {
  font-family: var(--ksf-font-ku-meta);
  font-size: 13px;
  font-weight: 500;
  letter-spacing: 0;
  text-transform: none;
}

.ksf-testimony__role,
.ksf-testimony__source {
  margin: 0;
  font-family: var(--ksf-font-meta);
  font-size: 12.5px;
  line-height: 1.5;
  color: var(--ksf-ink-secondary);
}
.ksf-testimony[lang="ckb"] .ksf-testimony__role,
.ksf-testimony[lang="ckb"] .ksf-testimony__source,
.ksf-testimony[lang="ar"] .ksf-testimony__role,
.ksf-testimony[lang="ar"] .ksf-testimony__source {
  font-family: var(--ksf-font-ku-meta);
  font-size: 13.5px;
}

/* Follows base.css's ground mechanism rather than duplicating its colour
   logic: the same two values base.css already uses to recolour
   ksf-label/ksf-small/ksf-note/ksf-code on a ceremonial ground
   (--ksf-ink-on-brass, and Bone at 72% for Evergreen/Walnut-closing) are
   reused here, following the same per-component pattern devices.css
   already uses for .ksf-on-brass .ksf-mark. base.css itself writes the
   72% figure as a raw rgba(251, 249, 244, 0.72); every colour in this
   file has to come through a var(), so it is reproduced here as
   color-mix() against the actual --ksf-ink-on-dark token (Bone) instead,
   which resolves to the identical value without a raw colour.
   The quotation itself needs no entry here: like ksf-body, it carries no
   colour of its own and simply inherits .ksf-on-*'s ink. */
.ksf-on-brass .ksf-testimony__speaker,
.ksf-on-brass .ksf-testimony__role,
.ksf-on-brass .ksf-testimony__source { color: var(--ksf-ink-on-brass); }
.ksf-on-evergreen .ksf-testimony__speaker,
.ksf-on-evergreen .ksf-testimony__role,
.ksf-on-evergreen .ksf-testimony__source,
.ksf-on-closing .ksf-testimony__speaker,
.ksf-on-closing .ksf-testimony__role,
.ksf-on-closing .ksf-testimony__source { color: color-mix(in srgb, var(--ksf-ink-on-dark) 72%, transparent); }


/* === src/components/band.css === */
/* Section band. ADOPTED (decision 38): in production use since Phase 3
   step 6's homepage pilot.
   A full-bleed ground plus a single-column content wrap plus an optional
   background ornament. Not an extension of ksf-rail/ksf-section: that pair
   is a two-column metadata spine used once per page. A band is a plain
   full-width zone, and a page can carry several.
   Grounds: exactly three (decision 20). Default page ground needs no
   class. Evergreen and Walnut/closing are the existing ksf-on-evergreen
   and ksf-on-closing utilities, composed here as modifiers, not
   redefined. Brass is never a ground. */

.ksf-band {
  position: relative;
  overflow: hidden; /* the ornament must never cause horizontal scroll */
  padding-block: var(--ksf-space-76);
}

.ksf-band__wrap {
  position: relative;
  z-index: 1; /* own stacking context: keeps the ornament's negative z-index contained to this box, not the whole band */
  max-width: 900px; /* matches ksf-section's established content measure, rail.css */
  margin-inline: auto;
  padding-inline: var(--ksf-space-32); /* nearest space token to ksf-section's 28px gutter */
}

/* Ornament. Decision 18: one short mark, optional, decorative only.
   Positioned relative to the wrap, not the full-bleed band: on a wide
   viewport the wrap stays at its 900px measure while the band keeps
   growing, so anchoring to the band would leave the ornament stranded
   at the viewport edge, far from the content column it is meant to sit
   near. inset-inline-end is measured from the wrap's own end edge for
   that reason.
   top is 0, relative to the wrap, not a positive offset: the wrap
   itself already sits one --ksf-space-76 (the band's own block padding)
   below the band's true top edge, so top: 0 here already reproduces
   "inset from the band's top edge by the same token the band uses for
   its block padding" without adding a second, redundant offset on top
   of it.
   z-index: -1, not 0: negative z-index reliably paints behind all
   in-flow content within the wrap's own stacking context (established
   above by the wrap's position + explicit z-index), which is a more
   certain guarantee than relying on paint-order behaviour for
   z-index: auto/0 positioned versus non-positioned siblings.
   Outlined rather than filled, in the display face, never Silkscreen
   (that face is reserved for catalogue and provenance signals).
   Coloured through -webkit-text-fill-color/-webkit-text-stroke rather
   than the `color` property, specifically so currentColor still resolves
   to the real ground ink (Walnut, or Bone-derived on a dark ground) for
   the stroke: setting `color` itself to transparent would make
   currentColor resolve to transparent too, breaking the mix. This is
   the same ground-adaptive technique as testimony's hairline, extended
   to an outline instead of a border. No shadow, no gradient, no
   transform, per decision 6.
   Sized with clamp() so it scales down smoothly at narrow widths rather
   than being hidden outright: it is small to begin with (five
   characters or fewer, decision 18a) and aria-hidden, so it never
   competes for space or attention on a narrow screen, only for
   legibility as a watermark, which clamp already protects by never
   letting it collapse below its minimum. */
.ksf-band__ornament {
  position: absolute;
  inset-inline-end: var(--ksf-space-76);
  top: 0;
  z-index: -1;
  pointer-events: none;
  user-select: none;
  font-family: var(--ksf-font-display);
  font-style: normal;
  font-weight: 400;
  line-height: 1;
  font-size: clamp(44px, 13vw, 168px);
  -webkit-text-fill-color: transparent;
  -webkit-text-stroke: 1.5px color-mix(in srgb, currentColor 22%, transparent);
}
.ksf-band[lang="ckb"] .ksf-band__ornament,
.ksf-band[lang="ar"] .ksf-band__ornament {
  font-family: var(--ksf-font-ku-display);
}


/* === src/components/figure.css === */
/* Figure. ADOPTED (decision 38): in production use since Phase 3
   step 7's chapter-page migration.
   A captioned frame for images: photographs, and (decision 12) the
   Rosary network and three-revolutions artwork, once those exist as
   image assets rather than markup. No duotone, no hover reveal: the
   image is shown as supplied, at all times.

   Frame: a hairline border around the image, nothing else. Chosen over
   no frame at all because the images this component holds (scanned
   family photographs, some old and pale, some dark) will vary widely in
   tone; a hairline gives every image the same clean edge against the
   page regardless of its own brightness, rather than leaving a bright
   scan to bleed into the Bone ground with no boundary. It also matches
   existing precedent rather than inventing a new one: ksf-plate already
   frames a raised surface this way, and the archive record page
   template already frames its own artefact photograph placeholder with
   an identical hairline box. No shadow, no radius, per decision 6.

   No fixed aspect ratio and no size variants: the image decides its own
   proportions, and width comes from wherever the figure is placed, not
   from a modifier class. */
.ksf-figure {
  display: block;
  margin: 0;
}
.ksf-figure__frame {
  border: var(--ksf-size-hairline) solid color-mix(in srgb, currentColor 16%, transparent);
  background: var(--ksf-surface-raised);
  line-height: 0; /* removes the small gap under an inline img */
}
.ksf-figure__frame img,
.ksf-figure__frame picture,
.ksf-figure__frame picture img {
  display: block;
  width: 100%;
  height: auto;
}
.ksf-figure__caption {
  margin-top: var(--ksf-space-8);
  font-family: var(--ksf-font-meta);
  font-size: 13px;
  line-height: 1.5;
  color: var(--ksf-ink-secondary);
}
.ksf-figure[dir="rtl"] .ksf-figure__caption,
.ksf-figure[lang="ckb"] .ksf-figure__caption,
.ksf-figure[lang="ar"] .ksf-figure__caption {
  font-family: var(--ksf-font-ku-meta);
  font-size: 14px;
}
.ksf-figure__provenance {
  display: block;
  margin-top: var(--ksf-space-4);
  font-size: 11px;
  letter-spacing: 0.06em;
  color: var(--ksf-ink-secondary);
}

/* Ground-adaptive, the same technique as testimony's hairline and
   band's ornament: the frame border is mixed from currentColor rather
   than a fixed token, so it reads correctly on any ground without a
   per-ground override. The caption and provenance text follow the same
   two values base.css already uses for ksf-label/ksf-small/ksf-note/
   ksf-lede on a ceremonial ground, per devices.css's precedent for
   ksf-mark and chrome.css's for ksf-footer, rather than a third repeat
   of that colour logic. */
.ksf-on-brass .ksf-figure__caption,
.ksf-on-brass .ksf-figure__provenance { color: var(--ksf-ink-on-brass); }
.ksf-on-evergreen .ksf-figure__caption,
.ksf-on-evergreen .ksf-figure__provenance,
.ksf-on-closing .ksf-figure__caption,
.ksf-on-closing .ksf-figure__provenance { color: color-mix(in srgb, var(--ksf-ink-on-dark) 72%, transparent); }


/* === src/components/masthead.css === */
/* Masthead. ADOPTED (decision 38): in production use since Phase 3
   step 6's homepage pilot.
   The homepage hero (decision 7): the Foundation emblem, a bilingual
   motto, an optional lede, a dates line and an action row, stacked and
   centred on the Evergreen ceremonial ground. Extends the pattern
   Templates/Remembrance started; Brass is deprecated as a ground
   (decision 31), so unlike the old two-ground Remembrance story there
   is only one ground here.

   A layout module of its own, not inline composition in the page
   template: the stack needs a responsive emblem size, an action row
   that wraps and re-centres on a narrow screen, and an optional fade,
   which is more structure than the template-level inline styles used
   for the old two-line ceremonial block.

   Rays (decision 33, supersedes the no-rays-layer clause of decision 23
   for the masthead specifically; the emblem keeps its own internal rays
   regardless). A full-bleed CSS layer behind the emblem, no image asset:
   a repeating-conic-gradient on a pseudo-element, Walnut wedges mixed to
   transparency with color-mix. It lives on ksf-masthead__emblem-wrap,
   not on the section itself, so it always radiates from the emblem's
   actual centre regardless of section height or breakpoint padding,
   rather than a hand-tuned pixel offset per breakpoint. overflow:hidden
   on the section clips it at the section edge, so it can never cause
   horizontal scroll. z-index:-1 keeps it behind every other child, the
   same technique devices.css already uses for the mark's on-dark disc;
   ksf-masthead itself needs its own z-index:0 alongside position:relative
   so that -1 stays contained inside the section instead of escaping to
   an ancestor with no local stacking context and painting behind the
   whole page. No motion beyond the optional fade below;
   prefers-reduced-motion is handled once, globally, in base.css.

   Opacity: tried 4%, 6% and 8% against the full masthead. Contrast is
   not the deciding factor: Walnut is close enough in luminance to
   Evergreen that even 8% directly under a wedge leaves text contrast
   against Bone slightly better than pure Evergreen (12.17:1 against
   11.89:1, both far past AAA). 8% is kept because it is the first of
   the three where the wedges read as a deliberate device rather than a
   texture you have to look for; 4% and 6% were nearly invisible in the
   rendered page. */
.ksf-masthead {
  position: relative;
  z-index: 0;
  overflow: hidden;
  padding: var(--ksf-space-76) var(--ksf-space-32);
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  gap: var(--ksf-space-16);
}

.ksf-masthead__emblem-wrap {
  position: relative;
}

.ksf-masthead--rays .ksf-masthead__emblem-wrap::before {
  content: "";
  position: absolute;
  top: 50%;
  left: 50%;
  width: 1400px;
  height: 1400px;
  transform: translate(-50%, -50%);
  z-index: -1;
  background: repeating-conic-gradient(
    color-mix(in srgb, var(--ksf-colour-walnut) 100%, transparent) 0deg 5deg,
    transparent 5deg 10deg
  );
  opacity: 0.08;
  -webkit-mask-image: radial-gradient(circle, color-mix(in srgb, var(--ksf-colour-walnut) 100%, transparent) 28%, transparent 68%);
  mask-image: radial-gradient(circle, color-mix(in srgb, var(--ksf-colour-walnut) 100%, transparent) 28%, transparent 68%);
  pointer-events: none;
}

.ksf-masthead__emblem {
  display: block;
  position: relative;
  width: 220px;
  height: 220px;
  margin-bottom: var(--ksf-space-16);
}

.ksf-masthead__motto-ku,
.ksf-masthead__motto-en {
  margin: 0;
}

.ksf-masthead__lede {
  max-width: 34rem;
  margin-top: var(--ksf-space-8);
}
/* EB Garamond (ksf-lede's font) has no Arabic-script glyphs, so a
   Sorani or Arabic lede needs the ku reading face instead, the same
   [lang] gate figure.css already uses for its own caption. Without
   this the browser silently falls back to an uncontrolled font for
   any real Sorani lede text, which is exactly what the first real
   homepage build (Phase 3 step 6) needed to pass through here. */
.ksf-masthead[lang="ckb"] .ksf-masthead__lede,
.ksf-masthead[lang="ar"] .ksf-masthead__lede {
  font-family: var(--ksf-font-ku-reading);
  font-style: normal;
  font-size: 19px;
  line-height: 1.95;
}

.ksf-masthead__dates {
  margin-top: var(--ksf-space-8);
}

.ksf-masthead__actions {
  display: flex;
  gap: var(--ksf-space-16);
  flex-wrap: wrap;
  justify-content: center;
  margin-top: var(--ksf-space-32);
}

@media (max-width: 640px) {
  .ksf-masthead {
    padding: var(--ksf-space-48) var(--ksf-space-16);
  }
  .ksf-masthead__emblem {
    width: 180px;
    height: 180px;
  }
}

/* Motto (ksf-display / ksf-ku-display) and the action row take their ink
   from ksf-on-evergreen's own color, since neither sets a fixed colour
   of its own. Lede and dates (ksf-lede, ksf-code) already have a dark
   ground override in base.css; nothing new is needed for either. */
.ksf-masthead--fade {
  animation: ksf-masthead-fade 0.4s ease both;
}
@keyframes ksf-masthead-fade {
  from { opacity: 0; }
  to { opacity: 1; }
}
