/*! OKNO Design System v1 — generated bundle. Do not edit.
 * Built from scripts/design-system-v1/{tokens.css, primitives.css, components.css, patterns.css, activation.css} by build-bundle.mjs.
 * Source grammar: Design Lab R2.3, build 9c3b80cfe400e61866d146e491918768eec209dacbb1d64a4a6cf57255d59944.
 * Edit the layer sources and rebuild; editing this file makes the system and its contract disagree.
 */

/* ===== tokens.css ===== */
/* OKNO Design System v1 — tokens.
 *
 * The only place in the system where a visual value is written down. Ported verbatim from the accepted
 * Design Lab R2.3 build (9c3b80cf…), which passed SYSTEM_ARCHITECTURE and VISUAL_LANGUAGE review; the
 * values were authored by the owner's foundation document, not by this implementation.
 *
 * Namespace note: production already defines 22 --okno-* properties of its own (--okno-navy,
 * --okno-radius, --okno-green and so on, in okno-global-styles.css). None of the 55 names below
 * collides with them — verified before the port, and re-checked by validate-system.mjs.
 */
:root {
  /* colour roles */
  --okno-ink-strong: #0c1e3c;
  --okno-ink-body: #33404f;
  --okno-ink-muted: #5b6472;
  --okno-accent: #0d7c6b;
  --okno-accent-strong: #166b56;
  --okno-canvas: #ffffff;
  --okno-surface-subtle: #f6f8fb;
  --okno-surface-accent: #eaf6f1;
  --okno-border: #e7ecf3;
  --okno-inverse: #ffffff;
  --okno-surface-warm: #f7f7f4;
  --okno-surface-inverse: var(--okno-ink-strong);
  /* The inverse surface needs an ink that sits on it. The delta names the surface but not its muted
     text, and an inverse panel is unusable without one, so the role is declared here rather than left
     to a literal inside a component. */
  --okno-ink-inverse-muted: rgba(255, 255, 255, .72);
  --okno-border-inverse: rgba(255, 255, 255, .18);

  /* spacing scale */
  --okno-space-4: 4px;
  --okno-space-8: 8px;
  --okno-space-12: 12px;
  --okno-space-16: 16px;
  --okno-space-20: 20px;
  --okno-space-24: 24px;
  --okno-space-32: 32px;
  --okno-space-40: 40px;
  --okno-space-48: 48px;
  --okno-space-64: 64px;
  --okno-space-80: 80px;
  --okno-space-96: 96px;

  /* width roles */
  --okno-container-wide: 1240px;
  --okno-container-default: 1120px;
  --okno-container-reading: 760px;
  --okno-container-narrow: 640px;

  /* The single rule weight this system draws. It was always 1px and was always written out by hand;
     naming it makes a divider gap self-documenting instead of an unexplained literal. */
  --okno-hairline: 1px;

  /* radius roles */
  --okno-radius-control: 10px;
  --okno-radius-card: 14px;
  --okno-radius-panel: 18px;
  --okno-radius-focal: 22px;
  --okno-radius-round: 999px;

  /* elevation — three steps, each a contact shadow plus an ambient one.
     Retuned in VISUAL_POLISH_R1. The R2.3 values were a single soft blur at 5–10% opacity, which on a
     white canvas produced no readable edge at all: the owner's verdict on the live page was that it
     reads as a white stream of text on white. A surface needs a short dark contact shadow to sit on
     the page and a long soft one to lift off it; one blur can only do one of those jobs. The steps are
     resting / lifted / focal, and every card family reads them through the tier variables below, so
     the whole system moves together. */
  --okno-shadow-low: 0 1px 1px rgba(12, 30, 60, .04), 0 3px 8px rgba(12, 30, 60, .05);
  --okno-shadow-mid: 0 2px 4px rgba(12, 30, 60, .05), 0 14px 32px rgba(12, 30, 60, .10);
  --okno-shadow-focal: 0 4px 10px rgba(12, 30, 60, .06), 0 28px 60px rgba(12, 30, 60, .14);

  /* SURFACE TIERS — the four-tier card family every surface in the system reads from.
     A component never writes its own background, border, radius or elevation: it names a tier. That is
     what keeps a page from growing ten unrelated card looks, and it means a change to a tier reaches
     every member at once instead of being copied into each family. */
  /* Base — ordinary navigational and informational cards. */
  --okno-tier-base-bg: var(--okno-canvas);
  --okno-tier-base-border: var(--okno-hairline) solid var(--okno-border);
  --okno-tier-base-radius: var(--okno-radius-card);
  --okno-tier-base-shadow: var(--okno-shadow-low);
  /* Raised — featured cards, the founder card, key CTA surfaces. Carries its own foreground identity. */
  --okno-tier-raised-bg: var(--okno-canvas);
  --okno-tier-raised-border: var(--okno-hairline) solid var(--okno-border);
  --okno-tier-raised-radius: var(--okno-radius-panel);
  --okno-tier-raised-shadow: var(--okno-shadow-mid);
  /* Quiet — secondary content that still needs containment and must not compete. */
  --okno-tier-quiet-bg: var(--okno-surface-subtle);
  --okno-tier-quiet-border: var(--okno-hairline) solid transparent;
  --okno-tier-quiet-radius: var(--okno-radius-card);
  --okno-tier-quiet-shadow: none;
  /* Cluster — several items that must read as one object: a route group, a service group, an index. */
  --okno-tier-cluster-bg: var(--okno-canvas);
  --okno-tier-cluster-border: var(--okno-hairline) solid var(--okno-border);
  --okno-tier-cluster-radius: var(--okno-radius-panel);
  --okno-tier-cluster-shadow: var(--okno-shadow-low);
  /* The hover state shared by every interactive surface, so a card cannot invent its own. */
  --okno-tier-hover-shadow: var(--okno-shadow-mid);
  --okno-tier-hover-border: var(--okno-accent);

  /* typography — fluid between the mobile and desktop targets in the foundation */
  --okno-font: "Mulish", system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
  /* Each ramp is solved so the foundation's mobile figure lands at a real phone width (390px) and the
     desktop figure lands at the wide rail, instead of reaching the desktop size while still on a phone. */
  --okno-h1-size: clamp(32px, 28.3px + .94vw, 40px);   /* 32 at 390, 40 from ~1240 */
  --okno-h1-line: 1.1;
  --okno-h2-size: clamp(26px, 24.2px + .47vw, 30px);   /* 26 at 390, 30 from ~1240 */
  --okno-h2-line: 1.2;
  --okno-h3-size: clamp(20px, 18.6px + .35vw, 23px);   /* 20 at 390, 23 from ~1240 */
  --okno-h3-line: 1.22;
  --okno-body-size: 16px;
  --okno-body-line: 1.6;
  --okno-compact-size: 14px;
  --okno-compact-line: 1.57;
  --okno-eyebrow-size: 13px;
  --okno-eyebrow-line: 1.3;
  /* Two roles the foundation scale does not name, proven missing by the Lab: the lead paragraph that
     opens a page sits between body and H3, and a card title sits between body and H3 without being a
     heading. Both are declared here so no component can reach for a literal. */
  --okno-lead-size: 18px;
  --okno-lead-size-editorial: 19px;
  --okno-card-title-size: 17px;

  /* motion
     Three durations and one easing curve. `--okno-motion` is the interaction step that was already
     here; `--okno-motion-enter` is the slower one an element uses when it arrives on screen, and it is
     deliberately long enough to read as staging rather than as a flicker. `--okno-lift` and
     `--okno-reveal-shift` are the only two distances the motion language moves anything, so nothing in
     the system can drift further than the system intends. Every one of these is inert under
     prefers-reduced-motion — see the reduced-motion block in primitives.css. */
  --okno-motion: 180ms cubic-bezier(.2, .6, .3, 1);
  --okno-motion-enter: 560ms cubic-bezier(.22, .68, .28, 1);
  --okno-ease-out: cubic-bezier(.22, .68, .28, 1);
  --okno-lift: -3px;
  --okno-reveal-shift: 16px;
  --okno-stagger: 70ms;
}

/* ===== primitives.css ===== */
/* OKNO Design System v1 — layout primitives.
 *
 * Nine primitives and a base layer, ported verbatim from Design Lab R2.3. Every primitive is intrinsic:
 * it reacts to the space it is given rather than to a device width, which is why there are almost no
 * media queries — columns collapse because the content stopped fitting, not because a phone was seen.
 *
 * READ BEFORE USE: the first block restyles bare html/body/h1-h4/p/img/a. It is a page-level base and
 * must only reach a page that has been migrated to this system. Loading the bundle onto a page that
 * still carries its own typography would restyle that page. This is why the bundle is dormant in M0.
 */
/* ---------- base — inside the activation boundary only ----------
   In the Lab the document *was* the system, so these were bare element selectors. In production the
   system owns a subtree: everything below is scoped to .okl-page, which a migrated page puts around
   its own content and nothing else. A legacy page, the global shell and every non-migrated builder
   therefore keep the typography they already have — the boundary is what makes that true, and
   activation.css is what makes it win against the legacy !important rules.
   Nothing here is page-specific: .okl-page is the one shared root every migrated family uses. */
/* The element rules below are wrapped in :where() so the boundary costs them no specificity. Scoping
   them to .okl-page raised `a { color: inherit }` from (0,0,1) to (0,1,1), which is one class more
   than .okl-btn--primary — so every anchor styled as a primary button silently inherited body ink on
   the green surface while a <button> rendered correctly. One component, two appearances, caused by
   the boundary rather than by the component. :where() keeps the scope and restores the weight these
   rules had before it. */
