:root {
  --s1: 4px;
  --s2: 8px;
  --s3: 12px;
  --s4: 16px;
  --s5: 20px;
  --s6: 24px;
  --s7: 32px;
  --s8: 40px;
  --s9: 44px;
  --s10: 52px;
  --s11: 64px;
  --measure: 68ch;
  --shell: 1180px;
  --measure-prose: var(--shell);
  --measure-standard: var(--shell);
  --measure-wide: var(--shell);
  --canvas: var(--bg-page);
  --canvas-edge: var(--rule);
  --accent-soft: var(--bg-surface-2);
  --on-accent: var(--fg-inverse);
  --gutter: var(--s9);
  --sheet-pad: var(--s9);
  --header-h: 62px;
  --sheet: none;
  --font-display: 'Source Serif 4', Georgia, 'Times New Roman', serif;
  --font-ui: 'IBM Plex Sans', system-ui, -apple-system, sans-serif;
  --font-deva: 'Tiro Devanagari Hindi', 'Noto Serif Devanagari', serif;
  --font-mono: 'IBM Plex Sans', system-ui, -apple-system, sans-serif;
}

:root {
  --bg-page: #FBF7F0;
  --bg-surface: #F4EDE1;
  --bg-surface-2: #EAE0D0;
  --bg-panel: #F7F1E7;
  --bg-row-tint: #F9F4EA;
  --bg-input: #FFFDF8;
  --bg-inverse: #221C15;
  --fg-inverse: #FBF7F0;
  --btn-edge: #221C15;
  --plate-btn-edge: #83765F;
  --fg-primary: #221C15;
  --fg-secondary: #574C40;
  --fg-label: #6A5F50;
  --fg-separator: #C9BBA2;
  --accent: #9E2B1C;
  --accent-hover: #7A1F12;
  --accent-press: #66190E;
  --gold: #D9AE4A;
  --rule: #DDD1BC;
  --rule-soft: #EAE0D0;
  --rule-hair: #F0E8DA;
  --rule-strong: #221C15;
  --card-border: #C9BBA2;
  --plate-bg: #15110C;
  --plate-surface: #1E1913;
  --plate-rule: #3A3126;
  --plate-rule-soft: #2A231A;
  --plate-fg: #EFE7D9;
  --plate-fg-2: #B8AB99;
  --plate-accent: #E8836A;
  --plate-gold: #E4BA61;
  --paper: var(--bg-page);
  --paper2: var(--bg-surface);
  --paper3: var(--bg-surface-2);
  --ink: var(--fg-primary);
  --ink2: var(--fg-secondary);
  --ink3: var(--fg-label);
  --rule2: var(--rule-soft);
  --band-ink: var(--bg-inverse);
  --plate-label: var(--plate-fg-2);
  --plate-border: var(--plate-rule);
}

:root[data-theme="dark"] {
  --bg-page: #15110C;
  --bg-surface: #1E1913;
  --bg-surface-2: #2A231A;
  --bg-panel: #1A1610;
  --bg-row-tint: #1B1710;
  --bg-input: #221D15;
  --bg-inverse: #3A3126;
  --fg-inverse: #EFE7D9;
  --btn-edge: #776B5A;
  --plate-btn-edge: #83765F;
  --fg-primary: #EFE7D9;
  --fg-secondary: #B8AB99;
  --fg-label: #9A8D7B;
  --fg-separator: #3A3126;
  --accent: #E8836A;
  --accent-hover: #F2A18C;
  --accent-press: #F8B9A8;
  --gold: #E4BA61;
  --rule: #3A3126;
  --rule-soft: #2A231A;
  --rule-hair: #241E17;
  --rule-strong: #6A5F50;
  --card-border: #3A3126;
  --plate-bg: #2E2619;
  --plate-surface: #38301F;
  --plate-rule: #4A4034;
  --plate-rule-soft: #3A3228;
  --plate-fg: #EFE7D9;
  --plate-fg-2: #B8AB99;
  --plate-accent: #E8836A;
  --plate-gold: #E4BA61;
  --paper: var(--bg-page);
  --paper2: var(--bg-surface);
  --paper3: var(--bg-surface-2);
  --ink: var(--fg-primary);
  --ink2: var(--fg-secondary);
  --ink3: var(--fg-label);
  --rule2: var(--rule-soft);
  --band-ink: var(--bg-inverse);
  --plate-label: var(--plate-fg-2);
  --plate-border: var(--plate-rule);
}

:root[data-theme="light"] {
  --bg-page: #FBF7F0;
  --bg-surface: #F4EDE1;
  --bg-surface-2: #EAE0D0;
  --bg-panel: #F7F1E7;
  --bg-row-tint: #F9F4EA;
  --bg-input: #FFFDF8;
  --bg-inverse: #221C15;
  --fg-inverse: #FBF7F0;
  --btn-edge: #221C15;
  --plate-btn-edge: #83765F;
  --fg-primary: #221C15;
  --fg-secondary: #574C40;
  --fg-label: #6A5F50;
  --fg-separator: #C9BBA2;
  --accent: #9E2B1C;
  --accent-hover: #7A1F12;
  --accent-press: #66190E;
  --gold: #D9AE4A;
  --rule: #DDD1BC;
  --rule-soft: #EAE0D0;
  --rule-hair: #F0E8DA;
  --rule-strong: #221C15;
  --card-border: #C9BBA2;
  --plate-bg: #15110C;
  --plate-surface: #1E1913;
  --plate-rule: #3A3126;
  --plate-rule-soft: #2A231A;
  --plate-fg: #EFE7D9;
  --plate-fg-2: #B8AB99;
  --plate-accent: #E8836A;
  --plate-gold: #E4BA61;
  --paper: var(--bg-page);
  --paper2: var(--bg-surface);
  --paper3: var(--bg-surface-2);
  --ink: var(--fg-primary);
  --ink2: var(--fg-secondary);
  --ink3: var(--fg-label);
  --rule2: var(--rule-soft);
  --band-ink: var(--bg-inverse);
  --plate-label: var(--plate-fg-2);
  --plate-border: var(--plate-rule);
}

/* ── 3. base ───────────────────────────────────────────────────────────── */
html { -webkit-text-size-adjust: 100%; }
/* THE FOOTER SITS ON THE FLOOR, not wherever the content happens to stop.
   Short documents — the empty search result, the 404, a 49-word plate entry —
   ended above the fold and left the footer floating with a stripe of page
   ground beneath it: 231px on /search/?q=<no match> at 1400x1000, and more on
   a taller screen, where the 404 and every plate entry join it. A band with
   nothing under it reads as a page that failed to load the rest.

   A column flex with main growing, rather than a margin-top:auto on the footer:
   the footer is a grid of four columns and giving it an auto margin would make
   the shell depend on the footer's own display type. This way the shell states
   the rule once and any last element obeys it. */
body {
  margin: 0;
  min-height: 100vh; display: flex; flex-direction: column;
  background: var(--bg-page);
  color: var(--fg-primary);
  font: 400 17px/1.62 var(--font-ui);
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}
/* flex-basis auto, so a long document still measures from its content — and
   min-height 0, so a child that scrolls internally can still shrink. */
body > main { flex: 1 0 auto; min-height: 0; }
p { margin: 0 0 var(--s5); max-width: var(--measure); text-wrap: pretty; }
a { color: var(--accent); text-decoration: none; }
a:hover { color: var(--accent-hover); text-decoration: underline; text-underline-offset: 3px; }
::selection { background: #F0DCA8; }
[data-theme="dark"] ::selection { background: #4A3A1E; }
:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
img { max-width: 100%; height: auto; }

/* Any number that sits in a column or is compared. */

/* Transitions are colour only, never layout, and are dropped entirely under
   reduced motion. There is no motion in this design that cannot be removed. */
@media (prefers-reduced-motion: reduce) {
  * { transition: none !important; animation: none !important; }
}

/* ── 4. page frame ─────────────────────────────────────────────────────── */
/* The main cause of the inconsistency the audit found: pages were rendering
   their content directly into body. */
/* .os-shell deleted: the sheet is .osa-sheet, and nothing has emitted .os-shell. */
.os-region { padding: var(--s10) var(--s9); }
.os-region + .os-region { border-top: 1px solid var(--rule); }
.os-region--surface { background: var(--bg-surface); }
@media (max-width: 767px) {
  .os-region { padding: 28px var(--s4); }
}

/* Announcement bar - the ONLY permitted header variation.

   Transcribed from the canvas, which draws this bar identically on screens 4A and
   5D: display:flex; align-items:center; justify-content:center; gap:10px;
   padding:9px 24px; background:#221C15; color:#EFE7D9, with the link at #E4BA61.

   Three things were off, and all three are the same mistake — reaching for the
   LIGHT-ground token on a dark ground. --fg-inverse (#FBF7F0) and --gold
   (#D9AE4A) are the pair for inverting the page; --plate-fg (#EFE7D9) and
   --plate-gold (#E4BA61) are the pair for sitting ON ink, which is what this bar
   does. And without display:flex the drawn 10px gap collapsed to a word space,
   about 3.5px, because the markup separates the sentence from the link with a
   literal space. */
.os-announce {
  background: var(--bg-inverse); color: var(--plate-fg);
  display: flex; align-items: center; justify-content: center; gap: 10px;
  padding: 9px var(--s6); text-align: center;
  font: 500 12px/1 var(--font-ui); letter-spacing: .06em;
}
.os-announce a { color: var(--plate-gold); }

/* .os-header, .os-wordmark* and .os-nav* are DELETED. They were the v1 chrome,
   superseded by .os-sh-header / .os-sh-brand / .os-sh-nav in oshatra-shell.js, and
   no page has emitted them since: a sweep of 724 routes and 61 states finds them
   zero times, and no string in any non-test source builds one.
   The nav rules mattered because they LOOKED current — .os-nav a[aria-current]
   carried the 2px accent underline 5A rules, correctly, while the live nav item had
   it on the header's bottom edge. The right rule existed on the wrong class. */

.os-footer {
  background: var(--bg-surface); border-top: 1px solid var(--rule);
  padding: var(--s8) var(--s9);
  display: grid; grid-template-columns: repeat(4, 1fr); gap: var(--s8);
  font: 400 13px/1.7 var(--font-ui); color: var(--fg-secondary);
}
.os-footer h2 { font: 600 10px/1 var(--font-ui); letter-spacing: .18em;
                text-transform: uppercase; color: var(--fg-label);
                margin: 0 0 var(--s2); }
.os-footer ul { list-style: none; margin: 0; padding: 0;
                display: flex; flex-direction: column; gap: 6px; }
@media (max-width: 1023px) { .os-footer { grid-template-columns: 1fr 1fr; } }
@media (max-width: 767px)  { .os-footer { grid-template-columns: 1fr; } }

/* ── 5.1 section head - on every section, everywhere ───────────────────── */
/* This single component is most of what makes the pages feel like one site.
   The right-hand note is NOT optional: it is where the design does its
   explaining, and dropping it is what makes the copy read as generated. */
.os-sechead {
  display: flex; align-items: baseline; gap: var(--s3);
  padding-bottom: 14px; border-bottom: 2px solid var(--rule-strong);
  margin-bottom: var(--s5);
}
.os-sechead__h    { font: 400 30px/1.1 var(--font-display); letter-spacing: -.015em;
                    color: var(--fg-primary); margin: 0; }
.os-sechead__note { margin-left: auto; font: 400 12px/1 var(--font-ui);
                    letter-spacing: .08em; text-transform: uppercase;
                    color: var(--fg-label); }
@media (max-width: 767px) {
  .os-sechead__h { font-size: 23px; line-height: 1.15; }
  .os-sechead__note { display: none; }
}

/* ── 5.2 article heading levels - by outline depth, NOT by tag name ────── */
/* 301 of 651 entries have no headings at all, and the median entry that does
   opens at h3 with no h2 anywhere. Keying the strongest treatment to h2 leaves
   most of the library rendering structureless. Compute the outline at render,
   promote the shallowest level present to L1, and emit data-hlevel. */
[data-hlevel="1"] {
  display: flex; align-items: baseline; gap: var(--s3);
  padding-bottom: 10px; border-bottom: 2px solid var(--rule-strong);
  margin: var(--s9) 0 22px;
  font: 400 30px/1.15 var(--font-display); letter-spacing: -.012em;
}
[data-hlevel="1"]::before {
  content: attr(data-numeral);
  font: 400 22px/1 var(--font-deva); color: var(--accent); flex: 0 0 auto;
}
[data-hlevel="2"] {
  display: flex; align-items: baseline; gap: 10px;
  margin: 36px 0 14px;
  font: 600 20px/1.3 var(--font-ui);
}
[data-hlevel="2"]::before {
  content: ''; width: 8px; height: 8px; flex: 0 0 8px; margin-top: 7px;
  background: var(--accent); transform: rotate(45deg);
}
[data-hlevel="3"] {
  display: flex; align-items: center; gap: 9px;
  padding-top: var(--s3); border-top: 1px solid var(--rule);
  margin: var(--s6) 0 var(--s3) 18px;
  font: 600 13px/1.3 var(--font-ui); letter-spacing: .09em; text-transform: uppercase;
}
[data-hlevel="3"]::before {
  content: ''; width: 6px; height: 6px; flex: 0 0 6px; background: var(--fg-secondary);
}
[data-hlevel="4"] {
  margin: var(--s5) 0 10px 33px;
  font: 600 12px/1.3 var(--font-ui); letter-spacing: .14em; text-transform: uppercase;
  color: var(--fg-secondary);
}
/* Past 20 L1 headings the numeral column reaching a high Devanagari value is
   noise; the 2px rule carries the level on its own. */
[data-hlevel="1"][data-numeral=""]::before { content: none; }
@media (max-width: 767px) {
  [data-hlevel="1"] { font-size: 23px; }
  [data-hlevel="1"]::before { font-size: 19px; }
  [data-hlevel="2"] { font-size: 18px; }
  [data-hlevel="2"]::before { width: 7px; height: 7px; flex-basis: 7px; }
}

/* ── 5.3 lists - ol and ul are different objects ───────────────────────── */
/* 402 real ol and 3,177 ul in the corpus. Numerals belong to ol alone; giving
   both a number makes an author's ordered list indistinguishable. */
.os-list { list-style: none; margin: 0 0 var(--s7); padding: 0;
           border: 1px solid var(--rule); background: var(--bg-page); }
.os-list > li {
  display: grid; grid-template-columns: 30px 1fr;
  align-items: baseline; gap: 0 var(--s4);
  padding: 9px 18px; border-bottom: 1px solid var(--rule-soft);
  font: 400 16px/1.5 var(--font-ui);
}
.os-list > li:last-child { border-bottom: 0; }
.os-list > li:nth-child(even) { background: var(--bg-row-tint); }

ol.os-list { counter-reset: osli; }
ol.os-list > li::before {
  counter-increment: osli; content: counter(osli, decimal-leading-zero);
  font: 500 12px/1.5 var(--font-ui); color: var(--accent);
  font-variant-numeric: tabular-nums;
}
ul.os-list > li::before {
  content: ''; width: 8px; height: 8px; margin-top: 7px;
  background: var(--accent); transform: rotate(45deg);
}

.os-list ul, .os-list ol {
  list-style: none; margin: 0 0 0 30px; padding-left: 18px;
  border-left: 2px solid var(--rule); grid-column: 1 / -1;
}
.os-list ul > li, .os-list ol > li {
  display: grid; grid-template-columns: 22px 1fr; align-items: baseline;
  padding: var(--s2) 0; border-bottom: 1px solid var(--rule-hair);
  font: 400 15px/1.5 var(--font-ui); color: var(--fg-secondary);
}
.os-list ul ul > li::before { content: ''; width: 5px; height: 5px; margin-top: 8px;
                              background: var(--fg-label); transform: none; }
.os-list ul ul ul > li { grid-template-columns: 16px 1fr; padding: 7px 0; border-bottom: 0;
                         font-size: 14px; }
.os-list ul ul ul > li::before { content: '\2014'; background: none; width: auto; height: auto;
                                 color: var(--fg-label); font: 400 15px/1.5 var(--font-ui); }

/* Term / definition is the DEFAULT item shape, not an edge case: nearly every
   list item in the corpus is a bold label, a colon, then text. */
/* The design names these os-li__term and os-li__def; the body pipeline emits
   os-li-t and os-li-d. Both are matched, because targeting only the design's
   names made this whole block DEAD CSS — it never matched a single row on the
   site, and the rows only looked right because content-render's own older layer
   happened to grid them. Removing that layer would have welded every term to
   its definition, which is exactly the "OmThe universal sound" defect §6.6
   describes, reintroduced by a cleanup rather than by an import. */
/* The design's own names are gone from these pairs. content-body builds a term row
   as <li class="os-li os-li-td"> with <span class="os-li-t"> and <span class="os-li-d">
   inside it — never os-li--term, os-li__term or os-li__def — and the row's GRID lives
   in content-render's .osa-body .os-li-td layer. So the design-named halves matched
   nothing, and the rule keyed on .os-list > li.os-li--term set a grid on an element
   that is not built. .os-num went with them: nothing emits it either, which made
   .os-li-t .os-num unreachable through the live half of its own selector too. */
.os-li-t { font: 600 15px/1.45 var(--font-ui); color: var(--fg-primary); }
.os-li-d { font: 400 15px/1.5  var(--font-ui); color: var(--fg-secondary); }
@media (max-width: 1023px) {
  .os-li-t { grid-column: 2; }
  .os-li-d { grid-column: 2; }
}

/* ── 5.4 tables ────────────────────────────────────────────────────────── */
.os-table { width: 100%; border: 1px solid var(--rule); border-collapse: collapse;
            margin: 0 0 var(--s7); }
/* --plate-gold. Same correction as .osa-body th: on an ink ground the text gold is
   the legible one and --gold is a fill. */
.os-table thead th {
  background: var(--bg-inverse); color: var(--plate-gold);
  padding: 12px var(--s4); text-align: left;
  /* 1.4, matching .osa-body th: a header cell's width is set by the table, not
     by the label, so it wraps whenever the column is narrow — and leading 1
     printed the second line on top of the first. */
  font: 600 11px/1.4 var(--font-ui); letter-spacing: .14em; text-transform: uppercase;
}
.os-table td { padding: 13px var(--s4); border-bottom: 1px solid var(--rule-soft);
               font: 400 15px/1.4 var(--font-ui); }
.os-table tbody tr:nth-child(even) { background: var(--bg-row-tint); }

/* THE RECORD-CARD TABLE IS DELETED, AND ITS COMMENT WAS WRONG.
   It read "the server adds os-table--cards when there are more than 6 columns" and
   the server does no such thing — the only class the table pipeline emits is
   os-table-wrap. So the wide-table treatment the comment describes was never
   reachable: the eleven-column table it was written for gets the phone stack below,
   which is keyed on .os-table itself and does work. A comment asserting a mechanism
   that does not exist is worse than no comment, because it stops the next reader
   looking. The ::before that printed each cell's data-label also needed the server
   to write data-label attributes, and nothing does that either. */
@media (max-width: 1023px) {
  .os-table, .os-table tbody, .os-table tr, .os-table td { display: block; width: auto; }
  .os-table thead { display: none; }
  .os-table tr { border: 1px solid var(--rule); background: var(--bg-page);
                 margin-bottom: var(--s3); }
  .os-table td:first-child {
    background: var(--bg-surface); border-bottom: 1px solid var(--rule);
    font: 600 16px/1.2 var(--font-ui); padding: 11px 14px;
  }
  .os-table td:not(:first-child) {
    display: flex; justify-content: space-between; gap: var(--s3);
    padding: 9px 14px; border-bottom: 1px solid var(--rule-hair);
    font: 400 14px/1.3 var(--font-ui);
  }
  .os-table td:not(:first-child)::before {
    content: attr(data-label);
    color: var(--fg-label); letter-spacing: .06em; text-transform: uppercase;
  }
}

/* ── 5.5 entry row - archives, indexes, search, browse ─────────────────── */
/* One component, used identically everywhere a list of entries appears.
   NEVER truncate a title: 72 exceed 70 characters, longest 122, and the
   namespace prefix is at the FRONT, so clipping destroys the meaning. */
.os-rows { list-style: none; margin: 0; padding: 0; }
/* THE ENTRY'S OWN PICTURE, in a track of its own. 643 of the 651 published
   entries carry a feature_image — 98.8%, and 100% of them on 31 of the 33
   collections holding five or more — so this column is very nearly solid, which
   is what makes it worth the width.

   48px, NOT the 80px used on /resources/ and the home page, and the size was
   chosen by measuring all four candidates on the two collections that bracket
   the library. Those other rows carry a two-line description that already stood
   80px tall; this one is a title and two figures, and the archive is NOT
   paginated — /tag/palmistry/ is 138 entries in one 10,427px document.

       size   articles-by-amit (prose)   palmistry (reference)
       48px   +5%                        +28%
       56px   +9%                        +38%
       64px   +15%                       +48%
       80px   +27%                       +69%

   48 is where it stops costing anything on the rows that were already tall: the
   floor becomes 83px, so a two-line row (87) and a three-line row (114) do not
   move at all and only the one-line rows grow. Above 48 every row pays.

   The track is auto rather than a fixed 48px, because .os-thumb carries a 1px
   border on each side and a fixed track clips it. */
.os-row {
  display: grid; grid-template-columns: 34px auto 1fr 88px 96px;
  gap: 18px; align-items: baseline;
  padding: var(--s4) 0; border-bottom: 1px solid var(--rule-soft);
}
/* align-self, because the row baseline-aligns and a 64px block has no useful
   baseline — left alone it hangs its bottom edge on the title's first line. */
.os-row > .os-thumb { width: 48px; height: 48px; align-self: start; }
.os-row:nth-child(even)  { background: var(--bg-row-tint); }
.os-row__n     { font: 500 12px/1.6 var(--font-ui); color: var(--fg-label);
                 font-variant-numeric: tabular-nums; }
.os-row__title { font: 400 20px/1.35 var(--font-display); color: var(--fg-primary);
                 text-wrap: pretty; }
.os-row__meta  { font: 400 12px/1.6 var(--font-ui); color: var(--fg-label);
                 font-variant-numeric: tabular-nums; }
.os-row:hover                { background: var(--bg-row-tint); }
.os-row:hover .os-row__title { color: var(--accent); }
/* 5A: "Archive / list row | bg -> bg-row-tint; title -> accent | bg -> bg-surface-2
   | bg bg-surface-2, 3px accent left border". Before [aria-current] so pressing the
   current row keeps its ground. */
.os-row:active               { background: var(--bg-surface-2); }
.os-row[aria-current] {
  background: var(--bg-surface-2);
  border-left: 3px solid var(--accent); padding-left: var(--s3);
}
.os-row[aria-current] .os-row__n { color: var(--accent); }
.os-row__title:visited { color: var(--fg-secondary); }
@media (max-width: 1023px) {
  /* The two figure columns drop under the title; the picture keeps its track,
     which is the whole point of it on the narrow view.
       THE MINUTES AND THE DATE SHARE ONE LINE, and until now they did not. As
     two undistinguished grid children in the same column they took a row each,
     with the row's own 18px gap between them — measured on the live site: "3
     MIN" at y=1042 and "04 SEP 2025" at y=1079, on every /tag/ page at every
     width below 1024. Placed in two tracks of one row they read as a meta line,
     which is what they are. row-gap is separated from column-gap so the 18px
     that spaces the COLUMNS stops padding the title away from its own figures. */
  .os-row { grid-template-columns: 34px auto auto 1fr;
            column-gap: 14px; row-gap: 6px; }
  .os-row__title { grid-column: 3 / -1; }
  .os-row__meta--time { grid-column: 3; }
  .os-row__meta--date { grid-column: 4; }
}
@media (max-width: 767px) {
  /* The ordinal goes. At 390 the row is 358 wide, and keeping it would leave
     the title 248px — a reference list's titles run to 122 characters, which is
     seven lines at that measure. The number is reading ORDER, which a list in
     order already shows; the picture is information the reader cannot get any
     other way. Between the two it is the number that yields the width. */
  .os-row { grid-template-columns: auto auto 1fr; }
  .os-row__n { display: none; }
  .os-row__title { grid-column: 2 / -1; }
  .os-row__meta--time { grid-column: 2; }
  .os-row__meta--date { grid-column: 3; }
}

/* ── 5.6 browse grid ──────────────────────────────────────────────────────
   DELETED, not disabled. .os-grid* was the v1 collections grid and nothing on the
   site has emitted it: a repo-wide search for the token finds it only in its own
   declarations. The two real grids superseded it — .os-arc__cell for the archive
   and the front door, .os-bhava__cell for the home page's twelve ways in — and both
   are styled where they are built.
   It mattered because it was not inert. 5A's three states for "Bhava Grid cell"
   were written onto this class and did nothing, and the test asserting them passed,
   because the class the home page actually renders is .os-bhava__cell and its
   ground was inline. Dead CSS that answers to a real name in the design is worse
   than dead CSS: it absorbs the fix. */

/* ── 5.7 Bhava Grid - the homepage signature ───────────────────────────── */
/* The South Indian chart: 4x4 with a hollow 2x2 centre. Pure rectangles, no
   SVG, no illustration, no image weight. */
.os-bhava {
  display: grid; grid-template-columns: repeat(4, 1fr); grid-template-rows: repeat(4, 84px);
  gap: 1px; background: var(--bg-inverse); border: 1px solid var(--bg-inverse);
}
.os-bhava__cell { background: var(--bg-page); padding: 9px 11px;
                  display: flex; flex-direction: column; justify-content: space-between; }
/* The tint is a MODIFIER, not nth-child. The canvas's chequer is not a plain
   alternation — 1,3,7,9,10,12 take the page ground and 2,4,5,8,11,13 the surface,
   with the centre plate at 6 — so an even/odd rule got six of the twelve wrong. It
   never showed, because the markup was writing the ground into a style attribute
   and outranking this rule entirely. */
.os-bhava__cell--tint { background: var(--bg-surface); }
/* 5A: "Bhava Grid cell | bg -> bg-surface-2; name -> accent | bg -> rule | bg
   bg-inverse, numeral + name gold". None of the three existed, and none COULD have
   while the ground was inline. */
.os-bhava__cell:hover { background: var(--bg-surface-2); text-decoration: none; }
.os-bhava__cell:hover .os-bhava__name { color: var(--accent); }
.os-bhava__cell:active { background: var(--rule); }
.os-bhava__cell[aria-current] { background: var(--bg-inverse); }
.os-bhava__cell[aria-current] .os-bhava__num  { color: var(--gold); }
.os-bhava__cell[aria-current] .os-bhava__name { color: var(--gold); }
.os-bhava__cell[aria-current] .os-bhava__cnt { color: var(--plate-fg-2); }
.os-bhava__num  { font: 400 14px/1 var(--font-deva); color: var(--accent); }
.os-bhava__name { font: 500 13px/1.15 var(--font-ui); color: var(--fg-primary);
                  display: block; }
.os-bhava__cnt  { font: 400 11px/1.4 var(--font-ui); color: var(--fg-label);
                  font-variant-numeric: tabular-nums; }
.os-bhava__centre {
  grid-column: span 2; grid-row: span 2;
  background: var(--plate-bg); color: var(--plate-fg);
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 9px;
}
.os-bhava__centre .glyph { font: 400 30px/1 var(--font-display); color: var(--plate-gold); }
.os-bhava__centre .lbl   { font: 500 11px/1.6 var(--font-ui); letter-spacing: .18em;
                           text-align: center; }
.os-bhava__centre a      { font: 400 11px/1 var(--font-ui); letter-spacing: .1em;
                           color: var(--plate-accent); }
@media (max-width: 1279px)   { .os-bhava { grid-template-rows: repeat(4, 72px); } }
@media (max-width: 1023px) { .os-bhava { grid-template-rows: repeat(4, 64px); }
                                     .os-bhava__cnt { display: none; } }
@media (max-width: 767px) {
  .os-bhava { grid-template-columns: repeat(2, 1fr); grid-template-rows: none; }
  .os-bhava__centre { grid-column: 1 / -1; grid-row: auto; padding: var(--s6) 0; order: -1; }
}

/* ── 5.8 the plate - every crossing to myday ───────────────────────────── */
/* Every link to the platform is a plate, never an inline link, one per page.
   It is the object that means: you are leaving the library. Two plates on a
   544-word page is a sales pitch, and this is a library. */
/* Metrics transcribed, not estimated. The canvas draws this object twice —
   screen 7A at padding 22px 28px and screen 3B at 20px 18px — and both use the
   SAME type: eyebrow 600 10px/1 at .16em, lead 400 17px/1.45 serif, button
   500 11px/1 at .1em with 12px 14px of padding.

   The lead was 19px here and the button 12px with 20px of side padding, which
   is 8px wider than the 3B rail's content box: "OPEN HASTA ↗" wrapped, and the
   arrow orphaned onto a line of its own. Type that is 2px too large does not
   look like a bug on the wide surface, so it survived until the narrow one. */
.os-plate { background: var(--plate-bg); padding: 22px 28px;
            border: 1px solid var(--plate-rule); }
/* LEADING THAT SURVIVES A WRAP. At 10px with .16em of tracking, "RUN IT ON YOUR
   OWN CHART" needs about 210px and the article's right rail gives it 214 minus
   padding — so it wraps, and at line-height 1 the two lines sat on top of each
   other. Tight leading is right for a label that fits on one line and wrong the
   moment it does not; 1.45 costs 4px on the labels that do fit. */
.os-plate__eyebrow { font: 600 10px/1.45 var(--font-ui); letter-spacing: .16em;
                     text-transform: uppercase; color: var(--plate-gold);
                     margin-bottom: 10px; }
.os-plate__lead    { font: 400 17px/1.45 var(--font-display); color: var(--plate-fg);
                     margin-bottom: 14px; }
.os-plate__sub     { font: 400 14px/1.5 var(--font-ui); color: var(--plate-fg-2);
                     margin-bottom: var(--s4); }
.os-plate .os-btn  { background: var(--plate-gold); color: var(--plate-bg);
                     border-color: var(--plate-gold);
                     font: 500 11px/1 var(--font-ui); letter-spacing: .1em;
                     padding: 12px 14px; }
.os-plate .os-btn:hover { background: #EFC97A; color: var(--plate-bg); }
/* Screen 3B: the same plate in a 250px rail, so the container tightens while
   the type does not. */
.os-article__rail-r .os-plate { padding: 20px 18px; }

/* ── 5.9 buttons, chips, filters ───────────────────────────────────────── */
.os-btn {
  display: inline-block; border: 1px solid var(--btn-edge);
  background: var(--bg-inverse); color: var(--fg-inverse);
  font: 500 12px/1 var(--font-ui); letter-spacing: .10em; text-transform: uppercase;
  padding: 14px var(--s5); cursor: pointer;
  transition: background-color 120ms ease-out, color 120ms ease-out;
}
/* EVERY BUTTON STATE RESTATES ITS COLOUR, because half of these buttons are
   anchors and the base sheet says a:hover { color: var(--accent-hover) } at the
   same specificity as .os-btn:hover. So an <a class="os-btn"> was ink-ground
   with accent-red text the moment the pointer touched it — #7A1F12 on #3A3126,
   about 1.4:1, which is a label that vanishes under the cursor. Live today on
   the three buttons on /products/. .os-plate .os-btn never showed it only
   because the plate rule happens to set a colour of its own.
   The same reasoning covers :active, and the ghost variant on both. */
.os-btn:hover  { background: #3A3126; color: var(--fg-inverse); text-decoration: none; }
.os-btn:active { background: #0F0C08; color: var(--fg-inverse); }
.os-btn--ghost { background: none; color: var(--fg-primary); }
.os-btn--ghost:hover  { background: var(--bg-surface-2); color: var(--fg-primary); }
/* 5A gives the secondary button a pressed step of its own: "bg -> rule". Only the
   primary had one, so the two read identically under the finger. */
.os-btn--ghost:active { background: var(--rule); color: var(--fg-primary); }
/* .os-btn--accent is DELETED. 5A's "Nav CTA (sindoor)" row is real and its three
   states are built — on .os-sh-btn in oshatra-shell.js, which is the control the
   header actually renders. This was a second copy on a class nothing emits, and it
   is the same trap as .os-grid__cell: the right rule on the wrong element, with a
   passing test pointed at it. The test now asserts .os-sh-btn. */

.os-chip {
  display: inline-flex; align-items: center; gap: var(--s2);
  /* 9px 12px, and NO GROUND. The canvas draws every chip at 9px 12px with border
     and colour only — border:1px solid #DDD1BC; color:#574C40 — so an inactive
     chip sits on the page. This had 11px 15px, which is the padding the canvas
     reserves for the header's pill, and --bg-input (#FFFDF8), which is the ground
     for a form field the reader can type into. A chip is neither. */
  border: 1px solid var(--rule); background: none; color: var(--fg-secondary);
  font: 500 11px/1 var(--font-ui); letter-spacing: .10em; text-transform: uppercase;
  padding: 9px 12px;
}
.os-chip:hover { border-color: var(--fg-label); color: var(--fg-primary); text-decoration: none; }
/* PRESSED is not CURRENT. 5A separates them — "bg -> bg-surface-2" for the finger
   down, "bg bg-inverse, text fg-inverse" for the chip you are standing on — and
   only the second was built, so a chip gave no feedback until the page reloaded
   under it. Written before the current rule, which weighs the same. */
.os-chip:active { background: var(--bg-surface-2); }
.os-chip[aria-pressed="true"], .os-chip--on {
  background: var(--bg-inverse); border-color: var(--bg-inverse); color: var(--fg-inverse);
}
/* No gold on a selected chip. 5A's interaction table gives the chip's CURRENT
   state "bg bg-inverse, text fg-inverse" and nothing else, and 6A draws the
   selected sort chip as #221C15 under #FBF7F0 — no third colour. The rule this
   replaces also targeted a class nothing on the site emits, so it had never painted
   anything: a wrong colour waiting on a typo being fixed. */

/* The block that holds a crossing to the platform, once content-body has turned
   the author's bare anchor into a button. A button needs air the paragraph
   rhythm does not give it, and it never takes the body's justification or
   centring — it sits where the text sits. */
.os-crossing { margin: var(--s7) 0; text-align: left; }
.os-crossing > .os-btn { margin: 0; }

.os-tag {
  border: 1px solid var(--rule); padding: 6px 10px;
  font: 500 11px/1 var(--font-ui); letter-spacing: .1em; color: var(--fg-secondary);
}

/* Disabled never uses opacity - it breaks every measured ratio. */
.os-btn[aria-disabled="true"], .os-chip[aria-disabled="true"] {
  color: var(--fg-label); border-color: var(--rule); background: none;
  cursor: default; pointer-events: none;
}

/* ── 5.10 recitation plate ─────────────────────────────────────────────── */
/* THE V1 PLAYER IS DELETED. .os-rec__* was superseded by .os-plate-* in
   content-render.js, which is what the 61 recitation entries actually render, and
   nothing has emitted an .os-rec__ class since. It was not inert: .os-rec__play
   carried border-radius:50%, and the radius guard was asserting the design's
   one-circle rule against THAT class rather than against the control the reader
   can see. */

/* ── 5.11 pull quote, figure, section break ────────────────────────────── */
/* 22px 26px is the spec's own figure (section 589), not a scale step. The header
   of this file rules that where the canvas and the spec disagree the spec wins,
   because it carries the measured values. */
.os-quote { border-left: 3px solid var(--accent); background: var(--bg-surface);
            padding: 22px 26px; margin: 0 0 var(--s7); }
.os-quote p    { font: italic 400 20px/1.55 var(--font-display); color: var(--fg-primary); }
.os-quote cite { font: 500 11px/1 var(--font-ui); letter-spacing: .14em;
                 text-transform: uppercase; color: var(--fg-label); font-style: normal; }

figure     { margin: 0 0 var(--s7); }
figure img { display: block; width: 100%; height: auto; border: 1px solid var(--rule); }
figcaption { font: 400 13px/1.5 var(--font-ui); color: var(--fg-label); margin-top: 10px; }

/* 385 authorial section breaks. Never a full-width line - that reads as a
   section head. An hr adjacent to a heading is stripped at import instead. */
hr.os-break {
  border: 0; height: 7px; width: 7px; margin: 22px auto;
  background: var(--rule); transform: rotate(45deg);
}

/* ── 5.12 bookmark card ────────────────────────────────────────────────── */
/* content-body builds a bookmark as .os-bookmark with .os-bm-t and .os-bm-h inside
   it, and both of those are styled in content-render's body layer. The design's own
   child names — __t, __d, __host — were never emitted, so only the outer card is
   kept here.
   The embed facade went with them. It was a click-to-load poster for third-party
   video; the renderer keeps real iframes for the handful of hosts the corpus embeds
   and sizes them by aspect-ratio instead, so nothing ever builds a facade. */
.os-bookmark { display: block; border: 1px solid var(--rule); background: var(--bg-page);
               padding: 18px var(--s5); margin: 0 0 var(--s7); }
.os-bookmark:hover  { border-color: var(--fg-primary); text-decoration: none; }

/* The dark-mode control, in the footer's fourth column per section 4.2. */
.os-sh-theme {
  display: inline-flex; align-items: center; gap: 9px; margin-top: var(--s4);
  background: none; border: 1px solid var(--rule); color: var(--fg-label);
  padding: 9px 12px; cursor: pointer;
  font: 500 11px/1 var(--font-ui); letter-spacing: .1em; text-transform: uppercase;
}
.os-sh-theme:hover { border-color: var(--fg-label); color: var(--fg-primary); }
/* THE HEADER'S FORM. Square, the glyph alone, and the same box as the search
   control beside it — 9px 11px, 1px --rule — so the two read as one pair of
   header controls rather than as a button that wandered up from the footer.
   No margin-top: that belongs to the footer column, where this used to sit
   under a paragraph. */
.os-sh-theme--compact {
  margin-top: 0; padding: 9px 11px; gap: 0; font-size: 13px; line-height: 1;
}
/* Pressed means dark is on. The canvas has no third colour for a toggled
   control, so it takes the same treatment as a current chip: the ink ground. */
.os-sh-theme--compact[aria-pressed="true"] {
  background: var(--bg-inverse); border-color: var(--bg-inverse); color: var(--fg-inverse);
}

/* ── 6.1 article - three variants, selected server-side ────────────────── */
/* Do not build one article template. 301 of 651 entries have no headings, so a
   fixed three-column layout leaves the contents rail empty on 45% of the
   library. A rail appears only when it has something to hold. */
/* ONE WIDTH FOR EVERY TEMPLATE — and this was the one that did not take it.
   Section 4 fixes the page at --shell, and nine templates say so in the same
   four declarations: max-width var(--shell), margin 0 auto, background
   var(--bg-page), border 1px solid var(--card-border). The home page, the
   index, the archive,
   both front doors, the consultation, products, learn, the gallery and the
   utility pages all sit in that card. The article did not: no max-width, no
   margin, no border and no ground, so at 1400px every one of the 651 entries
   and every standing page ran 0..1400 while everything else ran 109..1291.
   Measured, the prose then started at five different x positions depending on
   which page you were on — 52, 70, 150, 154 or 276.
   Taking the card puts the article's own three columns inside the same frame
   as everything else, so a reader moving from the index to an entry sees the
   page hold still. */
.os-article         { display: grid; gap: 0;
                      max-width: var(--shell); margin: 0 auto;
                      background: var(--bg-page); border: 1px solid var(--card-border); }
/* Edge to edge below the breakpoint, exactly as the other nine do it. */
@media (max-width: 1023px) {
  .os-article { border-left: 0; border-right: 0; }
}
.os-article--full   { grid-template-columns: 224px 1fr 250px; }
.os-article--narrow { grid-template-columns: 1fr 250px; }
/* NO RIGHT RAIL, NO COLUMN FOR IT. The variant is measured from the prose, so it
   reserved 250px whether the rail had anything to hold or not — and on 113 entries
   it did not. Devotional and writings entries get no plate by design and have no
   sibling runs to list, so a quarter of the width was a blank panel beside the
   body. The archetype knows whether it built a rail and says so in a modifier;
   this is the "no rail is ever rendered empty" rule applied to the column. */
.os-article--full.os-article--no-rail-r   { grid-template-columns: 224px 1fr; }
.os-article--narrow.os-article--no-rail-r { grid-template-columns: 1fr; }
.os-article--plate  { grid-template-columns: minmax(0,620px); justify-content: center; }
.os-article__rail-l { background: var(--bg-panel); border-right: 1px solid var(--rule);
                      padding: var(--s7) 22px; }
/* The left rail's contents travel together — same wrapper, same offset and same
   height cap as the right, so the two rails hold one line down the page. */
.os-article__rail-l-in { position: sticky; top: 92px;
                      max-height: calc(100vh - 120px); overflow-y: auto;
                      scrollbar-gutter: stable; }
.os-article__rail-l-in { scrollbar-width: thin; scrollbar-color: var(--rule) transparent; }
.os-article__rail-l-in::-webkit-scrollbar { width: 6px; }
.os-article__rail-l-in::-webkit-scrollbar-track { background: transparent; }
.os-article__rail-l-in::-webkit-scrollbar-thumb { background: var(--rule); }
.os-article__rail-r { background: var(--bg-panel); border-left: 1px solid var(--rule);
                      padding: var(--s7) 22px; }
/* Sticky on the INNER wrapper, so the blocks travel with the reader while the
   column's ground and border still run the full height of the row. Same top
   offset as .osa-toc, so the two rails hold the same line. */
/* scrollbar-gutter, because this box scrolls and its content is right-aligned.
   On a short window — 595px tall, which is an ordinary laptop with a toolbar —
   the plate plus the line list is 601px in 475px of room, so a scrollbar
   appears, takes 15px off the right, and lands squarely on the counts in THE
   LINES: "19", "16", "12" clipped to "1", "1", "1". Reserving the gutter costs
   the same 15px whether or not the bar is there, so nothing moves when it
   appears and nothing is ever underneath it. */
.os-article__rail-r-in { position: sticky; top: 92px;
                      max-height: calc(100vh - 120px); overflow-y: auto;
                      scrollbar-gutter: stable; }
/* And when it does appear it is the design's, not the platform's: a hairline in
   the rule colour on the panel ground, at the width of the gutter above. */
.os-article__rail-r-in::-webkit-scrollbar { width: 6px; }
.os-article__rail-r-in::-webkit-scrollbar-track { background: transparent; }
.os-article__rail-r-in::-webkit-scrollbar-thumb { background: var(--rule); }
.os-article__rail-r-in { scrollbar-width: thin; scrollbar-color: var(--rule) transparent; }
.osa-toc { scrollbar-width: thin; scrollbar-color: var(--rule) transparent; }
.osa-toc::-webkit-scrollbar { width: 6px; }
.osa-toc::-webkit-scrollbar-track { background: transparent; }
.osa-toc::-webkit-scrollbar-thumb { background: var(--rule); }
/* 44px at the sides, which is what every other card in the system pads by —
   .os-home__mast-l, .os-ix__disc, .os-arc__browse, .os-cs__mast and .os-ut2__in
   all say 44. This said --s10, which is 52, so an entry's first word started
   eight pixels further in than the same reader's last page. Vertically it keeps
   the article's own rhythm: 44 above, 64 below, because a body ends against the
   run footer and a masthead does not. */
.os-article__body   { padding: var(--s9) var(--s9) var(--s11); min-width: 0; }
.os-article__body > * { max-width: var(--measure); }

/* The rails drop INDEPENDENTLY - right first, then left. Dropping both at one
   stop wastes the 1024-1279 band, where the left rail still fits and is the
   more useful of the two. */
@media (max-width: 1279px) {
  .os-article--full   { grid-template-columns: 224px 1fr; }
  .os-article--narrow { grid-template-columns: 1fr; }
  .os-article__rail-r { grid-column: 1 / -1; border-left: 0;
                        border-top: 1px solid var(--rule); }
  /* Below the article it is a band, not a column: sticking it there would pin a
     block over the footer. */
  .os-article__rail-r-in { position: static; max-height: none; overflow: visible; }
}
@media (max-width: 1023px) {
  .os-article--full   { grid-template-columns: 1fr; }
  .os-article__rail-l { grid-column: 1 / -1; border-right: 0;
                        border-bottom: 1px solid var(--rule); }
}
@media (max-width: 767px) {
  .os-article__body { padding: var(--s6) var(--s4) var(--s8); }
}

/* THE INSET BELONGS TO THE COMPONENT, not to five copies of it. This is one
   breadcrumb, built once in os-archive.js and used by every card template, and
   the inset that puts it 44px inside the card was declared twice — on .os-arc
   and on .os-fd. The archive, both front doors, the index and products got it;
   the consultation, learn and the gallery did not, so on those three the
   breadcrumb sat hard against the card's own border at x=110 while the same
   breadcrumb on the page before sat at 154. 44px is what every card in the
   system pads by. */
.os-crumb { display: flex; align-items: center; gap: var(--s2);
            padding: 22px var(--s9) 0; margin: 0 0 var(--s6);
            font: 400 13px/1 var(--font-ui); color: var(--fg-label); }
@media (max-width: 767px) { .os-crumb { padding: 16px 16px 0; } }
/* The one template that already insets its own column: S3's form is a 720px
   band centred in the card, and the breadcrumb belongs to the band. */
.os-ut2__in > .os-crumb { padding-left: 0; padding-right: 0; }
.os-crumb .sep, .os-crumb__sep { color: var(--fg-separator); }
.os-crumb [aria-current] { color: var(--accent); }

.os-article h1 { font: 300 46px/1.08 var(--font-display); letter-spacing: -.034em;
                 margin: 0 0 var(--s5); text-wrap: pretty; }
@media (max-width: 767px) {
  .os-article h1 { font-size: 32px; line-height: 1.10; letter-spacing: -.022em; }
}
/* SCOPED, because it was losing. A bare .os-lede at (0,1,0) sits in the same
   document as .osa-body p at (0,1,1), which comes later and wins — so the lede
   rendered at body size, 17px with a 20px gap, on every entry that has one. It was
   written correctly and then silently overruled: the declaration is not dropped,
   it is simply outranked, which looks identical to never having written it.
   (No backticks in here — this whole block lives inside a template literal.)

   The canvas draws it at 400 19px/1.62 with margin 0 0 28px and a 64ch measure —
   narrower than the body's 68ch, deliberately, so the opening reads as an opening. */
.osa-body .os-lede, .os-lede {
  font: 400 19px/1.62 var(--font-ui); color: var(--fg-primary);
  margin: 0 0 28px; max-width: 64ch; text-wrap: pretty;
}
/* SPACE-BETWEEN, NOT AN AUTO MARGIN ON THE LAST ITEM. The canvas draws this row
   on one line: meta left, tag chips right. We wrap it for the phone, and an auto
   margin obeys the line it lands on — so on the ~half of entries whose byline is
   too long, the chips were pushed to the right of a line they were alone on,
   under a left-flush byline. justify-content applies per line, and a line with
   one item under space-between puts it at the main start. Both states, one rule.
   It needs .os-byline__meta to exist: with four loose spans, space-between would
   spread the byline itself across the measure. */
.os-byline {
  display: flex; align-items: center; gap: 18px; flex-wrap: wrap;
  justify-content: space-between;
  padding-bottom: var(--s5); border-bottom: 2px solid var(--rule-strong);
  margin-bottom: 36px;
  font: 400 13px/1 var(--font-ui); letter-spacing: .06em; text-transform: uppercase;
  color: var(--fg-label); font-variant-numeric: tabular-nums;
}
/* The middot the canvas joins byline items with, drawn on the item rather than
   emitted beside it. As its own element it wrapped independently, and at 390px
   the meta breaks after the date — leaving "AMIT SETHI · UPDATED 10 NOV 2024 ·"
   with a separator separating nothing at the end of the line. On a ::before it
   travels with the item it belongs to, and stays exactly as invisible to a
   screen reader as the aria-hidden span was.

   18px each side reproduces the canvas's spacing: it drew the middot as a flex
   item inside gap:18px, so item, 18, dot, 18, item. Hence gap 0 on the group. */
.os-byline__meta > span + span::before {
  content: '·'; color: var(--fg-separator); padding: 0 18px;
}
/* A MIDDOT IS A ONE-LINE DEVICE. Wherever the row wraps, a separator ends up
   alone at the edge of a line — dangling at the end when it is emitted between
   the items, leading the next line when it travels with them. There is no third
   option in CSS, because neither the element nor the pseudo can know it has
   become first or last on its line.
   So below the breakpoint the wrap stops being an accident and becomes the
   treatment: the extent takes a line of its own, and the separator that would
   have opened that line is not drawn. Two clean lines, author and date over
   words and minutes, which is what the row is anyway. */
@media (max-width: 1023px) {
  .os-byline__meta > span:last-child { flex-basis: 100%; margin-top: 4px; }
  .os-byline__meta > span + span:last-child::before { content: none; }
}

/* The chip row WRAPS. The byline itself already did, but the tags block inside it
   did not, so a multi-tag entry laid its chips out as one unbreakable 710px line
   and pushed the document 241px past a 485px viewport — measured, on
   /house-and-planet-afflictions…/, which carries five. flex-wrap alone is not
   enough: a flex item will not shrink below its content width without min-width,
   which is what let the row stay 710px wide inside a wrapping parent. */
/* The meta group: one flex item so the row has exactly two, wrapping internally
   at its own 18px so a long byline still breaks sensibly on a phone. */
/* gap 0: the 18px each side of the separator is the row's spacing now, carried
   on the ::before so it travels with its item across a wrap. */
.os-byline__meta {
  display: flex; align-items: center; flex-wrap: wrap; gap: 0; min-width: 0;
}
.os-byline .os-tags {
  display: flex; flex-wrap: wrap; gap: 7px; min-width: 0;
}

/* The contents render ONCE and are switched by breakpoint. Rendering them in
   the rail and again inline put about 36 lines above the first sentence on a
   phone, which is the defect this replaces. */
.os-toc a   { display: flex; gap: 9px; padding: 6px 0;
              font: 400 14px/1.45 var(--font-ui); color: var(--fg-secondary); }
.os-toc a:hover { color: var(--accent); text-decoration: none; }
.os-toc a[aria-current] {
  background: var(--bg-surface-2); color: var(--fg-primary); font-weight: 500;
  margin: 0 -10px; padding: 6px 10px; border-left: 2px solid var(--accent);
}
.os-toc--bar { display: none; }
@media (max-width: 1023px) {
  /* THE WHOLE RAIL, not its contents. This hid only the TOC, which left the rail
     element itself in the flow — a 64px empty panel with a bottom hairline
     stacked above the h1 on every variant-A entry at tablet and below. Invisible
     as a bug for as long as the rail held nothing else; the moment the locator
     moved in beside the TOC it rendered as a full-width grid of 120px squares
     ABOVE the article title, because the left rail comes first in the DOM.
     The TOC's mobile form is the bar below; the locator has no drawn mobile form,
     and the breadcrumb already says where you are, so it steps aside. */
  .os-article__rail-l { display: none; }
  .os-toc--bar {
    display: flex; align-items: center; justify-content: space-between;
    border: 1px solid var(--rule); background: var(--bg-panel);
    padding: 13px 15px; margin: var(--s4) 0;
    font: 600 11px/1 var(--font-ui); letter-spacing: .14em; text-transform: uppercase;
  }
}

.os-rail-block + .os-rail-block, .os-plate + .os-rail-block { margin-top: var(--s6); }
/* THE V1 CONTINUE LADDER IS DELETED — .os-rail-list, .os-continue*, .os-cont-card*.
   The live "Continue in..." is the archetype's .osa-runs block plus the locator,
   both of which are styled and emitted; these were the earlier draft of the same
   idea and no page has rendered one. */

/* ── 11.2 pagination ───────────────────────────────────────────────────── */
/* 20 per page. Absent on a filtered view under 20 results, on the fixed-URL
   gallery, and on home. */
.os-pager {
  margin-top: var(--s7); padding-top: var(--s6);
  border-top: 2px solid var(--rule-strong);
  display: flex; align-items: center; justify-content: space-between; gap: var(--s6);
}
.os-pager__count {
  font: 400 13px/1.5 var(--font-ui); letter-spacing: .06em; text-transform: uppercase;
  color: var(--fg-label); font-variant-numeric: tabular-nums;
}
/* 6C draws this one, and its note says "same chip metrics as the sort chips":
   gap 10px, 11px 14px on a number, 11px 15px on a step, and the resting label in
   --fg-secondary rather than full ink so the current page is the only one at
   weight. This had 7px and 13px 16px, which is a button's metrics, not a chip's. */
.os-pager__nav { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.os-pager a, .os-pager span[aria-current] {
  font: 500 12px/1 var(--font-ui); font-variant-numeric: tabular-nums;
}
.os-pager a { border: 1px solid var(--rule); color: var(--fg-secondary); padding: 11px 14px; }
.os-pager a:hover { border-color: var(--fg-primary); background: var(--bg-row-tint);
                    color: var(--fg-primary); text-decoration: none; }
.os-pager a:active { background: var(--bg-surface-2); }
.os-pager [aria-current] {
  border: 1px solid var(--bg-inverse); background: var(--bg-inverse);
  color: var(--fg-inverse); padding: 11px 14px;
}
/* The elision is a mark between chips, not a chip: no border, no ground. */
.os-pager__gap { font: 400 13px/1 var(--font-ui); }
/* Qualified with the element: .os-pager a weighs (0,1,1) and a bare
   .os-pager__step weighs (0,1,0), so the step's own padding lost to the number's
   and both rendered at 14px. Measured, not assumed — the browser reported
   pad=11px 14px on PREV. */
.os-pager a.os-pager__step { letter-spacing: .10em; text-transform: uppercase;
                             padding: 11px 15px; }
.os-pager a.os-pager__step[aria-disabled="true"] {
  border-color: var(--rule); color: var(--fg-label); cursor: default; pointer-events: none;
}
.os-pager__gap { color: var(--fg-label); padding: 0 var(--s1); }
@media (max-width: 767px) {
  .os-pager { flex-direction: column; align-items: stretch; gap: var(--s4); }
  .os-pager__nav { justify-content: space-between; }
  .os-pager__nav a:not(.os-pager__step), .os-pager [aria-current], .os-pager__gap { display: none; }
  .os-pager__nav::after {
    content: attr(data-position); order: 0;
    font: 500 12px/1 var(--font-ui); color: var(--fg-label);
    font-variant-numeric: tabular-nums; align-self: center;
  }
}

/* ══════════════════════════════════════════════════════════════════════════
   OSHATRA SHELL
   Transcribed from Oshatra Prototype.dc.html. Colours are tokens only.
   ══════════════════════════════════════════════════════════════════════════ */

/* ── the sheet ──────────────────────────────────────────────────────────────
   Verbatim from tokens.css and from every page wrapper in the prototype.
   The width calc() is load-bearing: without it the sheet runs edge to edge
   below its max-width, the canvas stops showing either side, and the whole
   three-tone system (canvas -> paper -> panel) collapses to one tone. Do not
   substitute padding for it — the gutter is SUBTRACTED from the width, which is
   what keeps the header row, the sheet and the CTA band on one vertical line. */
.sheet {
  max-width: var(--measure-wide);
  width: calc(100% - var(--gutter));
  box-sizing: border-box;
  margin: 30px auto 44px;
  padding: 0 var(--sheet-pad);
  background: var(--paper);
  border: 1px solid var(--rule);
  box-shadow: var(--sheet);
}
/* Doubled class on purpose. content-tokens.js also declares a bare .sheet at
   max-width: var(--measure-wide); a single-class modifier ties with it on
   specificity and would then be decided by whichever stylesheet happens to be
   concatenated last. Two classes outrank it in either order. */
.sheet.sheet--prose    { max-width: var(--measure-prose); }
.sheet.sheet--standard { max-width: var(--measure-standard); }
.sheet.sheet--wide     { max-width: var(--measure-wide); }
/* The Article sheet is followed immediately by the CTA band, so it drops its
   bottom margin — prototype writes margin: 30px auto 0 on exactly that page. */
.sheet.sheet--joined { margin-bottom: 0; }

/* ── the shell is interface, not prose ──────────────────────────────────────
   tokens.css spells a { text-decoration: none } site-wide, but the repo's base
   layer deliberately keeps an underline on links in BODY COPY, where it is the
   right call. The shell is furniture: the prototype draws not one underline in
   the header, the breadcrumb, the footer or the band, so every link this file
   emits restates it. Hover stays colour-only, per the README. */
.os-sh-skip, .os-sh-brand, .os-sh-nav__item, .os-sh-glyph, .os-sh-btn,
.os-sh-menu > summary, .os-sh-menu__item, .os-sh-crumb a, .os-sh-cta__btn,
.os-sh-colophon__brand, .os-sh-flink, .os-sh-footer__bar a {
  text-decoration: none;
}

/* ── skip link ──────────────────────────────────────────────────────────────
   Kept per the README. Parked off-screen with a transform rather than
   display:none so it stays focusable, and lifted above the sticky header's
   z-index: 50 so it is not covered when it appears. */
.os-sh-skip {
  position: fixed; top: 8px; left: 8px; z-index: 100;
  transform: translateY(-250%);
  background: var(--paper); color: var(--ink);
  border: 1px solid var(--rule);
  font: 400 12.5px/1 var(--font-mono); letter-spacing: 0.02em;
  padding: 10px 14px;
}
.os-sh-skip:focus { transform: none; color: var(--ink); }

/* ══════════ HEADER — one component, six items, every page ══════════ */
.os-sh-header {
  position: sticky; top: 0; z-index: 50;
  background: var(--canvas);
  border-bottom: 1px solid var(--canvas-edge);
}
/* Padding is ZERO here. The wordmark has to align with the outer edge of the
   sheet below it, and the sheet gets its inset from the width calc(), not from
   padding — so the header must get its inset the same way or the two rows sit
   on different verticals. Tokens carry the prototype's literals: --measure-wide
   1400, --gutter 56, --header-h 62. */
.os-sh-header__in {
  max-width: var(--measure-wide);
  width: calc(100% - var(--gutter));
  box-sizing: border-box;
  margin: 0 auto;
  padding: 0;
  height: var(--header-h);
  display: flex; align-items: center; gap: 28px;
}

/* Wordmark. flex: none — it is never the thing that gives up space. The
   font-size is restated at 16px: the prototype declares no font on this link,
   so it inherits the browser default of 16px, while this repo's base layer sets
   body to 17px — without the restatement the wordmark link measures a pixel
   off the prototype. */
.os-sh-brand { display: flex; align-items: center; gap: 9px; flex: none; font-size: 16px; color: var(--ink); }
.os-sh-brand:hover { color: var(--accent); }
/* Screen 5D, the canonical header, transcribed. The wordmark is SANS, uppercase,
   at .16em — a tracked lockup, not a serif name. It was 14.5px display serif at
   .01em, which is a different object entirely. The glyph is 21px, not 19px. */
.os-sh-brand__glyph { font: 400 21px/1 var(--font-display); color: var(--accent); }
.os-sh-brand__name  {
  font: 500 16px/1 var(--font-ui); letter-spacing: .16em;
  text-transform: uppercase; color: var(--fg-primary);
}

/* Nav. SIX items, on every page, always. There is deliberately no overflow
   rule anywhere in this block: an earlier iteration of the live site put the
   nav in a hidden-overflow scroller and Products silently disappeared off the
   right edge. When space runs out the nav is replaced by a real menu (below),
   never truncated and never scrolled. */
.os-sh-nav { display: flex; align-items: center; gap: 26px; height: 100%; flex: 0 1 auto; }
/* Full header height, so the active indicator can sit on the bottom edge of the
   header itself rather than under the word. */
.os-sh-nav__item {
  position: relative; height: 100%;
  display: flex; align-items: center;
  white-space: nowrap;
  font: 400 15px/1 var(--font-ui);
  color: var(--fg-primary);
}
/* THE UNDERLINE IS 3px UNDER THE WORD, not on the header's bottom edge. Four
   screens draw the current nav item identically — 5D, 4B, 3B and 3C, all
   color:#9E2B1C; border-bottom:2px solid #9E2B1C; padding-bottom:3px — and 5A's
   table adds the hover: "Nav item | text -> accent; 2px underline in rule | No
   change | accent + 2px accent underline".
   The rule went on a .os-sh-nav__bar absolutely positioned at bottom:0 of an item
   that is height:100%, so it sat on the header edge, ~14px below the word. The
   comment above it said as much and the CSS did the other thing.
   The border lives on an inner span and is TRANSPARENT at rest, so hover and current
   change a colour and never a box: a border appearing on hover would jog the word
   up 2.5px inside its centred flex line. */
.os-sh-nav__l { border-bottom: 2px solid transparent; padding-bottom: 3px; }
.os-sh-nav__item:hover { color: var(--accent); }
.os-sh-nav__item:hover .os-sh-nav__l { border-bottom-color: var(--rule); }
.os-sh-nav__item[aria-current="page"] { color: var(--accent); }
.os-sh-nav__item[aria-current="page"] .os-sh-nav__l { border-bottom-color: var(--accent); }

/* Right group. No margin-left: auto — the prototype packs the whole row from
   the left and leaves the slack on the right. Measured in the rendered
   prototype at 1500px before transcribing, because a flex row reads as if it
   ought to be pushed right. */
.os-sh-actions { display: flex; align-items: center; gap: 14px; flex: 0 0 auto; white-space: nowrap; }
/* A FIELD, not a glyph. The canvas draws "Search the library" as a 150px-wide
   bordered box — the one affordance on the page that says a reader may type. This
   shipped as a bare ⌕ character, which asks them to recognise a symbol instead.
   §4A's own note says most readers arrive from a search engine with one question. */
.os-sh-glyph {
  font: 400 14px/1 var(--font-ui); color: var(--fg-label);
  border: 1px solid var(--rule); padding: 9px 14px; min-width: 150px;
  background: none; cursor: pointer; text-align: left;
}
.os-sh-glyph:hover { border-color: var(--fg-label); color: var(--fg-primary); }
.os-sh-glyph__i { margin-right: 7px; }
/* The field's own label. Below its own threshold the header has no room for it,
   so the box shrinks back to the glyph rather than wrapping or overflowing.
   1142, NOT 1023 and not 1160. This is the widest of the three reliefs — the
   field is 150px, against 10px for the nav gap and 34px for the Tools label — so
   it is the one that should fire LAST, and it gets a measured threshold of its
   own rather than sharing the block below. The full-size row needs 1143px of
   viewport (1099 of content in a width that is the viewport less the 44px
   gutter); at 1143 and up the field fits and keeps its label, and at 1142 it is
   the thing that gives. Sharing the 1160 threshold cost the label across
   1143-1160 for no reason: there was room. */
@media (max-width: 1142px) {
  .os-sh-glyph { min-width: 0; padding: 9px 11px; }
  .os-sh-glyph__l { display: none; }
  .os-sh-glyph__i { margin-right: 0; }
}
/* .os-sh-glyph--theme deleted: the toggle is .os-sh-theme, its own element, and no
   glyph ever takes a --theme modifier. */
.os-sh-glyph:hover { color: var(--accent); }
/* ACCENT-FILLED, not outlined. The canvas gives the one crossing in the header an
   accent ground and inverse text; it was a ghost button indistinguishable from the
   search box beside it. */
.os-sh-btn {
  font: 500 12px/1 var(--font-ui); color: var(--fg-inverse);
  background: var(--accent); border: 1px solid var(--accent); padding: 11px 15px;
  letter-spacing: .08em; white-space: nowrap;
}
/* Hover is colour only. The README is explicit: no transitions beyond colour. */
/* accent-hover, not a hand-mixed red. #8A2418 appears nowhere in the canvas;
   5A rules this control "Nav CTA (sindoor) | bg -> accent-hover | bg -> #66190E",
   and accent-hover is a token that also flips correctly in dark mode, which a
   literal cannot. */
.os-sh-btn:hover  { background: var(--accent-hover); border-color: var(--accent-hover);
                    color: var(--fg-inverse); }
.os-sh-btn:active { background: var(--accent-press); border-color: var(--accent-press); }

/* THE ACCENT IS TEXT-SAFE ON THE PAGE; IT IS NOT A DARK GROUND IN DARK MODE.
   In light the accent is a deep red (#9E2B1C) and light text sits on it at
   6.98:1. In dark the same token is a light salmon (#E8836A), and this button
   kept the light text — #EFE7D9 on #E8836A is 2.17:1, on the most prominent
   control in the header, on every page of the site. The measured ratio in the
   canvas (7.5:1) is the accent AS TEXT on the page ground, which is a different
   question from the accent AS A GROUND, and nothing had ever asked the second.

   Same confusion as the two golds, a fifth time. In dark the ground is light,
   so the label is ink: 7.06:1 at rest, 9.18 on hover, 11.18 pressed. The border
   follows the ground, and the pressed step is a token now rather than a literal
   dark red, so the ladder runs one direction per theme instead of doubling back
   and stranding the text colour. */
[data-theme="dark"] .os-sh-btn,
[data-theme="dark"] .os-sh-btn:hover,
[data-theme="dark"] .os-sh-btn:active { color: var(--plate-bg); }
/* The OS-preference half of this pair is gone with the palette's: dark is now
   reached by asking for it, so [data-theme="dark"] above is the whole rule. */

/* The narrow-width menu. A real disclosure, closed by default, with all six
   items in the DOM — not a scroller, not a subset. Hidden at desk widths. */
.os-sh-menu { display: none; position: relative; flex: none; }
.os-sh-menu > summary {
  list-style: none; cursor: pointer;
  font: 400 12.5px/1 var(--font-mono); color: var(--ink);
  border: 1px solid var(--rule); padding: 7px 12px; letter-spacing: 0.02em;
}
.os-sh-menu > summary::-webkit-details-marker { display: none; }
.os-sh-menu > summary::marker { content: ""; }
.os-sh-menu__panel {
  position: absolute; top: calc(100% + 10px); right: 0; z-index: 60;
  /* min() so the panel can never be wider than the viewport it hangs in. A bare
     min-width: 200px overflows a 190px-wide screen and puts a scrollbar on the
     page from a CLOSED menu, because a closed details still lays its content
     box out. */
  min-width: min(200px, 78vw);
  background: var(--paper); border: 1px solid var(--rule); box-shadow: var(--sheet);
  padding: 10px 0;
}
/* A stacked list cannot carry the header's bottom-edge bar, so the active item
   takes the 2px accent bar on its LEFT edge — the same device, in the same
   weight, that the prototype's article contents rail already uses for a
   vertical list. Not a new indicator. */
.os-sh-menu__item {
  display: flex; justify-content: space-between; gap: 16px;
  padding: 10px 18px 10px 16px;
  border-left: 2px solid transparent;
  font: 400 14px/1.3 var(--font-display); color: var(--ink2);
}
.os-sh-menu__item:hover { color: var(--accent); }
.os-sh-menu__item[aria-current="page"] { border-left-color: var(--accent); }

/* ══════════ BREADCRUMB — from the URL path, never a CMS tag ══════════ */
/* The live site renders "Home > About Us > Consultation" because it builds the
   trail from the post's tag. The trail is passed in already derived from the
   path; this component only draws it. */
.os-sh-crumb { padding: 34px 0 0; font: 400 12px/1 var(--font-mono); color: var(--ink3); }
.os-sh-crumb ol { list-style: none; margin: 0; padding: 0; }
/* Inline, not flex: the prototype separates the parts with literal spaces, and
   inline text wraps on its own without a fixed track to overflow. */
.os-sh-crumb li { display: inline; }
.os-sh-crumb a { color: var(--ink3); }
.os-sh-crumb a:hover { color: var(--accent); }
.os-sh-crumb__sep { color: var(--rule); }

/* ══════════ SECTION HEADER — the one component used everywhere ══════════ */
/* The fix for eleven competing label voices on the live site. Two parts, always
   in this order, and never any other pairing. */
.os-sh-sechead { margin-bottom: 30px; }
/* A plain div, never a heading. Functional wording only. */
.os-sh-sechead__eyebrow {
  font: 500 10.5px/1 var(--font-mono);
  letter-spacing: 0.16em; text-transform: uppercase;
  color: var(--accent);
  margin-bottom: 11px;
}
/* THIS IS THE h2. The editorial voice lives here, where it cannot cost anyone
   their way around. letter-spacing is restated because content-tokens.js gives
   every h2 -.008em and the element selector's declaration survives the font
   shorthand set on this class. */
.os-sh-sechead__dek {
  font: 300 26px/1.3 var(--font-display);
  letter-spacing: normal;
  color: var(--ink2);
  margin: 0;
  max-width: 60ch;
  text-wrap: pretty;
}
/* Narrower columns — the two-up blocks at the foot of Home. */
.os-sh-sechead--sm .os-sh-sechead__dek { font-size: 20px; line-height: 1.35; }

/* ══════════ CTA BAND — one variant, one copy deck ══════════ */
/* Sheet geometry, so it lines up with the sheet it closes. The prototype draws
   it at the standard measure for both archetypes that use it. */
.os-sh-cta {
  max-width: var(--measure-standard);
  width: calc(100% - var(--gutter));
  box-sizing: border-box;
  margin: 0 auto 44px;
  background: var(--accent);
  color: var(--on-accent);
  box-shadow: var(--sheet);
}
.os-sh-cta--prose { max-width: var(--measure-prose); }
.os-sh-cta--wide  { max-width: var(--measure-wide); }
/* flex-wrap so the button drops under the copy rather than squeezing it; the
   prototype is a single unwrapped row because it was only ever drawn wide. */
.os-sh-cta__in {
  padding: 48px var(--sheet-pad);
  display: flex; flex-wrap: wrap; justify-content: space-between; align-items: center; gap: 50px;
}
.os-sh-cta__h   { font: 300 30px/1.25 var(--font-display); margin: 0 0 10px; text-wrap: pretty; }
.os-sh-cta__dek { font: 300 17px/1.5 var(--font-display); opacity: 0.68; margin: 0; }
.os-sh-cta__btn {
  border: 1px solid currentColor;
  font: 500 13.5px/1 var(--font-ui);
  padding: 15px 26px; white-space: nowrap;
  color: var(--on-accent);
}
/* The one place the accent hover rule is wrong: the ground here IS the accent,
   so the site-wide hover colour would erase the button. It holds --on-accent. */
.os-sh-cta__btn:hover { color: var(--on-accent); }

/* ══════════ FOOTER — one component, identical everywhere ══════════ */
/* --bg-surface, not the page. The footer is one of the few surfaces the canvas
   tints; on the page ground it did not read as a footer at all, only as the end of
   the content. */
.os-sh-footer { border-top: 1px solid var(--rule); background: var(--bg-surface); }
/* box-sizing is content-box here, and that is deliberate, not an oversight.
   This is the one box in the shell the prototype does NOT declare border-box
   on, so its max-width caps the CONTENT and the 60px padding sits outside it:
   the footer columns end up wider than the sheet above, inset from the canvas
   rather than aligned to the paper. Under the repo's global border-box reset
   the same declarations would pull the whole footer 50px inward and narrow the
   grid. Restated so the rendered result matches the prototype, which was
   measured, not assumed. */
.os-sh-footer__in {
  box-sizing: content-box;
  max-width: var(--measure-wide);
  margin: 0 auto;
  padding: 40px var(--sheet-pad);
}
/* Four columns: colophon, Disciplines, For business, The library. The colophon
   carries a paragraph, hence 1.4fr. Fractional tracks cannot overflow the grid,
   but their CONTENT has a min-content width, so the proportional layout is
   handed over to auto-fit below (see the breakpoints) before the 56px gaps eat
   the tracks. */
.os-sh-footer__cols { display: grid; grid-template-columns: 1.4fr 1fr 1fr 1fr; gap: 56px; }
.os-sh-colophon__brand { display: flex; align-items: center; gap: 9px; margin-bottom: 16px; color: var(--ink); }
.os-sh-colophon__glyph { font: 400 21px/1 var(--font-display); color: var(--accent); }
.os-sh-colophon__name  { font: 500 14.5px/1 var(--font-display); }
.os-sh-colophon        { display: flex; flex-direction: column; align-items: flex-start; max-width: 30ch; }
.os-sh-colophon__dek   { font: 400 13px/1.7 var(--font-ui); color: var(--fg-secondary); margin: 10px 0 12px; max-width: 30ch; text-wrap: pretty; }
.os-sh-colophon__legal { font: 400 12px/1.5 var(--font-ui); color: var(--fg-label); }
.os-sh-colophon__stat  { font: 400 12px/1.6 var(--font-mono); color: var(--ink3); }
/* A column head is the section-header eyebrow at the same values. One voice. */
/* LABEL GREY, not accent. Three accent-red heads in the footer make the quietest
   band on the page its loudest; the canvas sets them at #6A5F50, 600 10px, .18em,
   with 5px beneath rather than 16px. */
.os-sh-fcol__head {
  font: 600 10px/1 var(--font-ui);
  letter-spacing: .18em; text-transform: uppercase;
  color: var(--fg-label); margin-bottom: 5px;
}
/* The canvas sets the footer's register ONCE on the container — 400 13px/1.7 sans
   at #574C40 — and lets every column item inherit it. These were display serif at
   14px/1.3, so the whole band was in the wrong face at the wrong leading. */
.os-sh-flist { display: flex; flex-direction: column; gap: 6px; }
.os-sh-flink { font: 400 13px/1.7 var(--font-ui); color: var(--fg-secondary); }
.os-sh-flink:hover { color: var(--accent); }
/* Only the Disciplines column carries counts, and they are passed in. */
.os-sh-flink--count { display: flex; justify-content: space-between; gap: 16px; }
.os-sh-fcount { font: 400 11px/1.3 var(--font-mono); color: var(--ink3); flex: none; }
/* The colophon bar. flex-wrap is the only addition: at phone widths the
   copyright and the legal pair together exceed the column, and the shell may
   never be the thing that puts a horizontal scrollbar on the page. */
.os-sh-footer__bar {
  display: flex; flex-wrap: wrap; justify-content: space-between; align-items: center; gap: 20px;
  margin-top: 48px; padding-top: 20px;
  border-top: 1px solid var(--rule2);
  font: 400 11.5px/1 var(--font-mono); color: var(--ink3);
}
/* Privacy · Terms · RSS · Sign in. 5D's column four draws four things and these
   are not among them — but the gate on 219 members-only entries needs a route in
   from every page, and the legal pages have to be reachable, so they stay. They sit
   in the colophon's own register rather than inheriting a link size from elsewhere:
   at 14px display serif they were the loudest thing in the quietest band, and
   "Sign in" wrapped onto its own line. */
.os-sh-footer__legal {
  display: flex; flex-wrap: wrap; gap: 4px 14px; margin-top: 14px;
  font: 400 12px/1.5 var(--font-ui);
}
.os-sh-footer__legal a { font: 400 12px/1.5 var(--font-ui); color: var(--fg-label); }
.os-sh-footer__legal a:hover { color: var(--accent); }
.os-sh-footer__bar a { color: var(--ink3); }
.os-sh-footer__bar a:hover { color: var(--accent); }

/* ══════════ RESPONSIVE ══════════════════════════════════════════════════════
   The README's rule for a tight header, in its own words: reclaim space from
   padding and from the right group, NEVER from the item count. The gutter and
   the sheet padding already narrow at the token layer's breakpoints, so what is
   left to give is the row gaps and the "Tools" word. Measured at the prototype
   values, the six items plus wordmark plus right group need about 890px; the
   1000px rule buys back roughly 90px, which carries the inline nav to about
   790px. The menu takes over at 860px, comfortably above that floor rather
   than exactly on it, because the fallback font stacks are wider than the
   webfonts and a mis-set breakpoint here is the defect this rule exists to
   prevent. */
/* Descending order, deliberately: several of these blocks touch the same
   property at two widths and the narrower rule has to be the later one. */
@media (max-width: 1023px) {
  /* Every fixed-column grid becomes auto-fit, per the handoff's Responsive
     rule. min() inside minmax() is what guarantees it: a bare minmax(200px,1fr)
     still overflows a container narrower than 200px. */
  .os-sh-footer__cols { grid-template-columns: repeat(auto-fit, minmax(min(200px, 100%), 1fr)); gap: 40px; }
  .os-sh-cta__in { gap: 32px; }
}
/* 1120, NOT 1000. The header needs 1048px for brand + nav + search + button,
   and it only gets that much room above about 1100 — so between 1001 and 1099
   the relief below had not arrived yet and the row simply overflowed. Measured:
   at 1024 the header wanted 1048 in 980, and the whole document scrolled
   sideways by 46px on EVERY page of the site. 1024 is a laptop and an iPad in
   landscape, so this was not a corner of the range.
   1160 NOW, up from 1120: adding the theme toggle put a fourth control in the
   right group, worth 29px plus its 14px gap, and the full-size row went from
   needing 1100px of viewport to needing 1143. At 1121-1142 no relief had fired
   and the row was 22px over its box on every page — the same defect one band
   higher. The threshold carries headroom over the measured 1143 for the same
   reason it did before: the six nav items are authored strings, and a longer
   discipline name must not put the row back over the edge. */
@media (max-width: 1160px) {
  .os-sh-header__in { gap: 20px; }
  .os-sh-nav { gap: 16px; }
  .os-sh-btn { padding: 7px 9px; }
  /* THE CTA KEEPS ITS WORDS HERE NOW. This band used to drop the label to its
     glyph, which was defensible while the button said "Tools" — the arrow and
     the word said much the same thing. It says "Sign In", which is the one
     action on the page, and a bare red arrow does not ask anybody to do
     anything. Re-measured with the five-item bar (437px against the old six's
     473) the full row with the label fits every width down to 378px, so the
     word now survives to 400 and the rule that hides it moved there. */
}
/* 900, NOT 860 — the same measurement, one band lower. With the search field
   already collapsed the row still wants 836px (brand 195 + nav 473 + actions 128
   + two 20px gaps), and 836px of demand needs 880px of viewport once the 44px
   gutter is taken out. Between 861 and 879 the nav had not yet been replaced by
   the menu and the row overflowed by up to 9px. 900 rather than 880 for the same
   reason 1120 is not 1100: the six nav items are authored strings, and a longer
   discipline name must not put the row back over the edge. */
@media (max-width: 900px) {
  .os-sh-nav { display: none; }
  .os-sh-menu { display: block; }
}
@media (max-width: 767px) {
  .os-sh-footer__cols { grid-template-columns: 1fr; gap: 34px; }
  .os-sh-colophon__dek { max-width: none; }
  .os-sh-cta__in { padding: 36px var(--sheet-pad); }
  .os-sh-cta__h { font-size: 26px; }
}
/* The last of the padding, on the smallest screens. At 320px the wordmark, the
   right group and the menu button together are 15px wider than the viewport,
   and the shell is the one thing that may never cause a horizontal scroll. Every
   pixel here comes out of a gap or a padding — the README's sanctioned source —
   and none of it out of the item count, the wordmark or the right group itself. */
@media (max-width: 520px) {
  .os-sh-header__in { gap: 10px; }
  .os-sh-actions { gap: 8px; }
  .os-sh-glyph { padding: 4px 2px; }
  .os-sh-btn { padding: 7px 8px; }
  .os-sh-menu > summary { padding: 7px 8px; }
  .os-sh-theme--compact { padding: 7px 7px; }
  /* THE WORDMARK GIVES, BEFORE THE CONTROLS DO. Adding the theme toggle to the
     header put a fourth control in the right group and the row went from 332px
     to 369px of demand against the 346px a 390px phone offers — so every page of
     the site scrolled sideways by a pixel and the menu button was clipped. That
     was a regression from the toggle, and the toggle is the one thing here a
     reader asked for, so it is not what gets removed.
     195px of the 369 was the wordmark: "OCCULT SANCTUM" set at 16px with .16em
     of tracking is 56% of a phone header. At 13px/.09em it is 123px and still
     unmistakably the same lockup — the glyph, the caps and the tracking are all
     intact, only the scale changes. Below 360 the text still goes entirely
     (rule below) and the mercury glyph carries the mark alone. */
  .os-sh-brand__name { font-size: 13px; letter-spacing: .09em; }
}
/* The CTA's word is the last thing in the right group to go, and it goes at 400
   rather than at 1160 where it used to. Measured with every relief above already
   applied, the row with "Sign In" spelled out fits to 378px; 400 carries the
   same kind of headroom the other two thresholds do, because the label is an
   authored string and a longer one must not put the row back over the edge.
   The accessible name is on the element itself, so hiding the word costs a
   screen reader nothing — it costs a sighted visitor the only instruction the
   header gives, which is why it is held as long as it will fit. */
@media (max-width: 400px) {
  .os-sh-btn__label { display: none; }
}
/* Below 360px the gaps and paddings are spent and the row is still 37px too
   wide, so the last thing to give is the wordmark's TEXT — the mercury glyph
   stays, and the glyph is the mark ("no logo file exists; if one is
   commissioned it replaces the glyph in the header and footer"). It is hidden
   visually and NOT removed: the link keeps its accessible name, so the wordmark
   is still announced. The six nav items are never what gives, at any width.
   360px is the threshold rather than 320 so that the common Android and iPhone
   widths keep the full wordmark with room to spare, and only the sub-360
   folding screens see the glyph alone. */
@media (max-width: 359px) {
  .os-sh-brand__name {
    position: absolute; width: 1px; height: 1px;
    overflow: hidden; clip-path: inset(50%); white-space: nowrap;
  }
}

/* ══ Oshatra components ═══════════════════════════════════════════════════
   Every value here is transcribed from the prototype's inline styles. Colour is
   only ever named through a token, never spelled. */

/* ── the card-grid hairline technique ──────────────────────────────────────
   From tokens.css, and it is the OPPOSITE of the obvious approach. Do NOT paint
   the container with the rule colour and let a 1px gap show it through. That
   breaks the moment an auto-fit row is not exactly full: the empty tracks have
   no card covering them and render as solid rule-coloured slabs.
   Container transparent; each card carries its own 1px ring. A ring is drawn
   just outside the border box, so two neighbouring cards each fill the same 1px
   gap and the rings coincide — they read as one shared rule, and a half-empty
   row simply has nothing to draw. The ring is a spread box-shadow, not a
   border, because it must not take part in layout: a border would add 2px to
   every track and the 1px gap would stop meeting. */
.osc-grid { display: grid; gap: 1px; background: transparent; }
/* Responsive rule: EVERY fixed-column grid is auto-fit + minmax, so no grid can
   force the page wider than the viewport. The number is the prototype's own
   minimum track for that grid. */
.osc-grid--300 { grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); }
/* The 2x2 stat block. 150px keeps two columns inside the 340px rail it sits in
   and drops to one when that rail is squeezed. */
/* Practitioners: a real 36px gap, not a hairline, so these are NOT ringed cards
   in a hairline grid. */

/* List grids — rows, not cards, so the gap is horizontal only and the rows
   carry their own bottom rule. The prototype writes repeat(3,1fr) and
   repeat(2,1fr); the minimum track below is what holds those counts at the
   measure they were drawn for while still collapsing on a narrow page. */

/* ── 1. contents rail ──────────────────────────────────────────────────────
   One position, one label, one behaviour, on both archetypes that carry it. */
.osc-rail { position: sticky; top: 92px; }
/* The article rail can outrun the viewport on a 21-section entry, so it scrolls
   inside itself rather than pushing the page. */
.osc-rail--sections { max-height: calc(100vh - 130px); overflow-y: auto; }
.osc-rail__label { font: 500 10.5px/1 var(--font-mono); letter-spacing: .16em;
  text-transform: uppercase; color: var(--accent); margin: 0 0 14px; }
.osc-rail--subs .osc-rail__label { margin-bottom: 16px; }

.osc-rail__list--sections { display: flex; flex-direction: column; gap: 1px;
  border-left: 1px solid var(--rule); }
.osc-rail__item { display: grid; grid-template-columns: 24px 1fr; gap: 8px;
  padding: 6px 0 6px 12px; margin-left: -1px; border-left: 2px solid transparent;
  position: relative; text-decoration: none; color: inherit; }
/* The active marker is a pseudo-element rather than the prototype's absolutely
   positioned span: identical geometry, and the scroll spy toggles one class
   instead of inserting and removing a node on every tick. */
.osc-rail__item::before { content: ""; position: absolute; left: -1px; top: 0; bottom: 0;
  width: 2px; background: transparent; }
.osc-rail__item.is-active::before { background: var(--accent); }
.osc-rail__ord { font: 400 11px/1.5 var(--font-deva); color: var(--ink3); }
.osc-rail__name { font: 400 12.5px/1.5 var(--font-display); color: var(--ink2); }
/* Hover goes to the accent everywhere. These spans set their own colour, which
   outranks the base a:hover rule, so the rule is restated rather than inherited. */
.osc-rail__item:hover .osc-rail__name,
.osc-rail__item:hover .osc-rail__ord { color: var(--accent); }

.osc-rail__list--subs { border-top: 1px solid var(--rule); }
.osc-rail__sub { display: flex; justify-content: space-between; align-items: baseline;
  gap: 10px; padding: 9px 0; border-bottom: 1px solid var(--rule2);
  text-decoration: none; color: inherit; }
.osc-rail__sub-name { font: 400 13.5px/1.3 var(--font-display); color: var(--ink2); }
.osc-rail__sub-count { font: 400 11px/1 var(--font-mono); color: var(--ink3); flex: none; }
.osc-rail__sub:hover .osc-rail__sub-name { color: var(--accent); }

/* Sections are jumped to, so they clear the sticky header by the same offset
   the rail is stuck at. Kept beside the spy constants deliberately: this number
   and the JUMP offset in scrollSpyJs are one decision in two places. */
[data-sec] { scroll-margin-top: 92px; }

/* ── 2. collection row ─────────────────────────────────────────────────────
   Two sizes, one component: ordinal / name / count. */
.osc-crow { display: grid; align-items: baseline; padding: 15px 0;
  border-bottom: 1px solid var(--rule2); text-decoration: none; color: inherit; }
.osc-crow--lg { grid-template-columns: 30px 1fr auto; gap: 12px; }
.osc-crow--sm { grid-template-columns: 1fr auto; gap: 14px; padding: 11px 0; }
.osc-crow__ord { font: 400 18px/1 var(--font-deva); color: var(--accent); }
.osc-crow__name { font: 400 16px/1.3 var(--font-display); }
.osc-crow--sm .osc-crow__name { font-size: 15px; }
.osc-crow__count { font: 400 12px/1 var(--font-mono); color: var(--ink3); }
.osc-crow--sm .osc-crow__count { font-size: 11.5px; }
.osc-crow:hover .osc-crow__name { color: var(--accent); }

/* ── 3. entry row ──────────────────────────────────────────────────────────
   Two sizes. The large one is the numbered Start-here row. */
.osc-erow { display: grid; align-items: baseline;
  border-bottom: 1px solid var(--rule2); text-decoration: none; color: inherit; }
.osc-erow--lg { grid-template-columns: 34px 1fr 220px 64px; gap: 20px; padding: 19px 0;
  grid-template-areas: "ord title note read"; }
.osc-erow__ord { font: 400 24px/1 var(--font-deva); color: var(--accent); }
.osc-erow__title { font: 400 19px/1.35 var(--font-display); text-wrap: pretty; }
.osc-erow__note { font: 300 13.5px/1.45 var(--font-display); color: var(--ink3);
  font-style: italic; }
.osc-erow__read { font: 400 12px/1 var(--font-mono); color: var(--ink3); text-align: right; }
/* The areas are declared on the LARGE row only. Named placement leaks otherwise:
   the small row has no areas, and a child still asking for "title" gets pushed
   into an implicit track while the meta auto-places into the first cell — which
   put the date to the LEFT of the entry it dates. */
.osc-erow--lg .osc-erow__ord   { grid-area: ord; }
.osc-erow--lg .osc-erow__title { grid-area: title; }
.osc-erow--lg .osc-erow__note  { grid-area: note; }
.osc-erow--lg .osc-erow__read  { grid-area: read; }
.osc-erow--sm { grid-template-columns: 1fr auto; gap: 0 16px; padding: 10px 0; }
.osc-erow--sm .osc-erow__title { font-size: 15px; }
.osc-erow__meta { font: 400 11.5px/1 var(--font-mono); color: var(--ink3);
  white-space: nowrap; }
.osc-erow:hover .osc-erow__title { color: var(--accent); }

/* ── 4. entry card ─────────────────────────────────────────────────────────
   The ring is the card's own, per the hairline technique above. The flat state
   is the same anatomy without the box: the search results list. */
.osc-ecard { display: block; background: var(--paper); box-shadow: 0 0 0 1px var(--rule);
  padding: 24px 22px 26px; text-decoration: none; color: inherit; }
.osc-ecard--flat { background: transparent; box-shadow: none; padding: 20px 0;
  border-bottom: 1px solid var(--rule2); }
.osc-ecard__head { display: flex; align-items: baseline; gap: 12px; margin-bottom: 8px;
  flex-wrap: wrap; }
.osc-ecard__tag { font: 400 10.5px/1 var(--font-mono); letter-spacing: .1em;
  text-transform: uppercase; color: var(--accent); }
.osc-ecard__meta { font: 400 11px/1 var(--font-mono); color: var(--ink3); }
.osc-ecard__title { font: 400 21px/1.3 var(--font-display); margin: 0 0 8px;
  letter-spacing: 0; text-wrap: pretty; }
.osc-ecard__snippet { display: block; font: 300 16px/1.6 var(--font-display);
  color: var(--ink2); max-width: 70ch; text-wrap: pretty; }
.osc-ecard:hover .osc-ecard__title { color: var(--accent); }
/* Search results mark the matched words. The UA default is yellow-on-black in
   both themes; the design's pair for a marked run is the selection pair —
   accent-soft under ink — and nothing else. */
.osc-ecard mark { background: var(--accent-soft); color: var(--ink); }

/* ── 5. search field ───────────────────────────────────────────────────────
   One component. The 2px accent bottom border IS the field — there is no box. */
.osc-search { display: flex; gap: 0; max-width: 520px;
  border-bottom: 2px solid var(--accent); }
.osc-search--page { max-width: 760px; }
.osc-search__input { flex: 1; min-width: 0; background: transparent; border: 0;
  padding: 13px 2px; font: 300 18px/1.4 var(--font-display); color: var(--ink);
  border-radius: 0; -webkit-appearance: none; appearance: none; }
.osc-search--page .osc-search__input { padding: 12px 2px; font-size: 26px; line-height: 1.3; }
.osc-search__input::placeholder { color: var(--ink3); opacity: 1; }
/* UA artifact: webkit draws its own clear control in a search input — an
   off-palette blue X on the 404 and Search fields. Suppressed everywhere. */
input[type=search]::-webkit-search-cancel-button {
  -webkit-appearance: none; appearance: none; }
.osc-search__btn { flex: none; background: transparent; border: 0; cursor: pointer;
  padding: 13px 10px; font: 500 11px/1 var(--font-mono); letter-spacing: .12em;
  color: var(--accent); }
.osc-search__count { flex: none; align-self: center; padding-left: 16px;
  font: 400 12px/1 var(--font-mono); color: var(--ink3); white-space: nowrap; }
/* The 26px input is display type in a control, so it must not inflate on iOS. */
@media (max-width: 767px) {
  .osc-search--page .osc-search__input { font-size: 21px; }
}

/* ── 6. filter chip ────────────────────────────────────────────────────────
   The design authors exactly one chip — the article tag chip — so these are its
   metrics. The container wraps, which is what keeps a long facet list off the
   horizontal scrollbar. */
.osc-chip { display: inline-flex; align-items: baseline; gap: 7px;
  font: 400 11px/1 var(--font-mono); color: var(--ink2);
  border: 1px solid var(--rule); padding: 5px 9px; text-decoration: none;
  white-space: nowrap; }
.osc-chip__count { color: var(--ink3); }
.osc-chip:hover { border-color: var(--accent); color: var(--accent); }
.osc-chip.is-active { border-color: var(--accent); color: var(--accent); }
.osc-chip.is-active .osc-chip__count { color: inherit; }

/* ── 7. product card ───────────────────────────────────────────────────────
   One card for tools, oracles, apps, reseller pillars and the four on Home. The
   shapes differ only by which fields are present. */
.osc-pcard { display: flex; flex-direction: column;
  background: var(--paper); box-shadow: 0 0 0 1px var(--rule);
  padding: 26px 24px 28px; text-decoration: none; color: inherit; }
/* The oracles alone take a 2px accent top edge. It is a border rather than part
   of the ring because the ring must stay exactly 1px on the other three sides. */
.osc-pcard--accent { border-top: 2px solid var(--accent); padding: 24px 22px 26px; }
.osc-pcard__head { display: flex; justify-content: space-between; align-items: baseline;
  gap: 12px; margin-bottom: 16px; }
.osc-pcard__ord { font: 400 18px/1 var(--font-deva); color: var(--accent); }
.osc-pcard__badge { font: 400 10px/1 var(--font-mono); letter-spacing: .1em;
  text-transform: uppercase; color: var(--ink3); text-align: right; }
/* An app's status (Live / Coming soon) takes the same slot in the accent. */
.osc-pcard__badge--accent { color: var(--accent); }
.osc-pcard__title { font: 500 22px/1.2 var(--font-display); margin: 0 0 10px;
  letter-spacing: 0; }
.osc-pcard--md .osc-pcard__title { font-size: 21px; }
.osc-pcard--sm .osc-pcard__title { font-size: 18px; line-height: 1.3; margin: 16px 0 9px; }
.osc-pcard--accent .osc-pcard__title { margin-bottom: 8px; }
.osc-pcard__sub { display: block; font: 400 11.5px/1.4 var(--font-mono);
  color: var(--ink3); margin: 0 0 14px; }
.osc-pcard--md .osc-pcard__sub { margin-bottom: 16px; }
.osc-pcard__desc { display: block; font: 300 15.5px/1.6 var(--font-display);
  color: var(--ink2); margin: 0 0 16px; text-wrap: pretty; }
.osc-pcard--sm .osc-pcard__desc { font-size: 14.5px; line-height: 1.55; margin: 0; }
/* The oracle description takes the slack so every card's foot bottom-aligns. */
.osc-pcard--accent .osc-pcard__desc { font-size: 15px; line-height: 1.55; flex: 1; }
.osc-pcard__points { display: flex; flex-direction: column; gap: 7px; margin-top: auto; }
/* Two auto margins in one column split the free space between them and the
   bullets drift; when a foot follows, the foot is the one that gets pinned. */
.osc-pcard__points:not(:last-child) { margin-top: 0; }
.osc-pcard__point { display: grid; grid-template-columns: 14px 1fr; gap: 9px;
  font: 300 14.5px/1.5 var(--font-display); color: var(--ink2); }
.osc-pcard__bullet { color: var(--accent); }
.osc-pcard__foot { display: block; margin-top: auto;
  border-top: 1px solid var(--rule2); padding-top: 12px; }
.osc-pcard__best { display: block; font: 400 13.5px/1.45 var(--font-display);
  color: var(--ink2); }
.osc-pcard__best-lead { color: var(--accent); }
.osc-pcard__needs { display: block; font: 400 10.5px/1.4 var(--font-mono);
  color: var(--ink3); margin-top: 8px; }
a.osc-pcard:hover .osc-pcard__title { color: var(--accent); }

/* ── 8. practitioner card ──────────────────────────────────────────────────
   The 4:5 crop is fixed, in both states. Four real photographs are outstanding;
   until then the stripe is an honest placeholder rather than a stock face. */
.osc-pract { display: flex; flex-direction: column; text-decoration: none; color: inherit; }
.osc-pract__shot { display: flex; align-items: flex-end; aspect-ratio: 4 / 5;
  padding: 12px; margin-bottom: 18px; border: 1px solid var(--rule);
  background: repeating-linear-gradient(135deg, var(--paper2), var(--paper2) 6px,
    var(--paper3) 6px, var(--paper3) 12px); overflow: hidden; }
.osc-pract__shot--photo { padding: 0; background: var(--paper3); }
.osc-pract__shot--photo img { width: 100%; height: 100%; object-fit: cover; display: block; }
.osc-pract__shot-tag { font: 400 10.5px/1.4 var(--font-mono); color: var(--ink3);
  background: var(--paper); box-shadow: 0 0 0 1px var(--rule); padding: 4px 7px; }
.osc-pract__years { display: block; font: 400 11px/1 var(--font-mono); letter-spacing: .1em;
  text-transform: uppercase; color: var(--accent); margin-bottom: 10px; }
.osc-pract__name { font: 500 24px/1.18 var(--font-display); margin: 0 0 8px; letter-spacing: 0; }
.osc-pract__role { display: block; font: 400 12.5px/1.5 var(--font-mono);
  color: var(--ink3); margin-bottom: 16px; }
/* flex:1 is load-bearing — it is what bottom-aligns the fee lines across a row
   of cards whose bios differ by three lines. */
.osc-pract__bio { display: block; font: 300 15.5px/1.6 var(--font-display);
  color: var(--ink2); flex: 1; text-wrap: pretty; }
.osc-pract__fees { display: flex; justify-content: space-between; align-items: baseline;
  gap: 14px; border-top: 1px solid var(--rule); margin-top: 20px; padding-top: 14px; }
.osc-pract__fee { font: 400 20px/1 var(--font-mono); }
.osc-pract__fee-alt { font: 400 11.5px/1 var(--font-mono); color: var(--ink3);
  text-align: right; }

/* ── 9. stat block ─────────────────────────────────────────────────────────
   The number is always the accent; the label is always mono uppercase ink3. */
.osc-stat--card { background: var(--paper); box-shadow: 0 0 0 1px var(--rule);
  padding: 20px 18px; }
.osc-stat--right { text-align: right; }
.osc-stat__n { font: 300 34px/1 var(--font-display); color: var(--accent); }
.osc-stat--lg .osc-stat__n { font-size: 52px; line-height: .95; }
.osc-stat__l { font: 500 10px/1 var(--font-mono); letter-spacing: .14em;
  text-transform: uppercase; color: var(--ink3); margin-top: 8px; }
.osc-stat--lg .osc-stat__l { font-size: 10.5px; }
@media (max-width: 767px) {
  /* 52px display numerals are set against a 1400 measure. */
  .osc-stat--lg .osc-stat__n { font-size: 40px; }
}

/* ── 10. pagination ────────────────────────────────────────────────────────
   Mono at the header control's metrics; the current page marked the way the
   header marks the current nav item, with a 2px accent bar on the bottom edge. */
.osc-pager { display: flex; align-items: center; justify-content: space-between;
  flex-wrap: wrap; gap: 12px 20px; margin-top: 40px; padding-top: 20px;
  border-top: 1px solid var(--rule); }
.osc-pager__step { font: 400 12.5px/1 var(--font-mono); letter-spacing: .02em;
  color: var(--ink); border: 1px solid var(--rule); padding: 7px 12px;
  text-decoration: none; white-space: nowrap; }
.osc-pager__step:hover { border-color: var(--accent); color: var(--accent); }
/* Disabled never uses opacity — that breaks the measured contrast ratios. */
.osc-pager__step.is-off { color: var(--ink3); border-color: var(--rule2); }
.osc-pager__pages { display: flex; align-items: center; flex-wrap: wrap; gap: 4px; }
.osc-pager__n { position: relative; font: 400 12px/1 var(--font-mono); color: var(--ink2);
  padding: 8px 9px; text-decoration: none; }
.osc-pager__n:hover { color: var(--accent); }
.osc-pager__n.is-current { color: var(--accent); }
.osc-pager__n.is-current::after { content: ""; position: absolute; left: 2px; right: 2px;
  bottom: 0; height: 2px; background: var(--accent); }
.osc-pager__n.is-off { color: var(--ink3); }
.osc-pager__gap { font: 400 12px/1 var(--font-mono); color: var(--ink3); padding: 8px 2px; }

/* ── 11. audio player — the recitation block ───────────────────────────────
   Article archetype, unchanged: this sits between the byline and the first
   paragraph. There is no audio template.
   Every colour-carrying selector below is two classes deep (0,2,0) ON PURPOSE:
   the block renders inside article bodies whose generic text rules run at
   0,1,1, and a single-class selector here would lose to them — which is
   exactly how the player text once went invisible dark-on-dark. Do not
   flatten these back to one class. */
.osc-audio { display: flex; flex-wrap: wrap; align-items: center; gap: 16px;
  border: 1px solid var(--rule); padding: 14px 16px; margin: 0 0 34px; }
/* The native element is the no-JS fallback: it renders its own controls until
   the runtime binds, then it steps down to the engine behind the custom
   chrome. */
.osc-audio .osc-audio__el { display: block; flex: 0 0 100%; width: 100%; }
.osc-audio[data-osc-bound] .osc-audio__el { display: none; }
/* 44px is the minimum hit target and is not negotiable down to fit a layout. */
.osc-audio .osc-audio__play { flex: none; width: 44px; height: 44px; display: flex;
  align-items: center; justify-content: center; border: 0; border-radius: 0;
  background: var(--accent); color: var(--on-accent);
  font: 400 15px/1 var(--font-ui); cursor: pointer; }
.osc-audio .osc-audio__play[disabled] { cursor: default; }
.osc-audio .osc-audio__body { flex: 1; min-width: 0; }
.osc-audio .osc-audio__head { display: flex; justify-content: space-between;
  align-items: baseline; gap: 16px; margin-bottom: 9px; }
.osc-audio .osc-audio__label { font: 400 14px/1.3 var(--font-display); min-width: 0;
  color: var(--ink); }
.osc-audio .osc-audio__time { font: 400 11px/1 var(--font-mono); color: var(--ink3);
  white-space: nowrap; flex: none;
  /* Tabular figures: without them the elapsed time twitches the label sideways
     once a second as the digit widths change. */
  font-variant-numeric: tabular-nums; }
.osc-audio .osc-audio__rail { height: 2px; background: var(--rule2); cursor: pointer; }
.osc-audio .osc-audio__fill { height: 2px; width: 0%; background: var(--accent); }

/* ── responsive ────────────────────────────────────────────────────────────
   No horizontal page scroll at any width. The four-column Start-here row is the
   only fixed-template grid in this file that cannot become auto-fit without
   scrambling its columns (they are four different things, not four of one), so
   it re-flows by area instead. Nothing is hidden: a dropped note or read time
   is content the reader came for. */
@media (max-width: 1023px) {
  .osc-erow--lg { grid-template-columns: 34px 1fr auto; gap: 6px 20px;
    grid-template-areas: "ord title read" ". note note"; }
}
@media (max-width: 767px) {
  .osc-erow--lg { grid-template-columns: 28px 1fr; gap: 4px 14px;
    grid-template-areas: "ord title" ". note" ". read"; }
  .osc-erow--lg .osc-erow__read { text-align: left; }
  .osc-erow--lg .osc-erow__title { font-size: 17px; }
  .osc-pract__name { font-size: 21px; }
  .osc-audio { gap: 12px; padding: 12px 12px; }
}

/* The rail collapses above the content on a phone, which is the handoff's
   instruction. It is the TEMPLATE that changes the grid; all this does is stop
   the rail sticking to a viewport it now fills. */
@media (max-width: 1023px) {
  .osc-rail { position: static; }
  .osc-rail--sections { max-height: none; overflow-y: visible; }
}

/* Visually hidden, still announced. */
.osc-sr { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0; }

/* ── the sheet, per archetype ───────────────────────────────────────────────
   One base, three measures, and a page can never pick its own: the modifier is
   written by the archetype function, not passed in. The width: calc() is
   load-bearing — without it the sheet runs edge to edge below its max-width and
   the canvas frame disappears, which is the whole point of the three-tone
   system. Article and Hub close with margin-bottom 0 because the closing band
   follows them outside the sheet. */
.osa-sheet {
  width: calc(100% - var(--gutter));
  box-sizing: border-box;
  margin: 30px auto 44px;
  padding: 0 var(--sheet-pad);
  background: var(--paper);
  border: 1px solid var(--rule);
  box-shadow: var(--sheet);
}
/* The Article sheet is the STANDARD measure and the prose column inside it is
   720. That is not a contradiction of "Article: prose 720" — 720 is the measure
   of the body copy, and the rail plus its 64px gap need the rest. The prototype
   sets exactly this. It is also what gives a table room to break the measure
   without ever reaching the page edge. */
.osa-sheet--article { max-width: var(--measure-standard); margin-bottom: 0; }
.osa-sheet--hub     { max-width: var(--measure-standard); margin-bottom: 0; }
.osa-sheet--landing { max-width: var(--measure-wide); }

/* ── shared voices ── */
/* Named --font-ui, which is what --font-mono already resolved to: the token
   layer aliases the mono register to the UI face precisely so surviving
   declarations cannot ship a face this design does not have. So the FACE was
   never wrong here — the size and the colour were. The canvas sets every rail
   heading at 11px in label grey; this had 10.5px in accent red.

   It reached only the article's contents heading, which now takes .osa-rail__h.
   The three remaining call sites sit in archetypes with no live route, so the
   rule is corrected rather than deleted: a revived caller should inherit the
   right treatment, not the one that was wrong here. */
.osa-eyebrow {
  font: 600 11px/1 var(--font-ui);
  letter-spacing: 0.16em; text-transform: uppercase; color: var(--fg-label);
  margin-bottom: 11px;
}
.osa-eyebrow--hero { margin-bottom: 24px; }
/* The dek IS the h2. The editorial voice lives here, where it cannot cost
   anyone their way around. Display 300 and --ink2, never the accent. */
.osa-dek {
  font: 300 26px/1.3 var(--font-display);
  color: var(--ink2); margin: 0; max-width: 62ch; text-wrap: pretty;
  letter-spacing: 0;
}
.osa-dek--narrow { font-size: 20px; line-height: 1.35; }
.osa-h1 {
  font: 300 clamp(34px, 3.9vw, 54px)/1.1 var(--font-display);
  letter-spacing: -0.02em; margin: 0 0 18px; text-wrap: pretty;
}
/* Clamped so the title tracks its column. At a fixed size it collapsed to seven
   lines in a narrow viewport. */
.osa-h1--article { font-size: clamp(32px, 3.4vw, 46px); line-height: 1.14; margin: 0 0 20px; }
.osa-h1--landing {
  font-size: clamp(36px, 4.3vw, 62px); line-height: 1.08;
  letter-spacing: -0.022em; margin: 0 0 26px; max-width: 21ch;
}
.osa-lede {
  font: 300 20px/1.55 var(--font-display);
  color: var(--ink2); margin: 0; max-width: 54ch; text-wrap: pretty;
}
.osa-lede--article { font-size: 21px; margin: 0 0 26px; max-width: none; }
.osa-lede--landing { font-size: 20px; line-height: 1.6; margin: 0 0 34px; max-width: 56ch; }
/* The 34px buys the gap between the statement and the action beneath it. With no
   action to separate from, the dek is the last thing in the opener and the
   opener's own padding is the space — the prototype's consultation opener sets
   margin 0 for exactly this reason. */
.osa-lede--landing:last-child { margin-bottom: 0; }

/* ── breadcrumb — from the URL path, never from a CMS tag ── */
.osa-crumb {
  padding: 34px 0 0;
  font: 400 12px/1 var(--font-mono); color: var(--ink3);
}
.osa-crumb a { color: var(--ink3); text-decoration: none; }
.osa-crumb a:hover { color: var(--accent); }
.osa-crumb__sep { color: var(--rule); }

/* ── buttons and the action row ── */
.osa-actions { display: flex; gap: 14px; align-items: center; flex-wrap: wrap; }
.osa-btn {
  font: 500 14px/1 var(--font-ui);
  padding: 14px 24px; border: 1px solid var(--rule);
  color: var(--ink); background: transparent; text-decoration: none; cursor: pointer;
}
.osa-btn:hover { border-color: var(--accent); color: var(--accent); }
.osa-btn--primary {
  background: var(--accent); color: var(--on-accent); border-color: var(--accent);
  padding: 15px 26px;
}
/* The filled button already carries the accent, so hover cannot restate it.
   Holding the ground and the ink still leaves the border to move. */
.osa-btn--primary:hover { color: var(--on-accent); border-color: var(--ink); }
.osa-note { font: 400 12px/1.5 var(--font-mono); color: var(--ink3); }

/* ══════════ ARCHETYPE 1 — ARTICLE ══════════ */
/* .osa-article and .osa-article__body are DELETED. The article grid the site
   renders is .os-article, chosen per entry by the three-variant rule in this file,
   and it carries its own columns; this two-column version was the archetype's own
   earlier frame and no page has emitted it. */

/* The contents rail. One position, one label. The height is quoted verbatim in the
   canvas's extremes table, inside a code element: max-height: calc(100vh - 120px);
   overflow-y: auto. It was 130px here — ten pixels of the rail's own scroll track
   given away on exactly the entries long enough to need it. */
/* THE PADDING IS THERE TO CATCH THE BLEED. The current row is drawn with
   margin: 0 -10px so its ground runs into the rail's own padding — deliberate,
   and the canvas draws it that way. But this box is a scroll container (it has
   to be: a 25-section entry is taller than the viewport), and a scroll container
   counts that 10px of decoration as content it must let you reach. So a rail
   holding one highlighted row reported scrollWidth 189 in a 179px box and the
   browser drew a HORIZONTAL SCROLLBAR under the contents list, for ten pixels of
   background nobody needs to scroll to.
   Pulling the box out by 10 and padding it back by 10 puts the bleed inside the
   scroller's own padding: identical to look at, and nothing overflows. */
/* THE STICKY MOVED UP ONE LEVEL, to .os-article__rail-l-in, so the contents
   list and the locator beneath it travel together. Left here, the list pinned
   and the locator scrolled straight through it.
   The padding stays and still earns its place: the current row is drawn with
   margin: 0 -10px so its ground runs into the rail's padding, and a scroll
   container counts that bleed as content — scrollWidth 189 in a 179px box, and
   a horizontal scrollbar under the list for ten pixels of background. */
.osa-toc {
  margin: 0 -10px; padding: 0 10px;
  overflow-y: auto;
}
/* With the numerals dropped past twenty sections, the row has one child and the
   9px gap has nothing to separate. */
.osa-toc__item--plain { gap: 0; }

/* Back to top. display:none, not opacity — a link the reader cannot see must not
   be reachable by tab either. The scroll spy adds is-shown past three viewport
   heights. */
.osa-toc__top { display: none; }
.osa-toc__top.is-shown {
  display: inline-flex; align-items: center; gap: 7px; margin-top: 16px;
  padding-top: 12px; border-top: 1px solid var(--rule);
  font: 500 11px/1 var(--font-ui); letter-spacing: .1em; text-transform: uppercase;
  color: var(--fg-label); text-decoration: none;
}
.osa-toc__top.is-shown:hover { color: var(--accent); }
/* Screen 3B draws the contents as a FLAT column of flex rows — no track border,
   no indent — at display:flex; gap:9px; padding:6px 0, with the ordinal in
   Devanagari 13px ACCENT and the label in the UI sans at 14px. This had a 1px
   track, a 12px indent, a 24px grid column, the label in the display serif at
   12.5px and the ordinal in label grey. Every one of those is a different object
   from the one drawn. */
.osa-toc__list { display: flex; flex-direction: column; }
.osa-toc__item {
  display: flex; gap: 9px; padding: 6px 0;
  border-left: 2px solid transparent; text-decoration: none;
}
.osa-toc__ord  { font: 400 13px/1.5 var(--font-deva); color: var(--accent); flex: none; }
.osa-toc__name { font: 400 14px/1.45 var(--font-ui); color: var(--fg-secondary); }
.osa-toc__item:hover .osa-toc__name { color: var(--accent); }
/* The current row is a FILLED TAB, bleeding 10px into the rail's padding on both
   sides, with its label at 500 in full ink. It had only a left border colour, so
   "where am I" was a 2px hairline against six identical rows. */
.osa-toc__item.is-active {
  background: var(--bg-surface-2); border-left-color: var(--accent);
  margin: 0 -10px; padding-left: 10px; padding-right: 10px;
}
.osa-toc__item.is-active .osa-toc__name { color: var(--fg-primary); font-weight: 500; }

/* The byline row, between two rules. Author, date, read time. NO WORD COUNT. */
.osa-byline {
  display: flex; align-items: center; gap: 14px; padding: 16px 0;
  border-top: 1px solid var(--rule); border-bottom: 1px solid var(--rule);
  margin-bottom: 40px; flex-wrap: wrap;
}
/* Radius is 0 everywhere else by decree; the avatar is the one exception the
   prototype draws, and a 32px square of placeholder tone reads as a missing
   image rather than a person. */
.osa-byline__av {
  width: 32px; height: 32px; border-radius: 50%;
  background: var(--paper3); flex: none; display: block; object-fit: cover;
}
.osa-byline__who { flex: 1 1 200px; min-width: 0; }
.osa-byline__name { font: 500 14px/1.3 var(--font-display); color: var(--ink); }
.osa-byline__meta { font: 400 11.5px/1.3 var(--font-mono); color: var(--ink3); margin-top: 2px; }
.osa-chips { display: flex; gap: 6px; flex-wrap: wrap; margin-left: auto; }
.osa-chip {
  font: 400 11px/1 var(--font-mono); color: var(--ink2);
  border: 1px solid var(--rule); padding: 5px 9px; text-decoration: none;
  white-space: nowrap;
}
/* 5A: "Tag chip | border -> fg-label | bg -> bg-surface-2 | -". Border only. The
   filter chip is the one that gets a colour change on hover; a tag is metadata, and
   this had it shouting louder than the sort control beside it. */
a.osa-chip:hover  { border-color: var(--fg-label); }
a.osa-chip:active { background: var(--bg-surface-2); }

/* The recitation player — one block, between the byline and the first
   paragraph. 44px is the minimum hit target and is not negotiable downwards. */
.osa-audio {
  display: flex; align-items: center; gap: 16px;
  border: 1px solid var(--rule); padding: 14px 16px; margin: 0 0 34px;
}
.osa-audio__play {
  flex: none; width: 44px; height: 44px;
  display: flex; align-items: center; justify-content: center;
  background: var(--accent); color: var(--on-accent); border: 0;
  font: 400 15px/1 var(--font-ui); cursor: pointer;
}
.osa-audio__body { flex: 1; min-width: 0; }
.osa-audio__row {
  display: flex; justify-content: space-between; align-items: baseline;
  gap: 16px; margin-bottom: 9px;
}
.osa-audio__label { font: 400 14px/1.3 var(--font-display); color: var(--ink); }
.osa-audio__time  { font: 400 11px/1 var(--font-mono); color: var(--ink3); white-space: nowrap; }
.osa-audio__rail  { height: 2px; background: var(--rule2); }
.osa-audio__fill  { height: 2px; width: 0; background: var(--accent); }
/* The element exists for the script to drive. It never draws its own controls:
   a native control set beside the designed one is two players on one entry. */
.osa-audio__el { display: none; }

/* Sections. The 92px scroll margin is the sticky header plus the band the spy
   measures against, so a jumped-to heading lands below the header, not under it. */
.osa-sec { scroll-margin-top: 92px; }
/* Transcribed from screen 3B, which draws the section head as
   display:flex; align-items:baseline; gap:12px; padding-bottom:10px;
   border-bottom:2px solid #221C15; margin:44px 0 22px — the RULE is the part that
   was missing entirely. Every numbered section in the canvas closes its head with
   a full-measure 2px ink rule, and without it the six sections of a long entry ran
   together as plain bold text. The type was off in the same direction: 500 27px
   where the canvas draws 400 30px, and an ordinal at 24px where it draws 22px. */
.osa-sec__head {
  display: flex; align-items: baseline; gap: 12px;
  padding-bottom: 10px; border-bottom: 2px solid var(--rule-strong);
  margin: 44px 0 22px;
}
.osa-sec__ord { font: 400 22px/1 var(--font-deva); color: var(--accent); flex: none; }
.osa-sec__h {
  font: 400 30px/1.15 var(--font-display); color: var(--fg-primary);
  margin: 0; letter-spacing: -0.012em; text-wrap: pretty;
}

/* ── article body copy ──────────────────────────────────────────────────────
   Serif, 300, 19px. The display face carries the whole editorial voice here;
   the UI sans is for labels and controls only. */
/* Body copy is IBM Plex Sans, not the serif. CSS-IMPLEMENTATION-SPEC.md §3:
   "A sans body is deliberate. Entries are consulted mid-reading at 380px and
   carry 13,226 list items; the serif is for display only." The superseded
   bundle set prose in Source Serif at 19px/1.72; the approved body is 17/1.62
   and list rows are 16/1.5. */
.osa-body p { font: 400 17px/1.62 var(--font-ui); color: var(--fg-primary); margin: 0 0 var(--s5); max-width: var(--measure); text-wrap: pretty; }
.osa-body li { font: 400 16px/1.5 var(--font-ui); color: var(--fg-primary); }
.osa-body h3 { font: 500 20px/1.3 var(--font-display); color: var(--ink); margin: 30px 0 12px; }

/* The pull quote: a 2px accent bar and nothing else. No ground tint, no marks. */
.osa-body blockquote {
  border-left: 2px solid var(--accent);
  padding: 4px 0 4px 22px; margin: 30px 0;
  font-style: normal; color: var(--ink);
}
.osa-body blockquote p {
  font: 300 20px/1.6 var(--font-display); font-style: italic;
  color: var(--ink); margin: 0; text-wrap: pretty;
}

/* ── TABLES ─────────────────────────────────────────────────────────────────
   The superseded bundle widened tables to 112% and pulled them back 6% so they
   overhung the prose column. CSS-IMPLEMENTATION-SPEC.md §11.4 names that hack
   and rules it out: "Drop width:112%; margin-left:-6%". It is what makes the
   table bleed past the page card, and horizontal scroll is explicitly not the
   mobile strategy either — one table in the corpus reaches 11 columns, and a
   scroller hides ten of them.

   §5.4 is the approved treatment: a bounded table inside the measure, header
   row on --bg-inverse with gold labels, zebra rows, and record cards below
   768px or above 6 columns. Both triggers are the same code path. */
.osa-body .os-table-wrap { width: 100%; margin-left: 0; }
.osa-body .os-table-wrap > table { width: 100%; margin-left: 0; }
.osa-body > table { width: 100%; margin-left: 0; }
.osa-body table {
  border-collapse: collapse; border: 1px solid var(--rule);
  margin: 0 0 var(--s7); font-size: 15px;
}
/* --plate-gold, not --gold. §1 is emphatic that the two golds are not
   interchangeable: #D9AE4A is a FILL and #E4BA61 is the one that is legible as
   TEXT on an ink ground, which is exactly what a table header is. The canvas sets
   this row at color:#E4BA61. Fourth instance of this same confusion, after the
   announcement bar, the rail plate and the front door's inverted cell.

   And a header cell's <strong> is unwrapped: 116 tables carry
   <th><strong>House</strong></th> from the old site, which lands as weight 900 on
   top of the 600 this rule sets — a header shouting inside a header. */
   And the leading is 1.4, not 1. A header cell is the one label on the page
   whose width is set by the table, not by the label: "Distance Between Lines"
   in a four-column table gets 133px and wraps, and at leading 1 the two lines
   printed on top of each other. Eight tables were doing this. */
.osa-body th {
  background: var(--bg-inverse); color: var(--plate-gold);
  font: 600 11px/1.4 var(--font-ui);
  letter-spacing: 0.14em; text-transform: uppercase;
  text-align: left; padding: 12px var(--s4);
}
.osa-body th strong, .osa-body th b { font-weight: inherit; }
.osa-body td {
  padding: 13px var(--s4); border-bottom: 1px solid var(--rule-soft);
  font: 400 15px/1.4 var(--font-ui); color: var(--fg-primary); vertical-align: top;
}
.osa-body tbody tr:nth-child(even) { background: var(--bg-row-tint); }
/* The caption sits BELOW the table, in mono. */
.osa-body caption { caption-side: bottom; text-align: left; }
.osa-body caption, .osa-body table + figcaption {
  font: 400 11.5px/1.5 var(--font-mono); color: var(--ink3); margin: 6px 0 22px;
}

/* ── CONTINUE IN <RUN> — N ENTRIES ──────────────────────────────────────
   Screen 3B, transcribed. Two things here were wrong and both were invisible
   without the canvas beside them.

   The label was 10.5px at .14em where the canvas draws 12px with no tracking.
   (It was declared in var(--font-mono), but the token layer aliases that to the
   UI face, so the face on the page was right all along — only the metrics were
   off. Worth stating plainly: the alias is what kept a mono register from ever
   reaching a reader.)

   And the two cards were symmetrical, sharing one hairline via box-shadow with
   the next card right-aligned. The canvas draws them ASYMMETRICALLY on purpose:
   the previous card sits on the panel ground behind a plain hairline, and the
   next card is lifted onto the page ground with an INK border and an ACCENT
   label. Forward is the direction being offered, so it is the one that reads
   as a button. Making them identical threw that away. */
/* Leading 1.4, not 1: the label carries a collection name it did not choose.
   "Continue in Advanced KP Stellar Astrology: Essential Notes" runs past 516px
   at 11px with .16em of tracking, so it wraps, and at leading 1 the second line
   sat on the first. Same correction as .os-plate__eyebrow and .osa-body th. */
.osa-pn__h {
  font: 600 11px/1.4 var(--font-ui); letter-spacing: .16em;
  text-transform: uppercase; color: var(--fg-label); margin-bottom: 16px;
}
/* The heavy rule that CLOSES the article. The canvas wraps the run footer in
   border-top:2px solid #221C15; padding-top:26px. A .os-continue rule carrying
   exactly that pair exists in the token layer and matches no element on the site —
   so the rule was specified, written, and then landed nowhere. */
.osa-pn-wrap {
  margin: 56px 0 0; padding-top: 26px;
  border-top: 2px solid var(--rule-strong);
}
.osa-pn { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; }
.osa-pn__card {
  border: 1px solid var(--rule); background: var(--bg-panel);
  padding: 18px; text-decoration: none; color: var(--fg-primary);
}
.osa-pn__card--next { border-color: var(--fg-primary); background: var(--bg-page); }
.osa-pn__label {
  font: 400 12px/1 var(--font-ui); text-transform: uppercase;
  color: var(--fg-label); margin-bottom: 9px;
}
.osa-pn__card--next .osa-pn__label { color: var(--accent); }
.osa-pn__title { font: 400 19px/1.3 var(--font-display); text-wrap: pretty; }
/* 5A: "Prev / next card | border -> fg-primary; title -> accent | bg ->
   bg-surface-2 | -", and under VISITED: "Entry-list rows, prev/next cards, TOC:
   fg-primary -> fg-secondary. Reads as 'been there' without shouting. On a library
   people re-consult, this is functional." The list rows had all three; the cards had
   the title colour and nothing else. */
.osa-pn__card:hover { border-color: var(--fg-primary); }
.osa-pn__card:hover .osa-pn__title { color: var(--accent); }
.osa-pn__card:active { background: var(--bg-surface-2); }
.osa-pn__card:visited .osa-pn__title { color: var(--fg-secondary); }
/* One card on its own must not be stretched across both columns. */
.osa-pn__card:only-child { grid-column: 1; }
@media (max-width: 767px) { .osa-pn { grid-template-columns: 1fr; } }

/* ── WHERE YOU ARE — the left rail's locator ────────────────────────────
   3-up grid of square cells, current run inverted. The hairlines are gap:1px
   over a painted container, so a SHORT LAST ROW must be closed by one ground
   spanning the remainder — left open it paints as a slab of separate boxes,
   the defect already fixed in the home, front-door and archive grids.

   The tint is nth-child(3n+2) at (0,2,0), which OUTRANKS the --here modifier at
   (0,1,0). That exact specificity collision already shipped once, on the front
   door's inverted cell, so --here is scoped to the grid to win it. */
.osa-rail__h {
  font: 600 11px/1 var(--font-ui); letter-spacing: .16em;
  text-transform: uppercase; color: var(--fg-label);
  padding-bottom: 11px; border-bottom: 1px solid var(--rule); margin-bottom: 14px;
}
.osa-loc { margin-top: 34px; }
.osa-loc__grid {
  display: grid; grid-template-columns: repeat(3, 1fr); gap: 1px;
  background: var(--rule); border: 1px solid var(--rule);
}
.osa-loc__cell {
  background: var(--bg-page); aspect-ratio: 1;
  display: flex; align-items: center; justify-content: center;
  font: 500 10px/1.2 var(--font-ui); color: var(--fg-label);
  text-align: center; padding: 4px; overflow: hidden;
}
.osa-loc__grid .osa-loc__cell:nth-child(3n+2) { background: var(--bg-surface); }
.osa-loc__grid a.osa-loc__cell:hover { background: var(--bg-surface-2); text-decoration: none; }
/* --plate-gold, not --gold: this cell paints an ink ground, so it takes the text
   gold. I wrote this rule earlier in the same session that fixed the identical
   mistake on the announcement bar, and made it again — which is why the guard in
   the token tests now checks every rule that paints an ink ground rather than the
   four places it happened to appear. */
.osa-loc__grid .osa-loc__cell--here {
  background: var(--bg-inverse); color: var(--plate-gold); font-weight: 600;
}
.osa-loc__grid .osa-loc__cell--pad { background: var(--bg-page); aspect-ratio: auto; }

/* ── THE OTHER <RUNS> — the right rail's sibling list ───────────────────
   Name left, count right, on a soft separator. Tabular figures so a column of
   two- and one-digit counts does not jitter. */
.osa-runs { display: flex; flex-direction: column; }
.osa-runs__row {
  display: flex; justify-content: space-between; gap: 10px; padding: 9px 0;
  border-bottom: 1px solid var(--rule-soft);
  font: 400 14px/1.3 var(--font-ui); color: var(--fg-primary);
  text-decoration: none;
}
.osa-runs__row:last-child { border-bottom: 0; }
a.osa-runs__row:hover { color: var(--accent); text-decoration: none; }
.osa-runs__n { color: var(--fg-label); font-variant-numeric: tabular-nums; flex: none; }

/* ══════════ ARCHETYPE 2 — HUB ══════════ */
.osa-hub__head {
  padding: 34px 0 40px; border-bottom: 1px solid var(--rule);
  display: grid; grid-template-columns: 1fr 200px; gap: 60px; align-items: end;
}
.osa-count { text-align: right; }
.osa-count__n { font: 300 52px/0.95 var(--font-display); color: var(--accent); }
.osa-count__l {
  font: 500 10.5px/1 var(--font-mono);
  letter-spacing: 0.14em; text-transform: uppercase; color: var(--ink3); margin-top: 8px;
}
.osa-hub {
  display: grid; grid-template-columns: 210px 1fr; gap: 56px;
  padding: 44px 0 90px; align-items: start;
}
.osa-hub__body { min-width: 0; }
/* Same position, same "Contents" label, in BOTH states. Only the source of the
   rows differs: authored sub-collections, or generated range buckets.
   The 16px base is restated because it is INHERITED in the prototype: its page
   declares no body font, so the rail's strut comes from the browser's 16px at a
   normal line-height, while this repo's base layer sets body to 17px/1.62 —
   which quietly made every rail row taller than the design draws it. */
.osa-rail { position: sticky; top: 92px; font-size: 16px; line-height: normal; }
.osa-rail__list { border-top: 1px solid var(--rule); }
.osa-rail__row {
  display: flex; justify-content: space-between; align-items: baseline; gap: 10px;
  padding: 9px 0; border-bottom: 1px solid var(--rule2); text-decoration: none;
}
.osa-rail__name  { font: 400 13.5px/1.3 var(--font-display); color: var(--ink2); }
.osa-rail__count { font: 400 11px/1 var(--font-mono); color: var(--ink3); flex: none; }
.osa-rail__row:hover .osa-rail__name { color: var(--accent); }

/* Grouped state — authored groups, spaced for a description paragraph. */
.osa-groups { display: flex; flex-direction: column; gap: 46px; }
.osa-group { scroll-margin-top: 92px; }
.osa-group__head {
  display: flex; align-items: baseline; gap: 14px;
  padding-bottom: 12px; border-bottom: 1px solid var(--rule); margin-bottom: 4px;
}
.osa-group__ord { font: 400 20px/1 var(--font-deva); color: var(--accent); flex: none; }
.osa-group__h   { font: 500 20px/1.2 var(--font-display); color: var(--ink); margin: 0; letter-spacing: 0; }
.osa-group__desc {
  font: 300 16px/1.6 var(--font-display); color: var(--ink2);
  padding: 12px 0 16px; margin: 0; max-width: 62ch; text-wrap: pretty;
}

/* Flat state — generated range buckets, spaced 26px rather than the grouped
   46px, which is sized for a description the archive does not have. */
.osa-buckets { display: flex; flex-direction: column; gap: 26px; }
.osa-bucket { scroll-margin-top: 92px; }
.osa-bucket__head {
  display: flex; align-items: baseline; gap: 14px;
  padding-bottom: 12px; border-bottom: 1px solid var(--rule); margin-bottom: 4px;
}
/* The same heading LEVEL the grouped state uses for its group titles, because a
   rail labelled "Contents" must link to real headings. */
.osa-bucket__h { font: 500 20px/1.2 var(--font-display); color: var(--accent); margin: 0; letter-spacing: 0; }
.osa-bucket__count { font: 400 11.5px/1 var(--font-mono); color: var(--ink3); }

/* The entry grid. The prototype writes repeat(2, 1fr) for the grouped state and
   auto-fit minmax(240px, 1fr) for the flat one; the Responsive rule requires
   auto-fit everywhere. 300px is the minimum that still yields exactly the two
   columns the prototype renders in the 734px content track, so the desktop
   result is identical and a narrow viewport gets one column instead of two
   crushed ones. The flat state keeps its own 240px, which is a value the design
   states outright: a short bucket must not reserve an empty column. */
.osa-entries { display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: 0 40px; }
.osa-entries--flat { grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); }
.osa-entry {
  display: block; padding: 10px 0; border-bottom: 1px solid var(--rule2);
  font: 400 15px/1.35 var(--font-display); color: var(--ink); text-decoration: none;
}
.osa-entry:hover { color: var(--accent); }

/* ══════════ ARCHETYPE 3 — LANDING ══════════ */
/* Two states of one opener: with a stat block it is a wrapping two-column hero,
   without one it is a single column capped at 900px. Both from the prototype. */
.osa-hero {
  display: flex; flex-wrap: wrap; gap: 46px 80px;
  padding: 62px 0 54px; border-bottom: 1px solid var(--rule); align-items: flex-end;
}
.osa-hero__col { flex: 1 1 520px; min-width: 0; }
.osa-open { padding: 62px 0 52px; border-bottom: 1px solid var(--rule); max-width: 900px; }
.osa-hero__glyph {
  font: 400 46px/1 var(--font-display); color: var(--accent); margin: 0 0 18px;
}
.osa-stats {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
  gap: 1px; background: transparent; flex: 0 1 340px;
}
/* The home rail: one hairline down the left, figures stacked, no cells. The
   rail drops below the headline when the text column would fall under 520px,
   which flex-wrap already does for us. */
.osa-stats--rail {
  display: flex; flex-direction: column; gap: 26px;
  flex: 0 1 260px; background: none;
  border-left: 1px solid var(--rule); padding-left: 26px;
}
.osa-stats--rail .osa-stat { background: none; box-shadow: none; padding: 0; }
.osa-stats--rail .osa-stat__n { font: 300 52px/0.95 var(--font-display); }
.osa-stats--rail .osa-stat__l { margin-top: 6px; font-size: 10.5px; }
.osa-stat { background: var(--paper); box-shadow: 0 0 0 1px var(--rule); padding: 20px 18px; }
.osa-stat__n { font: 300 34px/1 var(--font-display); color: var(--accent); }
.osa-stat__l {
  font: 500 10px/1 var(--font-mono);
  letter-spacing: 0.14em; text-transform: uppercase; color: var(--ink3); margin-top: 8px;
}
/* Section blocks are padded 62px 0 0; the LAST block on a page carries the 90px
   bottom, and when the page closes with its own CTA that block is the CTA. */
.osa-block { padding: 62px 0 0; }
.osa-block--last, .osa-block--cta { padding-bottom: 90px; }
.osa-block__head { margin-bottom: 30px; }
.osa-block__head--split {
  display: flex; justify-content: space-between; align-items: baseline;
  gap: 30px; flex-wrap: wrap; margin-bottom: 26px;
}
.osa-block__aside {
  font: 400 12px/1.6 var(--font-mono); color: var(--ink3);
  max-width: 34ch; text-align: right;
}
.osa-cta__dek {
  font: 300 17px/1.65 var(--font-display); color: var(--ink2);
  margin: 0 0 26px; max-width: 64ch; text-wrap: pretty;
}

/* ══════════ THE CLOSING BAND — Article and Hub only ══════════ */
/* Sheet width, sitting on the canvas outside the sheet. Accent ground, one copy
   deck. The old site ran three headline and dek pairs for the same band. */
.osa-band {
  display: block;
  max-width: var(--measure-standard);
  width: calc(100% - var(--gutter));
  box-sizing: border-box;
  margin: 0 auto 44px;
  background: var(--accent); color: var(--on-accent);
  box-shadow: var(--sheet);
}
.osa-band__inner {
  padding: 48px var(--sheet-pad);
  display: flex; justify-content: space-between; align-items: center;
  gap: 50px; flex-wrap: wrap;
}
.osa-band__title { font: 300 30px/1.25 var(--font-display); margin-bottom: 10px; text-wrap: pretty; }
.osa-band__dek   { font: 300 17px/1.5 var(--font-display); opacity: 0.68; }
/* currentColor, not a token: the button sits ON the accent, so its outline has
   to be the band's ink whichever palette is running. */
.osa-band__btn {
  border: 1px solid currentColor; color: var(--on-accent);
  font: 500 13.5px/1 var(--font-ui); padding: 15px 26px;
  white-space: nowrap; text-decoration: none;
}
.osa-band__btn:hover { background: var(--on-accent); color: var(--accent); }

/* ══════════ RESPONSIVE ══════════ */
/* No horizontal page scroll at any width. Wide content scrolls inside its own
   container; nothing widens the page itself. */
/* BP.wide, not BP.compact — there is no such stop. content-tokens defines four
   (full 1280, wide 1279, tablet 1023, phone 767) and the typo interpolated
   undefined, so this shipped as "@media (max-width: undefinedpx)": an invalid
   query, which takes its whole block down with it. The rule has therefore never
   applied on any page, and the hub went straight from a 56px gap to the 32px
   single-column one with nothing in between. This is that missing step. */
@media (max-width: 1279px) {
  .osa-hub { gap: 40px; }
}
@media (max-width: 1023px) {
  /* The rails collapse ABOVE the content, which is the order they are already
     written in — so this is one line, not a reflow. A sticky rail on a short
     viewport is a rail that covers the thing it indexes. */
  .osa-hub { grid-template-columns: 1fr; gap: 32px; }
  /* .os-article__rail-l-in too: the sticky moved onto it, and a reset that
     names only the old element leaves the rail pinned over the article it
     indexes on exactly the viewports this block exists for. */
  .osa-toc, .osa-rail, .os-article__rail-l-in {
    position: static; max-height: none; overflow: visible; }
  .osa-toc__list { border-left: 0; }
  .osa-toc__item { margin-left: 0; padding-left: 0; border-left: 0; }
  .osa-toc__item.is-active .osa-toc__name { color: var(--accent); }
  .osa-hub__head { grid-template-columns: 1fr; gap: 24px; align-items: start; }
  .osa-count { text-align: left; }

  /* Below the standard measure there is no overhang to spend, so the table
     returns to the column and scrolls inside its own box instead. */
  .osa-body .os-table-wrap { width: 100%; margin-left: 0; }
  .osa-body > table { width: 100%; margin-left: 0; display: block; overflow-x: auto; }
  .osa-body tr > *:first-child { padding-left: 12px; }
  .osa-body tr > *:last-child  { padding-right: 12px; }
  .osa-band__inner { padding: 40px var(--sheet-pad); gap: 28px; }
}
@media (max-width: 767px) {
  .osa-crumb { padding-top: 26px; }

  .osa-hub { padding: 32px 0 64px; }
  .osa-hub__head { padding: 26px 0 30px; }
  .osa-count__n { font-size: 40px; }
  .osa-body p { font-size: 17px; line-height: 1.68; }
  .osa-sec__h { font-size: 22px; }
  .osa-sec__head { margin: 36px 0 16px; }
  .osa-chips { margin-left: 0; width: 100%; }
  .osa-hero, .osa-open { padding: 40px 0 34px; }
  .osa-block { padding-top: 44px; }
  .osa-block--last, .osa-block--cta { padding-bottom: 64px; }
  .osa-block__head--split { flex-direction: column; }
  .osa-block__aside { text-align: left; }
  .osa-band__inner { padding: 32px var(--sheet-pad); }
  .osa-band__title { font-size: 24px; }
}
.os-home {
  max-width: var(--shell); margin: 0 auto;
  background: var(--bg-page); border: 1px solid var(--card-border);
  overflow: hidden;
}
@media (max-width: 1023px) { .os-home { border-left: 0; border-right: 0; } }

/* Block 3 — masthead + Bhava Grid, 1fr 560px, closed by a 2px ink rule. */
.os-home__mast   { display: grid; grid-template-columns: 1fr 560px;
                   border-bottom: 2px solid var(--rule-strong); }
.os-home__mast-l { padding: 60px 44px 56px; }
.os-home__mast-r { border-left: 1px solid var(--rule); background: var(--bg-surface);
                   padding: 44px; display: flex; flex-direction: column; justify-content: center; }
.os-home__eyebrow { font: 400 13px/1 var(--font-ui); letter-spacing: .16em;
                    color: var(--accent); margin-bottom: 24px; }
.os-home__h1     { font: 300 54px/1.04 var(--font-display); color: var(--fg-primary);
                   letter-spacing: -.034em; margin: 0 0 22px; text-wrap: pretty; }
.os-home__lede   { font: 400 19px/1.6 var(--font-ui); color: var(--fg-secondary);
                   margin: 0 0 32px; max-width: 52ch; text-wrap: pretty; }
.os-home__search { display: flex; border: 1px solid var(--fg-primary); background: var(--bg-input);
                   max-width: 460px; margin-bottom: 14px; }
.os-home__search input {
  flex: 1; min-width: 0; padding: 16px 18px; border: 0; background: transparent;
  font: 400 17px/1.3 var(--font-ui); color: var(--fg-primary);
}
.os-home__search input::placeholder { color: var(--fg-label); }
/* 5A: "Search field | border -> fg-primary | Focus: 2px accent inset border, no
   outline". The field already carries the ink border at rest — 6A draws it
   border:1px solid #221C15 — so the hover row has nowhere to travel; the focus row
   did, and the global :focus-visible outline was drawing a ring 2px OUTSIDE a box
   the design wants marked from within. Inset, so the field does not grow. */
.os-home__search:focus-within { box-shadow: inset 0 0 0 2px var(--accent); }
.os-home__search input:focus-visible, .os-home__search button:focus-visible { outline: none; }
.os-home__search button:focus-visible { box-shadow: inset 0 0 0 2px var(--fg-inverse); }
.os-home__search button {
  background: var(--bg-inverse); color: var(--fg-inverse); border: 0; cursor: pointer;
  padding: 16px 22px; font: 500 12px/1.3 var(--font-ui); letter-spacing: .1em;
}
.os-home__helper { font: 400 13px/1.6 var(--font-ui); color: var(--fg-label); }

.os-home__gridhead { display: flex; align-items: baseline; justify-content: space-between;
                     margin-bottom: 16px; }
.os-home__gridlbl  { font: 600 11px/1 var(--font-ui); letter-spacing: .16em; color: var(--fg-label); }
.os-home__gridall  { font: 400 11px/1 var(--font-ui); letter-spacing: .1em; color: var(--accent); }
.os-home__gridnote { font: 400 12px/1.5 var(--font-ui); color: var(--fg-label); margin-top: 12px; }

/* Block 4 — the Today strip. */
.os-today      { display: flex; align-items: center; border-bottom: 1px solid var(--rule);
                 background: var(--bg-panel); }
.os-today__d   { padding: 18px 28px; border-right: 1px solid var(--rule);
                 font: 600 11px/1.5 var(--font-ui); letter-spacing: .14em; color: var(--accent); }
.os-today__d span { color: var(--fg-label); }
.os-today__row { display: flex; flex: 1; font: 400 13px/1.4 var(--font-ui);
                 color: var(--fg-primary); font-variant-numeric: tabular-nums; }
.os-today__c   { padding: 0 22px; border-right: 1px solid var(--rule); }
.os-today__c:last-child { border-right: 0; }
.os-today__l   { color: var(--fg-label); letter-spacing: .08em; font-size: 11px; display: block; }
.os-today__w   { padding: 18px 28px; border-left: 1px solid var(--rule);
                 font: 500 11px/1 var(--font-ui); letter-spacing: .1em; color: var(--accent);
                 white-space: nowrap; }

/* The latest entry — the Today strip's geometry, so the two read as one pair of
   bands rather than as a strip plus an addition. Same 18px/28px cell padding,
   same 1px rule, same 11px tracked label. It differs in exactly one way: this
   band is a link, so it has a hover state and the Today strip does not. */
.os-latest      { display: flex; align-items: baseline; gap: 20px;
                  border-bottom: 1px solid var(--rule); background: var(--bg-page);
                  padding: 16px 28px; text-decoration: none; color: inherit; }
.os-latest:hover { background: var(--bg-row-tint); text-decoration: none; }
.os-latest__lbl { flex: 0 0 auto; font: 600 11px/1.5 var(--font-ui); letter-spacing: .14em;
                  color: var(--accent); }
.os-latest__t   { flex: 1 1 auto; min-width: 0; font: 400 17px/1.35 var(--font-display);
                  color: var(--fg-primary); text-wrap: pretty; }
.os-latest:hover .os-latest__t { color: var(--accent); }
.os-latest__m   { flex: 0 0 auto; font: 400 11px/1.5 var(--font-ui); letter-spacing: .08em;
                  text-transform: uppercase; color: var(--fg-label);
                  font-variant-numeric: tabular-nums; white-space: nowrap; }
.os-latest__go  { flex: 0 0 auto; font: 400 13px/1 var(--font-ui); color: var(--fg-label); }
.os-latest:hover .os-latest__go { color: var(--accent); }

/* Block 5 — start here 1.4fr beside the product router 1fr. */
.os-home__two    { display: grid; grid-template-columns: 1.4fr 1fr; }
.os-home__start  { padding: 48px 44px; border-right: 1px solid var(--rule); }
.os-home__router { padding: 48px 44px; background: var(--bg-panel); }
.os-sechead--tight { margin-bottom: 8px; }
/* The canvas has no single button padding — it sets one per instance. This pair
   is drawn at 14px 18px; the masthead's own button at 15px 22px, and the plate
   band's at 15px 20px, both of which were already honoured. */
.os-router__btns .os-btn { padding: 14px 18px; }
.os-sechead__note--link { color: var(--accent); }
.os-start__p     { font: 400 15px/1.6 var(--font-ui); color: var(--fg-secondary);
                   margin: 0 0 20px; max-width: 64ch; }
/* THE THUMBNAIL SLOT, shared by both lists.
   Always drawn, even for the eight entries with no feature image, so one entry
   without art cannot pull its title 98px left and break the column the rows
   around it make. object-fit: cover because the sources are square and 2:3
   portrait and never landscape — a contain would letterbox two thirds of them. */
.os-thumb        { flex: 0 0 auto; display: block; width: 80px; height: 80px;
                   overflow: hidden; background: var(--bg-surface);
                   border: 1px solid var(--rule); }
.os-thumb img    { width: 100%; height: 100%; object-fit: cover; display: block; }
.os-thumb--none  { background: var(--bg-surface-2); }

.os-start__list  { display: flex; flex-direction: column; }
.os-start__row   { display: grid; grid-template-columns: auto 1fr 92px; gap: 20px;
                   align-items: center; padding: 15px 0;
                   border-bottom: 1px solid var(--rule-soft); }
.os-start__row:last-child { border-bottom: 0; }
.os-start__row:hover { background: var(--bg-row-tint); text-decoration: none; }
.os-start__t     { font: 400 21px/1.3 var(--font-display); color: var(--fg-primary);
                   text-wrap: pretty; }
.os-start__e     { font: 400 12px/1.4 var(--font-ui); color: var(--fg-label);
                   margin-top: 5px; letter-spacing: .06em; text-transform: uppercase; }
.os-start__m     { font: 400 12px/1.5 var(--font-ui); color: var(--fg-label);
                   font-variant-numeric: tabular-nums; text-align: right; }

.os-router__h    { font: 600 11px/1 var(--font-ui); letter-spacing: .16em; color: var(--fg-label);
                   padding-bottom: 14px; border-bottom: 2px solid var(--rule-strong);
                   margin-bottom: 18px; }
.os-router__p    { font: 400 16px/1.6 var(--font-ui); color: var(--fg-secondary); margin: 0 0 22px; }
.os-router__list { display: flex; flex-direction: column; }
.os-router__row  { display: grid; grid-template-columns: 1fr auto; gap: 14px;
                   align-items: baseline; padding: 13px 0; border-bottom: 1px solid var(--rule); }
.os-router__row:last-child { border-bottom: 0; }
.os-router__n    { font: 500 17px/1.25 var(--font-ui); color: var(--fg-primary); display: block; }
.os-router__d    { font: 400 14px/1.4 var(--font-ui); color: var(--fg-secondary); }
.os-router__t    { font: 400 11px/1 var(--font-ui); color: var(--fg-label);
                   letter-spacing: .08em; white-space: nowrap; }
.os-router__btns { display: flex; gap: 10px; margin-top: 24px; flex-wrap: wrap; }

/* Block 6 — recently updated, three columns. */
.os-home__recent { border-top: 1px solid var(--rule); padding: 48px 44px; }
.os-recent       { display: grid; grid-template-columns: repeat(3, 1fr); gap: 0 40px; }
.os-recent__row  { display: grid; grid-template-columns: auto 1fr auto; gap: 14px;
                   align-items: center; padding: 12px 0;
                   border-bottom: 1px solid var(--rule-soft);
                   font: 400 16px/1.35 var(--font-display); color: var(--fg-primary); }
.os-recent__row:hover { color: var(--accent); text-decoration: none; }
.os-recent__d    { font: 400 11px/1.6 var(--font-ui); color: var(--fg-label); white-space: nowrap; }

/* Block 7 — what we have built, four cells hairlined by a 1px gap. */
.os-home__built  { border-top: 1px solid var(--rule); padding: 48px 44px; }
.os-home__built .os-sechead { margin-bottom: 26px; }
.os-built        { display: grid; grid-template-columns: repeat(4, 1fr); gap: 1px;
                   background: var(--rule); border: 1px solid var(--rule); }
.os-built__cell  { background: var(--bg-page); padding: 24px 22px; }
.os-built__cell:hover { background: var(--bg-row-tint); text-decoration: none; }
.os-built__n     { font: 400 22px/1 var(--font-deva); color: var(--accent); margin-bottom: 12px; }
.os-built__t     { font: 500 20px/1.2 var(--font-ui); color: var(--fg-primary); margin-bottom: 8px; }
.os-built__d     { font: 400 14px/1.55 var(--font-ui); color: var(--fg-secondary); }

/* Block 8 — the reseller band, on the plate, 1.15fr .85fr. */
.os-reseller     { display: grid; grid-template-columns: 1.15fr .85fr;
                   background: var(--plate-bg); border-top: 1px solid var(--bg-inverse); }
.os-reseller__l  { padding: 48px 44px; }
.os-reseller__eb { font: 600 11px/1 var(--font-ui); letter-spacing: .18em;
                   color: var(--plate-gold); margin-bottom: 18px; }
.os-reseller__h  { font: 300 40px/1.08 var(--font-display); color: var(--plate-fg);
                   letter-spacing: -.034em; margin: 0 0 18px; max-width: 26ch; text-wrap: pretty; }
.os-reseller__p  { font: 400 17px/1.6 var(--font-ui); color: var(--plate-fg-2);
                   margin: 0 0 26px; max-width: 50ch; }
.os-reseller__btns { display: flex; gap: 12px; flex-wrap: wrap; }
.os-btn--gold    { background: var(--plate-gold); border-color: var(--plate-gold);
                   color: var(--plate-bg); padding: 15px 20px; }
.os-btn--gold:hover { background: #EFC97A; }
/* --plate-btn-edge, not --plate-rule: the rule is a hairline between table rows
   and is right at 1.4:1 for that; as the entire visible boundary of a button it
   left the control unidentifiable on dark. */
.os-btn--dark    { background: none; border: 1px solid var(--plate-btn-edge);
                   color: var(--plate-fg); padding: 15px 20px; }
.os-btn--dark:hover { background: var(--plate-surface); }
.os-reseller__r  { padding: 48px 44px 48px 0; display: flex; flex-direction: column;
                   justify-content: center; }
.os-reseller__list { display: flex; flex-direction: column; border-top: 1px solid var(--plate-rule); }
.os-reseller__row  { display: grid; grid-template-columns: 26px 1fr; gap: 14px;
                     align-items: baseline; padding: 14px 0;
                     border-bottom: 1px solid var(--plate-rule);
                     font: 400 15px/1.5 var(--font-ui); color: var(--plate-fg); }
.os-reseller__row:last-child { border-bottom: 0; }
.os-reseller__n    { font: 400 15px/1.4 var(--font-deva); color: var(--plate-gold); }

/* ── breakpoints, from section 7 ──────────────────────────────────────────
   The canvas carries no media queries: it draws desktop and 390px as separate
   screens, so the collapse between them is written from the section 7 table. */
@media (max-width: 1279px) {
  .os-home__mast { grid-template-columns: 1fr 480px; }
}
@media (max-width: 1023px) {
  .os-home__mast   { grid-template-columns: 1fr; }
  .os-home__mast-r { border-left: 0; border-top: 1px solid var(--rule); }
  .os-home__two    { grid-template-columns: 1fr; }
  .os-home__start  { border-right: 0; border-bottom: 1px solid var(--rule); }
  .os-recent       { grid-template-columns: repeat(2, 1fr); gap: 0 28px; }
  .os-built        { grid-template-columns: repeat(2, 1fr); }
  .os-reseller     { grid-template-columns: 1fr; }
  .os-reseller__r  { padding: 0 44px 48px; }
  .os-today        { flex-wrap: wrap; }
  .os-today__row   { flex-wrap: wrap; }
}
@media (max-width: 767px) {
  .os-home__mast-l, .os-home__mast-r,
  .os-home__start, .os-home__router,
  .os-home__recent, .os-home__built { padding: 28px 16px; }
  .os-home__h1  { font-size: 34px; line-height: 1.06; }
  .os-home__lede { font-size: 17px; }
  .os-recent    { grid-template-columns: 1fr; }
  .os-built     { grid-template-columns: 1fr; }
  /* THE META DROPS BELOW THE TITLE, or the title gets nothing. With a thumbnail
     in the row, three columns on a 390px phone leave the title about 110px and
     it wraps to six lines — "The Diagnostic Grids of Vastu" became a column of
     single words. The thumbnail spans both rows so the text block still starts
     at one edge, and the reading time and the date go under the eyebrow where
     there is room for them. */
  .os-start__row  { grid-template-columns: 80px 1fr; gap: 5px 14px; }
  .os-start__row > .os-thumb { grid-row: 1 / span 2; align-self: start; }
  .os-start__m    { grid-column: 2; text-align: left; }
  .os-recent__row { grid-template-columns: 80px 1fr; gap: 6px 14px; }
  .os-recent__row > .os-thumb { grid-row: 1 / span 2; align-self: start; }
  .os-recent__d   { grid-column: 2; }
  .os-reseller__l { padding: 28px 16px; }
  .os-reseller__r { padding: 0 16px 28px; }
  .os-reseller__h { font-size: 30px; }
  .os-today__d, .os-today__w { padding: 12px 16px; }
  .os-today__c { padding: 8px 16px; }
  /* On a phone the band stacks: label, then title, then meta. The arrow goes —
     the whole band is still the tap target, and a 13px glyph in a wrapped
     flex row reads as a stray character rather than as an affordance. */
  .os-latest      { flex-wrap: wrap; gap: 4px 14px; padding: 14px 16px; }
  .os-latest__lbl { flex: 1 0 100%; }
  .os-latest__t   { flex: 1 0 100%; font-size: 16px; }
  .os-latest__go  { display: none; }
}

/* The breadcrumb's inset is declared once, on .os-crumb in the token layer:
   it is one shared component and it was being positioned by whichever template
   happened to remember. */

.os-arc {
  max-width: var(--shell); margin: 0 auto;
  background: var(--bg-page); border: 1px solid var(--card-border); overflow: hidden;
}
@media (max-width: 1023px) { .os-arc { border-left: 0; border-right: 0; } }

/* Masthead — 1fr 380px, closed by the 2px ink rule. */
.os-arc__mast   { display: grid; grid-template-columns: 1fr 380px;
                  border-bottom: 2px solid var(--rule-strong); }
.os-arc__mast--solo { grid-template-columns: 1fr; }
.os-arc__mast-l { padding: 52px 44px 44px; }
.os-arc__deva   { font: 400 44px/1.2 var(--font-deva); color: var(--accent); margin-bottom: 10px; }
.os-arc__h1     { font: 300 60px/1 var(--font-display); color: var(--fg-primary);
                  letter-spacing: -.034em; margin: 0 0 20px; text-wrap: pretty; }
.os-arc__dek    { font: 400 19px/1.6 var(--font-ui); color: var(--fg-secondary);
                  margin: 0 0 24px; max-width: 56ch; text-wrap: pretty; }
/* No tracking. The canvas types this row in caps at 400 13px/1.5 with no
   letter-spacing declared, and .06em on a tabular figure row widens the gaps
   between digits as well as letters. */
.os-arc__stats  { display: flex; gap: 34px; flex-wrap: wrap;
                  font: 400 13px/1.5 var(--font-ui); color: var(--fg-label);
                  font-variant-numeric: tabular-nums; text-transform: uppercase; }
.os-arc__stat strong { font: 500 24px/1 var(--font-ui); color: var(--fg-primary);
                       display: block; margin-bottom: 5px; letter-spacing: 0; }

/* The link to this collection's card feed. A text link, not a button: it is an
   alternative view of the page you are already on, not an action, and the one
   button in this masthead region belongs to START HERE. */
.os-arc__altview { display: inline-block; margin-top: 22px;
                   font: 500 12px/1.4 var(--font-ui); letter-spacing: .1em;
                   text-transform: uppercase; color: var(--accent);
                   border-bottom: 1px solid var(--rule); padding-bottom: 3px; }
.os-arc__altview:hover { border-bottom-color: var(--accent); text-decoration: none; }

.os-arc__start   { border-left: 1px solid var(--rule); background: var(--bg-surface);
                   padding: 44px 44px; display: flex; flex-direction: column;
                   justify-content: center; }
.os-arc__start-h { font: 600 11px/1 var(--font-ui); letter-spacing: .16em;
                   color: var(--fg-label); margin-bottom: 14px; }
.os-arc__start-list { display: flex; flex-direction: column; }
.os-arc__route   { display: flex; gap: 12px; padding: 10px 0;
                   border-bottom: 1px solid var(--rule);
                   font: 400 16px/1.4 var(--font-display); color: var(--fg-primary); }
.os-arc__route:last-child { border-bottom: 0; }
.os-arc__route:hover { color: var(--accent); text-decoration: none; }
.os-arc__route-n { font: 400 14px/1.5 var(--font-deva); color: var(--accent); flex: none; }

/* Browse grid — five up, hairlined by a 1px gap over the rule colour. */
.os-arc__browse    { padding: 44px; border-bottom: 1px solid var(--rule);
                     background: var(--bg-panel); }
.os-arc__browse-h  { display: flex; align-items: baseline; justify-content: space-between;
                     gap: var(--s4); margin-bottom: 8px; }
.os-arc__browse-note { font: 400 13px/1 var(--font-ui); color: var(--fg-label);
                       text-transform: uppercase; }
.os-arc__browse-p  { font: 400 16px/1.6 var(--font-ui); color: var(--fg-secondary);
                     margin: 0 0 26px; max-width: 76ch; }
.os-arc__grid      { display: grid; grid-template-columns: repeat(5, 1fr); gap: 1px;
                     background: var(--rule); border: 1px solid var(--rule); }
.os-arc__cell      { background: var(--bg-page); padding: 20px 18px; min-height: 118px;
                     display: flex; flex-direction: column; justify-content: space-between; }
.os-arc__cell--muted { background: var(--bg-surface); }
/* A blank ground that completes the last row. Not interactive, not a category. */
.os-arc__cell--pad { background: var(--bg-page); min-height: 0;
                     grid-column: auto / -1; }
.os-arc__cell-n    { font: 500 26px/1 var(--font-ui); color: var(--accent);
                     font-variant-numeric: tabular-nums; }
.os-arc__cell-l    { font: 500 16px/1.25 var(--font-ui); color: var(--fg-primary); }
.os-arc__cell:hover { background: var(--bg-surface-2); text-decoration: none; }
.os-arc__cell:hover .os-arc__cell-l { color: var(--accent); }
/* 5A: "Browse-by-line cell | bg -> bg-surface-2 | bg -> rule | bg bg-inverse, count
   gold". Before [aria-current], which weighs the same, so pressing the selected
   cell does not drop it back to a resting ground. */
.os-arc__cell:active { background: var(--rule); }
.os-arc__cell[aria-current] { background: var(--bg-inverse); }
.os-arc__cell[aria-current] .os-arc__cell-n { color: var(--gold); }
.os-arc__cell[aria-current] .os-arc__cell-l { color: var(--fg-inverse); }
@media (max-width: 1279px)   { .os-arc__grid { grid-template-columns: repeat(4,1fr); } }
@media (max-width: 1023px) { .os-arc__grid { grid-template-columns: repeat(3,1fr); } }
@media (max-width: 767px)  { .os-arc__grid { grid-template-columns: repeat(2,1fr); }
                                     .os-arc__cell { min-height: 96px; } }

/* The list, and the sidebar beside it. */
.os-arc__two   { display: grid; grid-template-columns: 1fr 270px; }
.os-arc__two--solo { grid-template-columns: 1fr; }
.os-arc__list  { padding: 40px 44px 52px; min-width: 0; }
.os-arc__list-h { display: flex; align-items: center; justify-content: space-between;
                  gap: var(--s4); flex-wrap: wrap;
                  padding-bottom: 16px; border-bottom: 2px solid var(--rule-strong);
                  margin-bottom: 6px; }
.os-arc__list-t { display: flex; align-items: baseline; gap: 12px; }
.os-arc__list-t h2 { font: 400 26px/1.1 var(--font-display); color: var(--fg-primary); margin: 0; }
.os-arc__list-c { font: 400 14px/1 var(--font-ui); color: var(--fg-label); }
.os-arc__chips  { display: flex; gap: 7px; flex-wrap: wrap; }
.os-arc__all    { display: flex; align-items: center; gap: 14px; margin-top: 26px; flex-wrap: wrap; }
.os-arc__empty  { font: 400 16px/1.6 var(--font-ui); color: var(--fg-label); padding: 24px 0; }

.os-arc__aside    { border-left: 1px solid var(--rule); background: var(--bg-panel);
                    padding: 40px 22px; }
.os-arc__aside-h  { font: 600 11px/1 var(--font-ui); letter-spacing: .16em; color: var(--fg-label);
                    padding-bottom: 11px; border-bottom: 1px solid var(--rule); margin-bottom: 12px; }
.os-arc__aside-h--2 { margin: 30px 0 14px; }
.os-arc__aside-list { display: flex; flex-direction: column; gap: 11px;
                      font: 400 15px/1.4 var(--font-ui); color: var(--fg-primary); }
.os-arc__aside-c  { color: var(--fg-label); font-variant-numeric: tabular-nums; }
.os-arc__aside-p  { font: 400 14px/1.55 var(--font-ui); color: var(--fg-secondary); margin: 0 0 14px; }
.os-arc__small    { display: block; border: 1px solid var(--rule); background: var(--bg-page);
                    padding: 14px; margin-bottom: 10px; }
.os-arc__small:hover { border-color: var(--fg-primary); text-decoration: none; }
.os-arc__small-t  { font: 400 17px/1.25 var(--font-display); color: var(--fg-primary); }
.os-arc__small-m  { font: 400 12px/1.5 var(--font-ui); color: var(--fg-label); margin-top: 6px;
                    font-variant-numeric: tabular-nums; }

@media (max-width: 1023px) {
  .os-arc__mast  { grid-template-columns: 1fr; }
  .os-arc__start { border-left: 0; border-top: 1px solid var(--rule); }
  .os-arc__two   { grid-template-columns: 1fr; }
  .os-arc__aside { border-left: 0; border-top: 1px solid var(--rule); }
}
@media (max-width: 767px) {
  .os-arc__mast-l, .os-arc__start, .os-arc__browse, .os-arc__list, .os-arc__aside {
    padding: 28px 16px;
  }
  .os-arc__h1   { font-size: 36px; line-height: 1.04; }
  .os-arc__deva { font-size: 32px; }
  .os-arc__stats { gap: 20px; }
}

.os-fd {
  max-width: var(--shell); margin: 0 auto;
  background: var(--bg-page); border: 1px solid var(--card-border); overflow: hidden;
}
@media (max-width: 1023px) { .os-fd { border-left: 0; border-right: 0; } }
/* The breadcrumb's inset is declared once, on .os-crumb in the token layer:
   it is one shared component and it was being positioned by whichever template
   happened to remember. */

/* Masthead — the prose IS the intro, kept whole, closed by the 2px ink rule. */
.os-fd__mast  { padding: 30px 44px 24px; border-bottom: 2px solid var(--rule-strong); }
.os-fd__deva  { font: 400 32px/1.2 var(--font-deva); color: var(--accent); margin-bottom: 6px; }
.os-fd__h1    { font: 300 42px/1 var(--font-display); color: var(--fg-primary);
                letter-spacing: -.034em; margin: 0 0 16px; text-wrap: pretty; }
.os-fd__prose > *:first-child { margin-top: 0; }
/* Authorship, not a byline: §5.2 forbids a publication date, a reading time and
   a word count on a front door, and all three were being printed. */
.os-fd__attr  { font: 400 12px/1 var(--font-ui); color: var(--fg-label);
                letter-spacing: .06em; text-transform: uppercase; margin-top: 16px; }

/* §5's template table: a front door carries a stats row. Without it a
   single-collection discipline printed its total nowhere at all. */
.os-fd__stats { display: flex; gap: 34px; flex-wrap: wrap; margin-top: 20px;
                font: 400 13px/1.5 var(--font-ui); color: var(--fg-label);
                font-variant-numeric: tabular-nums;
                text-transform: uppercase; letter-spacing: .06em; }
.os-fd__stat strong { font: 500 24px/1 var(--font-ui); color: var(--fg-primary);
                      display: block; margin-bottom: 5px; letter-spacing: 0; }

.os-fd__h     { font: 600 10px/1 var(--font-ui); letter-spacing: .16em;
                text-transform: uppercase; color: var(--fg-label); margin-bottom: 14px; }

.os-fd__curated { padding: 24px 44px; background: var(--bg-panel);
                  border-bottom: 1px solid var(--rule); }
.os-fd__routes  { display: flex; flex-direction: column; }
.os-fd__route   { display: flex; gap: 10px; padding: 8px 0;
                  border-bottom: 1px solid var(--rule);
                  font: 400 16px/1.35 var(--font-display); color: var(--fg-primary); }
.os-fd__route:last-child { border-bottom: 0; }
.os-fd__route:hover { color: var(--accent); text-decoration: none; }
.os-fd__route-n { font: 400 13px/1.5 var(--font-deva); color: var(--accent); flex: none; }

.os-fd__browse { padding: 24px 44px; border-bottom: 1px solid var(--rule); }
/* Devotional and Writings get no plate, so on those doors the browse block is the
   LAST block and its bottom rule had nothing beneath it — a hairline closing a card
   that the card's own border already closes. A separator with nothing to separate. */
.os-fd__browse:last-child { border-bottom: 0; }
.os-fd__cell--pad { grid-column: auto / -1; }
.os-fd__grid   { display: grid; grid-template-columns: repeat(3, 1fr); gap: 1px;
                 background: var(--rule); border: 1px solid var(--rule); }
.os-fd__cell   { background: var(--bg-page); padding: 13px 12px;
                 display: flex; flex-direction: column; gap: 6px; }
/* A ROW BAND, not a column. The canvas paints cells 1-3 on #FBF7F0 and cells 4-5
   on #F9F4EA — alternate ROWS of the three-column grid. :nth-child(3n) tinted
   every THIRD CELL, which in a 3-column grid is the third COLUMN, so the zebra ran
   the wrong way through the whole grid. The token is even named --bg-row-tint. */
/* :not() ON BOTH MODIFIERS, so this is not a specificity race at all.
   It was one, and it was lost twice. The comment below records the first loss
   against :nth-child(3n) at (0,2,0); the zebra was then rewritten to the 6n+4/5/6
   form to band by ROW instead of by column, which raised it to (0,3,0) and beat
   the scoped modifier again — so the "All entries" cell, the one route out of
   the front door, painted gold-on-light and read as disabled on every grid whose
   last cell lands on 6n+4, 5 or 6. Astrology's did.
   Excluding the modifiers means the two rules can never both apply, whatever
   either one's specificity becomes next. */
.os-fd__grid .os-fd__cell:nth-child(6n+4):not(.os-fd__cell--all):not(.os-fd__cell--pad),
.os-fd__grid .os-fd__cell:nth-child(6n+5):not(.os-fd__cell--all):not(.os-fd__cell--pad),
.os-fd__grid .os-fd__cell:nth-child(6n+6):not(.os-fd__cell--all):not(.os-fd__cell--pad) {
                 background: var(--bg-row-tint); }
.os-fd__cell-n { font: 500 19px/1 var(--font-ui); color: var(--accent);
                 font-variant-numeric: tabular-nums; }
.os-fd__cell-l { font: 500 13px/1.2 var(--font-ui); color: var(--fg-primary); }
.os-fd__cell:hover { background: var(--bg-surface-2); text-decoration: none; }
/* Specificity, not source order: .os-fd__cell:nth-child(3n) is (0,2,0) and beat
   this modifier at (0,1,0), so the inverted cell painted its gold count on a
   light ground and its label vanished. Scoping to the grid matches it. */
.os-fd__grid .os-fd__cell--all   { background: var(--bg-inverse); }
/* PLATE roles on an ink ground, the same correction the announcement bar needed:
   --gold and --fg-inverse are the light-ground pair, --plate-gold and --plate-fg
   are the pair that sits ON ink. The canvas paints this cell #E4BA61 over #EFE7D9. */
.os-fd__grid .os-fd__cell--all .os-fd__cell-n { color: var(--plate-gold); }
.os-fd__grid .os-fd__cell--all .os-fd__cell-l { color: var(--plate-fg); }
.os-fd__grid .os-fd__cell--all:hover { background: var(--fg-primary); }
.os-fd__cell--pad { background: var(--bg-page); }

/* One plate per page, at the foot of the content — §4.5, and none at all on a
   devotional or recitation page.
   THE GUTTER IS THE CARD'S, not the component's. Every other block here is padded
   to 44px; the plate inherited .os-plate's own 28px, so the one block that is a
   different colour was also the one block that did not line up. And 7A draws its
   button at 11px 14px where 3B's rail draws 12px — per-instance padding, as
   everywhere else in the canvas. */
.os-fd__plate { margin: 0; padding-left: 44px; padding-right: 44px; }
.os-fd__plate .os-btn { padding: 11px 14px; }
@media (max-width: 767px) {
  .os-fd .os-crumb, .os-fd__mast, .os-fd__curated, .os-fd__browse,
  .os-fd__plate { padding-left: 16px; padding-right: 16px; }
  .os-fd__h1   { font-size: 32px; }
  .os-fd__deva { font-size: 26px; }
  .os-fd__grid { grid-template-columns: repeat(2, 1fr); }
}

/* ── /vaastu/ — the discipline rebuilt around its own shape ────────────────── */
/* One width for every template, and the border drops on the narrow views so the
   card becomes the page rather than a box inside it. */
.os-vs { max-width: var(--shell); margin: 0 auto;
         background: var(--bg-page); border: 1px solid var(--card-border); }
@media (max-width: 1023px) { .os-vs { border-left: 0; border-right: 0; } }

/* One left edge: 44px desktop, 16px phone, declared on the blocks themselves
   rather than on a wrapper, so nothing can drift out of alignment. */
.os-vs__mast, .os-vs__sec, .os-vs__plate { padding-left: 44px; padding-right: 44px; }

.os-vs__mast { padding-top: 40px; padding-bottom: 34px;
               border-bottom: 1px solid var(--rule); }
.os-vs__deva { font: 400 20px/1 var(--font-deva); color: var(--accent); margin-bottom: 10px; }
.os-vs__h1   { font: 400 52px/1.05 var(--font-display); letter-spacing: -.02em;
               color: var(--fg-primary); margin: 0 0 16px; }
.os-vs__prose { max-width: 68ch; }
.os-vs__attr { margin-top: 14px; font: 400 12px/1.5 var(--font-ui);
               letter-spacing: .06em; text-transform: uppercase; color: var(--fg-label); }

/* THE FRESHNESS SIGNAL IS ONE LINE, not a strip. The ten newest entries in the
   whole library are this collection's own series, so a "latest" block would
   print the section below it a second time. A line cannot duplicate a section. */
.os-vs__newest { display: flex; align-items: baseline; gap: 14px; flex-wrap: wrap;
                 margin-top: 24px; padding: 13px 16px; background: var(--bg-surface);
                 border-left: 3px solid var(--accent); }
.os-vs__newest:hover { background: var(--bg-surface-2); text-decoration: none; }
.os-vs__newest-l { font: 600 10.5px/1 var(--font-ui); letter-spacing: .14em;
                   color: var(--fg-label); flex: 0 0 auto; }
.os-vs__newest-t { font: 400 17px/1.35 var(--font-display); color: var(--fg-primary);
                   flex: 1 1 260px; }
.os-vs__newest-d { font: 400 11.5px/1.4 var(--font-ui); color: var(--fg-label);
                   font-variant-numeric: tabular-nums; }
.os-vs__newest:hover .os-vs__newest-t { color: var(--accent); }

.os-vs__sec  { padding-top: 34px; padding-bottom: 34px;
               border-bottom: 1px solid var(--rule-soft); }
.os-vs__head { display: flex; align-items: baseline; gap: 14px; flex-wrap: wrap;
               padding-bottom: 12px; border-bottom: 2px solid var(--rule-strong);
               margin-bottom: 24px; scroll-margin-top: 84px; }
.os-vs__head-n { font: 400 17px/1 var(--font-deva); color: var(--accent); }
.os-vs__h2   { font: 400 30px/1.15 var(--font-display); color: var(--fg-primary); margin: 0; }
.os-vs__head-e { margin-left: auto; font: 400 10.5px/1 var(--font-ui);
                 letter-spacing: .12em; color: var(--fg-label); }

/* ── the series ───────────────────────────────────────────────────────────── */
/* THE FIGURES ARE DIAGRAMS AND MUST NOT BE CROPPED. They arrive in three aspect
   ratios — 2:3 four times, square four, 3:2 twice — so a cover-cropped card of
   uniform height would slice the edges off the very grid each one is drawing.
   A fixed panel with object-fit: contain keeps every figure whole and still
   lines the cards up. The ground is the surface tint, so the letterboxing reads
   as a mount rather than as a gap. */
.os-vs__cards { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr));
                gap: 1px; background: var(--rule-soft);
                border: 1px solid var(--rule-soft); }
.os-vs__card  { display: block; background: var(--bg-page); padding: 16px 16px 18px; }
.os-vs__card:hover { background: var(--bg-surface); text-decoration: none; }
.os-vs__card-fig { display: flex; align-items: center; justify-content: center;
                   height: 200px; overflow: hidden; background: var(--bg-surface-2); }
.os-vs__card-fig img { max-width: 100%; max-height: 100%; width: auto; height: auto;
                       object-fit: contain; display: block; }
.os-vs__card-b { display: block; margin-top: 14px; }
.os-vs__card-n { display: block; font: 400 15px/1 var(--font-deva); color: var(--accent);
                 margin-bottom: 7px; }
.os-vs__card-t { display: block; font: 400 19px/1.3 var(--font-display);
                 color: var(--fg-primary); text-wrap: pretty; }
.os-vs__card:hover .os-vs__card-t { color: var(--accent); }
.os-vs__card-m { display: block; margin-top: 8px; font: 400 11.5px/1.4 var(--font-ui);
                 color: var(--fg-label); font-variant-numeric: tabular-nums; }

/* ── the compass ──────────────────────────────────────────────────────────── */
/* NINE CELLS, ALWAYS. The browse grid caps at five and content-stats drops any
   namespace under three entries, so East, South and North-East would vanish and
   the reader would meet a compass missing three of its points. A cell with no
   entries is drawn blank rather than omitted — the geometry is the information. */
.os-vs__compass { display: grid; grid-template-columns: repeat(3, 1fr);
                  gap: 1px; background: var(--rule-soft);
                  border: 1px solid var(--rule-soft); max-width: 620px; }
/* NOT A CENTRED SQUARE ANY MORE. A square held a number and a word; a list of
   names needs a left edge to read down and room to grow from two to five. The
   cells still line up because the grid rows stretch to the tallest. */
.os-vs__zone { display: flex; flex-direction: column; gap: 8px; min-height: 132px;
               background: var(--bg-page); padding: 13px 14px 15px; }
.os-vs__zone:hover { background: var(--bg-surface); text-decoration: none; }
.os-vs__zone--c { background: var(--bg-surface); }
.os-vs__zone--c:hover { background: var(--bg-surface-2); }
.os-vs__zone--none { background: var(--bg-page); }
.os-vs__zone-h { display: flex; align-items: baseline; gap: 10px;
                 padding-bottom: 7px; border-bottom: 1px solid var(--rule-soft); }
.os-vs__zone-l { font: 400 10.5px/1.3 var(--font-ui); letter-spacing: .1em;
                 text-transform: uppercase; color: var(--fg-label); }
.os-vs__zone-n { margin-left: auto; font: 400 17px/1 var(--font-display);
                 color: var(--accent); font-variant-numeric: tabular-nums; }
.os-vs__zone:hover .os-vs__zone-l { color: var(--fg-primary); }
/* The seated energies. Display face, because they are names rather than data —
   the same face the entry titles below them are set in. */
.os-vs__zone-names { display: flex; flex-direction: column; gap: 3px; }
.os-vs__zone-names span { font: 400 14px/1.35 var(--font-display);
                          color: var(--fg-primary); }
.os-vs__zone:hover .os-vs__zone-names span { color: var(--accent); }

/* ── the zone lists ───────────────────────────────────────────────────────── */
.os-vs__zsec  { margin-bottom: 26px; scroll-margin-top: 84px; }
.os-vs__zsec:last-child { margin-bottom: 0; }
.os-vs__zhead { display: flex; align-items: baseline; gap: 12px;
                padding-bottom: 8px; border-bottom: 1px solid var(--rule); margin-bottom: 4px; }
.os-vs__h3    { font: 400 21px/1.2 var(--font-display); color: var(--fg-primary); margin: 0; }
.os-vs__zcount { margin-left: auto; font: 400 10.5px/1 var(--font-ui);
                 letter-spacing: .12em; color: var(--fg-label); }

/* The same 48px square the /tag/ archives carry, for the same reason: 45 of the
   46 entries have a feature image and every one of them is different. */
.os-vs__rows  { display: flex; flex-direction: column; }
.os-vs__row   { display: grid; grid-template-columns: auto minmax(0, 1fr) 96px;
                gap: 16px; align-items: start; padding: 13px 0;
                border-bottom: 1px solid var(--rule-soft); }
.os-vs__row:last-child { border-bottom: 0; }
.os-vs__row:hover { background: var(--bg-row-tint); text-decoration: none; }
.os-vs__row > .os-thumb { width: 48px; height: 48px; align-self: start; }
.os-vs__row-t { min-width: 0; }
.os-vs__row-n { display: block; font: 400 17px/1.3 var(--font-display);
                color: var(--fg-primary); text-wrap: pretty; }
.os-vs__row:hover .os-vs__row-n { color: var(--accent); }
.os-vs__row-d { display: block; margin-top: 5px; max-width: 82ch;
                font: 400 13.5px/1.55 var(--font-ui); color: var(--fg-secondary);
                text-wrap: pretty; }
.os-vs__row-m { font: 400 11.5px/1.6 var(--font-ui); color: var(--fg-label);
                text-align: right; font-variant-numeric: tabular-nums; }

/* ── the plate ────────────────────────────────────────────────────────────── */
.os-vs__plate { padding-top: 30px; padding-bottom: 32px; margin: 0; }
.os-vs__tools { list-style: none; margin: 14px 0 20px; padding: 0; }
.os-vs__tools li { font: 400 14px/1.65 var(--font-ui); color: var(--plate-fg-2); }
.os-vs__tools strong { color: var(--plate-fg); font-weight: 400; }

@media (max-width: 1023px) {
  .os-vs__h1 { font-size: 42px; }
  .os-vs__cards { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .os-vs__compass { max-width: none; }
}
@media (max-width: 767px) {
  .os-vs__mast, .os-vs__sec, .os-vs__plate { padding-left: 16px; padding-right: 16px; }
  .os-vs__h1 { font-size: 33px; }
  .os-vs__h2 { font-size: 25px; }
  /* One card across. Two would put a 200px diagram in a 150px column, which is
     the mush this treatment exists to avoid. */
  .os-vs__cards { grid-template-columns: 1fr; }
  .os-vs__card-fig { height: 220px; }
  /* The date drops under the note; the square keeps its column. */
  .os-vs__row { grid-template-columns: auto minmax(0, 1fr); column-gap: 14px; row-gap: 4px; }
  .os-vs__row-m { grid-column: 2; text-align: left; }
  .os-vs__head-e { margin-left: 0; flex-basis: 100%; margin-top: 6px; }
  .os-vs__zone { min-height: 0; padding: 10px 10px 12px; gap: 6px; }
  .os-vs__zone-n { font-size: 15px; }
  .os-vs__zone-l { font-size: 9px; letter-spacing: .05em; }
  .os-vs__zone-names span { font-size: 12px; line-height: 1.3; }
}

/* ── /astrology/ — seventeen collections in four families ─────────────────── */
.os-as { max-width: var(--shell); margin: 0 auto;
         background: var(--bg-page); border: 1px solid var(--card-border); }
@media (max-width: 1023px) { .os-as { border-left: 0; border-right: 0; } }
.os-as__mast, .os-as__sec, .os-as__start, .os-as__plate, .os-as__newest-w {
  padding-left: 44px; padding-right: 44px;
}

/* The figures sit beside the prose rather than under it, so the first family
   heading starts above the fold — the same mast-l / mast-r split /resources/
   uses for exactly that reason. */
.os-as__mast { display: grid; grid-template-columns: minmax(0, 1fr) 260px; gap: 48px;
               padding-top: 40px; padding-bottom: 30px; }
.os-as__deva { font: 400 20px/1 var(--font-deva); color: var(--accent); margin-bottom: 10px; }
.os-as__h1   { font: 400 52px/1.05 var(--font-display); letter-spacing: -.02em;
               color: var(--fg-primary); margin: 0 0 16px; }
.os-as__prose { max-width: 68ch; }
.os-as__stats { display: flex; flex-direction: column; }
.os-as__stat  { display: flex; align-items: baseline; gap: 10px; padding: 9px 0;
                border-top: 1px solid var(--rule-soft);
                font: 400 10.5px/1 var(--font-ui); letter-spacing: .12em;
                text-transform: uppercase; color: var(--fg-label); }
.os-as__stat strong { font: 400 25px/1 var(--font-display); color: var(--fg-primary);
                      letter-spacing: 0; margin-left: auto;
                      font-variant-numeric: tabular-nums; }

.os-as__newest-w { padding-bottom: 30px; }
.os-as__newest { display: flex; align-items: baseline; gap: 14px; flex-wrap: wrap;
                 padding: 13px 16px; background: var(--bg-surface);
                 border-left: 3px solid var(--accent); }
.os-as__newest:hover { background: var(--bg-surface-2); text-decoration: none; }
.os-as__newest-l { font: 600 10.5px/1 var(--font-ui); letter-spacing: .14em;
                   color: var(--fg-label); flex: 0 0 auto; }
.os-as__newest-t { font: 400 17px/1.35 var(--font-display); color: var(--fg-primary);
                   flex: 1 1 260px; }
.os-as__newest-d { font: 400 11.5px/1.4 var(--font-ui); color: var(--fg-label);
                   font-variant-numeric: tabular-nums; }
.os-as__newest:hover .os-as__newest-t { color: var(--accent); }

.os-as__start { padding-top: 26px; padding-bottom: 28px; background: var(--bg-surface);
                border-top: 1px solid var(--rule); border-bottom: 1px solid var(--rule); }
.os-as__start-h { font: 600 10.5px/1 var(--font-ui); letter-spacing: .14em;
                  color: var(--fg-label); margin-bottom: 14px; }
.os-as__start-l { display: flex; flex-direction: column; }
.os-as__route { display: flex; align-items: baseline; gap: 14px; padding: 11px 0;
                border-bottom: 1px solid var(--rule-soft);
                font: 400 19px/1.35 var(--font-display); color: var(--fg-primary); }
.os-as__route:last-child { border-bottom: 0; }
.os-as__route:hover { color: var(--accent); text-decoration: none; }
.os-as__route-n { font: 400 15px/1 var(--font-deva); color: var(--accent); flex: 0 0 auto; }

.os-as__sec  { padding-top: 32px; padding-bottom: 32px;
               border-bottom: 1px solid var(--rule-soft); }
.os-as__head { display: flex; align-items: baseline; gap: 14px; flex-wrap: wrap;
               padding-bottom: 11px; border-bottom: 2px solid var(--rule-strong); }
.os-as__head-n { font: 400 17px/1 var(--font-deva); color: var(--accent); }
.os-as__h2   { font: 400 29px/1.15 var(--font-display); color: var(--fg-primary); margin: 0; }
.os-as__head-e { margin-left: auto; font: 400 10.5px/1 var(--font-ui);
                 letter-spacing: .12em; color: var(--fg-label); }
.os-as__note { margin: 12px 0 4px; max-width: 74ch;
               font: 400 14px/1.6 var(--font-ui); color: var(--fg-secondary); }

/* The collection row: the same 48px square the archives carry, then the name,
   the collection's own description, and its size. 12 of the 17 have an image. */
.os-as__rows { display: flex; flex-direction: column; }
.os-as__row  { display: grid; grid-template-columns: auto minmax(0, 1fr) 108px;
               gap: 18px; align-items: start; padding: 15px 0;
               border-bottom: 1px solid var(--rule-soft); }
.os-as__row:last-child { border-bottom: 0; }
.os-as__row:hover { background: var(--bg-row-tint); text-decoration: none; }
.os-as__row > .os-thumb { width: 48px; height: 48px; align-self: start; margin-top: 2px; }
.os-as__mono { display: flex; align-items: center; justify-content: center;
               font: 400 21px/1 var(--font-display); color: var(--fg-label);
               background: var(--bg-surface-2); }
.os-as__row-t { min-width: 0; }
.os-as__row-n { display: block; font: 400 21px/1.25 var(--font-display);
                color: var(--fg-primary); }
.os-as__row:hover .os-as__row-n { color: var(--accent); }
.os-as__row-d { display: block; margin-top: 6px; max-width: 84ch;
                font: 400 14px/1.55 var(--font-ui); color: var(--fg-secondary);
                text-wrap: pretty; }
.os-as__row-m { text-align: right; font-variant-numeric: tabular-nums; }
.os-as__row-c { font: 400 20px/1 var(--font-display); color: var(--fg-primary); }
.os-as__row-l { margin-left: 5px; font: 400 10.5px/1 var(--font-ui);
                letter-spacing: .1em; text-transform: uppercase; color: var(--fg-label); }
.os-as__row-u { display: block; margin-top: 7px; font: 400 11.5px/1.4 var(--font-ui);
                color: var(--fg-label); }

.os-as__plate { padding-top: 30px; padding-bottom: 32px; margin: 0; }

@media (max-width: 1023px) {
  .os-as__h1 { font-size: 42px; }
  .os-as__mast { grid-template-columns: 1fr; gap: 24px; }
  .os-as__stats { flex-direction: row; flex-wrap: wrap; gap: 0 30px; }
  .os-as__stat { border-top: 0; padding: 0; flex-direction: column;
                 align-items: flex-start; gap: 3px; }
  .os-as__stat strong { margin-left: 0; order: -1; }
}
@media (max-width: 767px) {
  .os-as__mast, .os-as__sec, .os-as__start, .os-as__plate, .os-as__newest-w {
    padding-left: 16px; padding-right: 16px;
  }
  .os-as__h1 { font-size: 33px; }
  .os-as__h2 { font-size: 24px; }
  .os-as__head-e { margin-left: 0; flex-basis: 100%; margin-top: 6px; }
  /* The count drops under the description; the square keeps its column. */
  .os-as__row { grid-template-columns: auto minmax(0, 1fr); column-gap: 14px; row-gap: 6px; }
  .os-as__row-m { grid-column: 2; text-align: left; }
  .os-as__row-u { display: inline; margin-top: 0; }
  .os-as__row-u::before { content: '\00B7'; padding: 0 8px; color: var(--fg-separator); }
  .os-as__row-n { font-size: 19px; }
  .os-as__route { font-size: 17px; }
}

/* ── /numerology/ — the Vedic grid, the yogas, the shelves ────────────────── */
.os-nu { max-width: var(--shell); margin: 0 auto;
         background: var(--bg-page); border: 1px solid var(--card-border); }
@media (max-width: 1023px) { .os-nu { border-left: 0; border-right: 0; } }
.os-nu__mast, .os-nu__sec, .os-nu__start, .os-nu__plate, .os-nu__newest-w, .os-nu__notes {
  padding-left: 44px; padding-right: 44px;
}

.os-nu__mast { display: grid; grid-template-columns: minmax(0, 1fr) 260px; gap: 48px;
               padding-top: 40px; padding-bottom: 30px; }
.os-nu__deva { font: 400 20px/1 var(--font-deva); color: var(--accent); margin-bottom: 10px; }
.os-nu__h1   { font: 400 52px/1.05 var(--font-display); letter-spacing: -.02em;
               color: var(--fg-primary); margin: 0 0 16px; }
.os-nu__prose { max-width: 68ch; }
.os-nu__stats { display: flex; flex-direction: column; }
.os-nu__stat  { display: flex; align-items: baseline; gap: 10px; padding: 9px 0;
                border-top: 1px solid var(--rule-soft);
                font: 400 10.5px/1 var(--font-ui); letter-spacing: .12em;
                text-transform: uppercase; color: var(--fg-label); }
.os-nu__stat strong { font: 400 25px/1 var(--font-display); color: var(--fg-primary);
                      letter-spacing: 0; margin-left: auto;
                      font-variant-numeric: tabular-nums; }

.os-nu__newest-w { padding-bottom: 30px; }
.os-nu__newest { display: flex; align-items: baseline; gap: 14px; flex-wrap: wrap;
                 padding: 13px 16px; background: var(--bg-surface);
                 border-left: 3px solid var(--accent); }
.os-nu__newest:hover { background: var(--bg-surface-2); text-decoration: none; }
.os-nu__newest-l { font: 600 10.5px/1 var(--font-ui); letter-spacing: .14em;
                   color: var(--fg-label); flex: 0 0 auto; }
.os-nu__newest-t { font: 400 17px/1.35 var(--font-display); color: var(--fg-primary);
                   flex: 1 1 260px; }
.os-nu__newest-d { font: 400 11.5px/1.4 var(--font-ui); color: var(--fg-label);
                   font-variant-numeric: tabular-nums; }
.os-nu__newest:hover .os-nu__newest-t { color: var(--accent); }

.os-nu__start { padding-top: 26px; padding-bottom: 28px; background: var(--bg-surface);
                border-top: 1px solid var(--rule); border-bottom: 1px solid var(--rule); }
.os-nu__start-h { font: 600 10.5px/1 var(--font-ui); letter-spacing: .14em;
                  color: var(--fg-label); margin-bottom: 14px; }
.os-nu__start-l { display: flex; flex-direction: column; }
.os-nu__route { display: flex; align-items: baseline; gap: 14px; padding: 11px 0;
                border-bottom: 1px solid var(--rule-soft);
                font: 400 19px/1.35 var(--font-display); color: var(--fg-primary); }
.os-nu__route:last-child { border-bottom: 0; }
.os-nu__route:hover { color: var(--accent); text-decoration: none; }
.os-nu__route-n { font: 400 15px/1 var(--font-deva); color: var(--accent); flex: 0 0 auto; }

.os-nu__sec  { padding-top: 32px; padding-bottom: 32px;
               border-bottom: 1px solid var(--rule-soft); }
.os-nu__head { display: flex; align-items: baseline; gap: 14px; flex-wrap: wrap;
               padding-bottom: 11px; border-bottom: 2px solid var(--rule-strong); }
.os-nu__head-n { font: 400 17px/1 var(--font-deva); color: var(--accent); }
.os-nu__h2   { font: 400 29px/1.15 var(--font-display); color: var(--fg-primary); margin: 0; }
.os-nu__head-e { margin-left: auto; font: 400 10.5px/1 var(--font-ui);
                 letter-spacing: .12em; color: var(--fg-label); }
.os-nu__note { margin: 12px 0 4px; max-width: 74ch;
               font: 400 14px/1.6 var(--font-ui); color: var(--fg-secondary); }
.os-nu__h3   { display: flex; align-items: baseline; gap: 10px;
               margin: 26px 0 2px; padding-bottom: 7px;
               border-bottom: 1px solid var(--rule);
               font: 400 19px/1.2 var(--font-display); color: var(--fg-primary); }
.os-nu__h3 span { margin-left: auto; font: 400 10.5px/1 var(--font-ui);
                  letter-spacing: .12em; color: var(--fg-label); }

/* ── the grid ─────────────────────────────────────────────────────────────── */
/* NINE CELLS, ALWAYS, in the owner's order 3 1 9 / 6 7 5 / 2 8 4. A number
   whose profile is missing is still drawn, unlinked — the geometry is the
   information, exactly as on the vāstu compass. */
.os-nu__grid { display: grid; grid-template-columns: repeat(3, 1fr);
               gap: 1px; background: var(--rule-soft);
               border: 1px solid var(--rule-soft); max-width: 620px; margin-top: 20px; }
/* min-height is CONTENT height on this box, not border-box, so the padding adds
   to it: 118 + 30 measured 148 with 60px of dead band under every epithet. 88
   is the tallest content a cell holds — a two-line epithet — so the floor is set
   there and the cells size to what is in them. */
.os-nu__cell { display: flex; flex-direction: column; gap: 4px; min-height: 88px;
               background: var(--bg-page); padding: 14px 15px 16px; }
.os-nu__cell:hover { background: var(--bg-surface); text-decoration: none; }
.os-nu__cell--none { background: var(--bg-surface); }
/* The numeral IS the content here, so it is set at display size rather than
   used as a label the way the compass used its direction. At 206px of cell it
   carries the grid: a reader should be able to find 7 without reading a word. */
.os-nu__cell-n { font: 400 46px/1 var(--font-display); color: var(--accent);
                 font-variant-numeric: tabular-nums; }
.os-nu__cell-g { font: 400 10.5px/1 var(--font-ui); letter-spacing: .12em;
                 text-transform: uppercase; color: var(--fg-label); }
.os-nu__cell-e { margin-top: 3px; font: 400 14px/1.35 var(--font-display);
                 color: var(--fg-primary); }
.os-nu__cell:hover .os-nu__cell-e { color: var(--accent); }

/* ── the yogas ────────────────────────────────────────────────────────────── */
.os-nu__yogas { display: flex; flex-direction: column; }
.os-nu__yoga  { display: grid; grid-template-columns: auto minmax(0, 1fr);
                gap: 16px; align-items: center; padding: 11px 0;
                border-bottom: 1px solid var(--rule-soft); }
.os-nu__yoga:last-child { border-bottom: 0; }
.os-nu__yoga:hover { background: var(--bg-row-tint); text-decoration: none; }
.os-nu__yoga > .os-thumb { width: 44px; height: 44px; }
.os-nu__yoga-t { min-width: 0; }
.os-nu__yoga-h { display: flex; align-items: baseline; gap: 12px; flex-wrap: wrap; }
.os-nu__yoga-c { font: 400 20px/1.2 var(--font-display); color: var(--accent);
                 font-variant-numeric: tabular-nums; flex: 0 0 auto; }
.os-nu__yoga-e { font: 400 18px/1.3 var(--font-display); color: var(--fg-primary); }
.os-nu__yoga:hover .os-nu__yoga-e { color: var(--accent); }
.os-nu__yoga-g { display: block; margin-top: 4px; font: 400 12px/1.4 var(--font-ui);
                 letter-spacing: .06em; color: var(--fg-label); }
/* Square, like every other chip on the site: the design's one visible circle is
   the play control, and tests/content-tokens holds that rule. */
.os-nu__line  { font: 600 9.5px/1 var(--font-ui); letter-spacing: .12em;
                color: var(--fg-label); border: 1px solid var(--rule);
                padding: 3px 6px; }

/* ── the notes ────────────────────────────────────────────────────────────── */
/* Not links, and drawn so nobody tries to click them. The owner's reading of
   five combinations that have no entry behind them. */
.os-nu__notes { padding-top: 24px; padding-bottom: 26px; background: var(--bg-surface);
                border-bottom: 1px solid var(--rule-soft); }
.os-nu__notes-h { font: 600 10.5px/1 var(--font-ui); letter-spacing: .14em;
                  color: var(--fg-label); margin-bottom: 12px; }
.os-nu__notes-l { list-style: none; margin: 0; padding: 0; max-width: 82ch; }
.os-nu__notes-l li { padding: 8px 0; border-bottom: 1px solid var(--rule-soft);
                     font: 400 14px/1.55 var(--font-ui); color: var(--fg-secondary); }
.os-nu__notes-l li:last-child { border-bottom: 0; }

/* ── the shelves ──────────────────────────────────────────────────────────── */
.os-nu__rows { display: flex; flex-direction: column; margin-top: 6px; }
.os-nu__row  { display: grid; grid-template-columns: auto minmax(0, 1fr) 64px;
               gap: 18px; align-items: start; padding: 14px 0;
               border-bottom: 1px solid var(--rule-soft); }
.os-nu__row:last-child { border-bottom: 0; }
.os-nu__row:hover { background: var(--bg-row-tint); text-decoration: none; }
.os-nu__row > .os-thumb { width: 48px; height: 48px; align-self: start; margin-top: 2px; }
.os-nu__mono { display: flex; align-items: center; justify-content: center;
               font: 400 21px/1 var(--font-display); color: var(--fg-label);
               background: var(--bg-surface-2); }
.os-nu__row-t { min-width: 0; }
.os-nu__row-n { display: block; font: 400 20px/1.28 var(--font-display);
                color: var(--fg-primary); }
.os-nu__row:hover .os-nu__row-n { color: var(--accent); }
.os-nu__row-d { display: block; margin-top: 5px; max-width: 84ch;
                font: 400 13.5px/1.55 var(--font-ui); color: var(--fg-secondary);
                text-wrap: pretty; }
.os-nu__row-m { text-align: right; font: 400 10.5px/1 var(--font-ui);
                letter-spacing: .1em; color: var(--fg-label);
                font-variant-numeric: tabular-nums; padding-top: 6px; }

.os-nu__plate { padding-top: 30px; padding-bottom: 32px; margin: 0; }

@media (max-width: 1023px) {
  .os-nu__h1 { font-size: 42px; }
  .os-nu__mast { grid-template-columns: 1fr; gap: 24px; }
  .os-nu__stats { flex-direction: row; flex-wrap: wrap; gap: 0 30px; }
  .os-nu__stat { border-top: 0; padding: 0; flex-direction: column;
                 align-items: flex-start; gap: 3px; }
  .os-nu__stat strong { margin-left: 0; order: -1; }
}
@media (max-width: 767px) {
  .os-nu__mast, .os-nu__sec, .os-nu__start, .os-nu__plate, .os-nu__newest-w, .os-nu__notes {
    padding-left: 16px; padding-right: 16px;
  }
  .os-nu__h1 { font-size: 33px; }
  .os-nu__h2 { font-size: 24px; }
  .os-nu__head-e { margin-left: 0; flex-basis: 100%; margin-top: 6px; }
  /* Three cells across still fit at 390px because a cell holds a numeral, a
     short label and one line of display text — no image and no count column. */
  .os-nu__cell { min-height: 86px; padding: 11px 11px 13px; }
  .os-nu__cell-n { font-size: 32px; }
  .os-nu__cell-e { font-size: 12.5px; }
  .os-nu__yoga > .os-thumb { width: 40px; height: 40px; }
  .os-nu__yoga-c { font-size: 18px; }
  .os-nu__yoga-e { font-size: 16px; }
  /* The minutes drop under the dek; the square keeps its column. */
  .os-nu__row { grid-template-columns: auto minmax(0, 1fr); column-gap: 14px; row-gap: 5px; }
  .os-nu__row-m { grid-column: 2; text-align: left; padding-top: 0; }
  .os-nu__row-n { font-size: 18px; }
  .os-nu__route { font-size: 17px; }
}

/* ── /palmistry/ — four elements, then the rest of the hand ───────────────── */
.os-pm { max-width: var(--shell); margin: 0 auto;
         background: var(--bg-page); border: 1px solid var(--card-border); }
@media (max-width: 1023px) { .os-pm { border-left: 0; border-right: 0; } }
.os-pm__mast, .os-pm__sec, .os-pm__start, .os-pm__plate, .os-pm__newest-w {
  padding-left: 44px; padding-right: 44px;
}

.os-pm__mast { display: grid; grid-template-columns: minmax(0, 1fr) 260px; gap: 48px;
               padding-top: 40px; padding-bottom: 30px; }
.os-pm__deva { font: 400 20px/1 var(--font-deva); color: var(--accent); margin-bottom: 10px; }
.os-pm__h1   { font: 400 52px/1.05 var(--font-display); letter-spacing: -.02em;
               color: var(--fg-primary); margin: 0 0 16px; }
.os-pm__prose { max-width: 68ch; }
.os-pm__stats { display: flex; flex-direction: column; }
.os-pm__stat  { display: flex; align-items: baseline; gap: 10px; padding: 9px 0;
                border-top: 1px solid var(--rule-soft);
                font: 400 10.5px/1 var(--font-ui); letter-spacing: .12em;
                text-transform: uppercase; color: var(--fg-label); }
.os-pm__stat strong { font: 400 25px/1 var(--font-display); color: var(--fg-primary);
                      letter-spacing: 0; margin-left: auto;
                      font-variant-numeric: tabular-nums; }

.os-pm__newest-w { padding-bottom: 30px; }
.os-pm__newest { display: flex; align-items: baseline; gap: 14px; flex-wrap: wrap;
                 padding: 13px 16px; background: var(--bg-surface);
                 border-left: 3px solid var(--accent); }
.os-pm__newest:hover { background: var(--bg-surface-2); text-decoration: none; }
.os-pm__newest-l { font: 600 10.5px/1 var(--font-ui); letter-spacing: .14em;
                   color: var(--fg-label); flex: 0 0 auto; }
.os-pm__newest-t { font: 400 17px/1.35 var(--font-display); color: var(--fg-primary);
                   flex: 1 1 260px; }
.os-pm__newest-d { font: 400 11.5px/1.4 var(--font-ui); color: var(--fg-label);
                   font-variant-numeric: tabular-nums; }
.os-pm__newest:hover .os-pm__newest-t { color: var(--accent); }

.os-pm__start { padding-top: 26px; padding-bottom: 28px; background: var(--bg-surface);
                border-top: 1px solid var(--rule); border-bottom: 1px solid var(--rule); }
.os-pm__start-h { font: 600 10.5px/1 var(--font-ui); letter-spacing: .14em;
                  color: var(--fg-label); margin-bottom: 14px; }
.os-pm__start-l { display: flex; flex-direction: column; }
.os-pm__route { display: flex; align-items: baseline; gap: 14px; padding: 11px 0;
                border-bottom: 1px solid var(--rule-soft);
                font: 400 19px/1.35 var(--font-display); color: var(--fg-primary); }
.os-pm__route:last-child { border-bottom: 0; }
.os-pm__route:hover { color: var(--accent); text-decoration: none; }
.os-pm__route-n { font: 400 15px/1 var(--font-deva); color: var(--accent); flex: 0 0 auto; }

.os-pm__sec  { padding-top: 32px; padding-bottom: 32px;
               border-bottom: 1px solid var(--rule-soft); }
.os-pm__head { display: flex; align-items: baseline; gap: 14px; flex-wrap: wrap;
               padding-bottom: 11px; border-bottom: 2px solid var(--rule-strong); }
.os-pm__head-n { font: 400 17px/1 var(--font-deva); color: var(--accent); }
.os-pm__h2   { font: 400 29px/1.15 var(--font-display); color: var(--fg-primary); margin: 0; }
.os-pm__head-e { margin-left: auto; font: 400 10.5px/1 var(--font-ui);
                 letter-spacing: .12em; color: var(--fg-label); }
.os-pm__note { margin: 12px 0 4px; max-width: 74ch;
               font: 400 14px/1.6 var(--font-ui); color: var(--fg-secondary); }

/* ── the four elements ────────────────────────────────────────────────────── */
/* Four cells, always, in his order: Earth, Water, Fire, Air. The hand sits
   OUTSIDE the cell rather than inside it, because a link inside a link is not
   markup a browser will honour. */
.os-pm__els  { display: grid; grid-template-columns: repeat(4, 1fr);
               gap: 1px; background: var(--rule-soft);
               border: 1px solid var(--rule-soft); margin-top: 20px; }
.os-pm__elw  { display: flex; flex-direction: column; background: var(--bg-page); }
.os-pm__el   { display: flex; flex-direction: column; gap: 5px; flex: 1 1 auto;
               padding: 15px 15px 12px; }
.os-pm__el:hover { background: var(--bg-surface); text-decoration: none; }
.os-pm__el--none { background: var(--bg-surface); }
.os-pm__el-h { display: flex; align-items: baseline; gap: 10px; }
.os-pm__el-e { font: 400 27px/1.05 var(--font-display); color: var(--accent); }
.os-pm__el-n { margin-left: auto; font: 400 15px/1 var(--font-display);
               color: var(--fg-label); font-variant-numeric: tabular-nums; }
.os-pm__el-l { font: 400 15px/1.3 var(--font-display); color: var(--fg-primary); }
.os-pm__el:hover .os-pm__el-l { color: var(--accent); }
.os-pm__el-r { font: 400 12.5px/1.45 var(--font-ui); color: var(--fg-secondary); }
.os-pm__hand { display: block; padding: 9px 15px 13px;
               border-top: 1px solid var(--rule-soft);
               font: 400 11.5px/1.3 var(--font-ui); color: var(--fg-label); }
.os-pm__hand:hover { color: var(--accent); text-decoration: none; }

/* ── a namespace and its entries ──────────────────────────────────────────── */
.os-pm__group { margin-top: 26px; }
.os-pm__ghead { display: flex; align-items: baseline; gap: 12px;
                padding-bottom: 7px; border-bottom: 1px solid var(--rule); }
.os-pm__ghead:hover { text-decoration: none; }
.os-pm__h3    { font: 400 20px/1.2 var(--font-display); color: var(--fg-primary); margin: 0; }
.os-pm__ghead:hover .os-pm__h3 { color: var(--accent); }
.os-pm__gcount { margin-left: auto; font: 400 10.5px/1 var(--font-ui);
                 letter-spacing: .12em; color: var(--fg-label); }
.os-pm__gnote  { margin: 8px 0 0; max-width: 78ch;
                 font: 400 13px/1.5 var(--font-ui); color: var(--fg-secondary); }

/* The 48px square the archives carry, for the same reason: 136 of the 138
   entries have a feature image and every one of them is different. */
.os-pm__rows { display: flex; flex-direction: column; }
.os-pm__row  { display: grid; grid-template-columns: auto minmax(0, 1fr) 64px;
               gap: 16px; align-items: start; padding: 12px 0;
               border-bottom: 1px solid var(--rule-soft); }
.os-pm__row:last-child { border-bottom: 0; }
.os-pm__row:hover { background: var(--bg-row-tint); text-decoration: none; }
.os-pm__row > .os-thumb { width: 48px; height: 48px; align-self: start; margin-top: 1px; }
.os-pm__mono { display: flex; align-items: center; justify-content: center;
               font: 400 21px/1 var(--font-display); color: var(--fg-label);
               background: var(--bg-surface-2); }
.os-pm__row-t { min-width: 0; }
.os-pm__row-n { display: block; font: 400 19px/1.28 var(--font-display);
                color: var(--fg-primary); }
.os-pm__row:hover .os-pm__row-n { color: var(--accent); }
.os-pm__row-d { display: block; margin-top: 4px; max-width: 84ch;
                font: 400 13.5px/1.55 var(--font-ui); color: var(--fg-secondary);
                text-wrap: pretty; }
.os-pm__row-m { text-align: right; font: 400 10.5px/1 var(--font-ui);
                letter-spacing: .1em; color: var(--fg-label);
                font-variant-numeric: tabular-nums; padding-top: 5px; }

.os-pm__plate { padding-top: 30px; padding-bottom: 32px; margin: 0; }

@media (max-width: 1023px) {
  .os-pm__h1 { font-size: 42px; }
  .os-pm__mast { grid-template-columns: 1fr; gap: 24px; }
  .os-pm__stats { flex-direction: row; flex-wrap: wrap; gap: 0 30px; }
  .os-pm__stat { border-top: 0; padding: 0; flex-direction: column;
                 align-items: flex-start; gap: 3px; }
  .os-pm__stat strong { margin-left: 0; order: -1; }
  /* Four cells of a line, a name and a reading do not survive a tablet column;
     two by two keeps every cell readable. */
  .os-pm__els { grid-template-columns: repeat(2, 1fr); }
}
@media (max-width: 767px) {
  .os-pm__mast, .os-pm__sec, .os-pm__start, .os-pm__plate, .os-pm__newest-w {
    padding-left: 16px; padding-right: 16px;
  }
  .os-pm__h1 { font-size: 33px; }
  .os-pm__h2 { font-size: 24px; }
  .os-pm__head-e { margin-left: 0; flex-basis: 100%; margin-top: 6px; }
  .os-pm__el-e { font-size: 23px; }
  /* The minutes drop under the dek; the square keeps its column. */
  .os-pm__row { grid-template-columns: auto minmax(0, 1fr); column-gap: 14px; row-gap: 5px; }
  .os-pm__row-m { grid-column: 2; text-align: left; padding-top: 0; }
  .os-pm__row-n { font-size: 18px; }
  .os-pm__route { font-size: 17px; }
}

.os-dv { max-width: var(--shell); margin: 0 auto;
  background: var(--bg-page); border: 1px solid var(--card-border); overflow: hidden; }
@media (max-width: 1023px) { .os-dv { border-left: 0; border-right: 0; } }

/* ── masthead ─────────────────────────────────────────────────────────────── */
/* ONE LEFT EDGE, declared once for every band on the page rather than per
   band. Measured against /astrology/, which does exactly this: the first
   version set each band's horizontal padding separately and used 22px at
   tablet while .os-crumb keeps 44 until phone — so between 768 and 1023 the
   breadcrumb text sat at 44 and the h1 under it at 22. */
.os-dv__mast, .os-dv__sec, .os-dv__start, .os-dv__newest-w, .os-dv__listen-i {
  padding-left: 44px; padding-right: 44px;
}
.os-dv__plate { margin-left: 44px; margin-right: 44px; }
.os-dv__mast { display: grid; grid-template-columns: minmax(0, 1fr) 260px; gap: 40px;
  padding-top: 12px; padding-bottom: 34px;
  border-bottom: 1px solid var(--rule); align-items: start; }
.os-dv__mast-l { min-width: 0; }
.os-dv__deva { font: 400 26px/1 var(--font-deva); color: var(--accent); margin-bottom: 12px; }
.os-dv__h1 { font: 300 52px/1.04 var(--font-display); margin: 0 0 16px;
  letter-spacing: -.02em; color: var(--fg-primary); }
.os-dv__prose { font-size: 16.5px; max-width: 62ch; }
.os-dv__prose p:last-child { margin-bottom: 0; }
.os-dv__stats { display: flex; flex-direction: column; gap: 14px; padding-top: 8px; }
.os-dv__stat { display: flex; flex-direction: column; font: 400 11px/1.4 var(--font-ui);
  letter-spacing: .1em; text-transform: uppercase; color: var(--fg-label); }
.os-dv__stat strong { font: 300 34px/1 var(--font-display); color: var(--fg-primary);
  letter-spacing: 0; text-transform: none; margin-bottom: 4px; }

.os-dv__newest-w { border-bottom: 1px solid var(--rule); }
.os-dv__newest { display: flex; align-items: baseline; gap: 14px; padding: 16px 0;
  text-decoration: none; }
.os-dv__newest-l { flex: 0 0 auto; font: 500 10.5px/1 var(--font-ui); letter-spacing: .12em;
  color: var(--fg-label); }
.os-dv__newest-t { font: 400 16px/1.4 var(--font-display); color: var(--fg-primary);
  min-width: 0; overflow-wrap: break-word; }
.os-dv__newest-d { margin-left: auto; flex: 0 0 auto; font: 400 12.5px/1 var(--font-mono);
  color: var(--fg-label); }

/* ── the recordings ───────────────────────────────────────────────────────── */
.os-dv__listen { background: var(--bg-surface); border-bottom: 1px solid var(--rule); }
.os-dv__listen-i { padding-top: 26px; padding-bottom: 26px; display: flex; align-items: center;
  gap: 26px; flex-wrap: wrap; }
.os-dv__listen-h { font: 500 10.5px/1 var(--font-ui); letter-spacing: .12em;
  color: var(--fg-label); flex: 0 0 auto; }
.os-dv__listen-p { flex: 1 1 380px; margin: 0; min-width: 0;
  font: 300 16px/1.6 var(--font-display); color: var(--fg-secondary); text-wrap: pretty; }
.os-dv__listen-p strong { font-weight: 400; color: var(--fg-primary); }

/* ── curated routes ───────────────────────────────────────────────────────── */
.os-dv__start { padding-top: 24px; padding-bottom: 26px; background: var(--bg-surface);
  border-bottom: 1px solid var(--rule); }
.os-dv__start-h { font: 500 10.5px/1 var(--font-ui); letter-spacing: .12em;
  color: var(--fg-label); margin-bottom: 14px; }
.os-dv__route { display: flex; align-items: baseline; gap: 12px; padding: 9px 0;
  border-top: 1px solid var(--rule); font: 400 16.5px/1.4 var(--font-display);
  color: var(--fg-primary); text-decoration: none; }
.os-dv__route:first-child { border-top: 0; }
.os-dv__route-n { flex: 0 0 auto; font: 400 13px/1 var(--font-deva); color: var(--accent); }

/* ── the traditions ───────────────────────────────────────────────────────── */
.os-dv__sec { padding-top: 34px; padding-bottom: 34px; border-bottom: 1px solid var(--rule); }
.os-dv__sec:last-of-type { border-bottom: 0; }
.os-dv__head { display: flex; align-items: baseline; gap: 12px; margin-bottom: 10px;
  padding-bottom: 12px; border-bottom: 1px solid var(--rule); }
.os-dv__head-n { font: 400 18px/1 var(--font-deva); color: var(--accent); flex: 0 0 auto; }
.os-dv__h2 { font: 400 26px/1.2 var(--font-display); margin: 0; color: var(--fg-primary); }
.os-dv__head-e { font: 500 11px/1.4 var(--font-ui); letter-spacing: .1em;
  color: var(--fg-label); margin-left: auto; flex: 0 0 auto; }
.os-dv__note { font: 300 15px/1.6 var(--font-display); color: var(--fg-secondary);
  margin: 0 0 22px; max-width: 68ch; text-wrap: pretty; }

.os-dv__ns { margin-bottom: 26px; }
.os-dv__ns:last-child { margin-bottom: 0; }
.os-dv__ns-h { display: flex; align-items: baseline; gap: 10px;
  font: 500 10.5px/1 var(--font-ui); letter-spacing: .12em; text-transform: uppercase;
  color: var(--fg-label); margin-bottom: 12px; }
.os-dv__ns-c { font-family: var(--font-mono); letter-spacing: 0; color: var(--fg-separator); }

/* Three across on the shell, so eighteen Veda entries are a block a reader can
   scan rather than an eighteen-deep list. minmax(0,1fr) is load-bearing: a grid
   child defaults to min-width:auto, and one long unbroken title would widen the
   track and push the third column off the card. */
.os-dv__rows { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 1px;
  background: var(--rule); border: 1px solid var(--rule); }
.os-dv__row { display: flex; align-items: center; gap: 12px; min-width: 0;
  background: var(--bg-page); padding: 10px 12px; text-decoration: none; }
.os-dv__row:hover { background: var(--bg-surface); }
.os-dv__shot { flex: 0 0 auto; width: 36px; height: 36px; overflow: hidden;
  background: var(--bg-surface); display: block; }
.os-dv__shot img { width: 100%; height: 100%; object-fit: cover; display: block; }
.os-dv__shot--mono { display: flex; align-items: center; justify-content: center;
  font: 400 15px/1 var(--font-display); color: var(--fg-label); }
.os-dv__row-t { min-width: 0; font: 400 14px/1.35 var(--font-display);
  color: var(--fg-primary); overflow-wrap: break-word; }
.os-dv__ear { flex: 0 0 auto; margin-left: auto; color: var(--accent);
  font: 400 14px/1 var(--font-ui); }
/* THE LABEL BESIDE THE MARK IS FOR SCREEN READERS ONLY. It was written against
   a class called .os-sr that does not exist anywhere in this codebase, so it
   printed — "♪Carries a recorded reading" in accent red beside fifty titles,
   wrapping every row it touched. Same technique the shell uses on the wordmark
   below 360px; a title attribute alone is not announced by every reader, and
   aria-hidden on the glyph without this leaves the mark silent. */
.os-dv__sr { position: absolute; width: 1px; height: 1px; overflow: hidden;
  clip-path: inset(50%); white-space: nowrap; }
.os-dv__ear--inline { margin-left: 0; display: inline; }

.os-dv__plate { margin-top: 34px; margin-bottom: 34px; }

@media (max-width: 1279px) {
  .os-dv__mast { grid-template-columns: minmax(0, 1fr); gap: 24px; }
  .os-dv__stats { flex-direction: row; gap: 34px; padding-top: 0; }
  .os-dv__rows { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (max-width: 1023px) {
  .os-dv__h1 { font-size: 38px; }
}
@media (max-width: 767px) {
  /* 16 is the system's phone edge, and .os-crumb drops to it at the same stop. */
  .os-dv__mast, .os-dv__sec, .os-dv__start, .os-dv__newest-w, .os-dv__listen-i {
    padding-left: 16px; padding-right: 16px;
  }
  .os-dv__plate { margin-left: 16px; margin-right: 16px; }
  .os-dv__h1 { font-size: 32px; }
  .os-dv__rows { grid-template-columns: minmax(0, 1fr); }
  .os-dv__newest { flex-wrap: wrap; gap: 6px; }
  .os-dv__newest-d { margin-left: 0; }
  .os-dv__stats { gap: 22px; }
}

.os-wr { max-width: var(--shell); margin: 0 auto;
  background: var(--bg-page); border: 1px solid var(--card-border); overflow: hidden; }
@media (max-width: 1023px) { .os-wr { border-left: 0; border-right: 0; } }

/* ONE LEFT EDGE, declared once — see the note in os-devotional.js. */
.os-wr__mast, .os-wr__sec, .os-wr__start, .os-wr__all-w {
  padding-left: 44px; padding-right: 44px;
}
.os-wr__plate { margin-left: 44px; margin-right: 44px; }
.os-wr__mast { display: grid; grid-template-columns: minmax(0, 1fr) 240px; gap: 40px;
  padding-top: 12px; padding-bottom: 34px;
  border-bottom: 1px solid var(--rule); align-items: start; }
.os-wr__mast-l { min-width: 0; }
.os-wr__deva { font: 400 26px/1 var(--font-deva); color: var(--accent); margin-bottom: 12px; }
.os-wr__h1 { font: 300 52px/1.04 var(--font-display); margin: 0 0 16px;
  letter-spacing: -.02em; color: var(--fg-primary); }
.os-wr__prose { font-size: 16.5px; max-width: 62ch; }
.os-wr__prose p:last-child { margin-bottom: 0; }
.os-wr__stats { display: flex; flex-direction: column; gap: 14px; padding-top: 8px; }
.os-wr__stat { display: flex; flex-direction: column; font: 400 11px/1.4 var(--font-ui);
  letter-spacing: .1em; text-transform: uppercase; color: var(--fg-label); }
.os-wr__stat strong { font: 300 34px/1 var(--font-display); color: var(--fg-primary);
  letter-spacing: 0; text-transform: none; margin-bottom: 4px; }

.os-wr__start { padding-top: 24px; padding-bottom: 26px; background: var(--bg-surface);
  border-bottom: 1px solid var(--rule); }
.os-wr__start-h { font: 500 10.5px/1 var(--font-ui); letter-spacing: .12em;
  color: var(--fg-label); margin-bottom: 14px; }
.os-wr__route { display: flex; align-items: baseline; gap: 12px; padding: 9px 0;
  border-top: 1px solid var(--rule); font: 400 16.5px/1.4 var(--font-display);
  color: var(--fg-primary); text-decoration: none; }
.os-wr__route:first-of-type { border-top: 0; }
.os-wr__route-n { flex: 0 0 auto; font: 400 13px/1 var(--font-deva); color: var(--accent); }

.os-wr__sec { padding-top: 34px; padding-bottom: 34px; border-bottom: 1px solid var(--rule); }
.os-wr__head { display: flex; align-items: baseline; gap: 12px; margin-bottom: 10px;
  padding-bottom: 12px; border-bottom: 1px solid var(--rule); }
.os-wr__head-n { font: 400 18px/1 var(--font-deva); color: var(--accent); flex: 0 0 auto; }
.os-wr__h2 { font: 400 26px/1.2 var(--font-display); margin: 0; color: var(--fg-primary); }
.os-wr__head-e { font: 500 11px/1.4 var(--font-ui); letter-spacing: .1em;
  color: var(--fg-label); margin-left: auto; flex: 0 0 auto; }
.os-wr__note-p { font: 300 15px/1.6 var(--font-display); color: var(--fg-secondary);
  margin: 0 0 22px; max-width: 68ch; text-wrap: pretty; }

/* THE ESSAYS get the picture. every one of the 27 has one, and this is the only band
   where the image is doing work — a note and a trade log are read by their
   title alone. minmax(0,1fr) because a grid child defaults to min-width:auto
   and one long unbroken title would widen its track. */
.os-wr__cards { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 26px 24px; }
.os-wr__card { display: block; text-decoration: none; min-width: 0; }
.os-wr__shot { display: block; aspect-ratio: 16 / 9; overflow: hidden;
  background: var(--bg-surface); border: 1px solid var(--rule); margin-bottom: 10px; }
.os-wr__shot img { width: 100%; height: 100%; object-fit: cover; display: block; }
.os-wr__shot--mono { display: flex; align-items: center; justify-content: center;
  font: 300 30px/1 var(--font-display); color: var(--fg-label); }
.os-wr__card-t { display: block; font: 400 16px/1.35 var(--font-display);
  color: var(--fg-primary); overflow-wrap: break-word; }
.os-wr__card-m { display: flex; gap: 12px; margin-top: 6px;
  font: 400 11.5px/1 var(--font-mono); color: var(--fg-label); }

.os-wr__notes { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 1px;
  background: var(--rule); border: 1px solid var(--rule); }
.os-wr__note { display: flex; align-items: baseline; gap: 14px; min-width: 0;
  background: var(--bg-page); padding: 13px 16px; text-decoration: none; }
.os-wr__note:hover { background: var(--bg-surface); }
/* An ODD last note spans the row. The grid paints its gaps by showing a rule
   ground through them, so a missing final cell is not a gap — it is a solid
   tinted block the width of half the card, which reads as a broken row. Nine
   notes in two columns is exactly that case. Same answer os-index and
   os-archive give their pad cells. */
.os-wr__note:last-child:nth-child(odd) { grid-column: 1 / -1; }
.os-wr__note-t { min-width: 0; font: 400 15px/1.4 var(--font-display);
  color: var(--fg-primary); overflow-wrap: break-word; }
.os-wr__note-d { margin-left: auto; flex: 0 0 auto;
  font: 400 11.5px/1 var(--font-mono); color: var(--fg-label); }

/* THE RECORD is a table, not a card grid: eleven rows of 30–58 words, read as a
   run. One per line so the outcomes stack into a column the eye can total. */
.os-wr__logs { border: 1px solid var(--rule); }
.os-wr__log { display: flex; align-items: baseline; gap: 14px; min-width: 0;
  padding: 11px 16px; border-top: 1px solid var(--rule); text-decoration: none; }
.os-wr__log:first-child { border-top: 0; }
.os-wr__log:hover { background: var(--bg-surface); }
.os-wr__log-t { min-width: 0; font: 400 14.5px/1.4 var(--font-display);
  color: var(--fg-primary); overflow-wrap: break-word; }
.os-wr__log-d { flex: 0 0 auto; margin-left: auto;
  font: 400 11.5px/1 var(--font-mono); color: var(--fg-label); }
/* No radius: the one circle the reader can see on this site is the play
   control. A chip is a rectangle like every other chip. */
.os-wr__chip { flex: 0 0 auto; font: 500 10.5px/1 var(--font-ui); letter-spacing: .08em;
  text-transform: uppercase; padding: 5px 8px; border: 1px solid var(--rule);
  color: var(--fg-label); white-space: nowrap; }
.os-wr__chip--win { color: var(--fg-primary); border-color: var(--fg-separator); }
.os-wr__chip--loss { color: var(--fg-label); }
.os-wr__chip--open { color: var(--accent); border-color: var(--accent); }

.os-wr__all-w { padding-top: 26px; padding-bottom: 26px; border-bottom: 1px solid var(--rule); }
.os-wr__all { font: 400 15px/1.5 var(--font-ui); color: var(--fg-primary);
  text-decoration: none; }
.os-wr__all strong { font: 300 22px/1 var(--font-display); color: var(--accent); }
.os-wr__plate { margin-top: 34px; margin-bottom: 34px; }

@media (max-width: 1279px) {
  .os-wr__mast { grid-template-columns: minmax(0, 1fr); gap: 24px; }
  .os-wr__stats { flex-direction: row; gap: 34px; padding-top: 0; }
  .os-wr__cards { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (max-width: 1023px) {
  .os-wr__h1 { font-size: 38px; }
  .os-wr__notes { grid-template-columns: minmax(0, 1fr); }
}
@media (max-width: 767px) {
  .os-wr__mast, .os-wr__sec, .os-wr__start, .os-wr__all-w {
    padding-left: 16px; padding-right: 16px;
  }
  .os-wr__plate { margin-left: 16px; margin-right: 16px; }
  .os-wr__h1 { font-size: 32px; }
  .os-wr__cards { grid-template-columns: minmax(0, 1fr); gap: 22px; }
  .os-wr__stats { gap: 22px; }
  /* The chip drops below the title rather than squeezing it to two characters. */
  .os-wr__log { flex-wrap: wrap; gap: 8px 12px; }
  .os-wr__log-d { margin-left: 0; }
}

.os-ix {
  max-width: var(--shell); margin: 0 auto;
  background: var(--bg-page); border: 1px solid var(--card-border); overflow: hidden;
}
@media (max-width: 1023px) { .os-ix { border-left: 0; border-right: 0; } }
.os-ix .os-crumb { padding: 22px 44px 0; margin: 0; }

.os-ix__mast   { display: grid; grid-template-columns: 1fr 420px;
                 border-bottom: 2px solid var(--rule-strong); }
.os-ix__mast-l { padding: 48px 44px 40px; }
.os-ix__mast-r { border-left: 1px solid var(--rule); background: var(--bg-surface);
                 padding: 44px 44px; display: flex; flex-direction: column;
                 justify-content: center; }
.os-ix__mast-deva { font: 400 38px/1.2 var(--font-deva); color: var(--accent); margin-bottom: 8px; }
.os-ix__h1     { font: 300 56px/1 var(--font-display); color: var(--fg-primary);
                 letter-spacing: -.034em; margin: 0 0 18px; text-wrap: pretty; }
.os-ix__dek    { font: 400 18px/1.6 var(--font-ui); color: var(--fg-secondary);
                 margin: 0 0 26px; max-width: 56ch; text-wrap: pretty; }
.os-ix__alt { display: inline-block; margin-top: 16px;
              font: 500 12px/1.4 var(--font-ui); letter-spacing: .1em;
              text-transform: uppercase; color: var(--accent);
              border-bottom: 1px solid var(--rule); padding-bottom: 3px; }
.os-ix__alt:hover { border-bottom-color: var(--accent); text-decoration: none; }
.os-ix__search { display: flex; border: 1px solid var(--fg-primary); background: var(--bg-input);
                 max-width: 440px; }
.os-ix__search input {
  flex: 1; min-width: 0; padding: 15px 18px; border: 0; background: transparent;
  font: 400 16px/1.3 var(--font-ui); color: var(--fg-primary);
}
.os-ix__search input::placeholder { color: var(--fg-label); }
/* 5A: "Search field | border -> fg-primary | Focus: 2px accent inset border, no
   outline". The field already carries the ink border at rest — 6A draws it
   border:1px solid #221C15 — so the hover row has nowhere to travel; the focus row
   did, and the global :focus-visible outline was drawing a ring 2px OUTSIDE a box
   the design wants marked from within. Inset, so the field does not grow. */
.os-ix__search:focus-within { box-shadow: inset 0 0 0 2px var(--accent); }
.os-ix__search input:focus-visible, .os-ix__search button:focus-visible { outline: none; }
.os-ix__search button:focus-visible { box-shadow: inset 0 0 0 2px var(--fg-inverse); }
.os-ix__search button {
  background: var(--bg-inverse); color: var(--fg-inverse); border: 0; cursor: pointer;
  padding: 15px 20px; font: 500 12px/1.3 var(--font-ui); letter-spacing: .1em;
}
.os-ix__figs   { display: grid; grid-template-columns: 1fr 1fr; gap: 22px 28px; }
.os-ix__fig    { font: 500 30px/1 var(--font-ui); color: var(--fg-primary);
                 font-variant-numeric: tabular-nums; }
.os-ix__fig-l  { font: 400 10px/1 var(--font-ui); letter-spacing: .14em;
                 color: var(--fg-label); margin-top: 8px; }

.os-ix__bar    { display: flex; align-items: center; justify-content: space-between;
                 gap: var(--s4); flex-wrap: wrap;
                 padding: 14px 44px; border-bottom: 1px solid var(--rule);
                 background: var(--bg-panel); }
.os-ix__chips  { display: flex; gap: 8px; }
.os-ix__jump   { display: flex; align-items: center; gap: 20px; flex-wrap: wrap;
                 font: 400 14px/1 var(--font-ui); color: var(--fg-label); }
.os-ix__jump-l { color: var(--fg-primary); }

.os-ix__disc   { padding: 44px; border-bottom: 1px solid var(--rule); }
.os-ix__head   { display: flex; align-items: baseline; gap: 16px; flex-wrap: wrap;
                 padding-bottom: 14px; border-bottom: 2px solid var(--rule-strong);
                 margin-bottom: 8px; }
.os-ix__deva   { font: 400 26px/1 var(--font-deva); color: var(--accent); }
.os-ix__h2     { font: 400 32px/1.1 var(--font-display); color: var(--fg-primary);
                 letter-spacing: -.018em; margin: 0; }
.os-ix__meta   { font: 400 14px/1 var(--font-ui); color: var(--fg-label);
                 font-variant-numeric: tabular-nums; }
.os-ix__open   { font: 500 12px/1 var(--font-ui); letter-spacing: .1em;
                 color: var(--accent); margin-left: auto; }
.os-ix__intro  { font: 400 15px/1.6 var(--font-ui); color: var(--fg-secondary);
                 margin: 0 0 22px; max-width: 76ch; }

.os-ix__grid   { display: grid; grid-template-columns: repeat(6, 1fr); gap: 1px;
                 background: var(--rule); border: 1px solid var(--rule); }
.os-ix__cell   { background: var(--bg-page); padding: 16px 15px; min-height: 88px;
                 display: flex; flex-direction: column; justify-content: space-between; }
.os-ix__cell-n { font: 500 22px/1 var(--font-ui); color: var(--accent);
                 font-variant-numeric: tabular-nums; }
.os-ix__cell-l { font: 500 14px/1.2 var(--font-ui); color: var(--fg-primary); }
/* A collection holding one entry is a tail, and reads as one: the count steps
   back to the label colour and the ground lightens by one step. Hiding it would
   make the index inaccurate. */
.os-ix__grid .os-ix__cell--tail { background: var(--bg-surface); }
.os-ix__cell--tail .os-ix__cell-n { color: var(--fg-label); }
/* The remainder: the second-largest cell on the grid it appears in, so it takes
   the surface-2 ground and a count in body ink rather than accent — it is a
   destination, not another named run. */
.os-ix__grid .os-ix__cell--rest { background: var(--bg-surface-2); }
.os-ix__cell--rest .os-ix__cell-n { color: var(--fg-secondary); }
.os-ix__cell:hover { background: var(--bg-surface-2); text-decoration: none; }
.os-ix__cell:hover .os-ix__cell-l { color: var(--accent); }
/* PLATE gold, not --gold. --gold is the fill pair for light grounds and is
   unreadable on ink; --plate-gold is the text-safe one. The canvas draws
   #E4BA61 here, which is the plate value in both themes. */
.os-ix__grid .os-ix__cell--all   { background: var(--bg-inverse); }
.os-ix__grid .os-ix__cell--all .os-ix__cell-n { color: var(--plate-gold); }
.os-ix__grid .os-ix__cell--all .os-ix__cell-l { color: var(--plate-fg); }
.os-ix__grid .os-ix__cell--all:hover { background: var(--plate-bg); }
.os-ix__cell--pad { background: var(--bg-page); min-height: 0;
                 grid-column: auto / -1; }

.os-ix__rows   { display: flex; flex-direction: column; }
.os-ix__row    { display: flex; justify-content: space-between; gap: var(--s4);
                 padding: 10px 0; border-bottom: 1px solid var(--rule);
                 font: 400 16px/1.3 var(--font-ui); color: var(--fg-primary); }
.os-ix__row:last-child { border-bottom: 0; }
.os-ix__row:hover { color: var(--accent); text-decoration: none; }
.os-ix__row-n  { color: var(--fg-label); font-variant-numeric: tabular-nums; }

.os-ix__routes { display: flex; flex-direction: column; margin-top: 14px; }
.os-ix__route  { display: flex; gap: 10px; padding: 8px 0;
                 border-bottom: 1px solid var(--rule);
                 font: 400 15px/1.35 var(--font-display); color: var(--fg-primary); }
.os-ix__route:last-child { border-bottom: 0; }
.os-ix__route:hover { color: var(--accent); text-decoration: none; }
.os-ix__route-n { font: 400 13px/1.5 var(--font-deva); color: var(--accent); flex: none; }

/* THE COMPOSITE BAND — the disciplines that do not grid, three up.
   The outer padding matches a full-width block's 44px and the inner gutters are
   32px, so the band's type sits on the same left edge as every block above it. */
.os-ix__tri     { display: grid; border-bottom: 2px solid var(--rule-strong); }
.os-ix__tri--2  { grid-template-columns: 1fr 1fr; }
.os-ix__tri--3  { grid-template-columns: 1fr 1fr 1fr; }
.os-ix__tri-col { padding: 40px 32px; border-right: 1px solid var(--rule); }
.os-ix__tri--2 .os-ix__tri-col:nth-child(2n+1),
.os-ix__tri--3 .os-ix__tri-col:nth-child(3n+1) { padding-left: 44px; }
.os-ix__tri--2 .os-ix__tri-col:nth-child(2n),
.os-ix__tri--3 .os-ix__tri-col:nth-child(3n) { padding-right: 44px; border-right: 0; }
.os-ix__tri-col:last-child { border-right: 0; }
.os-ix__tri-head { display: flex; align-items: baseline; gap: 12px;
                   padding-bottom: 12px; border-bottom: 2px solid var(--rule-strong);
                   margin-bottom: 16px; flex-wrap: wrap; }
.os-ix__tri-deva { font: 400 20px/1 var(--font-deva); color: var(--accent); }
.os-ix__tri-h2   { font: 400 26px/1.1 var(--font-display); color: var(--fg-primary);
                   letter-spacing: -.018em; margin: 0; }
/* Upper case in the band and sentence case at full width: the same sentence, set
   at 13px in a narrow column, needs the extra weight caps give it. */
.os-ix__tri-meta { font: 400 13px/1 var(--font-ui); color: var(--fg-label);
                   text-transform: uppercase; margin-bottom: 16px;
                   font-variant-numeric: tabular-nums; }
.os-ix__tri-intro { font: 400 15px/1.55 var(--font-ui); color: var(--fg-secondary);
                    margin: 0 0 14px; }
.os-ix__tri-all  { display: block; margin-top: 16px; font: 500 12px/1 var(--font-ui);
                   letter-spacing: .1em; color: var(--accent); }

/* Standing pages are not articles and are never counted as entries — two of
   them were being counted into an editorial collection. */
.os-ix__pages   { padding: 36px 44px; background: var(--bg-surface);
                  border-bottom: 1px solid var(--rule); }
.os-ix__pages-h { display: flex; align-items: baseline; justify-content: space-between;
                  gap: var(--s4); margin-bottom: 16px; }
.os-ix__pages-h h2 { font: 400 22px/1.1 var(--font-display); color: var(--fg-primary); margin: 0; }
.os-ix__pages-n { font: 400 12px/1 var(--font-ui); color: var(--fg-label); letter-spacing: .08em; }
.os-ix__chipset { display: flex; flex-wrap: wrap; gap: 8px; }
.os-ix__pchip   { border: 1px solid var(--rule); background: var(--bg-page);
                  padding: 8px 13px; font: 400 14px/1 var(--font-ui); color: var(--fg-primary); }
.os-ix__pchip:hover { border-color: var(--fg-primary); text-decoration: none; }

@media (max-width: 1279px)  { .os-ix__grid { grid-template-columns: repeat(4, 1fr); } }
@media (max-width: 1023px) {
  .os-ix__mast   { grid-template-columns: 1fr; }
  .os-ix__mast-r { border-left: 0; border-top: 1px solid var(--rule); }
  .os-ix__grid   { grid-template-columns: repeat(3, 1fr); }
  /* One column, so the vertical gutters become horizontal rules. Without this the
     band's border-right hangs off the side of a stack. */
  .os-ix__tri--2, .os-ix__tri--3 { grid-template-columns: 1fr; }
  .os-ix__tri-col { border-right: 0; border-bottom: 1px solid var(--rule);
                    padding-left: 44px; padding-right: 44px; }
  .os-ix__tri-col:last-child { border-bottom: 0; }
}
@media (max-width: 767px) {
  .os-ix .os-crumb, .os-ix__mast-l, .os-ix__mast-r,
  .os-ix__bar, .os-ix__disc, .os-ix__tri-col, .os-ix__pages { padding-left: 16px; padding-right: 16px; }
  .os-ix__mast-l, .os-ix__mast-r, .os-ix__disc, .os-ix__pages { padding-top: 28px; padding-bottom: 28px; }
  .os-ix__tri-col { padding-top: 28px; padding-bottom: 28px; }
  .os-ix__tri-h2 { font-size: 22px; }
  .os-ix__h1     { font-size: 34px; }
  .os-ix__mast-deva { font-size: 28px; }
  .os-ix__h2     { font-size: 24px; }
  .os-ix__grid   { grid-template-columns: repeat(2, 1fr); }
  .os-ix__open   { margin-left: 0; }
}

.os-pr {
  max-width: var(--shell); margin: 0 auto;
  background: var(--bg-page); border: 1px solid var(--card-border); overflow: hidden;
}
@media (max-width: 1023px) { .os-pr { border-left: 0; border-right: 0; } }
.os-pr .os-crumb { padding: 22px 44px 0; margin: 0; }

.os-pr__mast   { display: grid; grid-template-columns: 1fr 400px;
                 border-bottom: 2px solid var(--rule-strong); }
.os-pr__mast-l { padding: 56px 44px 48px; }
.os-pr__mast-r { border-left: 1px solid var(--rule); background: var(--bg-surface);
                 padding: 44px; display: flex; flex-direction: column;
                 justify-content: center; }
.os-pr__deva   { font: 400 40px/1.2 var(--font-deva); color: var(--accent); margin-bottom: 8px; }
.os-pr__h1     { font: 300 58px/1 var(--font-display); color: var(--fg-primary);
                 letter-spacing: -.034em; margin: 0 0 20px; }
.os-pr__lede   { font: 400 19px/1.6 var(--font-ui); color: var(--fg-secondary);
                 margin: 0 0 14px; max-width: 54ch; text-wrap: pretty; }
.os-pr__terms  { font: 400 14px/1.6 var(--font-ui); color: var(--fg-label);
                 margin: 0 0 26px; max-width: 52ch; }
.os-pr__btns   { display: flex; gap: 12px; flex-wrap: wrap; }

.os-pr__stat-h { font: 600 11px/1 var(--font-ui); letter-spacing: .16em; color: var(--fg-label);
                 padding-bottom: 12px; border-bottom: 1px solid var(--rule); }
.os-pr__stat   { display: flex; justify-content: space-between; align-items: baseline;
                 padding: 11px 0; border-bottom: 1px solid var(--rule);
                 font: 400 15px/1.3 var(--font-ui); color: var(--fg-secondary); }
.os-pr__stat-n { font: 500 22px/1 var(--font-ui); color: var(--fg-primary);
                 font-variant-numeric: tabular-nums; }
.os-pr__stat-note { font: 400 13px/1.55 var(--font-ui); color: var(--fg-label); margin-top: 16px; }

/* The four doors. A jump bar, not a tab strip: every section below is rendered
   whatever you click, because a crawler has to reach all four and a reader
   scrolling past should meet all four. */
/* The cells carry 22px so the four sit evenly against each other, and the bar
   carries the other 22px so the FIRST door's text still starts on the page's one
   left edge at 44px and the last ends on it. Padding the cells to 44px instead
   would have spaced the middle two twice as far apart as the design allows. */
.os-pr__doors  { display: grid; grid-template-columns: repeat(4, 1fr); gap: 1px;
                 padding: 0 22px; background: var(--rule);
                 border-bottom: 1px solid var(--rule-strong); }
.os-pr__door   { display: flex; flex-direction: column; gap: 6px;
                 background: var(--bg-surface); padding: 24px 22px 26px;
                 text-decoration: none; }
.os-pr__door:hover { background: var(--bg-page); }
.os-pr__door-n { font: 400 20px/1 var(--font-deva); color: var(--accent); }
.os-pr__door-t { font: 500 21px/1.15 var(--font-ui); color: var(--fg-primary); }
.os-pr__door-l { font: 400 14px/1.45 var(--font-ui); color: var(--fg-secondary); }
.os-pr__door-c { font: 600 10px/1.5 var(--font-ui); letter-spacing: .1em;
                 text-transform: uppercase; color: var(--fg-label); margin-top: auto;
                 padding-top: 10px; }

/* The L1 section head: Devanagari numeral, title, 2px ink rule, right note. */
.os-pr__head  { display: flex; align-items: baseline; gap: 14px; flex-wrap: wrap;
                padding-bottom: 14px; border-bottom: 2px solid var(--rule-strong);
                margin-bottom: 22px; }
/* THE OFFSET BELONGS TO WHATEVER CARRIES THE ID. It was set on the head alone,
   and two of the ids are on the SECTION — #reports and #businesses — so those
   two jumped to a title sitting under the 63px sticky header while the other
   two cleared it by 27px. Every element that can be a jump target gets it. */
.os-pr__head, .os-pr__sec, .os-pr__resell { scroll-margin-top: 90px; }
.os-pr__num   { font: 400 26px/1 var(--font-deva); color: var(--accent); }
.os-pr__h2    { font: 400 32px/1.1 var(--font-display); color: var(--fg-primary);
                letter-spacing: -.018em; margin: 0; }
.os-pr__note  { margin-left: auto; font: 400 12px/1 var(--font-ui); letter-spacing: .1em;
                text-transform: uppercase; color: var(--fg-label); }
.os-pr__head--dark { border-bottom-color: var(--plate-gold); }
.os-pr__head--dark .os-pr__num { color: var(--plate-gold); }
.os-pr__head--dark .os-pr__h2  { color: var(--plate-fg); }
.os-pr__head--dark .os-pr__note { color: var(--plate-fg-2); }

.os-pr__sec        { padding: 52px 44px; border-bottom: 1px solid var(--rule); }
.os-pr__sec--panel { background: var(--bg-panel); }
.os-pr__sec--surface { background: var(--bg-surface); }
.os-pr__p          { font: 400 17px/1.6 var(--font-ui); color: var(--fg-secondary);
                     margin: 0 0 26px; max-width: 70ch; text-wrap: pretty; }
.os-pr__p strong   { color: var(--fg-primary); font-weight: 500; }
.os-pr__also       { font-size: 15px; margin-bottom: 0; }
.os-pr__also a     { color: var(--accent); }
.os-pr__bring      { font-size: 15px; color: var(--fg-label); }
.os-pr__link       { font: 400 15px/1 var(--font-ui); color: var(--accent); }

/* THE HAIRLINES COME FROM THE CELLS, NOT FROM A PAINTED CONTAINER.
   The usual trick — 1px grid gaps over a ruled background — draws the rule
   colour across whatever the last row does not fill, and none of these grids
   divides evenly: ten oracles in three columns leaves a two-cell hole, and the
   seeker door has exactly one app. Top and left come from the container, right
   and bottom from each cell, so an unfilled cell is simply absent. */
.os-pr__grid3  { display: grid; grid-template-columns: repeat(3, 1fr);
                 border-top: 1px solid var(--rule); border-left: 1px solid var(--rule);
                 margin-bottom: 26px; }
.os-pr__cell   { padding: 22px 20px 24px; display: flex; flex-direction: column;
                 border-right: 1px solid var(--rule); border-bottom: 1px solid var(--rule); }
.os-pr__name   { font: 500 21px/1.15 var(--font-ui); color: var(--fg-primary);
                 margin-bottom: 8px; display: block; }
.os-pr__desc   { font: 400 14px/1.5 var(--font-ui); color: var(--fg-secondary); display: block; }
.os-pr__needs  { font: 400 11px/1 var(--font-ui); letter-spacing: .08em;
                 color: var(--fg-label); margin-top: 14px; padding-top: 14px;
                 border-top: 1px solid var(--rule); }
.os-pr__cell .os-pr__needs { margin-top: auto; }

/* Reports: every live one, grouped, each an address. */
.os-pr__reports    { border-top: 1px solid var(--rule-strong); }
.os-pr__rgroup-h   { display: flex; justify-content: space-between; align-items: baseline;
                     font: 600 11px/1 var(--font-ui); letter-spacing: .14em;
                     text-transform: uppercase; color: var(--fg-label);
                     padding: 22px 0 10px; border-bottom: 1px solid var(--rule); }
.os-pr__rgroup-h span { font-variant-numeric: tabular-nums; }
.os-pr__rrow       { display: grid; grid-template-columns: 340px 1fr 170px; gap: 24px;
                     align-items: baseline; padding: 13px 0;
                     border-bottom: 1px solid var(--rule); }
.os-pr__rname      { font: 500 16px/1.3 var(--font-ui); color: var(--fg-primary); }
a.os-pr__rname:hover { color: var(--accent); }
.os-pr__rtag       { font: 400 14px/1.5 var(--font-ui); color: var(--fg-secondary); }
.os-pr__rsample    { font: 400 13px/1.4 var(--font-ui); color: var(--accent); text-align: right; }
.os-pr__rsample--none { color: var(--fg-label); }

/* The practitioner table wants density, so it carries a header row the seeker
   cards do not need. */
.os-pr__tools  { display: flex; flex-direction: column; border-top: 1px solid var(--rule-strong);
                 margin-bottom: 26px; }
.os-pr__tool   { display: grid; grid-template-columns: 260px 1fr 170px; gap: 28px;
                 align-items: baseline; padding: 16px 0; border-bottom: 1px solid var(--rule); }
.os-pr__tool--h { font: 600 10px/1 var(--font-ui); letter-spacing: .14em;
                  text-transform: uppercase; color: var(--fg-label); padding: 12px 0; }
.os-pr__tool:last-child { border-bottom: 0; }
.os-pr__tool-n { font: 500 18px/1.25 var(--font-ui); color: var(--fg-primary); }
.os-pr__tool-n a { color: inherit; }
.os-pr__tool-n a:hover { color: var(--accent); }
.os-pr__qual   { display: block; font: 600 10px/1 var(--font-ui); letter-spacing: .14em;
                 color: var(--accent); margin-top: 7px; }
.os-pr__tool-w { font: 400 15px/1.5 var(--font-ui); color: var(--fg-secondary); }
.os-pr__tool-o { font: 400 13px/1.5 var(--font-ui); color: var(--fg-label); }

.os-pr__dev    { margin-top: 26px; padding: 18px 20px; background: var(--bg-surface);
                 border-left: 2px solid var(--rule-strong); }
.os-pr__dev-h  { font: 600 10px/1 var(--font-ui); letter-spacing: .14em;
                 color: var(--fg-label); margin-bottom: 8px; }
.os-pr__dev-l  { font: 400 14px/1.6 var(--font-ui); color: var(--fg-secondary); }

.os-pr__figs   { display: flex; gap: 36px; flex-wrap: wrap; margin-bottom: 26px; }
.os-pr__fig    { font: 500 30px/1 var(--font-ui); color: var(--fg-primary);
                 font-variant-numeric: tabular-nums; }
.os-pr__fig-l  { font: 400 10px/1 var(--font-ui); letter-spacing: .14em;
                 color: var(--fg-label); margin-top: 7px; }

.os-pr__progs  { border-top: 1px solid var(--rule-strong); margin-bottom: 24px; }
.os-pr__prog   { display: grid; grid-template-columns: 1fr 220px 260px; gap: 24px;
                 align-items: baseline; padding: 14px 0; border-bottom: 1px solid var(--rule); }
.os-pr__prog-t { font: 500 18px/1.25 var(--font-ui); color: var(--fg-primary); }
a.os-pr__prog-t:hover { color: var(--accent); }
.os-pr__prog-n { font: 400 14px/1.4 var(--font-ui); color: var(--fg-secondary);
                 font-variant-numeric: tabular-nums; }
.os-pr__prog-l { font: 600 11px/1.4 var(--font-ui); letter-spacing: .06em;
                 text-transform: uppercase; color: var(--fg-label); text-align: right; }

/* Sized to what is actually there. The seeker door has one app and the learner
   door has two; a fixed three-column grid gave the first a third of the width
   and two thirds of nothing. --n is set inline from the count, capped at 3. */
.os-pr__apps   { display: grid; grid-template-columns: repeat(var(--n, 3), 1fr);
                 border-top: 1px solid var(--rule); border-left: 1px solid var(--rule); }
.os-pr__app    { padding: 22px 20px 24px; display: flex; flex-direction: column;
                 border-right: 1px solid var(--rule); border-bottom: 1px solid var(--rule); }
.os-pr__app-h  { display: flex; gap: 12px; align-items: center; margin-bottom: 12px; }
.os-pr__mono   { flex: 0 0 auto; width: 38px; height: 38px; display: grid; place-items: center;
                 background: var(--bg-surface); border: 1px solid var(--rule);
                 font: 600 12px/1 var(--font-ui); letter-spacing: .04em; color: var(--fg-label); }
.os-pr__app-h .os-pr__name { margin-bottom: 2px; font-size: 19px; }
.os-pr__aud    { font: 600 10px/1 var(--font-ui); letter-spacing: .14em; color: var(--accent); }
.os-pr__feat   { list-style: none; padding: 0; margin: 12px 0 0; }
.os-pr__feat li { font: 400 13px/1.5 var(--font-ui); color: var(--fg-secondary);
                  padding-left: 14px; position: relative; }
.os-pr__feat li::before { content: "—"; position: absolute; left: 0; color: var(--accent); }
.os-pr__badges { display: flex; gap: 10px; flex-wrap: wrap; margin-top: auto; padding-top: 16px; }
.os-pr__badge  { font: 600 10px/1 var(--font-ui); letter-spacing: .1em;
                 color: var(--fg-primary); border: 1px solid var(--rule-strong);
                 padding: 9px 11px; }
.os-pr__badge:hover { border-color: var(--accent); color: var(--accent); }

/* The largest single section on the page, and the headline ask. */
/* In LIGHT mode the fill alone carries this: near-black on cream is 17.6:1 and
   the plate is the strongest thing on the page. In DARK mode the same two
   tokens land 1.26:1 apart and the section carrying the commercial argument
   dissolves into the page it sits on. So the gold rules bound it top and
   bottom — 10.3:1 against the dark page, where the fill gives almost nothing —
   and the plate is unmistakably a plate in both themes without either one
   depending on the fill. */
.os-pr__resell   { background: var(--plate-bg); padding: 56px 44px;
                   border-top: 2px solid var(--plate-gold);
                   border-bottom: 2px solid var(--plate-gold); }
.os-pr__resell-top { display: grid; grid-template-columns: 1fr 380px; gap: 48px;
                     align-items: start; margin-bottom: 40px; }
.os-pr__resell-h { font: 300 44px/1.05 var(--font-display); color: var(--plate-fg);
                   letter-spacing: -.034em; margin: 22px 0 20px; max-width: 24ch;
                   text-wrap: pretty; }
.os-pr__resell-p { font: 400 18px/1.6 var(--font-ui); color: var(--plate-fg-2);
                   margin: 0 0 20px; max-width: 56ch; text-wrap: pretty; }
.os-pr__resell-link { display: inline-block; margin-top: 28px;
                      font: 400 15px/1 var(--font-ui); color: var(--plate-gold); }
.os-pr__econ     { border: 1px solid var(--plate-rule); padding: 26px 24px; }
.os-pr__econ-h   { font: 600 10px/1 var(--font-ui); letter-spacing: .16em;
                   color: var(--plate-gold); margin-bottom: 14px; }
.os-pr__econ-r   { display: flex; justify-content: space-between; gap: 14px; padding: 11px 0;
                   border-bottom: 1px solid var(--plate-rule);
                   font: 400 14px/1.4 var(--font-ui); color: var(--plate-fg-2); }
.os-pr__econ-r:last-child { border-bottom: 0; }
.os-pr__econ-r span:last-child { color: var(--plate-fg); text-align: right; }
.os-pr__econ-a   { color: var(--plate-accent) !important; }
.os-pr__pillars  { display: grid; grid-template-columns: repeat(3, 1fr); gap: 1px;
                   background: var(--plate-rule); border: 1px solid var(--plate-rule); }
.os-pr__pillar   { background: var(--plate-surface); padding: 28px 26px; }
.os-pr__pillar-n { font: 400 22px/1 var(--font-deva); color: var(--plate-gold); margin-bottom: 14px; }
.os-pr__pillar-t { font: 500 21px/1.2 var(--font-ui); color: var(--plate-fg); margin-bottom: 6px; }
.os-pr__pillar-s { font: 600 10px/1 var(--font-ui); letter-spacing: .14em;
                   color: var(--plate-accent); margin-bottom: 14px; }
.os-pr__pillar-r { display: grid; grid-template-columns: 16px 1fr; gap: 10px; align-items: baseline;
                   /* --plate-rule-soft, not --bg-surface-2. The two share a value
                      in DARK mode, which is why this looked right in every dark
                      screenshot; in light mode --bg-surface-2 is a pale cream and
                      ruled twelve visible lines across a near-black card. */
                   padding: 8px 0; border-bottom: 1px solid var(--plate-rule-soft);
                   font: 400 14px/1.5 var(--font-ui); color: var(--plate-fg-2); }
.os-pr__pillar-r:last-child { border-bottom: 0; }
.os-pr__pillar-r span:first-child { color: var(--plate-gold); }

.os-pr__next   { border-bottom: 0; }
.os-pr__next-h { font: 600 10px/1 var(--font-ui); letter-spacing: .16em;
                 color: var(--fg-label); margin-bottom: 12px; }
.os-pr__next .os-pr__p { margin-bottom: 0; }

/* THE COLLAPSE KEYS OFF THE CARD, NOT THE WINDOW.
   The shell is capped at 1180px, so between 1180 and 1279 the card was already
   at full width while the old rules had it drawing the tablet layout: identical
   content width, four columns become two, and the page grew 862px taller for
   one pixel of viewport. A maximized browser on a 1280 monitor reports about
   1265 and landed squarely in it. Nothing may collapse above the point where
   the card itself starts to narrow. */
@media (max-width: 1023px) {
  .os-pr__mast   { grid-template-columns: 1fr; }
  .os-pr__mast-r { border-left: 0; border-top: 1px solid var(--rule); }
  .os-pr__doors  { grid-template-columns: repeat(2, 1fr); }
  .os-pr__grid3, .os-pr__apps { grid-template-columns: repeat(2, 1fr); }
  .os-pr__resell-top { grid-template-columns: 1fr; gap: 28px; }
  .os-pr__pillars { grid-template-columns: 1fr; }
  .os-pr__rrow   { grid-template-columns: 1fr 150px; gap: 8px 20px; }
  .os-pr__rtag   { grid-column: 1 / -1; }
  .os-pr__prog   { grid-template-columns: 1fr 200px; gap: 6px 20px; }
  .os-pr__prog-l { grid-column: 1 / -1; text-align: left; }
  .os-pr__tool   { grid-template-columns: 1fr; gap: 8px; }
  .os-pr__tool--h { display: none; }
}
@media (max-width: 767px) {
  .os-pr .os-crumb, .os-pr__mast-l, .os-pr__mast-r, .os-pr__sec,
  .os-pr__resell { padding-left: 16px; padding-right: 16px; }
  .os-pr__mast-l, .os-pr__mast-r, .os-pr__sec, .os-pr__resell {
    padding-top: 28px; padding-bottom: 28px; }
  /* The bar's own 22px goes too, or it stacks with the cell's 16px and the
     door text lands at 38px on a phone where every other left edge is 16. */
  .os-pr__doors  { padding-left: 0; padding-right: 0; }
  .os-pr__door   { padding-left: 16px; padding-right: 16px; }
  .os-pr__h1   { font-size: 34px; }
  .os-pr__deva { font-size: 30px; }
  .os-pr__h2   { font-size: 23px; }
  .os-pr__resell-h { font-size: 30px; }
  .os-pr__doors, .os-pr__grid3, .os-pr__apps { grid-template-columns: 1fr; }
  .os-pr__rrow, .os-pr__prog { grid-template-columns: 1fr; }
  .os-pr__rsample { text-align: left; }
  .os-pr__note { margin-left: 0; }
  .os-pr__figs { gap: 24px; }
}

.os-ut {
  max-width: var(--shell); margin: 0 auto;
  background: var(--bg-page); border: 1px solid var(--card-border); overflow: hidden;
}
@media (max-width: 1023px) { .os-ut { border-left: 0; border-right: 0; } }
.os-ut .os-crumb { padding: 22px 44px 0; margin: 0; }

.os-ut__body   { padding: 52px 44px 44px; }
/* The search heading is the query, so it sets smaller than the 404's statement
   and carries its count in a quieter register — the sentence is "ketu — 56
   entries", not two headings fighting. */
.os-ut__h1--search { font-size: 34px; margin: 18px 0 10px; }
.os-ut__h1-n   { color: var(--fg-label); font-size: .62em; }
.os-ut__h1     { font: 300 46px/1.06 var(--font-display); color: var(--fg-primary);
                 letter-spacing: -.034em; margin: 0 0 16px; max-width: 26ch; text-wrap: pretty; }
.os-ut__lede   { font: 400 19px/1.6 var(--font-ui); color: var(--fg-secondary);
                 margin: 0 0 26px; max-width: 52ch; }
.os-ut__helper { font: 400 13px/1.6 var(--font-ui); color: var(--fg-label); margin-bottom: 40px; }

.os-ut__search { display: flex; border: 1px solid var(--fg-primary); background: var(--bg-input);
                 max-width: 460px; margin-bottom: 10px; }
.os-ut__search--wide { max-width: 620px; margin-bottom: 20px; }
.os-ut__search input {
  flex: 1; min-width: 0; padding: 16px 18px; border: 0; background: transparent;
  font: 400 17px/1.3 var(--font-ui); color: var(--fg-primary);
}
.os-ut__search--wide input { font-size: 19px; }
.os-ut__search input::placeholder { color: var(--fg-label); }
/* 5A: "Search field | border -> fg-primary | Focus: 2px accent inset border, no
   outline". The field already carries the ink border at rest — 6A draws it
   border:1px solid #221C15 — so the hover row has nowhere to travel; the focus row
   did, and the global :focus-visible outline was drawing a ring 2px OUTSIDE a box
   the design wants marked from within. Inset, so the field does not grow. */
.os-ut__search:focus-within { box-shadow: inset 0 0 0 2px var(--accent); }
.os-ut__search input:focus-visible, .os-ut__search button:focus-visible { outline: none; }
.os-ut__search button:focus-visible { box-shadow: inset 0 0 0 2px var(--fg-inverse); }
.os-ut__search button {
  background: var(--bg-inverse); color: var(--fg-inverse); border: 0; cursor: pointer;
  padding: 16px 22px; font: 500 12px/1.3 var(--font-ui); letter-spacing: .1em;
}

/* The Bhava Grid is the 404's second offer: a reader who mistyped an address
   still knows which discipline they wanted. */
.os-ut__gridh  { font: 600 11px/1 var(--font-ui); letter-spacing: .16em; color: var(--fg-label);
                 padding-bottom: 12px; border-bottom: 2px solid var(--rule-strong);
                 margin-bottom: 18px; }
.os-ut .os-bhava { grid-template-rows: repeat(4, 78px); }

/* ── 6B ─────────────────────────────────────────────────────────────────── */
.os-ut__searchhead { padding: 40px 44px 28px; border-bottom: 2px solid var(--rule-strong); }
.os-ut__count  { display: flex; align-items: baseline; gap: 14px; flex-wrap: wrap;
                 font: 400 15px/1.4 var(--font-ui); color: var(--fg-secondary); }
.os-ut__count strong { font-weight: 600; color: var(--fg-primary);
                       font-variant-numeric: tabular-nums; }
.os-ut__sep    { color: var(--fg-separator); }

.os-ut__two    { display: grid; grid-template-columns: 1fr 250px; }
.os-ut__results { padding: 36px 44px 48px; min-width: 0; }
.os-ut__group  { margin-bottom: 38px; }
.os-ut__group:last-child { margin-bottom: 0; }
.os-ut__group-h { display: flex; align-items: baseline; gap: 12px; flex-wrap: wrap;
                  padding-bottom: 11px; border-bottom: 2px solid var(--rule-strong);
                  margin-bottom: 4px; }
.os-ut__group-h h2 { font: 600 13px/1 var(--font-ui); letter-spacing: .14em;
                     text-transform: uppercase; color: var(--fg-primary); margin: 0; }
.os-ut__group-n { margin-left: auto; font: 400 12px/1 var(--font-ui); color: var(--fg-label); }
/* The two groups carry the L2 and L3 ornaments, so the ranking is legible
   without reading the headings. */
.os-ut__mark--strong { width: 8px; height: 8px; flex: 0 0 8px;
                       background: var(--accent); transform: rotate(45deg); }
.os-ut__mark--weak   { width: 6px; height: 6px; flex: 0 0 6px; background: var(--fg-label); }

.os-ut__rows   { display: flex; flex-direction: column; }
.os-ut__row    { display: grid; grid-template-columns: 1fr 150px; gap: 22px;
                 align-items: baseline; padding: 15px 0;
                 border-bottom: 1px solid var(--rule-soft); }
.os-ut__row:hover { background: var(--bg-row-tint); text-decoration: none; }
.os-ut__row-t  { font: 400 21px/1.3 var(--font-display); color: var(--fg-primary);
                 text-wrap: pretty; }
.os-ut__row-s  { font: 400 15px/1.55 var(--font-ui); color: var(--fg-secondary); margin-top: 6px; }
/* Two lines, both 11px in label grey — 6B sets them identically and separates them
   only by what they say. The second was 12px, which made the read time read as the
   more important of the two. */
.os-ut__row-m  { text-align: right; }
.os-ut__row-d,
.os-ut__row-c  { font: 400 11px/1.5 var(--font-ui); color: var(--fg-label); letter-spacing: .06em;
                 text-transform: uppercase; }
.os-ut__row-c  { font-variant-numeric: tabular-nums; }
/* The highlight is the ::selection value, so a mark reads as "this is what you
   asked for" rather than as an unrelated colour. */
.os-ut__row mark { background: #F0DCA8; color: var(--fg-primary); }
[data-theme="dark"] .os-ut__row mark { background: #4A3A1E; color: var(--fg-primary); }

.os-ut__facets   { border-left: 1px solid var(--rule); background: var(--bg-panel);
                   padding: 36px 22px; }
.os-ut__facets-h { font: 600 11px/1 var(--font-ui); letter-spacing: .16em; color: var(--fg-label);
                   padding-bottom: 11px; border-bottom: 1px solid var(--rule); margin-bottom: 4px; }
.os-ut__facet    { display: flex; justify-content: space-between; gap: var(--s3); padding: 9px 0;
                   border-bottom: 1px solid var(--rule-soft);
                   font: 500 14px/1.3 var(--font-ui); color: var(--fg-primary); }
.os-ut__facet:hover { color: var(--accent); text-decoration: none; }
.os-ut__facet--on   { color: var(--accent); }
.os-ut__facet-n  { color: var(--fg-label); font-variant-numeric: tabular-nums; font-weight: 400; }

/* THE RAIL IS THREE BLOCKS. It held only the facets, so the two things 6B puts
   under them — how to search, and where to go when searching is not the answer —
   were not on the page at all. They share one panel ground and one left rule; the
   inner blocks divide with a hairline rather than repeating the border. */
.os-ut__rail     { border-left: 1px solid var(--rule); background: var(--bg-panel);
                   min-width: 0; }
.os-ut__facets, .os-ut__tips { border-left: 0; background: none; padding: 36px 22px; }
.os-ut__tips     { border-top: 1px solid var(--rule); padding-top: 30px; }
.os-ut__tip      { display: flex; flex-direction: column; gap: 3px; padding: 9px 0;
                   border-bottom: 1px solid var(--rule-soft); }
.os-ut__tip code { font: 500 12.5px/1.4 var(--font-ui); color: var(--fg-primary);
                   background: var(--bg-surface-2); padding: 3px 6px; align-self: flex-start; }
.os-ut__tip span { font: 400 13px/1.5 var(--font-ui); color: var(--fg-secondary); }
.os-ut__tip-p    { font: 400 12px/1.55 var(--font-ui); color: var(--fg-label);
                   margin: 14px 0 0; }
/* Section 15: every crossing to myday is an ink plate, never an inline link. */
.os-ut__ask      { margin: 0 22px 36px; }
.os-ut__ask-h    { font: 600 10px/1 var(--font-ui); letter-spacing: .16em;
                   color: var(--plate-gold); margin-bottom: 10px; }
.os-ut__ask-p    { font: 400 14px/1.5 var(--font-ui); color: var(--plate-fg-2); margin: 0 0 16px; }
/* The escape at the foot of a truncated group. */
.os-ut__more     { display: inline-block; margin-top: 14px;
                   font: 500 12px/1 var(--font-ui); letter-spacing: .1em; color: var(--accent); }
.os-ut__hint     { color: var(--fg-label); }

/* The empty state's generated subsets. Each is a chip carrying its own count, so
   the row reads as "here is what dropping a word would get you" rather than as a
   list of guesses. There is no spell-correction behind any of them and none is
   presented as one. */
.os-ut__sugg     { display: flex; flex-wrap: wrap; gap: 10px; margin-bottom: 26px; }
.os-ut__sugg-x   { display: inline-flex; align-items: baseline; gap: 10px;
                   border: 1px solid var(--rule); background: var(--bg-page);
                   padding: 11px 14px; color: var(--fg-primary); }
.os-ut__sugg-x:hover { border-color: var(--fg-primary); text-decoration: none; }
.os-ut__sugg-x:active { background: var(--bg-surface-2); }
.os-ut__sugg-t   { font: 400 16px/1.2 var(--font-ui); }
.os-ut__sugg-n   { font: 400 13px/1.2 var(--font-ui); color: var(--fg-label);
                   font-variant-numeric: tabular-nums; }
.os-ut__sugg-all { display: inline-block; font: 500 12px/1 var(--font-ui);
                   letter-spacing: .1em; color: var(--accent); }

@media (max-width: 1023px) {
  .os-ut__two    { grid-template-columns: 1fr; }
  .os-ut__rail   { border-left: 0; border-top: 1px solid var(--rule); }
}
@media (max-width: 767px) {
  .os-ut .os-crumb, .os-ut__body, .os-ut__searchhead,
  .os-ut__results, .os-ut__facets, .os-ut__tips { padding-left: 16px; padding-right: 16px; }
  .os-ut__ask { margin-left: 16px; margin-right: 16px; }
  .os-ut__body, .os-ut__searchhead, .os-ut__results { padding-top: 28px; padding-bottom: 28px; }
  .os-ut__h1  { font-size: 32px; }
  .os-ut__row { grid-template-columns: 1fr; gap: 6px; }
  .os-ut__row-m { text-align: left; }
}

.os-cs {
  max-width: var(--shell); margin: 0 auto;
  background: var(--bg-page); border: 1px solid var(--card-border); overflow: hidden;
}

/* ── masthead ─────────────────────────────────────────────────────────────── */
/* 44px at the sides — the system's one card inset, which the breadcrumb
   above and every other template already use. This said 40, so the h1 and the
   prose beneath it started four pixels left of the breadcrumb over them. */
.os-cs__mast { padding: 44px 44px 40px; border-bottom: 1px solid var(--rule); }
.os-cs__deva { font: 400 26px/1 var(--font-deva); color: var(--accent); margin-bottom: 14px; }
.os-cs__h1 { font: 300 52px/1.04 var(--font-display); margin: 0 0 18px;
  letter-spacing: -.02em; color: var(--fg-primary); }
.os-cs__lede { font: 300 19px/1.6 var(--font-display); color: var(--fg-secondary);
  max-width: 62ch; margin: 0 0 26px; text-wrap: pretty; }
.os-cs__btns { display: flex; flex-wrap: wrap; gap: 12px; }

/* ── section heads ────────────────────────────────────────────────────────── */
/* 44px at the sides — the card inset the masthead above and the breadcrumb
   above that both use. At 40 the section's prose started four pixels left of
   the title it sat under. The vertical stays 40: that is the section's own
   rhythm, and only the horizontal has to agree with the page. */
.os-cs__sec { padding: 40px 44px; border-bottom: 1px solid var(--rule); }
.os-cs__sec--panel { background: var(--bg-surface); }
.os-cs__head { display: flex; align-items: baseline; gap: 12px; margin-bottom: 26px;
  padding-bottom: 12px; border-bottom: 1px solid var(--rule); }
.os-cs__head-n { font: 400 18px/1 var(--font-deva); color: var(--accent); flex: 0 0 auto; }
.os-cs__head-t { font: 400 26px/1.2 var(--font-display); margin: 0; color: var(--fg-primary); }
.os-cs__head-e { font: 500 11px/1.4 var(--font-ui); letter-spacing: .1em;
  text-transform: uppercase; color: var(--fg-label); margin-left: auto; text-align: right; }

/* ── the four-up chooser ──────────────────────────────────────────────────── */
.os-cs__people { display: grid; grid-template-columns: repeat(4, 1fr); gap: 28px; }
/* The portraits are square head-and-shoulders shots dropped into the component's
   fixed 4:5 crop, so a centred crop cuts the forehead. Biasing the focal point
   upward is a property of portrait photography, not of this page. */
.os-cs__people .osc-pract__shot--photo img { object-position: center 22%; }

/* ── profiles ─────────────────────────────────────────────────────────────── */
.os-cs__prof { display: grid; grid-template-columns: minmax(0, 1fr) 300px; gap: 40px;
  padding: 32px 0; border-top: 1px solid var(--rule); }
.os-cs__prof:first-of-type { border-top: 0; padding-top: 0; }
/* min-width:0 is load-bearing, not decoration: a grid child defaults to
   min-width:auto, so one long unbroken string in a bio widens the track and
   pushes the fee column off the page. align-self keeps the right column from
   stretching to the prose height. */
.os-cs__prof-l { min-width: 0; }
.os-cs__prof-r-col { min-width: 0; align-self: start; }
.os-cs__prof-n { font: 400 30px/1.2 var(--font-display); margin: 0 0 8px;
  color: var(--fg-primary); scroll-margin-top: 90px; }
.os-cs__prof-role { font: 500 12px/1.6 var(--font-ui); letter-spacing: .04em;
  color: var(--fg-label); margin-bottom: 18px; }
.os-cs__p { font: 300 16.5px/1.68 var(--font-display); color: var(--fg-secondary);
  margin: 0 0 16px; max-width: 68ch; text-wrap: pretty; }
.os-cs__p:last-child { margin-bottom: 0; }
.os-cs__p--lead { max-width: 62ch; margin-bottom: 24px; }

.os-cs__inc { background: var(--bg-page); border: 1px solid var(--rule);
  padding: 20px 22px; margin-bottom: 18px; }
.os-cs__inc-h, .os-cs__fee-h { font: 500 10.5px/1 var(--font-ui); letter-spacing: .12em;
  text-transform: uppercase; color: var(--fg-label); margin-bottom: 14px; }
.os-cs__inc-l { list-style: none; margin: 0; padding: 0; }
.os-cs__inc-i { display: flex; gap: 10px; font: 300 14.5px/1.55 var(--font-display);
  color: var(--fg-secondary); margin-bottom: 10px; }
.os-cs__inc-i:last-child { margin-bottom: 0; }
.os-cs__tick { flex: 0 0 auto; color: var(--accent); font: 400 13px/1.6 var(--font-ui); }

.os-cs__fee { background: var(--bg-page); border: 1px solid var(--rule); padding: 20px 22px; }
.os-cs__fee-n { font: 300 34px/1 var(--font-display); color: var(--fg-primary); }
.os-cs__fee-a { font: 400 13px/1.5 var(--font-ui); color: var(--fg-label); margin-top: 6px; }
.os-cs__fee-note { font: 400 12.5px/1.6 var(--font-ui); color: var(--fg-label);
  margin-top: 14px; padding-top: 14px; border-top: 1px solid var(--rule); }
/* Only the placement is set here; .os-btn--ghost carries the rest, so the four
   cards cannot drift away from every other button on the site. The label runs
   long — "Request a session with Dr. Rai →" — so it takes the full width of the
   panel rather than sitting in a ragged half of it, and its leading survives the
   wrap that gives it at the phone's measure. */
.os-cs__fee-cta { display: block; text-align: center; margin-top: 18px;
  line-height: 1.35; padding: 12px 14px; }

/* ── the decision table ───────────────────────────────────────────────────── */
.os-cs__two { display: grid; grid-template-columns: 1fr 1fr; gap: 0;
  border: 1px solid var(--rule); }
.os-cs__col { padding: 26px 28px; }
.os-cs__col--alt { background: var(--bg-surface); border-left: 1px solid var(--rule); }
.os-cs__col-h { font: 400 20px/1.3 var(--font-display); color: var(--fg-primary);
  margin-bottom: 18px; padding-bottom: 12px; border-bottom: 1px solid var(--rule); }
.os-cs__col-l { list-style: none; margin: 0; padding: 0; }
.os-cs__col-i { display: flex; gap: 10px; font: 300 15px/1.6 var(--font-display);
  color: var(--fg-secondary); margin-bottom: 12px; }
.os-cs__col-i:last-child { margin-bottom: 0; }
.os-cs__arrow { flex: 0 0 auto; color: var(--fg-label); font: 400 13px/1.7 var(--font-ui); }

/* ── the plate ────────────────────────────────────────────────────────────── */
.os-cs__plate { margin: 40px; }

/* ── the form ─────────────────────────────────────────────────────────────── */
.os-cs__sec--form { border-bottom: 0; }
.os-cs__form { display: grid; grid-template-columns: 1fr 1fr; gap: 20px 24px;
  max-width: 760px; }
.os-cs__f { display: flex; flex-direction: column; min-width: 0; }
.os-cs__f--wide { grid-column: 1 / -1; }
.os-cs__lab { font: 500 11.5px/1.4 var(--font-ui); letter-spacing: .06em;
  text-transform: uppercase; color: var(--fg-label); margin-bottom: 8px; }
.os-cs__req { color: var(--accent); }
.os-cs__hint { text-transform: none; letter-spacing: 0; font-weight: 400; }
/* box-sizing is load-bearing here, not tidiness. A form control does not
   inherit the page's box model, so width:100% plus 26px of padding and 2px of
   border overflowed its column by 22px — measured at 390px, where the input ran
   past the right edge of the document and made the whole page scroll sideways. */
.os-cs__in { box-sizing: border-box; font: 400 15px/1.5 var(--font-ui);
  color: var(--fg-primary); background: var(--bg-page); border: 1px solid var(--rule);
  border-radius: 0; padding: 11px 13px; width: 100%; max-width: 100%; }
.os-cs__in:focus-visible { outline: 2px solid var(--accent); outline-offset: -1px;
  border-color: var(--accent); }
/* A select draws its own arrow inside the padding box, and at 13px of right
   padding a long option runs under it. Firefox in particular does not reserve
   the space itself. */
.os-cs__in--sel { padding-right: 32px; }
.os-cs__ta { resize: vertical; min-height: 130px; font-family: var(--font-display);
  font-weight: 300; font-size: 16px; line-height: 1.6; }
.os-cs__submit { flex-direction: row; align-items: center; flex-wrap: wrap; gap: 16px;
  margin-top: 4px; }
.os-cs__note { font: 400 13px/1.6 var(--font-ui); color: var(--fg-label); }
.os-cs__err { background: var(--bg-surface); border-left: 3px solid var(--accent);
  padding: 14px 18px; margin-bottom: 22px; max-width: 760px;
  font: 400 14.5px/1.6 var(--font-ui); color: var(--fg-primary); }
.os-cs__sent { background: var(--bg-surface); border-left: 3px solid var(--accent);
  padding: 24px 26px; max-width: 760px; }
.os-cs__sent-h { font: 400 24px/1.3 var(--font-display); color: var(--fg-primary);
  margin-bottom: 12px; }
/* Off-screen, not display:none — see the comment at the construction site. */
.os-cs__pot { position: absolute; left: -9999px; width: 1px; height: 1px;
  overflow: hidden; }

/* ── S3 UTILITY — one column, no rails, no chrome beside it ───────────────── */
/* THE CARD IS --shell, LIKE EVERY OTHER TEMPLATE. This was 720px, so the page
   drew an 800px frame at 300..1100 while the other fifteen drew 1182 at
   109..1291 — the frame jumped inward by 191px on the way in and back out on
   the way out. One column is a property of what is INSIDE the card, not of the
   card: the form column stays 720px, capped on .os-ut2__in, and the card it
   sits on is the same card as everywhere else. */
.os-ut2      { max-width: var(--shell); margin: 0 auto;
               background: var(--bg-page); border: 1px solid var(--card-border); }
.os-ut2__in  { max-width: 720px; margin: 0 auto; padding: 44px 44px 56px; }
@media (max-width: 1023px) { .os-ut2 { border-left: 0; border-right: 0; } }
.os-ut2__head { border-bottom: 1px solid var(--rule); padding-bottom: 24px; margin-bottom: 26px; }
.os-ut2__h1  { font: 300 44px/1.06 var(--font-display); margin: 0 0 16px;
  letter-spacing: -.02em; color: var(--fg-primary); text-wrap: pretty; }
.os-ut2__lede { font: 300 18px/1.6 var(--font-display); color: var(--fg-secondary);
  margin: 0; text-wrap: pretty; }
.os-ut2__form .os-cs__form { grid-template-columns: 1fr 1fr; max-width: none; }

/* WHERE TO GO WHILE YOU WAIT. Six doors on one row at full width, two on a
   phone. Deliberately quieter than the confirmation above it — the message
   going is the news; this is the way out. */
.os-ut2__onward { margin-top: 34px; padding-top: 26px; border-top: 1px solid var(--rule); }
.os-ut2__onward-h { font: 500 10.5px/1 var(--font-ui); letter-spacing: .12em;
  text-transform: uppercase; color: var(--fg-label); margin-bottom: 16px; }
.os-ut2__onward-g { display: grid; grid-template-columns: repeat(3, 1fr); gap: 1px;
  background: var(--rule); border: 1px solid var(--rule); }
.os-ut2__door { display: block; background: var(--bg-page); padding: 16px 18px;
  text-decoration: none; min-width: 0; }
.os-ut2__door:hover { background: var(--bg-surface); }
.os-ut2__door-n { display: block; font: 300 26px/1 var(--font-display); color: var(--accent); }
.os-ut2__door-l { display: block; margin-top: 6px; font: 400 13.5px/1.35 var(--font-ui);
  color: var(--fg-primary); overflow-wrap: break-word; }

/* The course panel that used to sit below the form is gone with its
   markup — see the note at contactPage. */
@media (max-width: 1279px) {
  .os-cs__people { grid-template-columns: repeat(2, 1fr); gap: 24px; }
  .os-cs__prof { grid-template-columns: minmax(0, 1fr); gap: 24px; }
}
@media (max-width: 1023px) {
  /* THE INSET IS THE COLUMN'S, NOT THE CARD'S. The 32px 22px 40px below was
     set on .os-ut2 while .os-ut2__in kept its 44 — so below 1024 the two
     stacked and the form sat 66px inside the card, against 44 on every page
     the reader had just come from, and stayed at 66 on a phone where the
     system's edge is 16. One left edge, on the card, at every width. */
  .os-ut2__in { padding: 32px 22px 40px; }
  .os-ut2__h1 { font-size: 34px; }
  .os-ut2__form .os-cs__form { grid-template-columns: 1fr; }
  .os-ut2__onward-g { grid-template-columns: repeat(2, 1fr); }
  .os-cs__mast { padding: 32px 22px 28px; }
  .os-cs__h1 { font-size: 38px; }
  .os-cs__lede { font-size: 17px; }
  .os-cs__sec { padding: 28px 22px; }
  .os-cs__plate { margin: 28px 22px; }
  .os-cs__two { grid-template-columns: 1fr; }
  .os-cs__col--alt { border-left: 0; border-top: 1px solid var(--rule); }
  .os-cs__form { grid-template-columns: 1fr; }
  .os-cs__f--half { grid-column: 1 / -1; }
}
@media (max-width: 767px) {
  .os-cs__people { grid-template-columns: 1fr; }
  .os-cs__h1 { font-size: 32px; }
  /* 16px is the system's phone edge — the same one .os-crumb drops to, so the
     breadcrumb and the h1 under it start at the same x. */
  .os-ut2__in { padding: 24px 16px 36px; }
  .os-ut2__h1 { font-size: 30px; }
}

.os-pg {
  max-width: var(--shell); margin: 0 auto;
  background: var(--bg-page); border: 1px solid var(--card-border); overflow: hidden;
}

/* ── masthead ─────────────────────────────────────────────────────────────── */
/* 44px at the sides — the system's one card inset, which the breadcrumb
   above and every other template already use. This said 40, so the h1 and the
   prose beneath it started four pixels left of the breadcrumb over them. */
.os-pg__mast { padding: 44px 44px 36px; border-bottom: 1px solid var(--rule); }
.os-pg__deva { font: 400 26px/1 var(--font-deva); color: var(--accent); margin-bottom: 14px; }
.os-pg__h1   { font: 300 50px/1.05 var(--font-display); margin: 0 0 20px;
  letter-spacing: -.02em; color: var(--fg-primary); text-wrap: pretty; }
.os-pg__intro { max-width: 72ch; margin-bottom: 26px; }
.os-pg__stats { display: flex; gap: 34px; padding-top: 20px;
  border-top: 1px solid var(--rule); }
.os-pg__stat  { font: 500 11px/1 var(--font-ui); letter-spacing: .12em;
  color: var(--fg-label); display: flex; align-items: baseline; gap: 9px; }
.os-pg__stat b { font: 300 30px/1 var(--font-display); color: var(--accent);
  letter-spacing: 0; }

/* ── the browse grid — 3C's, and the page's only navigation ───────────────── */
.os-pg__browse   { padding: 32px 44px 36px; background: var(--bg-surface);
  border-bottom: 1px solid var(--rule); }
.os-pg__browse-h { display: flex; align-items: baseline; justify-content: space-between;
  gap: 20px; margin-bottom: 18px; }
.os-pg__browse-l { font: 600 11px/1 var(--font-ui); letter-spacing: .16em;
  color: var(--fg-primary); }
.os-pg__browse-n { font: 400 10.5px/1 var(--font-ui); letter-spacing: .1em;
  color: var(--fg-label); }
.os-pg__grid { display: grid; grid-template-columns: repeat(5, 1fr); gap: 1px;
  background: var(--rule); border: 1px solid var(--rule); }
.os-pg__cell { display: flex; flex-direction: column; gap: 4px; padding: 14px 16px;
  background: var(--bg-page); text-decoration: none; color: inherit; min-width: 0; }
.os-pg__cell:hover { background: var(--bg-row-tint); text-decoration: none; }
.os-pg__cell:hover .os-pg__cell-t { color: var(--accent); }
.os-pg__cell-n { font: 400 12px/1 var(--font-deva); color: var(--accent); }
.os-pg__cell-t { font: 400 14.5px/1.35 var(--font-display); color: var(--fg-primary);
  text-wrap: pretty; }
.os-pg__cell-c { font: 400 12px/1 var(--font-ui); color: var(--fg-label);
  font-variant-numeric: tabular-nums; margin-top: auto; padding-top: 6px; }
/* THE CURRENT CELL — marked by an EDGE, not by a ground.
   This first used the ink ground with gold text, which is right in light mode
   and fails in dark. The text stays legible either way (gold on the dark-mode
   inverse is 6.98:1, the exact pairing the token table certifies), but the
   MARKING disappears: bg-inverse against bg-page is 15.8:1 in light and 1.49:1
   in dark, and the dark inverse (#3A3126) is the same value as --rule, so the
   cell reads as a smudge on the grid lines around it.
   So it takes the marking the system already uses for "the row you are on" —
   .os-row[aria-current]'s 3px accent edge — which does not depend on ground
   contrast and therefore cannot collapse in one theme. */
.os-pg__cell--on { background: var(--bg-surface-2); box-shadow: inset 3px 0 0 var(--accent); }
.os-pg__cell--on .os-pg__cell-t { color: var(--accent); }
.os-pg__cell--on:hover { background: var(--bg-surface-2); }
.os-pg__cell--on:hover .os-pg__cell-t { color: var(--accent); }
.os-pg__cell--all { justify-content: center; }
.os-pg__cell--all .os-pg__cell-t { font-size: 13px; letter-spacing: .04em; }

/* ── the filter banner ────────────────────────────────────────────────────── */
.os-pg__filter   { display: flex; align-items: baseline; gap: 16px; flex-wrap: wrap;
  padding: 16px 44px; border-bottom: 1px solid var(--rule); background: var(--bg-page); }
.os-pg__filter-l { font: 600 10.5px/1 var(--font-ui); letter-spacing: .16em;
  color: var(--fg-label); }
.os-pg__filter-t { font: 400 17px/1.3 var(--font-display); color: var(--fg-primary); }
.os-pg__filter-x { margin-left: auto; font: 500 11px/1 var(--font-ui); letter-spacing: .1em; }

/* ── section blocks ───────────────────────────────────────────────────────── */
.os-pg__sec   { padding: 36px 44px; border-bottom: 1px solid var(--rule); }
.os-pg__sec:last-child { border-bottom: 0; }
.os-pg__sec-h { display: flex; align-items: baseline; gap: 12px; padding-bottom: 12px;
  border-bottom: 2px solid var(--rule-strong); }
.os-pg__sec-i { font-size: 17px; line-height: 1; flex: 0 0 auto; }
.os-pg__sec-t { font: 400 27px/1.2 var(--font-display); margin: 0; color: var(--fg-primary);
  scroll-margin-top: 90px; }
.os-pg__sec-c { margin-left: auto; font: 500 11px/1 var(--font-ui); letter-spacing: .12em;
  color: var(--fg-label); font-variant-numeric: tabular-nums; }
.os-pg__sec-s { font: 300 16px/1.6 var(--font-display); color: var(--fg-secondary);
  margin: 14px 0 0; max-width: 68ch; }

/* ── one report ───────────────────────────────────────────────────────────── */
.os-pg__reps { margin-top: 20px; }
.os-pg__rep  { border-bottom: 1px solid var(--rule-soft); scroll-margin-top: 90px; }
.os-pg__rep:last-child { border-bottom: 0; }
.os-pg__sum  { display: grid; grid-template-columns: minmax(0, 1fr) auto 22px;
  align-items: baseline; gap: 6px 18px; padding: 16px 0; cursor: pointer;
  list-style: none; }
/* Safari draws its own triangle through ::-webkit-details-marker, which would
   sit beside ours. */
.os-pg__sum::-webkit-details-marker { display: none; }
.os-pg__sum:hover .os-pg__rep-t { color: var(--accent); }
.os-pg__sum:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.os-pg__rep-t { grid-column: 1; font: 400 19px/1.35 var(--font-display);
  color: var(--fg-primary); text-wrap: pretty; }
.os-pg__badge { display: inline-block; margin-left: 10px; padding: 3px 8px;
  font: 500 10px/1.2 var(--font-ui); letter-spacing: .08em; vertical-align: middle;
  color: var(--fg-label); background: var(--bg-surface-2); }
.os-pg__meta  { grid-column: 2; font: 400 11.5px/1.5 var(--font-ui); color: var(--fg-label);
  font-variant-numeric: tabular-nums; text-align: right; white-space: nowrap; }
/* Padding, not a space either side of the character: white-space is nowrap on
   this line, so a literal space would be a break opportunity the line cannot
   take, and the separator would sit tight against both fields. */
.os-pg__sep   { color: var(--fg-separator); padding: 0 7px; }
.os-pg__tags  { grid-column: 1; display: flex; flex-wrap: wrap; gap: 6px; }
.os-pg__tag   { font: 400 10.5px/1 var(--font-ui); letter-spacing: .06em;
  text-transform: uppercase; color: var(--fg-label);
  border: 1px solid var(--rule); padding: 4px 7px; }
.os-pg__chev  { grid-column: 3; grid-row: 1; justify-self: end; font: 400 12px/1 var(--font-ui);
  color: var(--fg-label); transition: transform .12s ease; }
.os-pg__rep[open] > .os-pg__sum .os-pg__chev { transform: rotate(180deg); }
.os-pg__rep[open] > .os-pg__sum .os-pg__rep-t { color: var(--accent); }
.os-pg__body  { padding: 4px 0 30px; border-left: 2px solid var(--rule);
  padding-left: 22px; margin-bottom: 8px; }
.os-pg__perma { display: inline-block; font: 400 11px/1 var(--font-ui); letter-spacing: .08em;
  color: var(--fg-label); margin-bottom: 16px; }
.os-pg__perma:hover { color: var(--accent); }

/* The stored bodies carry the author's own waqt-* class names, which no
   stylesheet on this site defines. They are left in place — rewriting a
   stored body to match a template is how a migration loses fidelity — and
   given the element defaults the rest of the prose already has. */
.os-pg__body .waqt-blank { height: 12px; }

@media (max-width: 1279px) {
  .os-pg__grid { grid-template-columns: repeat(3, 1fr); }
}
@media (max-width: 1023px) {
  .os-pg__mast, .os-pg__browse, .os-pg__sec { padding-left: 22px; padding-right: 22px; }
  .os-pg__filter { padding-left: 22px; padding-right: 22px; }
  .os-pg__h1 { font-size: 38px; }
  .os-pg__grid { grid-template-columns: repeat(2, 1fr); }
  .os-pg__sum { grid-template-columns: minmax(0, 1fr) 22px; }
  .os-pg__meta { grid-column: 1; text-align: left; white-space: normal; }
  .os-pg__chev { grid-column: 2; }
}
@media (max-width: 767px) {
  .os-pg__mast, .os-pg__browse, .os-pg__sec { padding-left: 16px; padding-right: 16px; }
  .os-pg__filter { padding-left: 16px; padding-right: 16px; }
  .os-pg__h1 { font-size: 31px; }
  .os-pg__grid { grid-template-columns: 1fr; }
  .os-pg__stats { gap: 22px; }
  .os-pg__body { padding-left: 14px; }
}

.os-ln {
  max-width: var(--shell); margin: 0 auto;
  background: var(--bg-page); border: 1px solid var(--card-border); overflow: hidden;
}

/* ── masthead ─────────────────────────────────────────────────────────────── */
/* 44px at the sides — the system's one card inset, which the breadcrumb
   above and every other template already use. This said 40, so the h1 and the
   prose beneath it started four pixels left of the breadcrumb over them. */
.os-ln__mast { padding: 44px 44px 38px; border-bottom: 1px solid var(--rule); }
.os-ln__eyebrow { font: 400 12px/1 var(--font-ui); letter-spacing: .16em;
  color: var(--accent); margin-bottom: 20px; }
.os-ln__h1 { font: 300 48px/1.08 var(--font-display); margin: 0 0 20px;
  letter-spacing: -.02em; color: var(--fg-primary); }
.os-ln__lede { font: 300 19px/1.6 var(--font-display); color: var(--fg-secondary);
  max-width: 64ch; margin: 0 0 26px; text-wrap: pretty; }
.os-ln__btns { display: flex; align-items: center; flex-wrap: wrap; gap: 16px; }
.os-ln__note { font: 400 13.5px/1.6 var(--font-ui); color: var(--fg-label); }

/* ── sections ─────────────────────────────────────────────────────────────── */
/* 44px at the sides — the card inset the masthead above and the breadcrumb
   above that both use. At 40 the section's prose started four pixels left of
   the title it sat under. The vertical stays 40: that is the section's own
   rhythm, and only the horizontal has to agree with the page. */
.os-ln__sec { padding: 40px 44px; border-bottom: 1px solid var(--rule); }
.os-ln__sec--panel { background: var(--bg-surface); }
.os-ln__head { display: flex; align-items: baseline; gap: 14px; margin-bottom: 26px;
  padding-bottom: 12px; border-bottom: 2px solid var(--rule-strong); }
.os-ln__head-t { font: 400 26px/1.2 var(--font-display); margin: 0; color: var(--fg-primary); }
.os-ln__head-e { margin-left: auto; font: 500 11px/1 var(--font-ui); letter-spacing: .12em;
  color: var(--fg-label); }
.os-ln__p { font: 300 16.5px/1.68 var(--font-display); color: var(--fg-secondary);
  margin: 0 0 16px; max-width: 68ch; text-wrap: pretty; }

/* ── how it works — the four-across row the inline CSS used to draw ───────── */
.os-ln__steps { display: grid; grid-template-columns: repeat(4, 1fr); gap: 1px;
  background: var(--rule); border: 1px solid var(--rule); }
.os-ln__step { background: var(--bg-page); padding: 22px 20px 24px; }
.os-ln__step-n { display: block; font: 400 16px/1 var(--font-deva); color: var(--accent);
  margin-bottom: 12px; }
.os-ln__step-t { font: 400 20px/1.2 var(--font-display); color: var(--fg-primary);
  margin-bottom: 10px; }
.os-ln__step-d { font: 300 14.5px/1.6 var(--font-display); color: var(--fg-secondary);
  margin: 0; text-wrap: pretty; }

/* ── one programme ────────────────────────────────────────────────────────── */
.os-ln__prog { display: grid; grid-template-columns: minmax(0, 1fr) 260px; gap: 40px;
  padding: 30px 0; border-top: 1px solid var(--rule); }
.os-ln__prog:first-of-type { border-top: 0; padding-top: 0; }
.os-ln__prog-l { min-width: 0; }
.os-ln__prog-e { font: 500 11px/1 var(--font-ui); letter-spacing: .12em;
  text-transform: uppercase; color: var(--fg-label); margin-bottom: 12px; }
.os-ln__prog-n { font: 400 30px/1.2 var(--font-display); margin: 0 0 6px;
  color: var(--fg-primary); }
.os-ln__prog-tier { font: 400 13px/1.5 var(--font-ui); color: var(--accent);
  margin-bottom: 16px; }
.os-ln__ladder { font: 400 14px/1.65 var(--font-ui); color: var(--fg-secondary);
  margin: 0 0 20px; max-width: 68ch; }
.os-ln__ladder span { color: var(--fg-label); letter-spacing: .04em; }
.os-ln__prog-btns { display: flex; flex-wrap: wrap; gap: 12px; }
/* The 2x2 stat block. It was a flex row of four; at 260px a row of four numbers
   sets them at 11px, which is not a figure a reader can take in. */
.os-ln__prog-r { display: grid; grid-template-columns: 1fr 1fr; gap: 1px;
  background: var(--rule); border: 1px solid var(--rule); align-self: start; }
.os-ln__stat { background: var(--bg-page); padding: 16px 18px; }
.os-ln__stat-n { display: block; font: 300 28px/1 var(--font-display); color: var(--accent); }
.os-ln__stat-l { display: block; font: 500 10px/1 var(--font-ui); letter-spacing: .12em;
  color: var(--fg-label); margin-top: 8px; }

/* ── what makes this different ────────────────────────────────────────────── */
.os-ln__diffs { display: grid; grid-template-columns: repeat(2, 1fr); gap: 26px 34px; }
/* min-width:0 on the grid child, for the same reason the practitioner column
   needs it: a grid item defaults to min-width:auto, so one long unbroken string
   widens the track instead of wrapping. */
.os-ln__diff { min-width: 0; }
.os-ln__diff-t { font: 400 19px/1.3 var(--font-display); color: var(--fg-primary);
  margin-bottom: 8px; padding-bottom: 8px; border-bottom: 1px solid var(--rule); }
.os-ln__diff-d { font: 300 15px/1.62 var(--font-display); color: var(--fg-secondary);
  margin: 0; text-wrap: pretty; }
.os-ln__more { margin-top: 34px; padding-top: 24px; border-top: 1px solid var(--rule); }
.os-ln__more-t { font: 400 19px/1.3 var(--font-display); color: var(--fg-primary);
  margin-bottom: 10px; }

/* ── the plate and the tail ───────────────────────────────────────────────── */
.os-ln__plate { margin: 40px; }
.os-ln__foot { margin: 0; padding: 0 40px 36px; font: 500 12.5px/1 var(--font-ui);
  letter-spacing: .04em; }

@media (max-width: 1279px) {
  .os-ln__steps { grid-template-columns: repeat(2, 1fr); }
  .os-ln__prog  { grid-template-columns: minmax(0, 1fr); gap: 24px; }
  .os-ln__prog-r { grid-template-columns: repeat(4, 1fr); }
}
@media (max-width: 1023px) {
  .os-ln__mast { padding: 32px 22px 28px; }
  .os-ln__sec  { padding: 28px 22px; }
  .os-ln__plate { margin: 28px 22px; }
  .os-ln__foot { padding: 0 22px 28px; }
  .os-ln__h1   { font-size: 36px; }
  .os-ln__lede { font-size: 17px; }
  .os-ln__diffs { grid-template-columns: 1fr; gap: 22px; }
}
@media (max-width: 767px) {
  .os-ln__mast, .os-ln__sec { padding-left: 16px; padding-right: 16px; }
  .os-ln__plate { margin: 22px 16px; }
  .os-ln__foot { padding: 0 16px 24px; }
  .os-ln__h1   { font-size: 30px; }
  .os-ln__steps { grid-template-columns: 1fr; }
  .os-ln__prog-r { grid-template-columns: 1fr 1fr; }
}

/* ── /resources/ — the annotated directory ────────────────────────────────── */
.os-dir {
  max-width: var(--shell); margin: 0 auto;
  background: var(--bg-page); border: 1px solid var(--card-border);
}
@media (max-width: 1023px) { .os-dir { border-left: 0; border-right: 0; } }

.os-dir__mast { display: grid; grid-template-columns: minmax(0, 1fr) 300px; gap: 52px;
                padding: 40px 44px 34px; border-bottom: 2px solid var(--rule-strong); }
.os-dir__mast-l { min-width: 0; }
.os-dir__h1 { font: 300 50px/1.05 var(--font-display); letter-spacing: -.025em;
              color: var(--fg-primary); margin: 0 0 16px; }
.os-dir__intro { max-width: 62ch; }
.os-dir__intro p { font: 400 16px/1.6 var(--font-ui); color: var(--fg-secondary);
                   margin: 0 0 11px; text-wrap: pretty; }
.os-dir__intro p:last-child { margin-bottom: 0; }

/* The figures STACK beside the prose rather than sitting under it. Four of them
   in a row across the full width was 90px of a masthead that was already
   pushing the directory below the fold; in a 300px column they are 4 lines. */
.os-dir__mast-r { padding-top: 8px; }
.os-dir__stats { display: flex; flex-direction: column; gap: 0; }
.os-dir__stat { display: flex; align-items: baseline; justify-content: space-between;
                gap: 14px; padding: 9px 0; border-bottom: 1px solid var(--rule-soft);
                font: 400 11px/1.4 var(--font-ui); letter-spacing: .12em;
                text-transform: uppercase; color: var(--fg-label); }
.os-dir__stat:first-child { border-top: 1px solid var(--rule-soft); }
.os-dir__stat strong { font: 400 24px/1 var(--font-display); color: var(--fg-primary);
                       font-variant-numeric: tabular-nums; order: 2; }

/* THE BAR: the two arrangements, then the jump anchors. Sticky, because its
   whole job is to be reachable from 8,000px down the page — and it is the one
   piece of chrome that stops this being a scroll a reader gives up on. */
.os-dir__bar { position: sticky; top: 62px; z-index: 5;
               background: var(--bg-surface); border-bottom: 1px solid var(--rule);
               padding: 14px 44px; }
.os-dir__orders { display: flex; gap: 8px; margin-bottom: 12px; }
.os-dir__chip { display: inline-block; border: 1px solid var(--rule);
                padding: 7px 13px; font: 400 12px/1 var(--font-ui);
                letter-spacing: .08em; text-transform: uppercase;
                color: var(--fg-secondary); background: var(--bg-page); }
.os-dir__chip:hover { border-color: var(--fg-label); color: var(--fg-primary);
                      text-decoration: none; }
.os-dir__chip--on { background: var(--bg-inverse); border-color: var(--bg-inverse);
                    color: var(--fg-inverse); }
.os-dir__chip--on:hover { color: var(--fg-inverse); }

.os-dir__jump { display: flex; flex-wrap: wrap; gap: 0 22px; }
.os-dir__jump-a { font: 400 13px/1.6 var(--font-ui); color: var(--fg-secondary); }
.os-dir__jump-a:hover { color: var(--accent); }
.os-dir__jump-n { font-variant-numeric: tabular-nums; color: var(--fg-label);
                  margin-left: 6px; font-size: 11px; }

/* ── a group ──────────────────────────────────────────────────────────────── */
.os-dir__sec { padding: 36px 44px 8px; scroll-margin-top: 150px; }
.os-dir__head { display: flex; align-items: baseline; gap: 12px;
                padding-bottom: 12px; border-bottom: 2px solid var(--fg-primary); }
.os-dir__deva { font: 400 20px/1 var(--font-deva); color: var(--accent); }
.os-dir__head-t { font: 400 28px/1.1 var(--font-display); letter-spacing: -.015em;
                  color: var(--fg-primary); margin: 0; }
.os-dir__head-e { margin-left: auto; font: 400 11px/1 var(--font-ui);
                  letter-spacing: .1em; text-transform: uppercase;
                  color: var(--fg-label); font-variant-numeric: tabular-nums; }
.os-dir__gintro { font: 400 15px/1.6 var(--font-ui); color: var(--fg-secondary);
                  max-width: 72ch; margin: 14px 0 0; }

/* ── one row ──────────────────────────────────────────────────────────────── */
/* The measure: 1180 frame less 2x44 inset is 1092. A 96px count column and a
   108px date column with two 18px gaps leaves about 852px for the annotation,
   which at 15px is roughly 113 characters a line — so a 170-character dek is
   two lines and a row is about 110px. That is the arithmetic that made this
   rows rather than a grid. */
.os-dir__rows { margin-top: 2px; }
/* THE COUNT AND THE DATE ARE ONE TRACK, NOT TWO. They were separate columns of
   96 and 108px, which left the annotation an 852px column it only filled to
   702 — 150px of dead space beside every one of forty rows. They are one meta
   block now and the text column is measured to what it actually needs. */
/* THE MARK IS THE THIRD TRACK, and it costs the annotation nothing. .os-dir__dek
   is capped at 88ch — 726px at this size — while the text column measures 914,
   so taking 108px for the square and its gap leaves 806 and the deks re-wrap on
   not one of the forty rows. Measured before it was written, because a
   thumbnail that pushed every description onto an extra line would have cost
   400px of page to buy 40 pictures. */
.os-dir__row { position: relative; display: grid;
               grid-template-columns: auto minmax(0, 1fr) 150px;
               gap: 28px; align-items: baseline;
               padding: 16px 0 15px; border-bottom: 1px solid var(--rule-soft); }
/* align-self, because the row baseline-aligns and an 80px block has no useful
   baseline — left to itself it hangs its bottom edge on the name's. The 3px is
   the display face's cap offset at 23px, so the square's top edge lines up with
   the top of the letters rather than with the line box above them. */
.os-dir__row > .os-thumb { align-self: start; margin-top: 3px; }
/* The monogram sits in the same 80px box as a real thumbnail — same border,
   same tint — so the eight without an image hold the column instead of
   punching holes in it. */
.os-dir__mono { display: flex; align-items: center; justify-content: center;
                font: 400 30px/1 var(--font-display); color: var(--fg-label);
                background: var(--bg-surface-2); }
.os-dir__row:hover { background: var(--bg-surface); text-decoration: none; }
.os-dir__row:hover .os-dir__name { color: var(--accent); }
.os-dir__main { min-width: 0; }
/* THE NAME OUTRANKS THE NUMBER. It was 20px against a 21px count, so the figure
   shouted louder than the thing it counted. */
.os-dir__name { display: block; font: 400 23px/1.25 var(--font-display);
                letter-spacing: -.01em; color: var(--fg-primary); }
.os-dir__dek { display: block; margin-top: 6px; max-width: 88ch;
               font: 400 15px/1.55 var(--font-ui); color: var(--fg-secondary);
               text-wrap: pretty; }
.os-dir__disc { display: inline-block; margin-top: 8px;
                font: 400 10.5px/1 var(--font-ui); letter-spacing: .12em;
                text-transform: uppercase; color: var(--fg-label); }
.os-dir__n { text-align: right; font-variant-numeric: tabular-nums; }
.os-dir__meta-gap { display: none; }
.os-dir__n-v { font: 400 20px/1 var(--font-display); color: var(--fg-primary); }
.os-dir__n-l { margin-left: 5px; font: 400 10.5px/1 var(--font-ui);
               letter-spacing: .1em; text-transform: uppercase; color: var(--fg-label); }
.os-dir__n-none { display: block; font: 400 11px/1.4 var(--font-ui);
                  letter-spacing: .06em; text-transform: uppercase;
                  color: var(--fg-label); }
.os-dir__when { display: block; margin-top: 7px; font: 400 11.5px/1.4 var(--font-ui);
                color: var(--fg-label); font-variant-numeric: tabular-nums; }

/* THE DIVIDER IS THE DATA. A proportional segment laid over the row's own
   bottom rule — the separator a list needs anyway, carrying the one fact that
   forty identical numerals could not convey: the shape of the library. It sits
   ON the border, so it costs no vertical space at all. */
/* NAMED __gauge, NOT __bar. The sticky chrome above is .os-dir__bar and a
   second element sharing that class made both rule sets apply to both — the
   arrangement chips inherited position:absolute and a 2px height. Two different
   things may not share a name. */
.os-dir__gauge { position: absolute; left: 0; bottom: -1px; height: 2px;
                 background: var(--accent-soft); }
.os-dir__row:hover .os-dir__gauge { background: var(--accent); }
/* A collection with nothing in it yet is not a broken row — it is a real part
   of the library that no other page admits exists. It is lightened, not hidden,
   the same ruling the index applies to its one-entry cells, and it is the one
   row with no bar because there is nothing to measure. */
.os-dir__row--empty .os-dir__name { color: var(--fg-secondary); }

/* ── the other doors ──────────────────────────────────────────────────────── */
.os-dir__more { padding: 30px 44px 44px; margin-top: 30px;
                border-top: 1px solid var(--rule); background: var(--bg-surface); }
.os-dir__doors { display: grid; grid-template-columns: 1fr 1fr; gap: 1px;
                 background: var(--rule-soft); margin-top: 22px;
                 border: 1px solid var(--rule-soft); }
.os-dir__door { display: block; background: var(--bg-page); padding: 22px 24px; }
.os-dir__door:hover { background: var(--bg-page); text-decoration: none; }
.os-dir__door:hover .os-dir__door-t { color: var(--accent); }
.os-dir__door-t { display: block; font: 400 19px/1.3 var(--font-display);
                  color: var(--fg-primary); }
.os-dir__door-d { display: block; margin-top: 7px; font: 400 14px/1.6 var(--font-ui);
                  color: var(--fg-secondary); }

@media (max-width: 1023px) {
  .os-dir__h1 { font-size: 40px; }
  /* The 300px figures column costs more than it gives once the prose column is
     under about 620px, so the masthead stacks and the figures go back to a row. */
  .os-dir__mast { grid-template-columns: 1fr; gap: 26px; }
  .os-dir__mast-r { padding-top: 0; }
  .os-dir__stats { flex-direction: row; flex-wrap: wrap; gap: 0 32px; }
  .os-dir__stat { border: 0; padding: 0; flex-direction: column;
                  align-items: flex-start; justify-content: flex-start; gap: 3px; }
  .os-dir__stat:first-child { border-top: 0; }
  .os-dir__stat strong { order: 0; }
  .os-dir__row { grid-template-columns: auto minmax(0, 1fr) 128px; gap: 20px; }
}
@media (max-width: 767px) {
  .os-dir__mast, .os-dir__bar, .os-dir__sec, .os-dir__more {
    padding-left: 16px; padding-right: 16px;
  }
  .os-dir__h1 { font-size: 32px; margin-bottom: 12px; }
  /* A personal introduction is why this page is not a generated list, so all
     three paragraphs stay — but at 16px/1.6 they were 400px of a phone before
     the directory began. 15px/1.5 with tighter gaps is about 300. */
  .os-dir__intro p { font-size: 15px; line-height: 1.55; margin-bottom: 9px; }
  .os-dir__mast { gap: 20px; padding-bottom: 26px; }
  .os-dir__stats { gap: 0 26px; }
  /* THE GROUP HEAD WRAPS. It is a flex row of Devanagari + a 28px serif label +
     a letter-spaced count pushed right by margin-left:auto, and auto margins do
     not shrink: measured at 330 and below the count ran 27px past the frame and
     the whole document scrolled sideways. Wrapping drops it to its own line
     instead, which is what the space actually allows. */
  .os-dir__head { flex-wrap: wrap; }
  .os-dir__head-e { margin-left: 0; flex-basis: 100%; margin-top: 6px; }
  /* THE ROW LOSES THE META COLUMN AND KEEPS A COLUMN FOR THE SQUARE. At 390 the
     count and date tracks take more than half the width and the annotation
     collapses to four words a line, so those two drop under the text. What is
     left is two columns: the 64px mark, and everything else.
       THE COUNT AND THE DATE MUST STAY ON ONE LINE, and that is the trap this
     rule set exists to avoid. As two grid children they take a row each and the
     separator leads the next line. They survive because the date is emitted
     INSIDE .os-dir__n — one grid child holding two inline spans — and because
     everything in the text column is placed explicitly in column 2 rather than
     left to auto-placement.
       A FLOAT WAS TRIED FIRST AND REJECTED BY LOOKING AT IT: text flows past
     the bottom of a float, so every annotation ran two lines beside the square
     and then broke out to full width underneath it — an L-shaped paragraph on
     all forty rows. A column holds its measure the whole way down.
       64px, NOT 80: at 390 the row is 358 wide, and 80 + 14 would leave the
     annotation 264px, which is one more line of dek on every row that has one.
       row-gap: 0 IS NOT DECORATION. The tablet rule above sets gap:20px on both
     axes. Re-declaring only column-gap left it in force, so the 9px
     .os-dir__meta-gap spacer sat between two 20px grid gaps and every row
     carried 49px of nothing above its meta line. The spacer owns that
     distance. */
  .os-dir__row { display: grid; grid-template-columns: 64px minmax(0, 1fr);
                 column-gap: 14px; row-gap: 0; align-items: start;
                 padding: 16px 0; }
  .os-dir__row > .os-thumb { grid-column: 1; grid-row: 1; width: 64px; height: 64px;
                             align-self: start; margin-top: 3px; }
  .os-dir__main, .os-dir__meta-gap, .os-dir__n { grid-column: 2; }
  .os-dir__main { display: block; }
  .os-dir__mono { font-size: 24px; }
  .os-dir__n, .os-dir__when { text-align: left; display: inline; }
  .os-dir__meta-gap { display: block; height: 9px; }
  .os-dir__n-v, .os-dir__n-l, .os-dir__n-none { display: inline; }
  .os-dir__n-v { font-size: 13px; font-family: var(--font-ui); }
  .os-dir__n-l { font-size: 11px; margin-left: 4px; }
  .os-dir__when::before { content: '\00B7'; padding: 0 8px; color: var(--fg-separator); }
  .os-dir__when { font-size: 11px; }
  .os-dir__doors { grid-template-columns: 1fr; }
  /* The bar is 2 rows of chrome on a phone; unsticking it gives the rows the
     viewport back. The jump links are still there, at the top, where a reader
     who wants them has just come past. */
  .os-dir__bar { position: static; }
}

/* ── /essays/ — the reading view ─────────────────────────────────────────── */
.os-fd {
  max-width: var(--shell); margin: 0 auto;
  background: var(--bg-page); border: 1px solid var(--card-border);
}
@media (max-width: 1023px) { .os-fd { border-left: 0; border-right: 0; } }

.os-fd__mast { padding: 44px 44px 36px; border-bottom: 2px solid var(--rule-strong); }
.os-fd__deva { font: 400 26px/1 var(--font-deva); color: var(--accent); margin-bottom: 10px; }
.os-fd__h1 { font: 300 54px/1.06 var(--font-display); letter-spacing: -.025em;
             color: var(--fg-primary); margin: 0 0 18px; text-wrap: pretty; }
.os-fd__dek-top { font: 400 19px/1.55 var(--font-ui); color: var(--fg-secondary);
                  margin: 0 0 26px; max-width: 62ch; text-wrap: pretty; }
.os-fd__stats { display: flex; gap: 40px; }
.os-fd__stat { display: flex; flex-direction: column; gap: 4px;
               font: 400 11px/1.4 var(--font-ui); letter-spacing: .14em;
               text-transform: uppercase; color: var(--fg-label); }
.os-fd__stat strong { font: 400 26px/1 var(--font-display); color: var(--fg-primary);
                      font-variant-numeric: tabular-nums; }

/* ONE LEFT EDGE. Every horizontal inset on this page is 44px measured from the
   frame, exactly like the masthead above — so the h1, the lead picture, the left
   column of the grid and the archive button all start on the same vertical. The
   padding therefore lives on the CARD, never on a wrapper around it: a wrapper
   with padding plus a card with padding gives two insets that add up, which is
   how the lead image ended up 45px right of the headline it belongs to. */
.os-fd__lead { padding: 0; }
.os-fd__grid {
  display: grid; grid-template-columns: repeat(2, 1fr);
  /* A 1px gap over the panel ground is how every other grid in this design
     draws its hairlines, so the cards are separated by the same line weight as
     the rows on the archive rather than by a second idea. */
  gap: 1px; background: var(--rule-soft);
  margin: 0; border-top: 1px solid var(--rule);
}
.os-fd__card { display: block; background: var(--bg-page); padding: 40px 44px 44px; }
.os-fd__card:hover { background: var(--bg-surface); text-decoration: none; }
.os-fd__card:hover .os-fd__t { color: var(--accent); }

/* THE LEAD IS NOT A HERO. Above 1023 it is a two-column editorial card — picture
   beside the words, not under them — for one measurable reason: stacked, a 21:9
   image at the full 1090px measure is 467px tall, and with the masthead above it
   the headline landed at y=940 on a 950px viewport. The lead essay's TITLE was
   below the fold on the page whose whole job is to show it. Beside the text the
   same picture is 317px tall and the title sits at about 600.
   Below 1023 it stacks, because two columns in 700px gives neither one enough. */
@media (min-width: 1024px) {
  .os-fd__card--lead {
    display: grid; grid-template-columns: 1.15fr 1fr;
    gap: 40px; align-items: center;
  }
  .os-fd__card--lead .os-fd__shot { margin-bottom: 0; }
}
.os-fd__eyebrow {
  display: block; margin-bottom: 14px;
  font: 500 11px/1 var(--font-ui); letter-spacing: .16em;
  text-transform: uppercase; color: var(--accent);
}

/* 16:9 AND A FIXED RATIO, always. The images are the author's and they are not
   one shape: letting each card size to its own picture gives a grid whose rows
   do not line up, which is the single most reliable way to make a feed look
   unfinished. object-fit crops rather than distorts. */
.os-fd__shot { display: block; aspect-ratio: 16 / 9; overflow: hidden;
               background: var(--bg-surface-2); border: 1px solid var(--rule-soft);
               margin-bottom: 18px; }
.os-fd__shot img { width: 100%; height: 100%; object-fit: cover; display: block; }

.os-fd__body { display: block; }
.os-fd__t { display: block; font: 400 22px/1.28 var(--font-display);
            color: var(--fg-primary); text-wrap: pretty; }
.os-fd__lead .os-fd__t { font-size: 34px; line-height: 1.14; }
.os-fd__dek { display: block; margin-top: 12px; max-width: 68ch;
              font: 400 16px/1.6 var(--font-ui); color: var(--fg-secondary); }
.os-fd__m { display: block; margin-top: 12px;
            font: 400 11.5px/1.5 var(--font-ui); letter-spacing: .08em;
            text-transform: uppercase; color: var(--fg-label);
            font-variant-numeric: tabular-nums; }
.os-fd__sep { color: var(--fg-separator); padding: 0 8px; }

.os-fd__more { display: flex; align-items: center; gap: 18px;
               padding: 36px 44px 44px; border-top: 1px solid var(--rule); }
.os-fd__more-n { font: 400 12px/1.4 var(--font-ui); letter-spacing: .06em;
                 text-transform: uppercase; color: var(--fg-label); }

@media (max-width: 1023px) {
  .os-fd__h1 { font-size: 40px; }
  .os-fd__lead .os-fd__t { font-size: 28px; }
}
@media (max-width: 767px) {
  .os-fd__grid { grid-template-columns: 1fr; }
  /* The same single inset, still one edge — 16px instead of 44px because that is
     the gutter every other template drops to on a phone. */
  .os-fd__mast, .os-fd__card, .os-fd__more { padding-left: 16px; padding-right: 16px; }
  .os-fd__card { padding-top: 28px; padding-bottom: 32px; }
  .os-fd__h1 { font-size: 32px; }
  .os-fd__dek-top { font-size: 17px; }
  .os-fd__stats { gap: 26px; flex-wrap: wrap; }
  .os-fd__lead .os-fd__t { font-size: 25px; }
  .os-fd__more { flex-direction: column; align-items: flex-start; gap: 12px; }
}

/* ══════════ THE STORED ARTICLE BODY ══════════════════════════════════════
   services/content-body.js rewrites every imported body into these classes.
   The archetype layer styles prose (p, li, h3, blockquote, table); what is here
   is the structure that only this corpus has — 1,971 computed-level headings,
   13,222 list rows, 3,448 of them term/definition pairs.

   Everything is scoped under .osa-body, the Article archetype's body column, so
   none of it can leak into the shell or a Landing page's own markup. */

/* ── headings, by COMPUTED outline level, never by tag name ────────────────
   os-hd-1 is the shallowest heading a document actually contains, whatever its
   tag: 238 of the 675 entries open at h3 and never use h2. Level 1 headings are
   normally consumed by the archetype (each becomes a section heading with its
   own Devanagari ordinal); this rule is what an entry gets when its markup is
   too tangled to split safely and the body renders whole. */
.osa-body .os-hd{scroll-margin-top:92px}
/* LEVEL 1 matches .osa-sec__h — same object, different route to the page: an
   entry whose markup could be split gets the archetype's section head, one whose
   markup could not gets this. They must not look like two different designs, so
   both carry the canvas's 400 30px/1.15 at -.012em AND the 2px ink rule. */
.osa-body .os-hd-1{font:400 30px/1.15 var(--font-display);letter-spacing:-.012em;
color:var(--fg-primary);margin:44px 0 22px;padding-bottom:10px;
border-bottom:2px solid var(--rule-strong);text-wrap:pretty}

/* LEVEL 2 is the canvas's h3: the UI SANS at 600 20px/1.3, preceded by an 8px
   accent lozenge turned 45 degrees, with the copy beneath it indented 18px so the
   lozenge reads as the start of a nested run rather than a bullet in the margin.
   It was set in the display serif at 500 with no marker and no indent, which made
   a sub-heading indistinguishable from a slightly smaller section heading. */
.osa-body .os-hd-2{font:600 20px/1.3 var(--font-ui);color:var(--fg-primary);
margin:30px 0 12px;display:flex;align-items:flex-start;gap:12px}
.osa-body .os-hd-2::before{content:"";flex:0 0 8px;width:8px;height:8px;
background:var(--accent);transform:rotate(45deg);margin-top:7px}
/* The 18px indent belongs to what FOLLOWS the sub-heading, not to the heading. */
.osa-body .os-hd-2 + p,
.osa-body .os-hd-2 + ul,
.osa-body .os-hd-2 + ol,
.osa-body .os-hd-2 + .os-ls{margin-left:18px}
.osa-body .os-hd-3{font:500 11px/1 var(--font-mono);letter-spacing:.14em;text-transform:uppercase;
color:var(--ink3);margin:28px 0 10px;padding-top:12px;border-top:1px solid var(--rule2)}
.osa-body .os-hd-4{font:500 11px/1 var(--font-mono);letter-spacing:.14em;text-transform:uppercase;
color:var(--ink3);margin:22px 0 8px}

/* A caption is a paragraph the author wrote as a list heading — 394 of them. */
/* 3A draws the caption at 600 13px/1.3 in INK with .08em and a hairline beneath —
   a small-caps heading for the list it introduces. It was a 10.5px accent
   micro-label, which reads as a tag rather than as the heading of 394 lists. */
.osa-body .os-list-cap{font:600 13px/1.3 var(--font-ui);letter-spacing:.08em;
text-transform:uppercase;color:var(--fg-primary);margin:24px 0 10px;
padding-bottom:8px;border-bottom:1px solid var(--rule)}
.osa-body .os-list-cap+.os-ls{margin-top:0}

/* ── list rows ──
   Bullets turn to mush at 19.6 items per entry, so a row is a ruled line with a
   marker gutter. Three depths, three gutters, three marker vocabularies. */
.osa-body .os-ls{list-style:none;margin:0 0 24px;padding:0}
/* An ordinary row is NOT a grid. A grid container turns every inline child into
   a grid item, which set the bold words of a sentence vertically down a 30px
   column. Only the term/definition rows, whose children content-body.js
   guarantees, can safely be grids. */
.osa-body .os-li{display:block;position:relative;margin:0}
/* THE UI SANS, AT 400. Screen 3A's list specimens set every row in IBM Plex Sans —
   level one at 400 16px/1.5 in ink, level two and three at 400 15px/1.5 in secondary
   — and the display serif appears in that whole screen twice, once as a plate lead
   and once as a heading. These rows were 300-weight display serif at 17/16/15px, so
   the largest single block of type on the site, 13,222 rows, was set in the face
   reserved for display and at a weight the design does not use for body text. */
.osa-body .os-li-1{padding:9px 0 9px 30px;border-bottom:1px solid var(--rule2);
font:400 16px/1.5 var(--font-ui);color:var(--fg-primary)}
.osa-body .os-li-2{padding:8px 0 8px 22px;border-bottom:1px solid var(--rule2);
font:400 15px/1.5 var(--font-ui);color:var(--fg-secondary)}
.osa-body .os-li-3{padding:7px 0 7px 16px;border:0;
font:400 15px/1.5 var(--font-ui);color:var(--fg-secondary)}
.osa-body .os-li>.os-ls{grid-column:1/-1;margin:6px 0 0}
/* 2px, which is the level's structural signal. At 1px it was the same hairline as
   the row separators it sits among. */
.osa-body .os-ls-2{margin-left:30px;padding-left:18px;border-left:2px solid var(--rule)}
.osa-body .os-ls-3{margin-left:22px;padding-left:16px;border-left:1px solid var(--rule2)}
/* Markers sit in the gutter the padding opened, aligned to the FIRST LINE of
   the text rather than to the row, so a three-line item keeps its marker up. */
.osa-body .os-ls-u>.os-li::before{content:"";position:absolute;left:0;display:block}
/* 5px in LABEL GREY. Every square marker in 3A is 5x5, and the accent square is
   reserved for the sub-heading lozenge — spending it on every one of 13,222 bullets
   leaves nothing for the lozenge to mean. */
.osa-body .os-ls-u>.os-li-1::before{width:5px;height:5px;top:16px;background:var(--fg-label)}
.osa-body .os-ls-u>.os-li-2::before{width:5px;height:5px;top:16px;background:var(--ink3)}
.osa-body .os-ls-u>.os-li-3::before{content:"\2014";width:auto;height:auto;top:6px;background:none;
color:var(--ink3);font:400 13px/1.6 var(--font-mono)}
.osa-body .os-li-n{position:absolute;left:0;font:500 12px/1.5 var(--font-ui);
color:var(--accent);font-variant-numeric:tabular-nums}
.osa-body .os-li-1>.os-li-n{top:11px}
.osa-body .os-li-2>.os-li-n,.osa-body .os-li-3>.os-li-n{top:9px;color:var(--ink3)}
/* Term/definition — the single largest pattern in the corpus. The term column is
   fixed so the definitions align down the page, which is the whole point of
   setting it in columns at all. */
.osa-body .os-li-td{display:grid;align-items:baseline;padding-left:0;
grid-template-columns:30px 200px 1fr;gap:0 16px}
.osa-body .os-li-2.os-li-td{grid-template-columns:22px 180px 1fr}
.osa-body .os-li-3.os-li-td{grid-template-columns:16px 160px 1fr}
/* Back into the flow — in a grid the marker and the ordinal ARE the first cell. */
.osa-body .os-ls-u>.os-li-td::before{position:static;top:auto;align-self:center}
.osa-body .os-li-td>.os-li-n{position:static;top:auto}
/* Screen 3B sets every list row in the UI SANS — 400 16px/1.55 for the definition
   with its term at font-weight 600 — and this had the definition in the DISPLAY
   SERIF at 300. Two faces for one row, on 3,448 term/definition pairs and 13,222
   rows overall, which is the largest single block of type on the site.
   This is the other half of the os-li-t/os-li-d reconciliation: that fix made the
   token layer's rule MATCH these elements, and this one stops this layer's older
   declarations overruling it on the very properties the canvas specifies. The
   GRID here stays — it is what actually lays the rows out. */
/* THE GUARD. A stored body can contain anything — a 60-character URL, a
   chemical name, a run of non-breaking spaces — and no template can enumerate
   what 651 imported documents hold. overflow-wrap only acts when a run genuinely
   cannot fit, so it costs nothing on prose that wraps normally and makes
   "content pushes the page sideways" impossible rather than unlikely. */
.osa-body{overflow-wrap:break-word}
.osa-body .os-li-t{font:600 15px/1.45 var(--font-ui);color:var(--fg-primary)}
/* SECONDARY ink. 3A sets the term in full ink at 600 and the definition in #574C40
   — the tonal difference is what makes two columns read as term and definition
   rather than as two equal columns of text. Both were full ink. */
.osa-body .os-li-d{font:400 16px/1.55 var(--font-ui);color:var(--fg-secondary)}
.osa-body .os-li-d>.os-ls{margin-top:8px}

/* ── the rest of a body ── */
.osa-body{overflow-wrap:break-word}
.osa-body figure{margin:32px 0}
.osa-body figcaption{font:400 11.5px/1.5 var(--font-mono);color:var(--ink3);margin-top:8px}
.osa-body dl{margin:0 0 1.1em}
.osa-body dt{font-weight:600;margin-top:.6rem}
.osa-body dd{margin:0 0 0 1.1rem;color:var(--ink2)}
.osa-body code{background:var(--paper2);padding:.1em .3em}
.osa-body pre code{background:none;padding:0}
/* EMBEDS MUST NOT PUSH THE PAGE SIDEWAYS. A YouTube embed ships width="560"
   height="315" as ATTRIBUTES, which are presentational hints and not CSS, so
   nothing here was constraining them: measured at a 500px viewport, the document
   scrollWidth was 576 — 76px of horizontal overflow on the seven entries that
   carry a video, and a page body must never scroll sideways.

   max-width alone stops the overflow but pins the height at 315 and letterboxes
   the video, so the video case takes its ratio as well. Scoped BY HOST rather
   than by the attribute pair, so a 640x360 embed is covered too — and so the
   corpus's other embed shape keeps its own: five entries carry a 98x120
   OneDrive badge, which is not 16:9.

   NOTE FOR EDITORS: this block is inlined into the stylesheet of every page, so
   whatever is written here is served to every reader. A third embed shape used
   to be named on this line and was therefore printed on all 651 documents after
   the service behind it was retired. Name hosts here only while they are in
   IFRAME_HOSTS — and never write a style tag's own name in this file, opening
   bracket and all: the document is asserted to carry exactly one, and a comment
   that spells it becomes the second. */
.osa-body iframe{border:0;max-width:100%}
.osa-body iframe[src*="youtube.com"],
.osa-body iframe[src*="youtube-nocookie.com"],
.osa-body iframe[src*="player.vimeo.com"]{
  display:block;width:100%;height:auto;aspect-ratio:16/9;margin:24px 0
}
.osa-body audio{width:100%;display:block;margin:24px 0}
/* Devanagari runs in the body get the Devanagari face, not a sans fallback. */
.osa-body :lang(hi),.osa-body :lang(sa),.osa-body :lang(mr){font-family:var(--font-deva)}
/* The feature image, on the 12 entries whose author wrote a caption for it. */
.os-hero{margin:0 0 32px}
.os-hero img{width:100%;display:block}

/* Ghost bookmark cards, reduced to what a bookmark is: one link with a title
   and, where it leaves the site, its host. 17 in the corpus. */
.os-bookmark{display:flex;justify-content:space-between;align-items:baseline;gap:16px;
padding:16px 18px;margin:24px 0;background:var(--paper);box-shadow:0 0 0 1px var(--rule);
text-decoration:none}
.os-bm-t{font:400 17px/1.35 var(--font-display);color:var(--ink)}
.os-bookmark:hover .os-bm-t{color:var(--accent)}
.os-bm-h{flex:0 0 auto;font:400 11px/1 var(--font-mono);color:var(--ink3)}

/* ≤680px each table row becomes a record card: the header row is dropped and
   every cell prints its own column label. Horizontal scroll is explicitly not
   the mobile strategy for an 11-column table — a card at least lets one row be
   read. Only inside a body: a table in the chrome has no data-col labels. */
@media(max-width:680px){
.osa-body .os-table-wrap{overflow-x:visible;border:0;margin:24px 0;width:100%;margin-left:0}
.osa-body table,.osa-body tbody,.osa-body tr,.osa-body td{display:block;width:auto}
.osa-body thead,.osa-body tr.os-thead{display:none}
.osa-body tr{background:var(--paper);box-shadow:0 0 0 1px var(--rule);margin:0 0 12px}
.osa-body td{display:flex;justify-content:space-between;gap:16px;padding:9px 14px;
border-bottom:1px solid var(--rule2);font-size:14px;line-height:1.35;text-align:right}
.osa-body tbody tr td:last-child{border-bottom:0}
.osa-body tr>td:first-child{display:block;background:var(--paper2);font-weight:600;
font-size:15px;line-height:1.2;padding:11px 14px;border-bottom:1px solid var(--rule);
text-align:left;padding-left:14px}
.osa-body tr>*:last-child{padding-right:14px}
.osa-body td[data-col]::before{content:attr(data-col);flex:0 0 auto;
font:400 11px/1.4 var(--font-mono);letter-spacing:.06em;text-transform:uppercase;
color:var(--ink3)}
.osa-body tr>td:first-child::before{content:none}
/* The term column stacks above the definition and keeps its weight. */
.osa-body .os-li-td{grid-template-columns:30px 1fr}
.osa-body .os-li-2.os-li-td{grid-template-columns:22px 1fr}
.osa-body .os-li-3.os-li-td{grid-template-columns:16px 1fr}
.osa-body .os-li-t{grid-column:2;margin-bottom:2px}
.osa-body .os-li-d{grid-column:2}
.osa-body .os-ls-2{margin-left:14px;padding-left:12px}
.osa-body .os-ls-3{margin-left:10px;padding-left:10px}
}

/* ══════════ THE RECITATION PLATE ═════════════════════════════════════════
   61 audio files across 51 entries, and on the shortest of those the player IS
   the page. The plate is generated by sanitizeBody() from Ghost's dead
   kg-audio-card and lives INSIDE the stored body, which is why it is styled
   here rather than by the component layer: the designed player block that sits
   between the byline and the first paragraph needs the audio lifted out of the
   body first, and that is task 30, not this one.

   Three states, in order of how much the browser gives us: a bare <audio>, the
   plate with the native control on it, and the plate with the custom control.
   The native element is always the real player. */
.os-recite{background:var(--plate-bg);color:var(--plate-fg);
border:1px solid var(--plate-border);padding:32px;margin:36px 0}
.os-plate-head{display:flex;justify-content:space-between;align-items:baseline;gap:16px;
margin-bottom:10px}
/* QUALIFIED WITH THE ELEMENT, like the title below it and for exactly the same
   reason. These three are <p> inside .osa-body, and .osa-body p weighs (0,1,1)
   against a bare class's (0,1,0), so it won outright: RECITATION, the duration and
   the Devanagari line all computed to 17px/400 in #221C15 — body ink — on a #15110C
   plate. That is 1.1:1. Measured in the browser, not inferred: all three came back
   rgb(34, 28, 21) while the figcaption beside them, which had already been
   qualified, came back correct.
   The plate is the one place on the site where inheriting the body's colour is
   catastrophic rather than merely wrong, so every line inside it is qualified. */
.osa-body p.os-plate-label{font:500 10.5px/1 var(--font-mono);letter-spacing:.16em;
text-transform:uppercase;color:var(--plate-label);margin:0}
.osa-body p.os-plate-dur{font:400 11.5px/1.4 var(--font-mono);letter-spacing:.04em;
color:var(--plate-label);margin:0;font-variant-numeric:tabular-nums}
/* Qualified with the element and scoped to the body: a bare .os-plate-title
   ties with .osa-body figcaption on specificity and then loses on source order,
   which set the plate title in 11.5px grey mono. */
.osa-body figcaption.os-plate-title{font:400 30px/1.2 var(--font-display);
color:var(--plate-fg);margin:0 0 22px;text-align:left}
/* The Devanagari line leads the plate where the recitation has one — 35 of the
   60 do. The Latin title steps down beneath it so the two do not compete. */
.osa-body p.os-plate-deva{font:400 36px/1.35 var(--font-deva);color:var(--plate-accent);
margin:0 0 6px}
.osa-body .os-recite-deva figcaption.os-plate-title{font-size:20px;line-height:1.3}
.os-plate-body{display:none;align-items:center;gap:22px}
/* GOLD, NOT SINDOOR. All four canvas drawings of this control — 3A at 56px, 3D at
   44px and 5C twice at 52px — are background:#E4BA61 with color:#15110C, which is
   --plate-gold on --plate-bg. It was --plate-accent, the dark-side red, which is a
   different colour family on the one control 61 entries lead with. 5A's hover value
   is the proof: "Play control | bg -> #EFC97A", a lightened GOLD, which only reads
   as a hover if the rest state is gold. Hovering to --band-ink darkened it instead.
   The 600 weight and .06em tracking are the drawings' too. */
.os-plate-play{flex:0 0 auto;width:56px;height:56px;border-radius:50%;cursor:pointer;
background:var(--plate-gold);border:1px solid var(--plate-gold);color:var(--plate-bg);
font:600 13px/1 var(--font-ui);letter-spacing:.06em}
.os-plate-play:hover{background:#EFC97A;border-color:#EFC97A}
/* 5A: "scale 0.96". The one transform in the design, and it is on this control. */
.os-plate-play:active{transform:scale(.96)}
.os-plate-play:focus-visible{outline:2px solid var(--plate-accent);outline-offset:3px}
.os-plate-wave{flex:1 1 auto;display:flex;align-items:flex-end;gap:3px;height:44px;min-width:0}
.os-plate-wave>i{flex:1 1 0;min-width:0;background:var(--plate-rule);display:block}
.os-w12{height:12px}.os-w15{height:15px}.os-w18{height:18px}.os-w21{height:21px}.os-w24{height:24px}.os-w27{height:27px}.os-w30{height:30px}.os-w33{height:33px}.os-w36{height:36px}.os-w39{height:39px}.os-w42{height:42px}.os-w44{height:44px}
.os-plate-wave>i.on{background:var(--plate-accent)}
.os-plate-wave:focus-visible{outline:2px solid var(--plate-accent);outline-offset:4px}
.os-plate-foot{display:flex;justify-content:space-between;gap:16px;margin-top:18px;
font:400 11px/1 var(--font-mono);letter-spacing:.1em;color:var(--plate-label);
font-variant-numeric:tabular-nums}
.os-plate-file{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.os-plate-audio{width:100%;display:block;margin-top:4px}
/* The custom control only appears once its script has run — an inert circle
   beside a hidden native player would leave the page with no way to listen. */
.os-plate-js .os-plate-body{display:flex}
.os-plate-js .os-plate-audio{position:absolute;width:1px;height:1px;overflow:hidden;
clip:rect(0 0 0 0);white-space:nowrap}
.os-plate-js .os-plate-wave{cursor:pointer}

/* ══════════ THE LOGIN GATE (F1a) ═════════════════════════════════════════ */
.os-gate{margin:40px 0 0;padding:28px 26px;background:var(--paper2);
box-shadow:0 0 0 1px var(--rule);text-align:center}
.os-gate h2{margin:0 0 8px;font:500 22px/1.25 var(--font-display)}
.os-gate p{margin:0 0 18px;color:var(--ink2);font:300 16px/1.6 var(--font-display)}
/* An anonymous reader and Googlebot get the same bytes; the fade is the visual
   half of that, and the mask uses #000 as an ALPHA CHANNEL, not as a colour. */
.os-fade{-webkit-mask-image:linear-gradient(180deg,#000 55%,transparent);
mask-image:linear-gradient(180deg,#000 55%,transparent)}

/* ══════════ SMALL SHARED PIECES ══════════════════════════════════════════ */
/* An honest empty state. Never a placeholder row. */
.os-empty{color:var(--ink2);font:300 18px/1.6 var(--font-display);padding:40px 0}
/* The index groups and the composed screens reuse the archetype's own group
   furniture; this is the one thing it has no slot for — a count beside a group
   heading, in the same mono voice the rail uses. */
.osa-group__count{margin-left:auto;font:400 11.5px/1 var(--font-mono);color:var(--ink3);
flex:none;font-variant-numeric:tabular-nums}
/* The composed screens (index, search, 404) sit inside the Hub sheet and need
   the same vertical rhythm the archetype gives its body. */
.osa-plain{padding:44px 0 90px}
.osa-plain>*+*{margin-top:34px}
@media(max-width:767px){.osa-plain{padding:32px 0 64px}}