:where(.okl-page), :where(.okl-page) *, :where(.okl-page) *::before, :where(.okl-page) *::after { box-sizing: border-box; }
.okl-page {
  display: block;
  -webkit-text-size-adjust: 100%;
  font-family: var(--okno-font);
  font-size: var(--okno-body-size);
  line-height: var(--okno-body-line);
  color: var(--okno-ink-body);
  background: var(--okno-canvas);
  -webkit-font-smoothing: antialiased;
  overflow-wrap: break-word;       /* long words wrap at the edge instead of pushing the page sideways */
  word-break: normal;              /* but never inside a word */
  hyphens: none;
}
:where(.okl-page) :where(h1, h2, h3, h4) { color: var(--okno-ink-strong); margin: 0; font-weight: 800; letter-spacing: -.02em; text-wrap: balance; }
:where(.okl-page) :where(h1) { font-size: var(--okno-h1-size); line-height: var(--okno-h1-line); }
:where(.okl-page) :where(h2) { font-size: var(--okno-h2-size); line-height: var(--okno-h2-line); }
:where(.okl-page) :where(h3) { font-size: var(--okno-h3-size); line-height: var(--okno-h3-line); font-weight: 700; }
:where(.okl-page) :where(p) { margin: 0; text-wrap: pretty; }
:where(.okl-page) :where(img) { max-width: 100%; height: auto; display: block; }
:where(.okl-page) :where(a) { color: inherit; }
:where(.okl-page) :focus-visible { outline: 2px solid var(--okno-accent); outline-offset: 3px; border-radius: var(--okno-radius-control); }

/* ---------- Container — semantic max-width rails ---------- */
.okl-container { width: 100%; margin-inline: auto; padding-inline: var(--okno-space-12); max-width: calc(var(--okno-container-default) + var(--okno-space-40)); }
@media (min-width: 380px) { .okl-container { padding-inline: var(--okno-space-20); } }
.okl-container--wide { max-width: calc(var(--okno-container-wide) + var(--okno-space-40)); }
.okl-container--reading { max-width: calc(var(--okno-container-reading) + var(--okno-space-40)); }
.okl-container--narrow { max-width: calc(var(--okno-container-narrow) + var(--okno-space-40)); }
@media (min-width: 768px) { .okl-container { padding-inline: var(--okno-space-32); } }

/* ---------- Stack — vertical rhythm ---------- */
.okl-stack { display: flex; flex-direction: column; gap: var(--okno-space-16); }
.okl-stack--4 { gap: var(--okno-space-4); }
.okl-stack--8 { gap: var(--okno-space-8); }
.okl-stack--12 { gap: var(--okno-space-12); }
.okl-stack--20 { gap: var(--okno-space-20); }
.okl-stack--24 { gap: var(--okno-space-24); }
.okl-stack--32 { gap: var(--okno-space-32); }
.okl-stack--40 { gap: var(--okno-space-40); }
.okl-stack--48 { gap: var(--okno-space-48); }

/* ---------- Cluster — inline wrapping groups ---------- */
.okl-cluster { display: flex; flex-wrap: wrap; gap: var(--okno-space-12); align-items: center; }
.okl-cluster--8 { gap: var(--okno-space-8); }
.okl-cluster--16 { gap: var(--okno-space-16); }
.okl-cluster--24 { gap: var(--okno-space-24); }
.okl-cluster--start { align-items: flex-start; }

/* ---------- Grid — repeated peers, column count from available width ---------- */
.okl-grid { display: grid; gap: var(--okno-space-20); grid-template-columns: repeat(auto-fit, minmax(min(100%, var(--okl-min, 260px)), 1fr)); }
.okl-grid--24 { gap: var(--okno-space-24); }
.okl-grid--32 { gap: var(--okno-space-32); }
.okl-grid--wide { --okl-min: 320px; }
.okl-grid--narrow { --okl-min: 200px; }
/* fixed peer counts: used where the peers must stay peers rather than reflow into an odd row */
.okl-grid--2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.okl-grid--3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
@media (max-width: 860px) { .okl-grid--3 { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 640px) { .okl-grid--2, .okl-grid--3 { grid-template-columns: minmax(0, 1fr); } }

/* ---------- Split — text + media, or two peer panels ---------- */
.okl-split { display: grid; gap: var(--okno-space-32); align-items: center; grid-template-columns: minmax(0, 1fr); }
@media (min-width: 900px) {
  .okl-split { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: var(--okno-space-48); }
  .okl-split--text-lead { grid-template-columns: minmax(0, 1.15fr) minmax(0, .85fr); }
  .okl-split--media-lead { grid-template-columns: minmax(0, .85fr) minmax(0, 1.15fr); }
}
.okl-split--top { align-items: start; }

/* ---------- Switcher — stacks when a child would get too narrow ---------- */
.okl-switcher { display: flex; flex-wrap: wrap; gap: var(--okno-space-20); }
.okl-switcher > * { flex: 1 1 var(--okl-switch, 320px); min-width: 0; }

/* ---------- Sidebar — main content with a bounded supporting rail ---------- */
.okl-sidebar { display: grid; gap: var(--okno-space-32); grid-template-columns: minmax(0, 1fr); align-items: start; }
@media (min-width: 1000px) {
  .okl-sidebar { grid-template-columns: minmax(0, 1fr) minmax(240px, 280px); gap: var(--okno-space-48); }
  .okl-sidebar--lead { grid-template-columns: minmax(240px, 280px) minmax(0, 1fr); }
  .okl-sidebar__rail { position: sticky; top: var(--okno-space-24); }
}

/* ---------- Frame — controlled media ratio and crop ---------- */
.okl-frame { position: relative; overflow: hidden; border-radius: var(--okno-radius-card); background: var(--okno-surface-subtle); }
/* Production serves art-directed media as <picture><source><img>. The Lab only ever had a bare <img>,
   so a child selector would miss the real image and the frame would collapse. */
.okl-frame > picture { display: contents; }
.okl-frame > img, .okl-frame > picture > img { width: 100%; height: 100%; object-fit: cover; display: block; }
.okl-frame--21x9 { aspect-ratio: 21 / 9; }
.okl-frame--16x9 { aspect-ratio: 16 / 9; }
.okl-frame--3x2 { aspect-ratio: 3 / 2; }
.okl-frame--4x3 { aspect-ratio: 4 / 3; }
.okl-frame--1x1 { aspect-ratio: 1 / 1; }
.okl-frame--4x5 { aspect-ratio: 4 / 5; }
.okl-frame--9x16 { aspect-ratio: 9 / 16; }
.okl-frame--panel { border-radius: var(--okno-radius-panel); }
/* MediaFrame / studio-group.
   For studio group photography on a neutral backdrop, where the neutral space around the subjects is
   part of the photograph rather than a defect to crop out. The frame takes no ratio of its own: it
   follows the picture, so the picture is never cropped and never sits inside a mat the layout invented.
   Clipping exists only so the shared rounded edge still applies. */
.okl-frame--studio-group { aspect-ratio: auto; background: transparent; }
.okl-frame--studio-group > img, .okl-frame--studio-group > picture > img { width: 100%; height: auto; display: block; object-fit: initial; transform: none; }

/* ---------- Inset — internal panel padding ---------- */
.okl-inset { padding: var(--okno-space-20); }
.okl-inset--24 { padding: var(--okno-space-24); }
.okl-inset--32 { padding: var(--okno-space-32); }
@media (min-width: 768px) {
  .okl-inset { padding: var(--okno-space-24); }
  .okl-inset--24 { padding: var(--okno-space-32); }
  .okl-inset--32 { padding: var(--okno-space-40); }
}

/* ---------- surfaces: L0 canvas, L1 section, L2 raised panel, L3 focal ---------- */
.okl-s1 { background: var(--okno-surface-subtle); }
.okl-s1--accent { background: var(--okno-surface-accent); }
.okl-s1--warm { background: var(--okno-surface-warm); }
.okl-s2 { background: var(--okno-canvas); border: 1px solid var(--okno-border); border-radius: var(--okno-radius-panel); box-shadow: var(--okno-shadow-mid); }
/* L3 is the only inverse surface in the system. It is scarce by construction: one per page at most. */
.okl-s3 { background: var(--okno-surface-inverse); border-radius: var(--okno-radius-focal); box-shadow: var(--okno-shadow-focal); color: var(--okno-inverse); }
.okl-s3 h1, .okl-s3 h2, .okl-s3 h3 { color: var(--okno-inverse); }
.okl-s3 .okl-muted, .okl-s3 .okl-compact { color: var(--okno-ink-inverse-muted); }
.okl-s3 :focus-visible { outline-color: var(--okno-inverse); }

/* ---------- section rhythm ---------- */
.okl-section { padding-block: var(--okno-space-48); }
@media (min-width: 768px) { .okl-section { padding-block: var(--okno-space-64); } }
@media (min-width: 1200px) { .okl-section { padding-block: var(--okno-space-80); } }
.okl-section--tight { padding-block: var(--okno-space-32); }
@media (min-width: 768px) { .okl-section--tight { padding-block: var(--okno-space-48); } }

/* ---------- SURFACE TIERS — the shared card family ----------
 * Four tiers, and a page picks one rather than describing a look. They exist as classes so a future
 * page family can compose a surface without a new component, and every existing card family reads the
 * same tier variables, so the four of them are genuinely one system rather than four descriptions
 * that happen to agree today.
 *
 *   okl-surface            Base     ordinary navigational and informational cards
 *   okl-surface--raised    Elevated featured objects, the founder card, key CTA surfaces
 *   okl-surface--quiet     Quiet    secondary content that still needs containment
 *   okl-surface--cluster   Cluster  several items that must read as one object
 *
 * Add okl-surface--act to any surface a visitor can click: it gets the shared hover, press and focus
 * behaviour, so no family invents its own interaction.
 */
.okl-surface { background: var(--okno-tier-base-bg); border: var(--okno-tier-base-border);
  border-radius: var(--okno-tier-base-radius); box-shadow: var(--okno-tier-base-shadow); }
.okl-surface--raised { background: var(--okno-tier-raised-bg); border: var(--okno-tier-raised-border);
  border-radius: var(--okno-tier-raised-radius); box-shadow: var(--okno-tier-raised-shadow); }
.okl-surface--quiet { background: var(--okno-tier-quiet-bg); border: var(--okno-tier-quiet-border);
  border-radius: var(--okno-tier-quiet-radius); box-shadow: var(--okno-tier-quiet-shadow); }
.okl-surface--cluster { background: var(--okno-tier-cluster-bg); border: var(--okno-tier-cluster-border);
  border-radius: var(--okno-tier-cluster-radius); box-shadow: var(--okno-tier-cluster-shadow); overflow: hidden; }
.okl-surface--pad { padding: var(--okno-space-24); }
@media (min-width: 768px) { .okl-surface--pad { padding: var(--okno-space-32); } }

/* The one interaction behaviour in the system. A clickable surface lifts by a fixed distance, borrows
   the accent for its edge and steps up exactly one elevation — the same everywhere, so a card cannot
   feel more or less alive than its neighbour for no reason. */
.okl-surface--act { transition: transform var(--okno-motion), box-shadow var(--okno-motion), border-color var(--okno-motion); }
.okl-surface--act:hover { transform: translateY(var(--okno-lift)); box-shadow: var(--okno-tier-hover-shadow); border-color: var(--okno-tier-hover-border); }
.okl-surface--act:active { transform: translateY(0); box-shadow: var(--okno-shadow-low); }

/* ---------- MOTION — reveal on entry ----------
 * The whole motion language: an element arrives by fading up over a fixed distance, and a set of
 * siblings arrives in order rather than all at once. Nothing else moves.
 *
 * The pre-reveal state is behind `[data-okl-motion="on"]`, and only the runtime sets that attribute —
 * after it has checked that the browser can observe intersections and that the visitor has not asked
 * for reduced motion. A page whose script fails to load therefore renders every word visible, which is
 * the only acceptable default: motion may never be the reason copy is not on the screen.
 */
/* The family's base state is VISIBLE. That is the declaration, not an oversight: everything below is
   an override that only exists while the runtime is driving. */
.okl-rv { opacity: 1; transform: none; }
[data-okl-motion="on"] .okl-rv { opacity: 0; transform: translateY(var(--okno-reveal-shift)); }
[data-okl-motion="on"] .okl-rv { transition: opacity var(--okno-motion-enter), transform var(--okno-motion-enter); }
[data-okl-motion="on"] .okl-rv.okl-rv--in { opacity: 1; transform: none; }
/* Six steps is the whole stagger. Past that a set reads as a queue rather than as a scene arriving. */
[data-okl-motion="on"] .okl-rv--s1 { transition-delay: calc(var(--okno-stagger) * 1); }
[data-okl-motion="on"] .okl-rv--s2 { transition-delay: calc(var(--okno-stagger) * 2); }
[data-okl-motion="on"] .okl-rv--s3 { transition-delay: calc(var(--okno-stagger) * 3); }
[data-okl-motion="on"] .okl-rv--s4 { transition-delay: calc(var(--okno-stagger) * 4); }
[data-okl-motion="on"] .okl-rv--s5 { transition-delay: calc(var(--okno-stagger) * 5); }

/* The runtime already declines to start under prefers-reduced-motion, so this block is the second of
   two independent guarantees rather than the only one. It also covers the hover lift and every
   transition the components declare, which the runtime has no say over. */
@media (prefers-reduced-motion: reduce) {
  .okl-page *, .okl-page *::before, .okl-page *::after {
    animation-duration: 1ms !important; animation-iteration-count: 1 !important;
    transition-duration: 1ms !important; transition-delay: 0ms !important; scroll-behavior: auto !important;
  }
  [data-okl-motion="on"] .okl-rv, [data-okl-motion="on"] .okl-rv.okl-rv--in { opacity: 1; transform: none; }
  .okl-surface--act:hover { transform: none; }
}

/* ---------- fragment alias ----------
 * A migrated page may inherit fragment ids from the page it replaces: an old bookmark, a campaign
 * link or an external citation must keep landing in the right place after the section id changes.
 * The alias is an empty element placed immediately before its section, so its top edge is the
 * section's top edge and both fragments scroll to the same position. It has no box, no content and
 * no text, so it adds nothing a visitor can read or reach. It is never a second visible heading. */
.okl-alias { display: block; height: 0; overflow: hidden; }

/* ---------- reading measure ---------- */
.okl-measure { max-width: var(--okno-container-reading); }
.okl-measure--narrow { max-width: var(--okno-container-narrow); }

@media (prefers-reduced-motion: reduce) {
  /* Scoped like the rest of the base. A visitor who asks for less motion is honoured inside migrated
     content; legacy pages keep whatever they already do, which is not this system's call to change. */
  .okl-page, .okl-page *, .okl-page *::before, .okl-page *::after { animation-duration: .001ms !important; transition-duration: .001ms !important; scroll-behavior: auto !important; }
}

/* ---------- visibility roles: one element, shown where its composition belongs ---------- */
.okl-only-wide { display: none; }
.okl-only-narrow { display: block; }
@media (min-width: 1000px) {
  .okl-only-wide { display: block; }
  .okl-only-narrow { display: none; }
}

/* ===== components.css ===== */
/* OKNO Design System v1 — components.
 *
 * One family per species, ported verbatim from Design Lab R2.3. A page may not add a button, card or
 * panel family of its own; a genuine variation is added here and recorded in contract.json.
 */
/* ---------- Button ---------- */
.okl-btn {
  display: inline-flex; align-items: center; justify-content: center; gap: var(--okno-space-8);
  min-height: 48px; padding: var(--okno-space-12) var(--okno-space-24);
  border-radius: var(--okno-radius-control);          /* deliberately not a pill */
  font: inherit; font-weight: 700; line-height: 1.2; text-decoration: none; cursor: pointer;
  border: 1px solid transparent; transition: background-color var(--okno-motion), border-color var(--okno-motion), box-shadow var(--okno-motion), color var(--okno-motion);
}
.okl-btn--primary { background: var(--okno-accent); color: var(--okno-inverse); box-shadow: var(--okno-shadow-low); }
.okl-btn--primary:hover { background: var(--okno-accent-strong); }
.okl-btn--secondary { background: var(--okno-canvas); color: var(--okno-ink-strong); border-color: var(--okno-border); }
.okl-btn--secondary:hover { border-color: var(--okno-accent); color: var(--okno-accent-strong); }
.okl-btn--quiet { background: transparent; color: var(--okno-accent-strong); padding-inline: var(--okno-space-4); min-height: 44px; font-weight: 600; font-size: var(--okno-compact-size); text-decoration: underline; text-underline-offset: 4px; text-decoration-color: var(--okno-border); }
.okl-btn--quiet:hover { color: var(--okno-ink-strong); text-decoration-color: currentColor; }
.okl-btn--lg { min-height: 56px; padding: var(--okno-space-16) var(--okno-space-32); font-size: var(--okno-card-title-size); }

/* ---------- TextLink ---------- */
.okl-link { color: var(--okno-accent-strong); text-decoration: none; font-weight: 700; border-bottom: 1px solid transparent; transition: border-color var(--okno-motion), color var(--okno-motion); }
.okl-link:hover { border-bottom-color: currentColor; }
.okl-link--arrow::after { content: "→"; display: inline-block; margin-inline-start: var(--okno-space-8); transition: transform var(--okno-motion); }
.okl-link--arrow:hover::after { transform: translateX(5px); }

/* ---------- NavigationCard — fully clickable, one family ---------- */
/* Base tier. The family no longer writes its own background, border, radius or elevation — it names
   the tier, so a change to the card family reaches every member of the system at once. */
.okl-navcard {
  display: flex; flex-direction: column; gap: var(--okno-space-8);
  background: var(--okno-tier-base-bg); border: var(--okno-tier-base-border); border-radius: var(--okno-tier-base-radius);
  box-shadow: var(--okno-tier-base-shadow);
  padding: var(--okno-space-24); text-decoration: none; color: inherit; min-height: 100%;
  transition: border-color var(--okno-motion), box-shadow var(--okno-motion), transform var(--okno-motion);
}
.okl-navcard:hover { border-color: var(--okno-tier-hover-border); box-shadow: var(--okno-tier-hover-shadow); transform: translateY(var(--okno-lift)); }
.okl-navcard:active { transform: translateY(0); box-shadow: var(--okno-shadow-low); }
.okl-navcard__t { font-size: var(--okno-card-title-size); font-weight: 800; color: var(--okno-ink-strong); line-height: 1.3; }
.okl-navcard__d { font-size: var(--okno-compact-size); line-height: var(--okno-compact-line); color: var(--okno-ink-muted); }
/* The action affordance is a drawn object rather than a loose glyph at the end of a paragraph: a
   round chip on the accent tint, aligned to the card's baseline grid. It is the same chip everywhere
   an arrow means "this whole surface is the link", so the eye learns it once. */
.okl-navcard__go { margin-top: auto; padding-top: var(--okno-space-16); font-size: var(--okno-compact-size); font-weight: 700; color: var(--okno-accent-strong); display: flex; align-items: center; gap: var(--okno-space-8); }
.okl-navcard__go::after { content: "→"; display: inline-flex; align-items: center; justify-content: center;
  width: 30px; height: 30px; border-radius: var(--okno-radius-round); background: var(--okno-surface-accent);
  transition: transform var(--okno-motion), background-color var(--okno-motion); }
.okl-navcard:hover .okl-navcard__go::after { transform: translateX(4px); background: var(--okno-accent); color: var(--okno-inverse); }
/* Same narrow-width rule as the index: the chip is decoration, the arrow is the affordance, and below
   420px the card needs its width more than it needs the chip. */
@media (max-width: 419px) {
  .okl-navcard { padding: var(--okno-space-20); }
  .okl-navcard__go::after, .okl-case__go::after { width: auto; height: auto; background: none; }
}
/* on a subtle section the card keeps its contrast against the surface it sits on */
.okl-s1 .okl-navcard { box-shadow: var(--okno-shadow-mid); }

/* ---------- InformationRow — never clickable, must not imitate a NavigationCard ---------- */
.okl-inforow { display: grid; gap: var(--okno-space-4); padding-block: var(--okno-space-16); border-top: 1px solid var(--okno-border); }
.okl-inforow:last-child { border-bottom: 1px solid var(--okno-border); }
.okl-inforow__t { font-weight: 700; color: var(--okno-ink-strong); }
.okl-inforow__d { font-size: var(--okno-compact-size); line-height: var(--okno-compact-line); color: var(--okno-ink-muted); }
@media (min-width: 720px) { .okl-inforow { grid-template-columns: minmax(0, 260px) minmax(0, 1fr); gap: var(--okno-space-24); align-items: baseline; } }

/* ---------- Eyebrow / Badge ----------
 * No `text-transform`. The eyebrow carries owner-authored group labels — whole sentences such as
 * «Если вы живёте в Португалии уже несколько лет» — and a component may not decide how the owner's
 * copy is capitalised. Until the GPT copy review of 2026-09-27 this rule carried
 * `text-transform: uppercase`, which silently rewrote four approved strings per locale on the
 * Homepage; a copy check that read textContent could not see it, because CSS casing never reaches
 * textContent. Removed at the shared component, not patched per page. */
.okl-eyebrow { font-size: var(--okno-eyebrow-size); line-height: var(--okno-eyebrow-line); font-weight: 800; letter-spacing: .02em; color: var(--okno-accent-strong);
  display: flex; align-items: center; gap: var(--okno-space-12); }
/* A drawn mark makes the label read as interface furniture rather than as a stray small sentence. The
   tracking above is the sentence-case figure: .08em was chosen for capitals, and the capitals are
   gone. */
.okl-eyebrow::before { content: ""; width: 18px; height: 2px; flex: none; border-radius: var(--okno-radius-round); background: var(--okno-accent); }
.okl-badge { display: inline-flex; align-self: flex-start; width: fit-content; align-items: center; gap: var(--okno-space-8); padding: var(--okno-space-8) var(--okno-space-16); border-radius: var(--okno-radius-round); background: var(--okno-surface-accent); color: var(--okno-accent-strong); font-size: var(--okno-eyebrow-size); font-weight: 800; line-height: var(--okno-eyebrow-line); }
.okl-badge--quiet { background: var(--okno-surface-subtle); color: var(--okno-ink-muted); }

/* ---------- MediaFrame caption ---------- */
.okl-media { display: grid; gap: var(--okno-space-8); text-decoration: none; color: inherit; }
a.okl-media:hover .okl-media__cap { color: var(--okno-accent-strong); }
.okl-media__cap { font-size: var(--okno-compact-size); line-height: var(--okno-compact-line); color: var(--okno-ink-muted); }

/* ---------- AccordionItem ---------- */
.okl-acc { border-top: 1px solid var(--okno-border); }
.okl-acc:last-of-type { border-bottom: 1px solid var(--okno-border); }
.okl-acc__q { display: flex; align-items: flex-start; justify-content: space-between; gap: var(--okno-space-16); padding-block: var(--okno-space-20); min-height: 48px; cursor: pointer; font-weight: 700; color: var(--okno-ink-strong); list-style: none; }
.okl-acc__q::-webkit-details-marker { display: none; }
.okl-acc__q::after { content: "+"; font-size: 22px; line-height: 1; color: var(--okno-accent-strong); transition: transform var(--okno-motion); flex: none; }
.okl-acc[open] .okl-acc__q::after { transform: rotate(45deg); }
.okl-acc__a { padding-bottom: var(--okno-space-20); display: grid; gap: var(--okno-space-12); color: var(--okno-ink-body); max-width: var(--okno-container-reading); }

/* ---------- CasePreview — image optional, species unchanged ---------- */
/* Raised tier: a featured case is a foreground object, not a row. */
.okl-case { display: flex; flex-direction: column; background: var(--okno-tier-raised-bg); border: var(--okno-tier-raised-border); border-radius: var(--okno-tier-raised-radius); box-shadow: var(--okno-tier-raised-shadow); overflow: hidden; text-decoration: none; color: inherit; min-height: 100%; transition: border-color var(--okno-motion), box-shadow var(--okno-motion), transform var(--okno-motion); }
.okl-case:hover { border-color: var(--okno-tier-hover-border); box-shadow: var(--okno-shadow-focal); transform: translateY(var(--okno-lift)); }
.okl-case:active { transform: translateY(0); }
/* The media inside a raised card reacts with the card rather than sitting still inside a moving box. */
.okl-case .okl-frame img { transition: transform var(--okno-motion-enter); }
.okl-case:hover .okl-frame img { transform: scale(1.03); }
.okl-case__body { display: flex; flex-direction: column; gap: var(--okno-space-8); padding: var(--okno-space-24); flex: 1; }
.okl-case__t { font-size: var(--okno-card-title-size); font-weight: 800; line-height: 1.3; color: var(--okno-ink-strong); }
/* Canonical responsive behaviour of the family, not a fix for one string. Below 360px the card is
   ~238px wide and a long single word — «Высококвалифицированный» is 22 characters — no longer fits a
   line at 17px/800, and breaking a word is forbidden. The title steps down to the approved body size
   at the weight h3 already uses, keeping its line-height and the rest of its geometry. No new token,
   no literal, no variant, no page in the selector. */
@media (max-width: 359px) {
  .okl-case__t { font-size: var(--okno-body-size); font-weight: 700; }
}
.okl-case__d { font-size: var(--okno-compact-size); line-height: var(--okno-compact-line); color: var(--okno-ink-muted); }
.okl-case__go { margin-top: auto; padding-top: var(--okno-space-16); font-size: var(--okno-compact-size); font-weight: 700; color: var(--okno-accent-strong); display: flex; align-items: center; gap: var(--okno-space-8); }
.okl-case__go::after { content: "→"; display: inline-flex; align-items: center; justify-content: center;
  width: 30px; height: 30px; border-radius: var(--okno-radius-round); background: var(--okno-surface-accent);
  transition: transform var(--okno-motion), background-color var(--okno-motion); }
.okl-case:hover .okl-case__go::after { transform: translateX(4px); background: var(--okno-accent); color: var(--okno-inverse); }

/* ---------- Byline — a named person as attribution, not as a second hero ----------
   The rejected M1 team section put a 600x900 portrait beside a 401x417 group photo: the supporting
   face outweighed the subject by 3.2x in area and the section filled with empty field. A person who
   signs a claim is an identity, not a picture: this family gives them a face at avatar scale, their
   name and their role, and it cannot grow into a second focal object because its media size is fixed
   by the component rather than by the page. */
.okl-byline { display: grid; grid-template-columns: auto minmax(0, 1fr); gap: var(--okno-space-16); align-items: center; }
.okl-byline__a { width: 72px; height: 72px; border-radius: var(--okno-radius-round); overflow: hidden; background: var(--okno-surface-subtle); flex: none; }
.okl-byline__a img { width: 100%; height: 100%; object-fit: cover; display: block; }
/* Name and role are two lines, and the component guarantees it. They were inline spans with no
   whitespace between them in the markup, so they rendered as one run — «Андре МихалевичОснователь
   OKNO Agency» — and at 390 that run wrapped mid-phrase. A component whose two text slots weld
   together is broken for every future user of it, so the separation belongs here and not on a page. */
.okl-byline__n { display: block; font-weight: 800; color: var(--okno-ink-strong); }
.okl-byline__r { display: block; margin-top: var(--okno-space-4); font-size: var(--okno-compact-size); line-height: var(--okno-compact-line); color: var(--okno-ink-muted); }

/* ---------- ExpertIdentity — a named founder or specialist, at a size you can recognise ----------
   Two human-identity components, two jobs, and the distinction is semantic rather than decorative:
     Byline         — authorship and signature metadata. Avatar scale, fixed by the component.
     ExpertIdentity — a named founder, specialist or expert who stands behind the work.
   The Homepage founder was rendered as a Byline and read as list metadata at 72px against a 542px
   team photograph — an inversion of the rejected M1 defect rather than a fix for it. This family gives
   a named person a recognisable portrait while keeping them plainly subordinate to the section's
   subject: the portrait width is owned here, so no page can grow it into a second focal object. */
.okl-expert { display: grid; grid-template-columns: minmax(0, auto) minmax(0, 1fr); gap: var(--okno-space-20); align-items: start; }
.okl-expert__m { width: 132px; flex: none; border-radius: var(--okno-radius-card); overflow: hidden; background: var(--okno-surface-subtle); aspect-ratio: 3 / 4; box-shadow: var(--okno-shadow-low); }
@media (min-width: 1100px) { .okl-expert__m { width: 148px; } }
.okl-expert__m img, .okl-expert__m picture > img { width: 100%; height: 100%; object-fit: cover; display: block; }
.okl-expert__b { display: grid; gap: var(--okno-space-8); }
.okl-expert__n { font-size: var(--okno-card-title-size); font-weight: 800; color: var(--okno-ink-strong); line-height: 1.3; }
.okl-expert__r { font-size: var(--okno-compact-size); line-height: var(--okno-compact-line); color: var(--okno-accent-strong); font-weight: 700; }
@media (max-width: 519px) {
  /* On a phone the portrait stays recognisable rather than shrinking to a token: the row becomes a
     stack so the face keeps its width instead of competing with the text for it. */
  .okl-expert { grid-template-columns: minmax(0, 1fr); gap: var(--okno-space-16); }
  .okl-expert__m { width: 120px; }
}

/* ---------- Icon / pictogram: one family, drawn from the accent stroke ---------- */
.okl-icon { display: inline-flex; width: 20px; height: 20px; flex: none; color: var(--okno-accent); }
.okl-icon svg { width: 100%; height: 100%; }

/* ---------- editorial body text ---------- */
.okl-prose { display: grid; gap: var(--okno-space-20); max-width: var(--okno-container-reading); }
.okl-prose h2 { margin-top: var(--okno-space-16); }
.okl-prose h3 { margin-top: var(--okno-space-8); }
.okl-prose ul, .okl-prose ol { margin: 0; padding-inline-start: var(--okno-space-24); display: grid; gap: var(--okno-space-8); }
.okl-prose li::marker { color: var(--okno-accent); }
.okl-lead { font-size: var(--okno-lead-size); line-height: 1.55; color: var(--okno-ink-body); max-width: var(--okno-container-reading); }
.okl-muted { color: var(--okno-ink-muted); }
.okl-compact { font-size: var(--okno-compact-size); line-height: var(--okno-compact-line); }

/* ===== patterns.css ===== */
/* OKNO Design System v1 — patterns.
 *
 * Each registry pattern implemented once, on top of the primitives and components. A pattern owns
 * composition — how its slots sit together — never a new colour, spacing, radius or shadow value.
 *
 * Ported from Design Lab R2.3 with exactly one omission: the Lab's stand-in header/footer
 * (.okl-shell, .okl-shell__in, .okl-shell__logo, .okl-foot, .okl-foot__n). That was prototype
 * scaffolding standing in for the real shell while the Lab ran headless. Production has its own
 * shell in okno-global-shell.css, and the M0 spec forbids porting Lab prototype markup, so the
 * stand-in does not travel. Nothing else was added, removed or reordered.
 */
/* ---------- Hero ---------- */
.okl-hero { padding-block: var(--okno-space-40) var(--okno-space-48); }
@media (min-width: 768px) { .okl-hero { padding-block: var(--okno-space-64) var(--okno-space-80); } }
.okl-hero__lead { margin-top: var(--okno-space-20); }
.okl-hero__actions { margin-top: var(--okno-space-32); }
/* The trust line is the page's only hard numbers above the fold. Left as 14px grey on white it was
   the least visible sentence on the first screen; contained on the quiet tier with an accent rule it
   reads as a credential strip. The wording is untouched — this is containment, not copy. */
.okl-hero__meta { margin-top: var(--okno-space-24); }
.okl-hero--support .okl-hero__meta { padding: var(--okno-space-16) var(--okno-space-20);
  background: var(--okno-tier-quiet-bg); border-radius: var(--okno-tier-quiet-radius);
  box-shadow: inset 4px 0 0 var(--okno-accent); color: var(--okno-ink-body); }
/* Editorial and case heroes lead with text on a reading rail; service heroes pair text with media. */
.okl-hero--editorial .okl-hero__lead { font-size: var(--okno-lead-size-editorial); }
/* When the hero ends on media, the section that follows supplies the breathing room. */
.okl-hero--case, .okl-hero--editorial { padding-bottom: var(--okno-space-24); }

/* Supporting Hero — the opening surface leads with text and is accompanied by one photograph, not
   headed by it. The text rail takes the larger share, the media is capped so it cannot grow into a
   first-fold banner, and on a phone the picture follows the words at a controlled height instead of
   pushing them off the screen. This is the Hero's supporting-media role, reusable by any page whose
   opening is an orientation rather than a product claim. */
.okl-hero--support { padding-block: var(--okno-space-40) var(--okno-space-48); }
.okl-hero--support .okl-hero__media .okl-frame { box-shadow: var(--okno-shadow-focal); max-height: 300px; border-radius: var(--okno-radius-panel); }
@media (min-width: 900px) {
  .okl-hero--support .okl-split { grid-template-columns: minmax(0, 58fr) minmax(0, 42fr); align-items: center; }
  .okl-hero--support .okl-hero__media .okl-frame { max-height: 380px; }
}

/* Product Hero — Homepage, Legal Hub and D7 Service.
   The media is a raised object rather than a flat picture, and on a wide screen it takes the single
   controlled offset the asymmetry budget allows: it rises 24px above the text rail and reaches into
   the gutter. Below 1000px the offset is removed entirely, so the phone gets a plain stacked hero. */
.okl-hero--product { padding-bottom: var(--okno-space-40); }
.okl-hero--product .okl-frame { box-shadow: var(--okno-shadow-focal); border-radius: var(--okno-radius-panel); }
@media (min-width: 1000px) {
  .okl-hero--product .okl-hero__media { margin-top: calc(var(--okno-space-24) * -1); }
  .okl-hero--product .okl-hero__media .okl-frame { margin-inline-end: calc(var(--okno-space-24) * -1); }
}

/* ---------- Entry Choice — exactly two peer branches, equal weight ---------- */
.okl-entry { display: grid; gap: var(--okno-space-20); grid-template-columns: minmax(0, 1fr); }
@media (min-width: 860px) { .okl-entry { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--okno-space-24); } }
.okl-entry__b { display: flex; flex-direction: column; gap: var(--okno-space-16); padding: var(--okno-space-24); text-decoration: none; color: inherit;
  background: var(--okno-tier-raised-bg); border: var(--okno-tier-raised-border); border-radius: var(--okno-tier-raised-radius); box-shadow: var(--okno-tier-raised-shadow);
  transition: border-color var(--okno-motion), box-shadow var(--okno-motion), transform var(--okno-motion); }
@media (min-width: 768px) { .okl-entry__b { padding: var(--okno-space-32); } }
.okl-entry__b:hover { border-color: var(--okno-tier-hover-border); box-shadow: var(--okno-shadow-focal); transform: translateY(var(--okno-lift)); }
.okl-entry__b:active { transform: translateY(0); box-shadow: var(--okno-tier-raised-shadow); }
.okl-entry__t { font-size: var(--okno-h3-size); line-height: var(--okno-h3-line); font-weight: 800; color: var(--okno-ink-strong); }
.okl-entry__d { color: var(--okno-ink-muted); }
/* The chooser action is the strongest affordance on the first screen after the hero, so it is drawn
   as an interface control — a bordered pill that fills with the accent on hover — instead of as a
   coloured word with an arrow after it. The wording is untouched. */
.okl-entry__go { margin-top: auto; font-weight: 700; color: var(--okno-accent-strong);
  display: inline-flex; align-items: center; align-self: flex-start; gap: var(--okno-space-8);
  min-height: 44px; padding: var(--okno-space-8) var(--okno-space-20);
  border: var(--okno-hairline) solid var(--okno-border); border-radius: var(--okno-radius-round);
  font-size: var(--okno-compact-size);
  transition: background-color var(--okno-motion), border-color var(--okno-motion), color var(--okno-motion); }
.okl-entry__go::after { content: "→"; display: inline-block; transition: transform var(--okno-motion); }
.okl-entry__b:hover .okl-entry__go { background: var(--okno-accent); border-color: var(--okno-accent); color: var(--okno-inverse); }
.okl-entry__b:hover .okl-entry__go::after { transform: translateX(4px); }

/* ---------- Status Lifecycle — actions on an existing status ---------- */
/* Status Lifecycle — one surface holding four navigation rows, not four raised cards. */
.okl-life { background: var(--okno-tier-cluster-bg); border: var(--okno-tier-cluster-border); border-radius: var(--okno-tier-cluster-radius);
  box-shadow: var(--okno-tier-raised-shadow); overflow: hidden; display: grid; grid-template-columns: minmax(0, 1fr); }
@media (min-width: 700px) { .okl-life { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
/* The Lab's lifecycle row was a single line because the Lab's content was a single line. Every real
   status action on the production homepage carries one sentence of guidance — what we check, what it
   covers — and dropping it to keep the row thin would be silent content loss. The row becomes a
   two-line cell with the arrow held to the right; the pattern keeps its identity as one enclosing
   surface of rows, which is what distinguishes it from the ruled index above it. */
.okl-life__i { display: grid; grid-template-columns: minmax(0, 1fr) auto; align-items: start; column-gap: var(--okno-space-16);
  padding: var(--okno-space-20) var(--okno-space-24);
  border-top: 1px solid var(--okno-border); text-decoration: none; color: var(--okno-ink-strong); font-weight: 700; min-height: 68px;
  transition: background-color var(--okno-motion), color var(--okno-motion); }
.okl-life__d { grid-column: 1; font-size: var(--okno-compact-size); line-height: var(--okno-compact-line);
  font-weight: 400; color: var(--okno-ink-muted); margin-top: var(--okno-space-4); }
.okl-life__i:first-child { border-top: 0; }
@media (min-width: 700px) {
  .okl-life__i:nth-child(2) { border-top: 0; }
  .okl-life__i:nth-child(odd) { border-inline-end: 1px solid var(--okno-border); }
}
.okl-life__i:hover { background: var(--okno-surface-accent); color: var(--okno-accent-strong); }
/* The arrow inside a clustered row is a chip like everywhere else, so the same gesture means the same
   thing whether it sits on a card or inside a group surface. */
.okl-life__i .okl-icon { grid-row: 1; grid-column: 2; width: 30px; height: 30px; padding: var(--okno-space-4);
  border-radius: var(--okno-radius-round); background: var(--okno-surface-accent); color: var(--okno-accent-strong);
  transition: transform var(--okno-motion), background-color var(--okno-motion), color var(--okno-motion); }
.okl-life__i:hover .okl-icon { transform: translateX(4px); background: var(--okno-accent); color: var(--okno-inverse); }

/* ---------- Long-term Status — later-stage outcomes, compact ---------- */
.okl-lt { display: grid; gap: var(--okno-space-16); grid-template-columns: minmax(0, 1fr); }
@media (min-width: 700px) { .okl-lt { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--okno-space-20); } }
.okl-lt__i { display: flex; flex-direction: column; gap: var(--okno-space-8); padding: var(--okno-space-24);
  background: var(--okno-surface-accent); border: var(--okno-hairline) solid transparent; border-radius: var(--okno-tier-base-radius); text-decoration: none; color: inherit;
  transition: box-shadow var(--okno-motion), background-color var(--okno-motion), border-color var(--okno-motion), transform var(--okno-motion); }
.okl-lt__i:hover { box-shadow: var(--okno-tier-hover-shadow); border-color: var(--okno-tier-hover-border); transform: translateY(var(--okno-lift)); }
.okl-lt__i:active { transform: translateY(0); }
.okl-lt__t { font-weight: 800; color: var(--okno-ink-strong); }
.okl-lt__go { font-size: var(--okno-compact-size); font-weight: 700; color: var(--okno-accent-strong); }
.okl-lt__go::after { content: "→"; display: inline-block; margin-inline-start: var(--okno-space-8); transition: transform var(--okno-motion); }
.okl-lt__i:hover .okl-lt__go::after { transform: translateX(5px); }

/* ---------- Route / Service Directory ---------- */
.okl-dir { display: grid; gap: var(--okno-space-20); grid-template-columns: repeat(auto-fit, minmax(min(100%, 280px), 1fr)); }
/* Projects render as an exact two by two on a wide screen — four peers, no orphan row. */
.okl-dir--2x2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
@media (max-width: 720px) { .okl-dir--2x2 { grid-template-columns: minmax(0, 1fr); } }

/* Service Directory — an institutional index, not a grid of cards. Rows are ruled, share one surface,
   carry no shadow and no rounded chrome of their own, and the whole row is the target.
   The column count is INTRINSIC, which is what the rest of this layer already claims to be: the index
   splits when each column would still be at least 480px, not when the viewport passes a number. 480 is
   the width at which a ruled row still fits a title, its line of copy and the arrow without crowding:
   the page rail of 1096 gives exactly two, and a 524px split rail gives one. A
   viewport breakpoint put two columns inside a 524px rail on the Homepage — three times — because the
   page was wide even though the rail was not.
   Dividers come from a 1px gap over the surface colour rather than from per-cell borders, so they are
   correct at any column count and there is no nth-child arithmetic to get wrong when the count changes. */
.okl-ruled { background: var(--okno-border); border: var(--okno-tier-cluster-border); border-radius: var(--okno-tier-cluster-radius);
  box-shadow: var(--okno-tier-raised-shadow); overflow: hidden;
  display: grid; gap: var(--okno-hairline); grid-template-columns: repeat(auto-fit, minmax(min(100%, 480px), 1fr)); }
.okl-ruled__r { display: grid; grid-template-columns: minmax(0, 1fr) auto; align-items: start; column-gap: var(--okno-space-16);
  padding: var(--okno-space-20) var(--okno-space-24); min-height: 88px; text-decoration: none; color: inherit;
  background: var(--okno-canvas); transition: background-color var(--okno-motion); }
.okl-ruled__r:hover { background: var(--okno-surface-accent); }
/* The first route of a scenario group is the one most visitors need. Without it every row is an
   equal, and an index of equals is the card wall in ruled clothing. The lead row states itself
   larger and carries the accent rule; it is one row, never a second family. */
/* The lead states itself with the accent surface, the accent rule and a larger title — not by
   spanning the grid. Spanning made the lead occupy a whole row, so every group with an odd number of
   remaining rows ended on a half-empty one; the reviewer saw three of them in one section and the
   orphan rule confirmed it once it was taught to look at ruled indexes. Hierarchy that costs the
   layout its parity is not hierarchy, it is a side effect. */
.okl-ruled__r--lead { background: var(--okno-surface-accent); box-shadow: inset 4px 0 0 var(--okno-accent); }
.okl-ruled__r--lead:hover { background: var(--okno-surface-accent); }
.okl-ruled__r--lead .okl-ruled__t { font-size: var(--okno-card-title-size); }
/* Same canonical rule as the Case Card title, for the same reason: below 360px a ruled row is about
   210px wide and «Высококвалифицированный» does not fit a line at weight 800. The index owns its own
   responsive behaviour; no page-local rule, no new token. */
.okl-ruled__t { font-weight: 800; color: var(--okno-ink-strong); }
@media (max-width: 359px) {
  /* At 320 a ruled cell is about 194px wide once its padding, the arrow column and the gap are taken
     out — narrower than a 23-character Russian word set at 16px/800. The index gives back some of its
     inset and sets the title at the compact size, keeping weight as the thing that carries hierarchy.
     Same canonical principle as the Case Card title: the family owns its own narrow-width behaviour. */
  .okl-ruled__r { padding-inline: var(--okno-space-16); }
  .okl-ruled__t { font-size: var(--okno-compact-size); font-weight: 700; }
}
.okl-ruled__d { grid-column: 1; font-size: var(--okno-compact-size); line-height: var(--okno-compact-line); color: var(--okno-ink-muted); margin-top: var(--okno-space-4); }
/* The same affordance chip the cards use. An index row and a card row now say "this is the link" with
   one gesture instead of two, which is most of what makes a long index scannable. */
.okl-ruled__go { grid-row: 1; grid-column: 2; color: var(--okno-accent-strong); width: 30px; height: 30px; padding: var(--okno-space-4);
  border-radius: var(--okno-radius-round); background: var(--okno-surface-accent);
  transition: transform var(--okno-motion), background-color var(--okno-motion), color var(--okno-motion); }
.okl-ruled__r:hover .okl-ruled__go { transform: translateX(4px); background: var(--okno-accent); color: var(--okno-inverse); }
.okl-ruled__r--lead .okl-ruled__go { background: var(--okno-canvas); }
/* Below 420px the chip gives its inset back and becomes the bare glyph again. The drawn chip is worth
   30px of a 1096px rail and is not worth 30px of a 194px one: at 320 it pushed
   «Высококвалифицированный» over the edge of its cell and the title broke mid-word, which the family
   forbids. The affordance survives, its decoration does not. */
@media (max-width: 419px) {
  .okl-ruled__go, .okl-life__i .okl-icon { width: 20px; height: 20px; padding: 0; background: none; }
  .okl-ruled__r:hover .okl-ruled__go, .okl-life__i:hover .okl-icon { background: none; color: var(--okno-accent-strong); }
}

/* ---------- Eligibility / Requirements — structured list, not a card wall ---------- */
.okl-elig { display: grid; gap: 0; }
.okl-elig__i { display: grid; gap: var(--okno-space-4); padding-block: var(--okno-space-16); border-top: 1px solid var(--okno-border); }
.okl-elig__i:last-child { border-bottom: 1px solid var(--okno-border); }
.okl-elig__t { font-weight: 700; color: var(--okno-ink-strong); display: flex; gap: var(--okno-space-12); align-items: flex-start; }
.okl-elig__d { color: var(--okno-ink-muted); font-size: var(--okno-compact-size); line-height: var(--okno-compact-line); }
@media (min-width: 720px) { .okl-elig__i { grid-template-columns: minmax(0, 320px) minmax(0, 1fr); gap: var(--okno-space-24); align-items: baseline; } }

/* ---------- Process — sequence on a shared rail ---------- */
.okl-proc { display: grid; gap: 0; }
.okl-proc__s { display: grid; grid-template-columns: 40px minmax(0, 1fr); gap: var(--okno-space-16); padding-bottom: var(--okno-space-24); position: relative; }
.okl-proc__s:last-child { padding-bottom: 0; }
.okl-proc__s::before { content: ""; position: absolute; left: 19px; top: 40px; bottom: 0; width: 1px; background: var(--okno-border); }
.okl-proc__s:last-child::before { display: none; }
.okl-proc__n { width: 40px; height: 40px; border-radius: var(--okno-radius-round); display: flex; align-items: center; justify-content: center;
  background: var(--okno-surface-accent); color: var(--okno-accent-strong); font-weight: 800; position: relative; z-index: 1; }
.okl-proc__b { display: grid; gap: var(--okno-space-8); padding-top: var(--okno-space-8); }
.okl-proc__t { font-weight: 800; color: var(--okno-ink-strong); }
.okl-proc__d { color: var(--okno-ink-muted); }

/* ---------- Pricing ---------- */
.okl-pricing { display: grid; gap: var(--okno-space-24); }

/* ---------- Comparison — real alternatives on shared dimensions ---------- */
.okl-cmp { width: 100%; overflow-x: auto; }
.okl-cmp table { width: 100%; border-collapse: collapse; }
@media (min-width: 641px) { .okl-cmp table { min-width: 520px; } }
/* On a phone a wide table is not scrolled sideways — every row becomes a labelled block, so no column
   is ever pushed off-canvas and no value is truncated. */
@media (max-width: 640px) {
  /* The header row stays for assistive technology. It must not wrap inside its 1px box, or every word
     in it is laid out one letter per line — which reads to a measurement as a mid-word break. */
  .okl-cmp thead { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; }
  .okl-cmp tr { display: block; padding-block: var(--okno-space-12); border-bottom: 1px solid var(--okno-border); }
  .okl-cmp tr:last-child { border-bottom: 0; }
  .okl-cmp td, .okl-cmp th { display: block; border-bottom: 0; padding: var(--okno-space-4) 0; }
  .okl-cmp tbody th { font-size: var(--okno-body-size); padding-bottom: var(--okno-space-8); }
  .okl-cmp td::before { content: attr(data-label); display: block; font-size: var(--okno-eyebrow-size);
    line-height: var(--okno-eyebrow-line); font-weight: 800; letter-spacing: .08em; text-transform: uppercase; color: var(--okno-ink-muted); }
}
.okl-cmp th, .okl-cmp td { text-align: start; padding: var(--okno-space-16); border-bottom: 1px solid var(--okno-border); vertical-align: top; font-size: var(--okno-compact-size); line-height: var(--okno-compact-line); }
.okl-cmp thead th { font-weight: 800; color: var(--okno-ink-strong); background: var(--okno-surface-subtle); font-size: var(--okno-body-size); }
.okl-cmp tbody th { font-weight: 700; color: var(--okno-ink-strong); }
.okl-cmp tr:last-child td, .okl-cmp tr:last-child th { border-bottom: 0; }

/* ---------- Proof / Stats ---------- */
/* Proof — four facts, never clickable. Four across when they fit, two by two when they do not. */
/* Six facts in four columns leave half the second row empty, which reads as a group that ran out of
   content rather than as proof. The column count follows the set: three across on a wide screen gives
   two full rows, two across on a phone gives three. The pattern states the counts it supports instead
   of letting auto-fit decide how ragged the last row is. */
.okl-proof { display: grid; gap: var(--okno-space-16); grid-template-columns: repeat(2, minmax(0, 1fr)); }
@media (min-width: 900px) { .okl-proof { grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--okno-space-20); } }
@media (max-width: 380px) { .okl-proof { grid-template-columns: minmax(0, 1fr); } }
/* Quiet tier. Six facts drawn as a hairline over white are six pieces of text on a white page — the
   owner's "white stream" in its purest form. Contained on a tinted surface, with the icon promoted to
   a drawn chip above the fact, the same six facts read as a block of proof. Quiet rather than base
   because proof supports the page; it does not lead it. */
.okl-proof__i { display: grid; gap: var(--okno-space-8); align-content: start;
  padding: var(--okno-space-24); background: var(--okno-tier-quiet-bg);
  border: var(--okno-tier-quiet-border); border-radius: var(--okno-tier-quiet-radius);
  transition: background-color var(--okno-motion), border-color var(--okno-motion); }
.okl-proof__i:hover { border-color: var(--okno-border); background: var(--okno-canvas); }
.okl-proof__i .okl-icon { width: 40px; height: 40px; padding: var(--okno-space-8); margin-bottom: var(--okno-space-4);
  border-radius: var(--okno-radius-control); background: var(--okno-surface-accent); color: var(--okno-accent-strong); }
.okl-proof__t { font-size: var(--okno-h3-size); line-height: var(--okno-h3-line); font-weight: 800; color: var(--okno-ink-strong); }
.okl-proof__d { font-size: var(--okno-compact-size); line-height: var(--okno-compact-line); color: var(--okno-ink-muted); }
/* Containment costs width, and on a phone the two-column proof grid has none to spare: with the card
   inset added, each fact had a 111px column and «Основатель в Португалии с 2012 года» set at the H3
   size fell to four lines. The family gives back its inset and steps the fact down to the card-title
   size — the same canonical narrow-width move the Case Card and the ruled index already make — so the
   set stays two across and stays readable. */
@media (max-width: 479px) {
  .okl-proof__i { padding: var(--okno-space-20); }
  .okl-proof__i .okl-icon { width: 34px; height: 34px; }
  .okl-proof__t { font-size: var(--okno-card-title-size); line-height: 1.3; }
}

/* ---------- Expert / Trust ----------
   The media is a deliberate 38% of the split on a wide screen — large enough to be a photograph rather
   than a thumbnail — with a controlled frame and medium elevation. On a phone it goes full width above
   the text. Both compositions keep the section's left rail. */
.okl-trust { display: grid; gap: var(--okno-space-24); grid-template-columns: minmax(0, 1fr); align-items: center; }
@media (min-width: 900px) { .okl-trust { grid-template-columns: minmax(320px, 38fr) minmax(0, 62fr); gap: var(--okno-space-40); } }
.okl-trust__m .okl-frame { box-shadow: var(--okno-shadow-mid); border-radius: var(--okno-radius-panel); }
/* The 38/62 split above assumes landscape media. A studio group portrait is near-square, so at 38% it
   renders as a small object marooned in a wide field — the defect the owner rejected in M1. This
   variant gives a portrait-shaped subject the larger share and narrows the text beside it, so the two
   columns read as one balanced object instead of a thumbnail next to a paragraph. */
@media (min-width: 900px) {
  .okl-trust--portrait { grid-template-columns: minmax(360px, 52fr) minmax(0, 48fr); gap: var(--okno-space-48); }
}

/* ---- the two-object variant ----
 * Same family, same gate coverage, a different presentation. In the live owner-preview the team
 * photograph floated on the white page and the founder sat beside it as a portrait and three
 * paragraphs with nothing around them — two loose fragments rather than a section. The owner asked for
 * two intentional, separate but related objects, and that is what a variant of this family can give:
 * the subject keeps its share of the split (the media-occupancy and secondary-media rules still
 * measure okl-trust__m against okl-trust, unchanged), and both halves become surfaces of the same
 * family so they read as one section made of two cards.
 *
 * The subject is on the raised tier and the supporting identity on the quiet tier: they are equals in
 * containment and not in weight, which is the hierarchy the section needs.
 */
/* Content height, not equal height. Stretching the two cards to match made the founder card 503x651
   holding 197px of content — 453px of empty grey box, which is the same "small object in a big field"
   the owner rejected in M1, just wearing a border. Two cards of honest height sharing a top edge read
   as a composition; one card inflated to match its neighbour reads as an unfinished layout. */
.okl-trust--cards { align-items: start; } 
.okl-trust--cards .okl-trust__m { display: flex; flex-direction: column; gap: var(--okno-space-20);
  padding: var(--okno-space-20); background: var(--okno-tier-raised-bg); border: var(--okno-tier-raised-border);
  border-radius: var(--okno-tier-raised-radius); box-shadow: var(--okno-tier-raised-shadow); }
@media (min-width: 768px) { .okl-trust--cards .okl-trust__m { padding: var(--okno-space-24); } }
/* Inside a card the photograph keeps the card's inner radius and loses the elevation it had when it
   floated: two shadows on one object is how a photograph starts to look pasted on. */
.okl-trust--cards .okl-trust__m .okl-frame { box-shadow: none; border-radius: var(--okno-radius-card); }
.okl-trust__cap { font-size: var(--okno-compact-size); line-height: var(--okno-compact-line); color: var(--okno-ink-body); margin: 0; }
.okl-trust__side { display: flex; flex-direction: column; justify-content: flex-start; gap: var(--okno-space-20);
  padding: var(--okno-space-24); background: var(--okno-tier-quiet-bg); border: var(--okno-hairline) solid var(--okno-border);
  border-radius: var(--okno-tier-raised-radius); }
@media (min-width: 768px) { .okl-trust__side { padding: var(--okno-space-32); } }
/* In the side card the identity becomes a vertical composition: the portrait leads at a size you can
   read a face at, the name and role follow, the paragraph closes it. The row form is right when the
   card is full width — under 900px it still is — but beside a tall subject card a row leaves the
   portrait stranded at the top of a column it cannot fill. The portrait is still owned by the
   component, and it is still a fraction of the subject's area. */
@media (min-width: 900px) {
  .okl-trust__side .okl-expert { grid-template-columns: minmax(0, 1fr); gap: var(--okno-space-20); }
  .okl-trust__side .okl-expert__m { width: 200px; }
}

/* ---------- Case Grid ---------- */
.okl-cases { display: grid; gap: var(--okno-space-20); grid-template-columns: repeat(auto-fit, minmax(min(100%, 300px), 1fr)); }


/* ---------- Community — balanced composition, media occupies its panel ---------- */
/* Equal semantic weight is not equal box size. Two 50/50 panels forced to a shared height let the
   taller object — a portrait device mock — set the row, and the wider panel beside it filled with dead
   field below its content. The video panel carries three thumbnails and needs the room; the messaging
   panel carries one device and does not. Each is content-driven in height and neither stretches to
   match the other. */
.okl-comm { display: grid; gap: var(--okno-space-24); grid-template-columns: minmax(0, 1fr); align-items: start; }
@media (min-width: 980px) { .okl-comm { grid-template-columns: minmax(0, 62fr) minmax(0, 38fr); gap: var(--okno-space-32); align-items: start; } }
.okl-comm__p { display: flex; flex-direction: column; gap: var(--okno-space-20); padding: var(--okno-space-24); background: var(--okno-tier-raised-bg);
  border: var(--okno-tier-raised-border); border-radius: var(--okno-tier-raised-radius); box-shadow: var(--okno-tier-raised-shadow); }
@media (min-width: 768px) { .okl-comm__p { padding: var(--okno-space-32); } }
.okl-comm__videos { display: grid; gap: var(--okno-space-16); grid-template-columns: minmax(0, 1fr); }
.okl-comm__videos .okl-frame { box-shadow: var(--okno-shadow-low); }
@media (min-width: 520px) { .okl-comm__videos { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
/* The Telegram panel is a media-led composition: the device fills the panel width on mobile and takes a
   deliberate half of it from 520px up, so the panel never grows around a small image. */
.okl-comm__tg { display: grid; gap: var(--okno-space-20); align-items: center; grid-template-columns: minmax(0, 1fr); }
/* The text keeps a readable column and the device keeps a deliberate share of the panel; below that
   the two stack rather than squeezing either one. */
@media (min-width: 520px) { .okl-comm__tg { grid-template-columns: minmax(180px, 1fr) minmax(130px, .7fr); } }
/* At its narrower share the messaging panel keeps text and device side by side: stacking them turned
   a 600x1080 phone into an 800px column beside a 477px video panel. Side by side the device reads as
   a proportionate object and the two panels differ in height because their content differs, which is
   the point. */
@media (min-width: 980px) { .okl-comm__tg { grid-template-columns: minmax(0, 1.1fr) minmax(120px, .9fr); align-items: center; } }
.okl-comm__dev { border-radius: var(--okno-radius-panel); overflow: hidden; background: var(--okno-surface-subtle); box-shadow: var(--okno-shadow-focal); }
.okl-comm__dev img { width: 100%; display: block; }

/* ---------- Key Fact ----------
   One enclosing surface with internal dividers. The cells carry no shadow and no radius of their own,
   so a set of facts reads as one grouped information surface instead of a wall of little cards.
   This is the same Key Fact family as R1, in its grouped presentation — not a new pattern. */
.okl-key { background: var(--okno-surface-accent); border-radius: var(--okno-radius-panel); overflow: hidden;
  display: grid; grid-template-columns: minmax(0, 1fr); }
/* The column count follows the number of facts, so a single fact fills its surface and a set of four
   reads as one grouped row rather than one narrow column beside an empty field. */
@media (min-width: 720px) { .okl-key--4 { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (min-width: 1100px) { .okl-key--4 { grid-template-columns: repeat(4, minmax(0, 1fr)); } }
.okl-key__i { display: grid; gap: var(--okno-space-8); align-content: start; padding: var(--okno-space-24);
  border-top: 1px solid var(--okno-border); }
.okl-key__i:first-child { border-top: 0; }
@media (min-width: 720px) {
  .okl-key--4 .okl-key__i:nth-child(2) { border-top: 0; }
  .okl-key--4 .okl-key__i:nth-child(odd) { border-inline-end: 1px solid var(--okno-border); }
}
@media (min-width: 1100px) {
  .okl-key--4 .okl-key__i { border-top: 0; border-inline-end: 1px solid var(--okno-border); }
  .okl-key--4 .okl-key__i:last-child { border-inline-end: 0; }
}
.okl-key__t { font-weight: 800; color: var(--okno-ink-strong); }
.okl-key__d { font-size: var(--okno-compact-size); line-height: var(--okno-compact-line); color: var(--okno-ink-body); }

/* ---------- Conversion — one pattern, two variants ----------
   Focal: the dark inverse object, used once on Homepage, Hub, Service and Case.
   Contextual: a light raised panel, used in the article so reading is never interrupted by a dark band. */
/* Orientation before action. The two-column centred composition put the primary button's top 78px
   ABOVE the section's own H2, because the action column was taller than the heading column and
   centring lifted it — so a visitor met a green button before knowing what section they were in. The
   panel is now one vertical order: say what this is, then offer the way in. */
/* The focal panel is the one object on the page that is allowed to look expensive: it gets the focal
   radius and the deepest elevation in the system, so the last thing a visitor scrolls to reads as the
   place where the page resolves. */
.okl-conv { position: relative; }
.okl-conv--focal .okl-conv__p { border-radius: var(--okno-radius-focal); box-shadow: var(--okno-shadow-focal); }
.okl-conv__p { display: grid; gap: var(--okno-space-24); padding: var(--okno-space-32) var(--okno-space-24); }
@media (min-width: 768px) { .okl-conv__p { padding: var(--okno-space-48); gap: var(--okno-space-32); } }
.okl-conv__h { font-size: var(--okno-h2-size); line-height: var(--okno-h2-line); }
.okl-conv__b { margin-top: var(--okno-space-12); color: var(--okno-ink-body); }
/* The contextual variant is the light panel the article uses; it takes no inverse ink. */
.okl-conv--contextual .okl-conv__b { color: var(--okno-ink-body); }
.okl-conv--focal .okl-conv__b, .okl-conv--focal li { color: var(--okno-ink-inverse-muted); }
.okl-conv--focal .okl-btn--quiet { color: var(--okno-inverse); text-decoration-color: var(--okno-border-inverse); }
.okl-conv--focal .okl-btn--quiet:hover { color: var(--okno-inverse); text-decoration-color: currentColor; }
/* The action zone sits below the orientation zone and reads left to right: one focal action, then the
   subordinate ones. Nothing is centred against anything, so there is one spine. */
.okl-conv__act { display: grid; gap: var(--okno-space-20); justify-items: stretch; }
@media (min-width: 768px) { .okl-conv__act { grid-template-columns: minmax(0, auto) minmax(0, 1fr); align-items: start; gap: var(--okno-space-32); justify-items: start; } }
/* The action zone sits on its own inset plane inside the panel, so the three offers read as one
   control group rather than as buttons scattered across a dark field. */
.okl-conv--focal .okl-conv__act { padding: var(--okno-space-24); border-radius: var(--okno-radius-panel);
  background: var(--okno-border-inverse); border: var(--okno-hairline) solid var(--okno-border-inverse); }
@media (min-width: 768px) { .okl-conv--focal .okl-conv__act { padding: var(--okno-space-32); align-items: center; } }
/* Subordinate options are deliberate secondary actions, not browser-default links. On the inverse
   surface an underline at white-on-navy is indistinguishable from an unstyled anchor, which is exactly
   how they read — so on the focal surface they take a quiet outline instead and keep their arrow. */
.okl-conv__sec { display: flex; flex-wrap: wrap; gap: var(--okno-space-12) var(--okno-space-16); }
.okl-conv--focal .okl-conv__sec .okl-btn--quiet {
  text-decoration: none; border-color: var(--okno-border-inverse); padding-inline: var(--okno-space-16);
  min-height: 44px; border-radius: var(--okno-radius-control); font-size: var(--okno-compact-size); }
.okl-conv--focal .okl-conv__sec .okl-btn--quiet::after { content: "→"; display: inline-block; margin-inline-start: var(--okno-space-8); }
.okl-conv--focal .okl-conv__sec .okl-btn--quiet:hover { border-color: var(--okno-inverse); background: var(--okno-border-inverse); }
/* Each subordinate offer keeps its own supporting line directly under it, and the two sit as equal
   columns instead of stacking into a ragged list beside the focal button. */
/* Intrinsic, not a breakpoint. The two options sit side by side only when each would still be 240px
   wide; a viewport rule put them in two 150px columns at 768, where the primary button is beside them
   and takes most of the row — and «конкретный» broke mid-word in the supporting line. The zone asks
   how much room it has, not how wide the window is. */
.okl-conv__sec { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 240px), 1fr)); align-items: start; }
.okl-conv--focal .okl-btn--lg { box-shadow: var(--okno-shadow-focal); }

/* ---------- FAQ ---------- */
.okl-faq { display: grid; }

/* ---------- Related ---------- */
/* Balanced by declared count, not by auto-fit. Five items across a wide rail must read as 3+2; an
   auto-fitting track makes them 4+1, which looks like a grid that ran out of content. The map below
   is the whole behaviour and lives only here — no page may restate it.
   >=900: 1->1  2->2  3->3  4->2  5->3  6->3      600-899: at most 2      <600: 1
   Seven or more items has no entry on purpose: this pattern tops out at six, and the Lab gate fails
   rather than inventing a layout for a case that belongs to a future Directory pattern. */
.okl-rel { display: grid; gap: var(--okno-space-16); grid-template-columns: 1fr; }
@media (min-width: 600px) {
  .okl-rel:not(.okl-rel--n1) { grid-template-columns: repeat(2, 1fr); }
}
@media (min-width: 900px) {
  /* Written at the same specificity as the tablet rule above, which is itself two classes because of
     its :not(). A count rule that loses the cascade to the rule it is meant to override is a rule that
     silently does nothing. */
  .okl-rel.okl-rel--n2, .okl-rel.okl-rel--n4 { grid-template-columns: repeat(2, 1fr); }
  .okl-rel.okl-rel--n3, .okl-rel.okl-rel--n5, .okl-rel.okl-rel--n6 { grid-template-columns: repeat(3, 1fr); }
}

/* ---------- TOC ---------- */
.okl-toc { display: grid; gap: var(--okno-space-8); padding: var(--okno-space-20); background: var(--okno-surface-subtle); border-radius: var(--okno-radius-card); }
.okl-toc__t { font-size: var(--okno-eyebrow-size); font-weight: 800; letter-spacing: .08em; text-transform: uppercase; color: var(--okno-ink-muted); }
.okl-toc a { display: block; padding-block: var(--okno-space-8); color: var(--okno-ink-body); text-decoration: none; font-size: var(--okno-compact-size); line-height: var(--okno-compact-line); border-bottom: 1px solid transparent; }
.okl-toc a:hover { color: var(--okno-accent-strong); }
/* On narrow screens the contents collapse into a disclosure so the article starts immediately. */
.okl-toc--mobile summary { list-style: none; font-weight: 800; color: var(--okno-ink-strong); padding-block: var(--okno-space-8); min-height: 44px; display: flex; align-items: center; justify-content: space-between; cursor: pointer; }
.okl-toc--mobile summary::-webkit-details-marker { display: none; }
.okl-toc--mobile summary::after { content: "+"; font-size: 20px; color: var(--okno-accent-strong); }
.okl-toc--mobile[open] summary::after { content: "–"; }

/* ===== activation.css ===== */
/* OKNO Design System v1 — activation layer.
 *
 * The boundary that lets migrated content use this system on a site that still runs its own.
 *
 * Two separate jobs, and only these two:
 *
 * 1. THE ROOT. `.okl-page` is the single shared root a migrated page wraps its own content in.
 *    primitives.css scopes the whole base layer to it, so nothing here reaches the global shell,
 *    a legacy page, or a builder that has not been migrated. There is no page-specific variant of
 *    this root and there must never be one: Homepage and Legal Hub use the same class, and so will
 *    every family after them.
 *
 * 2. THE CASCADE. Production ships okno-typography.css, which forces bare h1/h2/h3 with !important:
 *
 *       h1 { font-size … !important; line-height … !important; font-weight … !important;
 *            text-transform: none !important; letter-spacing: normal !important; }
 *       @media (max-width: 640px) { h1 { font-size: 30px !important; } }
 *
 *    An important declaration is not beaten by specificity alone, so the scoped base in
 *    primitives.css — which is normal weight — loses inside migrated content. The rules below
 *    re-assert exactly those five properties, at important weight, from a selector that is one class
 *    more specific. Important-versus-important is settled by specificity, so `.okl-page h1` wins over
 *    `h1`, and only inside the boundary.
 *
 *    This is the whole escalation. It is not a licence: it names five properties on three elements,
 *    it introduces no new value (every one comes from the token layer), it is not page-specific, and
 *    it is reusable unchanged by every future migrated family. Nothing else in this system may use
 *    !important to win an argument with legacy CSS — if another conflict appears, it is added here,
 *    in the open, with the rule it answers written down.
 *
 *    When okno-typography.css eventually stops forcing bare headings, this file shrinks to the root
 *    declaration and nothing else has to change.
 */

/* The root's own declaration lives in primitives.css, where every other primitive is defined — one
   family, one owner. This file only settles the cascade. */

/* the five properties production forces on h1/h2/h3, re-asserted inside the boundary */
.okl-page h1 {
  font-size: var(--okno-h1-size) !important;
  line-height: var(--okno-h1-line) !important;
  font-weight: 800 !important;
  text-transform: none !important;
  letter-spacing: -.02em !important;
}
.okl-page h2 {
  font-size: var(--okno-h2-size) !important;
  line-height: var(--okno-h2-line) !important;
  font-weight: 800 !important;
  text-transform: none !important;
  letter-spacing: -.02em !important;
}
.okl-page h3 {
  font-size: var(--okno-h3-size) !important;
  line-height: var(--okno-h3-line) !important;
  font-weight: 700 !important;
  text-transform: none !important;
  letter-spacing: -.02em !important;
}

/* The legacy narrow-screen override (h1 → 30px !important below 640px) is answered by the same
   selector at the same width, so the fluid ramp keeps its own mobile figure instead of being
   flattened to a fixed one. */
@media (max-width: 640px) {
  .okl-page h1 { font-size: var(--okno-h1-size) !important; }
}

/* Inside the focal inverse surface the heading colour is a system role, and the legacy sheet has no
   opinion about it — so this stays at normal weight, like every other rule in the system. */
.okl-page .okl-s3 h1, .okl-page .okl-s3 h2, .okl-page .okl-s3 h3 { color: var(--okno-inverse); }

/* ---- legacy anchor typography ----
 * A legacy sheet carries `.onot-back, .oleg-back, .orea-back, .obus-back, .okno-p1 main a
 * { font-weight: var(--w-semi) }`. The last selector in that list reaches every anchor inside <main>
 * at specificity (0,1,2), which beats a single-class component selector. The visible result was one
 * component with two appearances: an anchor-backed primary button rendered at weight 600 while the
 * native <button> next to it rendered at 700, and nothing in the validator or the composition rules
 * could see it because both carried the right class and the right surface.
 *
 * Restated here at boundary specificity, for both elements equally, so the family owns its own
 * typography again inside migrated content. No !important is needed: two classes beat one class and
 * two types. Nothing outside .okl-page is touched. */
.okl-page .okl-btn { font-weight: 700; }
.okl-page .okl-btn--quiet { font-weight: 600; }
