/* ==========================================================================
   mcprush — core.css
   Design language: "Datasheet" — warm paper canvas, hairline rules,
   monospace spec labels, emerald economy accent. No gradients, no pills,
   no pastel washes. Density over decoration.
   ========================================================================== */

/* ---------- 1. tokens ---------------------------------------------------- */
:root {
  /* surfaces */
  --paper:      #FBFAF7;
  --surface:    #FFFFFF;
  --surface-2:  #F4F3EE;
  --surface-3:  #EBE9E1;
  --line:       #E4E2D9;
  --line-2:     #D3D0C4;

  /* ink */
  --ink:        #15171B;
  --ink-2:      #565C64;
  --ink-3:      #878C93;

  /* accent — emerald = money, growth, go */
  --accent:      #0C7A4B;
  --accent-2:    #0A6440;
  --accent-soft: #E6F2EA;
  --accent-line: #B4DCC7;

  /* semantic */
  --risk:       #B23F2B;
  --risk-soft:  #FAEDE9;
  --risk-line:  #EDCFC7;
  --warn:       #96680B;
  --warn-soft:  #F9F1DE;
  --warn-line:  #E9DAB4;
  --info:       #2A4FBF;
  --info-soft:  #EAEEFA;
  --info-line:  #C3CEEE;

  /* The second product line used to carry a hue of its own — servers emerald,
     skills ink-blue — and as a way of saying "these are two different nouns"
     it worked everywhere except where it mattered most: in a header. An
     Install skill button painted a colour the rest of the page never uses does
     not read as a second product line, it reads as a second website.

     So the family is an alias now, declared once, here. Every one of the rules
     that letters with --skill keeps working untouched, the skills pages take
     whichever accent the scheme is wearing, and no scheme can quietly hand the
     product a second accent again — there is nowhere left to declare one.

     What tells a skill from a server is still colour, just not a second hue:
     server marks are neutral tiles and skill marks are accent-tinted, which is
     the contrast that was doing the work in a stack row all along. */
  --skill:      var(--accent);
  --skill-2:    var(--accent-2);
  --skill-soft: var(--accent-soft);
  --skill-line: var(--accent-line);

  /* inverted panel */
  --panel:      #121413;
  --panel-2:    #1C1F1D;
  --panel-line: #2C302D;
  --panel-ink:  #F2F1EC;
  --panel-ink2: #9BA29C;

  /* geometry */
  --r1: 6px;
  --r2: 9px;
  --r3: 13px;
  --w:  1400px;   /* one content width everywhere, header included */
  --w-wide: 1400px;
  --nav-h: 60px;

  --sh1: 0 1px 1px rgba(21,23,27,.035), 0 1px 3px rgba(21,23,27,.045);
  --sh2: 0 10px 30px -14px rgba(21,23,27,.28), 0 2px 6px rgba(21,23,27,.05);
  --sh3: 0 24px 60px -20px rgba(21,23,27,.35), 0 4px 12px rgba(21,23,27,.08);

  --sans: 'Instrument Sans', system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;
  --mono: 'Spline Sans Mono', ui-monospace, SFMono-Regular, Menlo, monospace;

  /* What sits ON a filled surface. A scheme can repaint the accent, and an
     accent light enough to need dark text on it — lime — cannot be served by a
     hard-coded #fff three rules further down. --panel-accent is the one accent
     used inside the dark panel that every scheme keeps dark. */
  --on-accent:    #fff;
  --on-skill:     var(--on-accent);
  --panel-accent: #4FD695;
}

[data-theme="dark"] {
  --on-accent:    #06180F;
  --panel-accent: #4FD695;
  --paper:      #0F1110;
  --surface:    #161918;
  --surface-2:  #1D211F;
  --surface-3:  #262B28;
  --line:       #282D2A;
  --line-2:     #3A403C;

  --ink:        #ECEBE6;
  --ink-2:      #A2A9A5;
  --ink-3:      #7B827E;

  --accent:      #35C481;
  --accent-2:    #4FD695;
  --accent-soft: #10281D;
  --accent-line: #1F4936;

  --risk:       #E8836B;
  --risk-soft:  #2A1712;
  --risk-line:  #4A2A21;
  --warn:       #D9A63F;
  --warn-soft:  #251D0D;
  --warn-line:  #453718;
  --info:       #7A9AF5;
  --info-soft:  #151B2E;
  --info-line:  #26325A;

  --panel:      #0A0C0B;
  --panel-2:    #141716;
  --panel-line: #232725;
  --panel-ink:  #F2F1EC;
  --panel-ink2: #9BA29C;

  --sh1: 0 1px 1px rgba(0,0,0,.3), 0 1px 3px rgba(0,0,0,.3);
  --sh2: 0 10px 30px -14px rgba(0,0,0,.7), 0 2px 6px rgba(0,0,0,.3);
  --sh3: 0 24px 60px -20px rgba(0,0,0,.8), 0 4px 12px rgba(0,0,0,.4);
}

/* ---------- 2. reset ----------------------------------------------------- */
*, *::before, *::after { box-sizing: border-box; }
* { margin: 0; padding: 0; }
/* ANCHORS UNDER A FIXED HEADER

   Every in-page link on this site — the FAQ ids, the docs rail, #payouts,
   #security, the section nav — scrolls the target to the top of the viewport,
   which is where the header is. One rule fixes all of them: reserve the header
   plus a band of breathing room at the top of every scroll the browser makes.

   It is a variable rather than the 84px that used to be typed here, because
   several pages put a second sticky bar directly under the header — the
   catalogue toolbar, the dashboard's own rail, the calculator bar on the
   publisher page — and on those the reserved space has to grow by exactly that
   bar's height. Each of them raises --scroll-pad next to the rule that makes
   it sticky, so the two numbers can never drift apart. */
:root { --scroll-pad: calc(var(--nav-h) + 24px); }
html { -webkit-text-size-adjust: 100%; scroll-behavior: smooth; scroll-padding-top: var(--scroll-pad); }
/* The same reservation for anything scrolled by scrollIntoView() rather than
   by the browser's own hash handling — scroll-padding only covers the second.
   Headings are in the list because half the JS on this site scrolls to one
   directly (`head.scrollIntoView({block:'start'})`), and a heading is exactly
   what a reader looks for when the page stops moving. */
:target, [id], h1, h2, h3, h4 { scroll-margin-top: var(--scroll-pad); }
/* the studio scrolls to a row it just opened rather than to an anchor */
[data-line], [data-panel], [data-rev], [data-row] { scroll-margin-top: var(--scroll-pad); }
body {
  background: var(--paper);
  color: var(--ink);
  font: 400 15px/1.55 var(--sans);
  font-feature-settings: 'ss01' 1, 'cv05' 1;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
  overflow-x: hidden;
}
/* THE PAGE CANNOT BE DRAGGED SIDEWAYS
   `overflow-x: hidden` above hides a horizontal scrollbar; on iOS it does not
   hide the scroll. Because `hidden` forces the other axis to `auto`, the body
   becomes a scroll container whose horizontal axis is live but chromeless, and
   Safari lets a finger drag it. Measured on an iPhone 17 / iOS 26.3 simulator
   with a 900px-wide probe element on the home page: one horizontal swipe over
   the hero — not over a card rail — moved window.scrollX from 0 to 498 and left
   it there, so the whole site slid off the screen and stayed off. That is the
   fault the owner reported.

   `clip` is the same clipping with no scroll container behind it: it leaves
   overflow-y at `visible` instead of promoting it to `auto`, so there is no
   horizontal axis for a finger to find. Same probe, same swipe, with this rule:
   scrollWidth fell from 900 to 402 and scrollX never left 0.

   It has to be on BOTH elements. Measured separately on the same simulator:
   `body` alone still panned to 498 (scrollWidth stayed 900), and `html` alone
   panned too, because the body underneath was still `hidden` and still a
   scroller. Only the pair holds.

   The declaration is repeated rather than merged into the block above so that a
   browser without `clip` (Safari before 16) drops this line as invalid and
   keeps exactly the `hidden` it has today. Nothing on the site relies on being
   able to scroll the page sideways — every wide thing here (the card decks, the
   chip strips, the tables, the code blocks) carries its own overflow-x: auto
   and still scrolls inside its own box under this rule. */
html, body { overflow-x: clip; }
img, svg { display: block; max-width: 100%; }
[data-icon] { display: inline-flex; align-items: center; flex: none; }
/* inline icons carry no width attribute — sized SVGs (sparklines, charts) keep theirs */
svg:not([width]) { width: 16px; height: 16px; }
a { color: inherit; text-decoration: none; }
button, input, select, textarea { font: inherit; color: inherit; background: none; border: none; }
button { cursor: pointer; }
ul, ol { list-style: none; }
table { border-collapse: collapse; width: 100%; }

/* ---------- 2b. scrollbars ------------------------------------------------
   Two separate things went wrong in the filter rail after dark, and only one
   of them is decoration.

   The first is `color-scheme`. It was never declared, so the browser assumed
   the page was a light one and drew every scrollbar it owns in the light UA
   style: a white trough with a grey thumb, sitting inside a #141414 card. It
   is not a colour we chose, it is the colour of not having said anything —
   and it also governs the native select popup, the autofill highlight and the
   caret. One line per theme fixes all of them at once.

   The second is that the UA scrollbar is ugly in either theme. So every
   scrolling box gets the same one: a 5px thumb in the palette's strongest
   hairline, inset 3px from each edge by a transparent border rather than by a
   coloured one, which is what lets it sit on a card, a canvas or an inverted
   panel without carrying a background of its own. Track transparent, corner
   transparent, hover a step darker.

   The rules that deliberately hide a scrollbar — the client strips, the docs
   rail, the blog filters — are all class-scoped, so they still win. */
:root { color-scheme: light; }
[data-theme="dark"] { color-scheme: dark; }

* { scrollbar-width: thin; scrollbar-color: var(--line-2) transparent; }
::-webkit-scrollbar { width: 11px; height: 11px; }
::-webkit-scrollbar-track { background: transparent; }
::-webkit-scrollbar-corner { background: transparent; }
::-webkit-scrollbar-thumb {
  background: var(--line-2); border-radius: 6px;
  border: 3px solid transparent; background-clip: padding-box;
}
::-webkit-scrollbar-thumb:hover { background: var(--ink-3); background-clip: padding-box; }

:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: 3px; }

/* One ring, not two.

   The rule above is the fallback, and it is the right one for a link, a
   button, a checkbox or a tab: they have no focus style of their own, so they
   get a 2px outline. A text field on this site is different — it is already
   drawn focused. Either the .field / .big-field wrapper takes the accent
   border and a soft halo, or the control draws the same thing on itself.

   On macOS a text input matches :focus-visible on a plain mouse click, not
   only on Tab, so clicking into the search box produced both indicators at
   once: the wrapper's 4px halo outside the box and the input's own 2px outline
   inside it, one nested in the other.

   Below is every control that already says it is focused; the list was read
   off the stylesheets rather than guessed, so anything not on it — including
   any control added later — keeps the fallback ring. */
.field :focus-visible,
.big-field :focus-visible,
.sk-new .input:focus-visible,
.sk-new-md:focus-visible,
.pop-f input:focus-visible,
.db-form input[type="text"]:focus-visible,
.db-in:focus-visible,
.db-list-in:focus-visible,
.db-num:focus-visible,
.db-tkin:focus-visible,
.fp-numin:focus-visible,
.pay-in:focus-visible,
.rw-body:focus-visible,
.rw-in:focus-visible { outline: none; }
/* .input and .select are only given a ring of their own by the v2 language —
   on the Datasheet schemes a bare select has nothing but the fallback outline,
   so the suppression has to be scoped as narrowly as the ring it replaces */
[data-lang="v2"] .input:focus-visible,
[data-lang="v2"] textarea.input:focus-visible,
[data-lang="v2"] .select:focus-visible { outline: none; }
/* The one exception that is not a control at all: modal() focuses the panel
   itself on open, so a keyboard reader lands inside the dialog rather than
   behind it. That is a landing point and not something to press, and opening
   a modal from the keyboard drew the fallback ring around all 640px of it. */
.cmp[tabindex="-1"]:focus-visible { outline: none; }
::selection { background: var(--accent); color: var(--on-accent); }
[data-theme="dark"] ::selection { color: #0A0C0B; }

/* ---------- 3. typography ------------------------------------------------ */
h1, h2, h3, h4 { font-weight: 600; letter-spacing: -.022em; line-height: 1.15; }
.display {
  font-size: clamp(38px, 5.4vw, 62px);
  line-height: 1.03;
  letter-spacing: -.038em;
  font-weight: 600;
}
.h2 { font-size: clamp(26px, 3vw, 34px); letter-spacing: -.03em; line-height: 1.1; }
.h3 { font-size: 19px; letter-spacing: -.02em; }
.h4 { font-size: 15.5px; letter-spacing: -.012em; }
.lead { font-size: 17px; line-height: 1.55; color: var(--ink-2); }
.small { font-size: 13.5px; }
.muted { color: var(--ink-2); }
.dim { color: var(--ink-3); }
.mono { font-family: var(--mono); font-variant-numeric: tabular-nums; }
.num { font-family: var(--mono); font-variant-numeric: tabular-nums; letter-spacing: -.02em; }

/* signature spec label: mono caps with a square tick

   IT WAS ONLY EVER DESIGNED FOR ONE LINE. `line-height: 1` and a centred tick
   are right for a label that fits, and both fall apart the moment it wraps —
   which on a phone is most of the longer ones. Two lines set solid nearly
   touch, and the tick floats to the middle of the block instead of marking
   where the label starts. So: a line-height that leaves room between wrapped
   lines, and a tick pinned to the first line, nudged down by the difference
   between that line box and its own 6px so it sits on the letters rather than
   above them. A label on one line looks exactly as it did. */
.spec {
  display: inline-flex; align-items: flex-start; gap: 8px;
  font: 500 11px/1.5 var(--mono);
  text-transform: uppercase; letter-spacing: .14em;
  color: var(--ink-3);
}
.spec::before {
  content: ''; width: 6px; height: 6px; border-radius: 1px;
  background: var(--accent); flex: none; margin-top: 5px;
}
.spec.plain::before { display: none; }

/* ---------- 4. layout ---------------------------------------------------- */
.wrap { width: 100%; max-width: var(--w); margin-inline: auto; padding-inline: 24px; }
.wrap-wide { width: 100%; max-width: var(--w-wide); margin-inline: auto; padding-inline: 24px; }
.section { padding: 72px 0; }
.section-sm { padding: 48px 0; }
.row { display: flex; align-items: center; gap: 10px; }
.row-b { display: flex; align-items: center; justify-content: space-between; gap: 16px; }
.col { display: flex; flex-direction: column; }
.wrapg { display: flex; flex-wrap: wrap; }
.grow { flex: 1 1 auto; min-width: 0; }
.rule { height: 1px; background: var(--line); border: 0; }
.rule-dot {
  height: 1px; border: 0;
  background: repeating-linear-gradient(90deg, var(--line-2) 0 2px, transparent 2px 6px);
}
.grid { display: grid; gap: 16px; }
/* grid items default to min-width:auto — that lets a wide scroll strip blow up the column */
.grid > *, .split > *, .aside > *, .hero-grid > *, .panel-grid > *,
.pub-layout > *, .cat-layout > *, .srv-layout > * { min-width: 0; }
.g2 { grid-template-columns: repeat(2, minmax(0,1fr)); }
.g3 { grid-template-columns: repeat(3, minmax(0,1fr)); }
.g4 { grid-template-columns: repeat(4, minmax(0,1fr)); }
.section-head { display: flex; align-items: flex-end; justify-content: space-between; gap: 24px; margin-bottom: 26px; flex-wrap: wrap; }
.section-head p { color: var(--ink-2); margin-top: 8px; max-width: 62ch; }

/* ---------- 5. buttons --------------------------------------------------- */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 7px;
  height: 38px; padding: 0 15px; border-radius: var(--r1);
  font-size: 14px; font-weight: 550; letter-spacing: -.01em;
  border: 1px solid transparent; white-space: nowrap;
  transition: background .14s, border-color .14s, color .14s, transform .08s, box-shadow .14s;
}
.btn:active { transform: translateY(1px); }
.btn svg { width: 15px; height: 15px; flex: none; }

.btn-primary { background: var(--accent); color: var(--on-accent); box-shadow: inset 0 -1px 0 rgba(0,0,0,.16); }
.btn-primary:hover { background: var(--accent-2); }

.btn-ink { background: var(--ink); color: var(--paper); }
.btn-ink:hover { background: #2A2E33; }
[data-theme="dark"] .btn-ink:hover { background: #DCDBD6; }

/* primary action on the skills surface */
.btn-skill { background: var(--skill); color: var(--on-skill); box-shadow: inset 0 -1px 0 rgba(0,0,0,.16); }
.btn-skill:hover { background: var(--skill-2); }
[data-theme="dark"] .btn-skill { color: #0B1020; }

.btn-ghost { border-color: var(--line-2); background: var(--surface); color: var(--ink); }
.btn-ghost:hover { border-color: var(--ink-3); background: var(--surface-2); }

.btn-quiet { color: var(--ink-2); }
.btn-quiet:hover { color: var(--ink); background: var(--surface-2); }

.btn-sm { height: 31px; padding: 0 11px; font-size: 13px; border-radius: var(--r1); }
.btn-sm svg { width: 14px; height: 14px; }
.btn-lg { height: 46px; padding: 0 22px; font-size: 15.5px; }
.btn-block { width: 100%; }
.btn-icon { width: 34px; height: 34px; padding: 0; }
.btn-sm.btn-icon { width: 31px; height: 31px; }
.btn[disabled] { opacity: .45; pointer-events: none; }
/* toggled state for save / in-a-list buttons */
.btn.on { color: var(--accent); border-color: var(--accent-line); background: var(--accent-soft); }
.btn.on:hover { border-color: var(--accent); background: var(--accent-soft); }

/* ---------- 6. form controls --------------------------------------------- */
.field {
  display: flex; align-items: center; gap: 9px;
  height: 38px; padding: 0 12px;
  background: var(--surface); border: 1px solid var(--line-2); border-radius: var(--r1);
  transition: border-color .14s, box-shadow .14s;
}
.field:focus-within { border-color: var(--accent); box-shadow: 0 0 0 3px var(--accent-soft); }
.field svg { width: 16px; height: 16px; color: var(--ink-3); flex: none; }
/* height:100% — the box is a <div>, not a <label>, so a tap on the padding above
   or below the text does not reach the input unless the input fills the box.
   Measured: on a 44px phone field the input itself was 24.8px, so the top and
   bottom ~10px of every search field on the site did nothing. */
.field input, .field select { width: 100%; height: 100%; background: none; font-size: 14px; }
.field input::placeholder { color: var(--ink-3); }
select.select {
  height: 34px; padding: 0 28px 0 10px; border: 1px solid var(--line-2); border-radius: var(--r1);
  background: var(--surface) url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%23878C93' stroke-width='1.6'%3E%3Cpath d='M4 6.5 8 10.5 12 6.5'/%3E%3C/svg%3E") no-repeat right 8px center/14px;
  font-size: 13.5px; -webkit-appearance: none; appearance: none; cursor: pointer;
}
/* THE COUNT AS A LINK, and why the row needed a wrapper for it. A category
   and a client have landings of their own (/catalog/data, /catalog/openai);
   the number beside the name is the one part of the row that says how big
   that place is, so it is the part that opens it. An arrow was tried here
   first and removed: a second glyph per row, saying what the number said.

   Outside the <label> on purpose — inside it, a click on the number toggles
   the checkbox on the way to the page. Hence .check-row, which puts the label
   and the number side by side and leaves them looking exactly as they did. */
.check-row { display: flex; align-items: center; gap: 9px; }
.check-row .check { flex: 1; min-width: 0; }
.cnt-go { text-decoration: none; transition: color .12s; }
.cnt-go:hover { color: var(--ink); text-decoration: underline; }
.cnt-go:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: 3px; }

.check { display: flex; align-items: center; gap: 9px; cursor: pointer; font-size: 13.5px; color: var(--ink-2); padding: 3px 0; }
.check input { appearance: none; width: 15px; height: 15px; border: 1px solid var(--line-2); border-radius: 4px; background: var(--surface); flex: none; transition: .12s; }
/* The tick was white on the lime fill — two light colours on top of each other,
   and at 11px the mark all but disappeared. It is drawn in the ink the accent
   is designed to carry (the same pairing the primary button uses), so the box
   reads as ticked at a glance and in both themes. */
.check input:checked { background: var(--accent) url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%230C1116' stroke-width='2.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m3.5 8.5 3 3 6-6.5'/%3E%3C/svg%3E") center/11px no-repeat; border-color: var(--accent); }
.check input:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.check:hover { color: var(--ink); }
.check:hover input { border-color: var(--ink-3); }
.switch { position: relative; width: 34px; height: 20px; border-radius: 20px; background: var(--surface-3); border: 1px solid var(--line-2); transition: .16s; flex: none; }
.switch::after { content: ''; position: absolute; inset: 2px auto 2px 2px; width: 14px; border-radius: 50%; background: var(--surface); box-shadow: var(--sh1); transition: .16s; }
.switch[aria-checked="true"] { background: var(--accent); border-color: var(--accent); }
.switch[aria-checked="true"]::after { left: 15px; background: #fff; }
input[type="range"] { -webkit-appearance: none; appearance: none; width: 100%; height: 20px; background: none; cursor: pointer; }
input[type="range"]::-webkit-slider-runnable-track { height: 4px; background: var(--surface-3); border-radius: 2px; }
input[type="range"]::-webkit-slider-thumb { -webkit-appearance: none; width: 16px; height: 16px; margin-top: -6px; border-radius: 50%; background: var(--surface); border: 2px solid var(--accent); box-shadow: var(--sh1); }

/* ---------- 7. badges / tags --------------------------------------------- */
.badge {
  display: inline-flex; align-items: center; gap: 5px;
  height: 21px; padding: 0 7px; border-radius: 5px;
  font: 500 11.5px/1 var(--mono); letter-spacing: .02em;
  border: 1px solid var(--line); background: var(--surface-2); color: var(--ink-2);
  white-space: nowrap;
}
.badge svg { width: 12px; height: 12px; }
.badge-accent { background: var(--accent-soft); border-color: var(--accent-line); color: var(--accent); }
.badge-risk   { background: var(--risk-soft); border-color: var(--risk-line); color: var(--risk); }
.badge-warn   { background: var(--warn-soft); border-color: var(--warn-line); color: var(--warn); }
.badge-ink    { background: var(--ink); border-color: var(--ink); color: var(--paper); }
.badge-free   { background: var(--surface-2); border-color: var(--line-2); color: var(--ink-2); }
.badge-skill  { background: var(--skill-soft); border-color: var(--skill-line); color: var(--skill); }
.tag {
  display: inline-flex; align-items: center; height: 22px; padding: 0 8px;
  border: 1px solid var(--line); border-radius: 5px; background: var(--surface);
  font-size: 12px; color: var(--ink-2); transition: .12s;
}
a.tag:hover { border-color: var(--ink-3); color: var(--ink); }
.verified { color: var(--accent); display: inline-flex; }
.verified svg { width: 14px; height: 14px; }

/* grade chip: A+ / A / B / C */
.grade {
  display: inline-flex; align-items: center; justify-content: center;
  min-width: 26px; height: 21px; padding: 0 5px; border-radius: 5px;
  font: 600 11.5px/1 var(--mono); letter-spacing: .02em;
  border: 1px solid var(--accent-line); background: var(--accent-soft); color: var(--accent);
}
.grade[data-g="B"] { border-color: var(--warn-line); background: var(--warn-soft); color: var(--warn); }
.grade[data-g="C"] { border-color: var(--risk-line); background: var(--risk-soft); color: var(--risk); }

/* ---------- 8. cards ----------------------------------------------------- */
.card {
  background: var(--surface); border: 1px solid var(--line); border-radius: var(--r3);
  box-shadow: var(--sh1);
}
.card-pad { padding: 18px; }
.card-hover { transition: border-color .16s, box-shadow .16s, transform .16s; }
.card-hover:hover { border-color: var(--line-2); box-shadow: var(--sh2); transform: translateY(-2px); }
.card-head { padding: 14px 18px; border-bottom: 1px solid var(--line); display: flex; align-items: center; justify-content: space-between; gap: 12px; }
.card-body { padding: 18px; }
.card-foot { padding: 12px 18px; border-top: 1px solid var(--line); background: var(--surface-2); border-radius: 0 0 var(--r3) var(--r3); }
.card-foot.row, .card-foot.row-b { flex-wrap: wrap; }

/* icon tile for servers */
.tile {
  width: 38px; height: 38px; flex: none; border-radius: var(--r2);
  display: grid; place-items: center;
  background: var(--surface-2); border: 1px solid var(--line);
  font: 600 15px/1 var(--mono); letter-spacing: -.04em; color: var(--ink);
}
.tile-lg { width: 54px; height: 54px; font-size: 20px; border-radius: 11px; }
.tile svg { width: 19px; height: 19px; }

/* ---------- 9. tables ---------------------------------------------------- */
.tbl { font-size: 13.5px; }
.tbl th {
  text-align: left; font: 500 11px/1 var(--mono); text-transform: uppercase; letter-spacing: .1em;
  color: var(--ink-3); padding: 10px 12px; border-bottom: 1px solid var(--line); white-space: nowrap;
}
.tbl td { padding: 11px 12px; border-bottom: 1px solid var(--line); vertical-align: middle; }
.tbl tbody tr:last-child td { border-bottom: 0; }
.tbl tbody tr { transition: background .12s; }
.tbl-hover tbody tr:hover { background: var(--surface-2); }
.tbl .r { text-align: right; }

/* ---------- 10. meters / sparks ------------------------------------------ */
.meter { display: inline-flex; gap: 2px; align-items: center; }
.meter i { width: 9px; height: 5px; border-radius: 1px; background: var(--surface-3); display: block; }
.meter i.on { background: var(--accent); }
.meter i.on[data-t="warn"] { background: var(--warn); }
.meter i.on[data-t="risk"] { background: var(--risk); }
.bar { height: 5px; border-radius: 3px; background: var(--surface-3); overflow: hidden; }
.bar > span { display: block; height: 100%; background: var(--accent); border-radius: 3px; }
.stars { display: inline-flex; gap: 1px; color: var(--ink); }
.stars svg { width: 13px; height: 13px; }
.stars .off { color: var(--surface-3); }

/* ---------- 11. header --------------------------------------------------- */
.nav {
  position: sticky; top: 0; z-index: 60;
  height: var(--nav-h); border-bottom: 1px solid var(--line);
  background: color-mix(in srgb, var(--paper) 88%, transparent);
  backdrop-filter: saturate(1.6) blur(12px);
}
.nav-in { height: 100%; display: flex; align-items: center; gap: 22px; }
/* everything except the search box holds its natural width, so a narrow window
   eats into the search rather than squashing the buttons into slivers */
.nav-in > .logo, .nav-in > .btn, .nav-in > .badge { flex: none; }
.logo { display: flex; align-items: center; gap: 9px; font-size: 16.5px; font-weight: 600; letter-spacing: -.035em; }
.logo-mark {
  width: 24px; height: 24px; border-radius: 6px; background: var(--ink);
  display: grid; place-items: center; flex: none;
}
.logo-mark svg { width: 14px; height: 14px; color: var(--paper); }
/* flex:none — the labels are fixed copy and must never squash; the search box
   to their right is the flexible element that gives way as the window narrows */
.nav-links { display: flex; align-items: center; gap: 2px; flex: none; }
.nav-links a {
  position: relative; padding: 7px 10px; border-radius: var(--r1); font-size: 14px;
  color: var(--ink-2); white-space: nowrap; transition: .12s;
}
.nav-links a:hover { color: var(--ink); background: var(--surface-2); }
/* every item carries the same weight — the two product sections are not
   privileged over the utility links, only the current page is marked */
.nav-links a.on { color: var(--ink); font-weight: 550; }
.nav-search {
  display: flex; align-items: center; gap: 8px; width: 100%; max-width: 300px;
  min-width: 0; flex-shrink: 1;
  height: 34px; padding: 0 9px; border: 1px solid var(--line-2); border-radius: var(--r1);
  background: var(--surface); color: var(--ink-3); font-size: 13.5px; cursor: text;
  transition: .14s;
}
.nav-search:hover { border-color: var(--ink-3); }
.nav-search svg { width: 15px; height: 15px; flex: none; }
/* one line, ellipsised — the placeholder must never wrap inside the 34px box */
.nav-search .grow { overflow: hidden; white-space: nowrap; text-overflow: ellipsis; }
.lib-dot {
  position: absolute; top: -3px; right: -4px; min-width: 15px; height: 15px; padding: 0 4px;
  border-radius: 5px; background: var(--accent); color: var(--on-accent);
  font: 600 10px/15px var(--mono); text-align: center; box-shadow: 0 0 0 2px var(--paper);
}
[data-theme="dark"] .lib-dot { color: #06180F; }
.kbd {
  font: 500 11px/1 var(--mono); padding: 3px 5px; border: 1px solid var(--line);
  border-radius: 4px; background: var(--surface-2); color: var(--ink-3);
}
.nav-burger { display: none; }

/* announcement strip */
.strip {
  border-bottom: 1px solid var(--line); background: var(--surface);
  font-size: 13px; color: var(--ink-2);
}
/* .wrap-wide is what index.html actually uses here, and the old rule named only
   .wrap — so the strip was never a flex row at all: the live dot collapsed to a
   zero-width sliver and the sentence sat unaligned against the left gutter.
   min-height rather than height, because at 390px the sentence takes two lines
   and a fixed 36px box would spill them over the header's border. */
.strip .wrap, .strip .wrap-wide { display: flex; align-items: center; justify-content: center; flex-wrap: wrap; gap: 2px 10px; min-height: 36px; padding-block: 7px; }
.strip a { color: var(--ink); font-weight: 550; display: inline-flex; align-items: center; gap: 4px; }
.strip a:hover { color: var(--accent); }
.strip svg { width: 13px; height: 13px; }

/* ---------- 12. footer --------------------------------------------------- */
.foot { border-top: 1px solid var(--line); background: var(--surface); padding: 56px 0 30px; margin-top: 40px; }
.foot-grid { display: grid; grid-template-columns: 1.6fr repeat(4, minmax(0,1fr)); gap: 32px; }
/* the way out, last in the phone menu and drawn as what it is: the one entry
   there that ends something rather than going somewhere */
.nsheet a.nsheet-out { color: var(--ink-2); }
.nsheet a.nsheet-out:hover { color: var(--ink); }

.foot h5, .foot .foot-t { font: 500 11px/1 var(--mono); text-transform: uppercase; letter-spacing: .12em; color: var(--ink-3); margin-bottom: 14px; }
.foot li { margin-bottom: 9px; }
.foot li a { font-size: 13.5px; color: var(--ink-2); }
.foot li a:hover { color: var(--ink); }
/* ---- the social row -------------------------------------------------------
   Under the status badge in the footer's first column, and drawn by the server
   only for the addresses somebody entered — see withFooterFirstColumn in server.ts.
   Square, quiet, and the same size as the badge beside them, because they are
   the least important thing in that column and should not out-shout the
   sentence that says what this site is. */
.foot-socs { flex-wrap: wrap; }
.foot-soc {
  width: 30px; height: 30px; border-radius: var(--r1);
  display: inline-grid; place-items: center; flex: none;
  border: 1px solid var(--line); background: var(--surface-2); color: var(--ink-3);
  transition: color .12s ease, border-color .12s ease, background .12s ease;
}
.foot-soc svg { width: 15px; height: 15px; display: block; }
.foot-soc:hover { color: var(--ink); border-color: var(--line-2, var(--line)); background: var(--surface-3); }
/* padding-bottom clears the role switcher, which is fixed over the bottom-right
   corner of every page. It lives on the base rule rather than on each page's
   markup so a footer can never forget it. */
.foot-bot { display: flex; justify-content: space-between; align-items: center; gap: 16px; margin-top: 44px; padding-top: 20px; padding-bottom: 44px; border-top: 1px solid var(--line); font-size: 12.5px; color: var(--ink-3); flex-wrap: wrap; }

/* ---------- 13. tabs ----------------------------------------------------- */
.tabs { display: flex; gap: 2px; border-bottom: 1px solid var(--line); overflow-x: auto; scrollbar-width: none; }
.tabs::-webkit-scrollbar { display: none; }
.tabs button {
  position: relative; padding: 11px 12px; font-size: 14px; font-weight: 500;
  color: var(--ink-2); white-space: nowrap; transition: color .12s;
}
.tabs button:hover { color: var(--ink); }
.tabs button.on { color: var(--ink); font-weight: 600; }
.tabs button.on::after { content: ''; position: absolute; left: 8px; right: 8px; bottom: -1px; height: 2px; background: var(--accent); border-radius: 2px 2px 0 0; }
.tabs .count { font: 500 11px/1 var(--mono); color: var(--ink-3); margin-left: 5px; }

/* segmented control */
.seg { display: inline-flex; padding: 2px; background: var(--surface-2); border: 1px solid var(--line); border-radius: 7px; gap: 2px; }
/* A SEGMENT MAY HOLD EITHER SHAPE, and which one it holds is a fact about what
   it does. The view picker on the catalogue switches a layout — a preference,
   with no address — and stays buttons. The two boards on /leaderboard are two
   addresses, so they are links; the rules are shared so the two look identical.
   `text-decoration` and `display` are the only things an anchor does not
   already inherit here. */
.seg button, .seg > a { padding: 5px 10px; border-radius: 5px; font-size: 13px; color: var(--ink-2); font-weight: 500; transition: .12s; display: inline-flex; align-items: center; gap: 6px; text-decoration: none; }
.seg button svg, .seg > a svg { width: 14px; height: 14px; }
.seg button.on, .seg > a.on { background: var(--surface); color: var(--ink); box-shadow: var(--sh1); }

/* ---------- 14. code block ----------------------------------------------- */
.code {
  position: relative; background: var(--panel); border: 1px solid var(--panel-line);
  border-radius: var(--r2); color: var(--panel-ink);
  font: 400 12.5px/1.7 var(--mono); padding: 12px 44px 12px 13px;
  overflow-x: auto; white-space: pre; scrollbar-width: thin;
}
.code.soft { white-space: pre-wrap; overflow-wrap: anywhere; }
.code .c-dim { color: var(--panel-ink2); }
.code .c-key { color: #7FD8A8; }
.code .c-str { color: #E4C77A; }
.code-copy {
  position: absolute; top: 7px; right: 7px; width: 28px; height: 28px;
  display: grid; place-items: center; border-radius: 5px; color: var(--panel-ink2);
  border: 1px solid var(--panel-line); background: var(--panel-2); transition: .12s;
}
.code-copy:hover { color: var(--panel-ink); border-color: #444; }
.code-copy svg { width: 14px; height: 14px; }

/* ---------- 14b. the phone's section menu ---------------------------------
   What the burger opens. It is deliberately not the palette: the palette
   searches listings, and a burger is asked for the site. Six links, the
   current one marked, in a sheet that rises from the edge the thumb is on. */
.nsheet-back {
  position: fixed; inset: 0; z-index: 210; background: rgba(12,13,16,.5);
  backdrop-filter: blur(2px);
  display: flex; flex-direction: column; justify-content: flex-end;
}
/* the same handle the palette's sheet carries, for the same reason: the strip
   of dimmed page above a sheet has to read as "tap to close", and this is the
   one mark that says so on every phone the reader has used */
.nsheet-back::before {
  content: ''; flex: none; width: 44px; height: 4px; border-radius: 2px;
  margin: 0 auto 12px; background: rgba(255,255,255,.62);
}
/* the entrance is an animation rather than a class toggled on the next frame:
   a backgrounded tab never runs that frame, and the sheet would open at
   opacity 0 with the page showing straight through it */
.nsheet {
  background: var(--surface); border-top: 1px solid var(--line-2);
  border-radius: var(--r3) var(--r3) 0 0; box-shadow: var(--sh3);
  padding: 6px 8px 14px; max-height: 84dvh; overflow-y: auto;
  animation: nsheet-in .16s ease-out;
}
@keyframes nsheet-in { from { transform: translateY(10px); } to { transform: none; } }
.nsheet-h { display: flex; align-items: center; justify-content: space-between; padding: 8px 8px 6px; }
/* :not(.btn), on both rules. The two account buttons at the top of the sheet
   are also <a> inside .nsheet, so they were picking up the row styling meant
   for the navigation list under them: `color: var(--ink)` put dark type on the
   filled green Sign in, and `a + a { border-radius: 0 }` squared off Create an
   account while its neighbour stayed round. A button is not a row. */
.nsheet a:not(.btn) {
  display: flex; align-items: center; justify-content: space-between; gap: 10px;
  min-height: 48px; padding: 0 10px; border-radius: var(--r1);
  font-size: 15.5px; font-weight: 550; letter-spacing: -.01em; color: var(--ink);
}
.nsheet a:not(.btn) + a:not(.btn) { border-top: 1px solid var(--line); border-radius: 0; }
/* both doors the same height and the same shape */
.nsheet-acct-out .btn { min-height: 44px; border-radius: var(--r1); }
/* the account, at the top of the menu */
.nsheet-acct {
  display: flex; align-items: center; gap: 11px;
  padding: 10px; margin: 2px 0 8px;
  border: 1px solid var(--line); border-radius: var(--r2);
  background: var(--surface-2);
}
.nsheet-acct .nav-acct-av { width: 34px; height: 34px; border-radius: 8px; font-size: 12.5px; }
.nsheet-acct b { display: block; font-size: 15px; letter-spacing: -.01em; }
.nsheet-acct .s { display: block; font-size: 12.5px; color: var(--ink-3); }
.nsheet-acct .btn { flex: none; pointer-events: none; }
.nsheet-acct-out { gap: 8px; background: none; border: 0; padding: 2px 0 10px; }
.nsheet-acct-out .btn { flex: 1 1 0; pointer-events: auto; }
/* the site's search, which the phone header has nowhere to put */
.nsheet-search {
  display: flex; align-items: center; gap: 10px; width: 100%;
  min-height: 44px; padding: 0 12px; margin-bottom: 8px;
  border: 1px solid var(--line-2); border-radius: var(--r2);
  background: var(--surface); color: var(--ink-3);
  font: 400 14px/1 var(--sans); text-align: left;
}
.nsheet-search span:first-of-type { flex: 1 1 auto; }
.nsheet-search svg { width: 16px; height: 16px; flex: none; }
.nsheet-search .kbd { flex: none; }
/* a group heading inside the list */
.nsheet-sep { display: block; padding: 14px 10px 6px; }
.nsheet-sep + a { border-top: 0; }
.nsheet a svg { width: 15px; height: 15px; color: var(--ink-3); }
.nsheet a.on { color: var(--accent); }
.nsheet a:active { background: var(--surface-2); }

/* ---------- 14c. the section bar on a long page ---------------------------
   Mounted by initSectionNav() in app.js on any page carrying a section rail.
   A phone reads a 9,000px document with no idea where it is in it and no way
   back to the top of the list; this names the section under the reader and
   opens the whole list on a tap. Desktop keeps the rail it already has, so the
   bar exists only below the phone breakpoint. */
.secnav { display: none; }
@media (max-width: 620px) {
  .secnav {
    position: fixed; left: 12px; right: 12px; bottom: 12px; z-index: 120;
    display: flex; align-items: center; gap: 10px;
    height: 46px; padding: 0 12px 0 14px; text-align: left;
    background: var(--surface); border: 1px solid var(--line-2);
    border-radius: 10px; box-shadow: var(--sh2);
  }
  .secnav .spec { flex: none; }
  .secnav-t {
    flex: 1 1 auto; min-width: 0; font-size: 14px; font-weight: 550;
    letter-spacing: -.01em; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  }
  .secnav svg { flex: none; width: 16px; height: 16px; color: var(--ink-3); }
  /* the end of the page has to clear a bar that floats over it */
  body.has-secnav .foot-bot { padding-bottom: 78px !important; }
  /* a sheet is the whole screen; the bar under it is noise */
  body:has(.nsheet-back) .secnav, body:has(.pal-back.on) .secnav { display: none; }
}

/* ---------- 15. command palette ------------------------------------------ */
.pal-back {
  position: fixed; inset: 0; z-index: 200; background: rgba(12,13,16,.5);
  backdrop-filter: blur(2px); display: none; padding-top: 12vh;
}
.pal-back.on { display: block; }
.pal {
  width: min(640px, calc(100% - 32px)); margin-inline: auto;
  background: var(--surface); border: 1px solid var(--line-2); border-radius: var(--r3);
  box-shadow: var(--sh3); overflow: hidden;
}
.pal-in { display: flex; align-items: center; gap: 10px; padding: 14px 16px; border-bottom: 1px solid var(--line); }
.pal-in svg { width: 17px; height: 17px; color: var(--ink-3); }
.pal-in input { width: 100%; font-size: 15.5px; }
.pal-list { max-height: 52vh; overflow-y: auto; padding: 6px; }
.pal-sec { font: 500 10.5px/1 var(--mono); text-transform: uppercase; letter-spacing: .12em; color: var(--ink-3); padding: 10px 10px 6px; }
.pal-item { display: flex; align-items: center; gap: 11px; padding: 8px 10px; border-radius: var(--r1); cursor: pointer; }
.pal-item .tile { width: 28px; height: 28px; font-size: 12px; border-radius: 7px; }
.pal-item.on { background: var(--surface-2); }
.pal-item .t { font-size: 14px; font-weight: 500; }
.pal-item .s { font-size: 12.5px; color: var(--ink-3); }
.pal-foot { padding: 9px 14px; border-top: 1px solid var(--line); background: var(--surface-2); display: flex; gap: 16px; font-size: 12px; color: var(--ink-3); }

/* ---------- 15b. popover (add to list) ----------------------------------- */
.pop {
  position: fixed; z-index: 220; width: 272px;
  background: var(--surface); border: 1px solid var(--line-2);
  border-radius: var(--r2); box-shadow: var(--sh3); overflow: hidden;
}
.pop-h { padding: 11px 13px 9px; border-bottom: 1px solid var(--line); }
.pop-h .spec { letter-spacing: .1em; color: var(--ink-2); }
.pop-b { padding: 7px 13px; max-height: 208px; overflow-y: auto; }
.pop-b .check { padding: 5px 0; }
.pop-b .check span.grow { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.pop-f { display: flex; gap: 6px; padding: 10px 13px; border-top: 1px solid var(--line); background: var(--surface-2); }
.pop-f input {
  flex: 1; min-width: 0; height: 31px; padding: 0 9px; font-size: 13px;
  border: 1px solid var(--line-2); border-radius: var(--r1); background: var(--surface);
}
.pop-f input:focus { border-color: var(--accent); outline: none; box-shadow: 0 0 0 3px var(--accent-soft); }

/* ---------- 16. toast ---------------------------------------------------- */
.toasts { position: fixed; bottom: 20px; left: 50%; transform: translateX(-50%); z-index: 300; display: flex; flex-direction: column; gap: 8px; align-items: center; }
.toast {
  display: flex; align-items: center; gap: 9px; padding: 9px 14px;
  background: var(--panel); color: var(--panel-ink); border-radius: 8px; font-size: 13.5px;
  box-shadow: var(--sh2); animation: toastIn .22s cubic-bezier(.2,.8,.3,1);
}
/* the toast is the dark panel too, wherever it is shown */
.toast svg { width: 15px; height: 15px; color: var(--panel-accent); }
@keyframes toastIn { from { opacity: 0; transform: translateY(8px); } }

/* ---------- 17. misc ----------------------------------------------------- */
.link { color: var(--ink); border-bottom: 1px solid var(--line-2); transition: .12s; }
.link:hover { color: var(--accent); border-color: var(--accent-line); }
.link-a { color: var(--accent); font-weight: 500; display: inline-flex; align-items: center; gap: 4px; }
.link-a svg { width: 14px; height: 14px; transition: transform .15s; }
.link-a:hover svg { transform: translateX(2px); }
.dot { width: 5px; height: 5px; border-radius: 50%; background: var(--accent); flex: none; }
.dot-live { box-shadow: 0 0 0 0 color-mix(in srgb, var(--accent) 60%, transparent); animation: pulse 2.2s infinite; }
@keyframes pulse { 70% { box-shadow: 0 0 0 6px transparent; } 100% { box-shadow: 0 0 0 0 transparent; } }
[hidden] { display: none !important; }
.sr { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); }
/* the mirror of .hide-sm: markup that only exists once the phone breakpoint
   has taken a label away and left an icon standing in for it */
.show-sm { display: none !important; }

/* .rv is a no-op hook kept for markup stability — content is never hidden by JS */
.rv { opacity: 1; }

/* ---------- 18. responsive ----------------------------------------------- */
/* The header sheds one thing at a time, cheapest first: gaps, then the ⌘K hint,
   then the search label, then the utility links. The two product sections are
   the last labels standing before the burger takes over at 860px. */
@media (max-width: 1360px) {
  .nav-in { gap: 14px; }
  .nav-search { max-width: 210px; }
  .nav-search .kbd { display: none; }
}
@media (max-width: 1240px) {
  /* Icon-only search: the label no longer fits beside six nav labels.
     Fixed square, and it must not shrink — a shrinkable box in a crowded row
     collapses to a 1px hairline and the flex:none magnifier overflows beside
     it, which reads as a stray vertical rule in the header. */
  .nav-search { width: 34px; max-width: 34px; flex: none; padding: 0; justify-content: center; }
  .nav-search .grow { display: none; }
}
@media (max-width: 1120px) {
  /* the last utility link folds into the burger; the product sections stay */
  .nav-links a:nth-last-child(-n+1) { display: none; }
  .nav-burger { display: inline-flex; }
}
/* Between 861 and ~960 the row held six labels, the search box, four icon
   buttons, Publish, Sign in AND the burger, and overflowed to 948px behind the
   body's overflow-x:hidden — the burger arrives at 1120 but Publish only left
   at 620. It goes now instead: the burger already carries it, and every other
   route into publishing is a link on the page. */
@media (max-width: 1000px) {
  .nav-in > a.hide-sm { display: none; }
}
/* One more label folds away, because between 861 and 880 the five remaining
   ones still needed 866px against an 846px row. Both of the two hidden here
   are in the burger, which is already on screen at this width. */
@media (max-width: 940px) {
  .nav-links a:nth-last-child(-n+2) { display: none; }
}
@media (max-width: 1080px) {
  .g4 { grid-template-columns: repeat(2, minmax(0,1fr)); }
  .foot-grid { grid-template-columns: repeat(3, minmax(0,1fr)); }
}
@media (max-width: 860px) {
  .nav-links, .nav-search { display: none; }
  .nav-burger { display: inline-flex; }
  .g3 { grid-template-columns: repeat(2, minmax(0,1fr)); }
  .section { padding: 52px 0; }
  /* Dense tables scroll inside their own box instead of stretching the page.
     A table that lives in a wrapper (.results-tbl) lets the WRAPPER scroll —
     making the <table> itself display:block nests a second scroller inside the
     first and, worse, breaks position:sticky on the cells of the pinned first
     column. Only tables with no scrolling parent become the scroller. */
  .card > .tbl, .schema > .tbl { display: block; overflow-x: auto; white-space: nowrap; }

  /* ---- the other answer: fold instead of scroll ---------------------------
     A sideways scroller is right for a matrix — twelve columns of numbers read
     across. It is wrong for a two-column table of prose, which is what the
     legal pages, the referral states and the scan passes all are: the second
     column is a sentence, `nowrap` puts it on one line 700px long, and the
     reader gets a scrollbar where they wanted a paragraph.

     .tbl-fold turns such a table into one block per row: the first cell is the
     row's name, every other labelled cell prints its column name from data-l
     and wraps underneath. .lg-fold is the same thing under the name the legal
     pages were already written with. */
  .card > .tbl.tbl-fold, .card > .tbl.lg-fold,
  .tbl.tbl-fold, .tbl.lg-fold {
    display: block; overflow: visible; white-space: normal;
  }
  .tbl-fold thead, .lg-fold thead { display: none; }
  .tbl-fold tbody, .tbl-fold tr, .tbl-fold td,
  .lg-fold tbody, .lg-fold tr, .lg-fold td { display: block; }
  .tbl-fold tbody tr, .lg-fold tbody tr {
    padding: 13px 14px; border-bottom: 1px solid var(--line);
  }
  .tbl-fold tbody tr:last-child, .lg-fold tbody tr:last-child { border-bottom: 0; }
  .tbl-fold td, .lg-fold td { border: 0; padding: 0; white-space: normal; }
  .tbl-fold td.r, .lg-fold td.r { text-align: left; }
  .tbl-fold td:first-child, .lg-fold td:first-child {
    font-size: 14px; font-weight: 550; padding-bottom: 8px;
  }
  /* The label is the column header, printed once per cell, and it sits BESIDE
     its value rather than above it: a ledger row stacked as nine full-width
     lines is taller than the screen, and the same row as five label/value
     pairs is four lines. The label column is 104px — wide enough for
     "BALANCE AFTER" on one line at 10.5px mono — and a long value simply
     wraps under itself, which is what prose does in the legal tables. */
  .tbl-fold td[data-l]:not(:first-child),
  .lg-fold td[data-l]:not(:first-child) {
    display: flex; flex-wrap: wrap; align-items: baseline;
    gap: 1px 10px; padding: 5px 0 0;
  }
  .tbl-fold td[data-l]:not(:first-child)::before,
  .lg-fold td[data-l]:not(:first-child)::before {
    content: attr(data-l); flex: none; width: 104px;
    font: 500 10.5px/1.7 var(--mono); text-transform: uppercase; letter-spacing: .1em;
    color: var(--ink-3);
  }
  .tbl-fold td:empty, .lg-fold td:empty { display: none; }

  /* A two-column table whose second column is the explanation of the first
     does not need "MEANS" printed above every explanation — the row IS the
     definition. .tbl-bare drops the labels and keeps the block. Tables of
     numbers keep theirs, because "$240.00" alone says nothing. */
  .tbl-fold.tbl-bare td[data-l]:not(:first-child)::before { display: none; }
  .tbl-fold.tbl-bare td[data-l]:not(:first-child) { padding-top: 0; }
  .tbl-fold.tbl-bare td:first-child { padding-bottom: 5px; }

  /* A ledger's first column is the date, and a date is not the name of the
     row — the row is what happened. .tbl-kick demotes it to a kicker line
     above the heading, which is where a date belongs in a stacked layout. */
  .tbl-fold.tbl-kick td:first-child {
    font: 400 11.5px/1.5 var(--mono); letter-spacing: .02em;
    color: var(--ink-3); padding-bottom: 4px;
  }
  /* …which makes the SECOND cell the row's name, so it loses its label and
     takes the heading the date gave up. */
  .tbl-fold.tbl-kick td:nth-child(2) { display: block; padding: 0 0 4px; }
  .tbl-fold.tbl-kick td:nth-child(2)::before { display: none; }
  .tbl-fold.tbl-kick td:nth-child(2) b { font-size: 14px; font-weight: 550; }
  /* An "in" column on an outgoing row holds an em dash. Stacked, that becomes
     a labelled line saying nothing, and six of them per row is why a folded
     ledger reads worse than the table it came from. */
  .tbl-fold td[data-l][data-empty]:not(:first-child) { display: none; }

  /* A folded table has no columns left to keep apart, so the min-width the two
     cabinets put on a scrolling table (.db-scroll > .tbl, 520px) is what would
     push the page sideways instead. */
  .db-scroll > .tbl.tbl-fold,
  .db-scroll > .tbl.tbl-fold.db-wide { min-width: 0; }
  .db-scroll:has(> .tbl-fold) { overflow-x: visible; }
}

/* ---------- 18a. the bottom edge on a phone --------------------------------
   720px is where the role preview switcher stops being shown at all (see
   roles.css): it is scaffolding, and on a phone a permanent floating bar
   covers whatever the reader came for. So nothing has to be reserved for it
   any more, and the two things that had made room go back to normal — the end
   of the page and the toast. The inline padding-bottom:44px every page writes
   on .foot-bot is left to stand. */
@media (max-width: 720px) {
  .toasts { bottom: 20px; }
  /* THE TRACKING IS A DESKTOP MEASURE. At .14em an eleven-pixel mono capital
     carries a gap wider than some of its own letters, which reads as even
     spacing across a wide card and as ragged, unequal gaps once the label
     wraps on a phone — the same words, broken differently on every card. It
     comes in a little, and the label keeps its shape. */
  .spec { letter-spacing: .1em; }
}

/* ---------- 18b. the phone ------------------------------------------------
   620px is where the last two-column grid collapses, so it is also where the
   pointer stops being a mouse. Two forces meet here and the first version of
   this block only respected one of them. A thumb needs a bigger target than a
   cursor — but a 390px screen is a third of the width the type and the spacing
   were drawn for, so lifting every control to 44px and leaving 62px headlines,
   17px leads and 72px section padding in place produced a site where each part
   was individually tappable and the whole thing read as a poster: the home page
   measured 16,082px of scroll, the hero alone a screen and a third.
   So the ladder here is proportion, not maximum. Controls land between 34 and
   44 depending on how primary they are — the page's own actions at 40+, dense
   toolbars and chrome at 34–38 — and the display sizes step DOWN to match the
   column they are set in. Text fields stay at 16px regardless, because iOS
   zooms the page under anything smaller. */
@media (max-width: 620px) {
  /* ---- the phone type scale ----------------------------------------------
     A 62px display on a 358px column runs to four lines and stops being a
     sentence. These are the desktop sizes re-derived for the measure the
     phone actually has, not shrunk arbitrarily: the display keeps its clamp
     so it stays proportional between 320 and 620. */
  .display { font-size: clamp(26px, 7.2vw, 32px); line-height: 1.08; letter-spacing: -.03em; }
  .h2 { font-size: 21px; line-height: 1.14; }
  .h3 { font-size: 17px; }
  .lead { font-size: 14.5px; line-height: 1.5; }

  /* Two bars of chrome above every page's first line: the announcement, which
     repeats the section it links to, and the role strip, which repeats the
     account chip already in the header. On a 780px screen they cost 110px
     before the headline starts, so on a phone neither is shown. */
  .strip, .rstrip { display: none; }

  /* The header's own row is chrome, not content: its controls sit a notch below
     the page's, so a filled Sign in does not outweigh the wordmark. The icons
     keep a 36px box but grow their glyph — a 15px mark drawn for a 34px desk
     button reads as a speck at arm's length, and the bell and the bookmark are
     the two things in this row a reader actually aims at. */
  .nav-in .btn-sm { height: 34px; padding: 0 11px; font-size: 13px; }
  .nav-in .btn-icon { width: 34px; height: 34px; padding: 0; }
  .nav-in .btn-sm.btn-icon { width: 34px; height: 34px; padding: 0; }
  .nav-in .btn-icon svg { width: 18px; height: 18px; }
  /* the theme switch is the least of the four — it changes how the page looks,
     not what the account holds — so it sits a step below its neighbours rather
     than competing with the bell and the library */
  .nav-in [data-theme-toggle] svg { width: 15.5px; height: 15.5px; }

  /* The wordmark stays for as long as it fits. Below 620 the links and the
     search box are already gone, so what is left is the mark, the word, a
     flexible spacer and five 44px controls — and on a 375pt phone the word
     fits once the row's gap comes in to 10px and the word itself to 15.5px.
     It is clamped away only at the width where it genuinely stops fitting,
     which is measured in the rule below rather than guessed at. */
  .nav-in { gap: 8px; }
  .logo { gap: 7px; font-size: 15.5px; min-width: 24px; }

  .g2, .g3, .g4 { grid-template-columns: 1fr; }
  /* Two columns of links, and the brand block above them rather than beside
     one of them: at 358px it was a 169px cell holding a wordmark, a sentence
     and a status badge next to a column of six links, which read as a broken
     column rather than as the mark the footer belongs to. Full width, it is a
     line the reader crosses once on the way to the links. */
  /* Two columns of links under a full-width brand block. As a grid the four
     groups sat in two rows, and because the columns are unequal (six links
     against five) the shorter one left a ragged 90px hole before the next row
     started — which reads as a spacing bug rather than as a column ending.
     Columns flow instead: PRODUCT is followed immediately by DEVELOPERS in the
     same column, the browser balances the two, and the gaps are even. */
  .foot-grid { display: block; column-count: 2; column-gap: 20px; }
  .foot-grid > * { break-inside: avoid; margin-bottom: 20px; }
  .foot-grid > :first-child {
    column-span: all; margin-bottom: 18px; padding-bottom: 14px;
    border-bottom: 1px solid var(--line);
  }
  .foot-grid > :first-child p { max-width: none; margin-top: 8px !important; }
  .foot-grid > :first-child .row { margin-top: 10px !important; }
  .foot h5, .foot .foot-t { margin-bottom: 2px; }
  .wrap, .wrap-wide { padding-inline: 16px; }
  /* .row and .row-b are the two grouping utilities the whole site builds bars
     out of, and neither wrapped. On a desk that is right — a bar is one line.
     On a phone a group of three links or two buttons is routinely wider than
     the screen, and because the body clips at overflow-x:hidden the part that
     did not fit was not merely off the edge, it was unreachable. Measured: a
     row of section links on about.html ran to 359px inside a 288px column.
     They wrap here and nowhere else; the gap each already carries becomes the
     space between the lines. The header is not built from these — it is
     .nav-in, which sheds labels on a schedule of its own and stays one line. */
  .row, .row-b { flex-wrap: wrap; }
  .hide-sm { display: none !important; }
  .show-sm { display: inline-flex !important; }
  /* 40px above and below every section is 80px of nothing between two ideas on
     a screen 780px tall — the desk's rhythm at a third of the desk's width */
  .section { padding: 26px 0; }
  .section-sm { padding: 18px 0; }
  .section-head { margin-bottom: 14px; gap: 10px; }
  .section-head p { font-size: 14px; line-height: 1.5; margin-top: 6px; }

  /* iOS Safari zooms the whole page whenever a focused field is under 16px,
     which leaves the reader panned sideways on a page that never had a
     horizontal scrollbar. Every field you TYPE into therefore sits at 16px. */
  input, textarea,
  .field input, .pal-in input, .pop-f input { font-size: 16px; }
  /* A select is not one of them. It is never typed into — a tap opens the
     platform's own picker — and at 16px it was the largest text on a page whose
     body copy is 15 and whose labels are 13.5, so every sort control and every
     period picker read as a heading. It follows the labels around it instead. */
  select, .field select, select.select { font-size: 14px; }

  /* ---- the 12px floor -----------------------------------------------------
     The Datasheet mono label is 11px on a desk, where it sits under a cursor
     at arm's length. On a phone held at 30cm the same label is the smallest
     thing on the page and it carries the facts — the column head, the badge,
     the grade, the section tick. So every shared micro-label steps to 12px at
     phone width and nothing else about them changes: same mono, same caps,
     same tracking. The desktop typography is untouched.
     Two-letter monograms inside .tile and the account avatar are deliberately
     not in this list — they are marks, not text, and each is sized to its box
     rather than read as a word. */
  .spec, .badge, .grade, .kbd, .tabs .count,
  .foot h5, .foot .foot-t, .tbl th, .pal-sec, .rail-sec { font-size: 12px; }
  /* the count on the library icon is a 10px numeral in a 15px pill; both grow
     together so the digits keep their circle */
  .lib-dot { min-width: 17px; height: 17px; font-size: 12px; line-height: 17px; }

  /* The control ladder, by how primary the control is rather than one number
     for everything. 40px is a page's own action — the thing the screen exists
     for. 34–36 is a control in a toolbar or a card foot, where four of them sit
     in a row and 44 each turns a bar into a block. Nothing lands under 34,
     which is the point below which a thumb starts missing. */
  .btn { height: 40px; padding: 0 14px; font-size: 13.5px; }
  .btn-sm { height: 34px; padding: 0 11px; font-size: 13px; }
  .btn-lg { height: 46px; font-size: 15px; }
  /* padding:0 as well as the box: the phone rule above gives .btn 14px of side
     padding, and an icon button that inherits it has an 8px content box — the
     glyph inside it renders 6px wide however large the svg is set. */
  .btn-icon { width: 38px; height: 38px; padding: 0; }
  .btn-sm.btn-icon { width: 34px; height: 34px; padding: 0; }
  /* !important is the only way past the inline `gap:6px` that app.js writes on
     the card and row action groups; three buttons 6px apart mis-tap. */
  .scard-foot .row, .card-foot .row, .tbl .row { gap: 8px !important; }

  /* a text field is 16px inside (see the iOS rule above), so its box cannot go
     below about 40 without the text touching the border */
  .field { height: 42px; }
  select.select { height: 38px; padding: 0 26px 0 10px; background-position: right 8px center; }
  .check { gap: 11px; padding: 8px 0; min-height: 38px; font-size: 14px; }
  .check input { width: 18px; height: 18px; background-size: 13px; }
  .switch { width: 42px; height: 25px; }
  .switch::after { width: 19px; }
  .switch[aria-checked="true"]::after { left: 20px; }
  /* the whole 44px strip is the drag target, not a 34px slice of it */
  input[type="range"] { height: 44px; }
  input[type="range"]::-webkit-slider-thumb { width: 24px; height: 24px; margin-top: -10px; }

  .tabs button { padding: 11px 12px; }
  /* 9px sides, not 12: on a 360px catalogue toolbar those six pixels are the
     difference between the sort select showing "Sort: Trending" and "Trendin".
     The buttons hold that size instead of being squeezed by the row around
     them — measured at 320px, the catalogue's two view buttons had been
     shrunk to 18.7px wide, narrower than the icon inside them. A group that
     no longer fits scrolls inside its own box rather than crushing its parts. */
  .seg { max-width: 100%; overflow-x: auto; scrollbar-width: none; }
  .seg::-webkit-scrollbar { display: none; }
  .seg button, .seg > a { flex: none; padding: 8px 9px; min-width: 36px; min-height: 34px; }
  .tag { height: 26px; padding: 0 9px; }
  /* a tag that is a link is a control and takes the control floor; a tag that
     is only a label keeps the tighter box */
  a.tag { min-height: 34px; padding: 0 11px; }
  /* the wordmark is a link home and was a 26px slice of a 60px bar */
  .logo { padding-block: 8px; }
  .strip a { padding-block: 7px; }

  /* a copy button on a code block is the one control in the block, so it gets
     a real target and the block's right gutter grows to match */
  .code { padding: 11px 48px 11px 12px; }
  .code-copy { width: 36px; height: 36px; top: 5px; right: 5px; }

  /* a card is a card, not a screenful: 18px of padding on a 358px column is
     10% of the width given to air on each side */
  .card-head { padding: 12px 14px; gap: 10px; }
  .card-body { padding: 14px; }
  /* 16px between two stacked cards is a desk's gap seen at a third of the
     width — on a phone the column is the layout, so the cards sit closer */
  .grid { gap: 10px; }

  /* a mono value that runs past the column breaks rather than widening it */
  .kv { flex-wrap: wrap; row-gap: 2px; }
  .kv .v { overflow-wrap: anywhere; }

  /* footer links are the densest list on any page — 22px apart is a lottery.
     10px of padding puts a 14px line in a 41px row, which is the floor. */
  .foot { padding: 28px 0 20px; margin-top: 20px; }
  .foot li { margin-bottom: 1px; }
  .foot li a, .foot li .foot-note { display: inline-block; padding: 8px 0; font-size: 13.5px; }
  /* И ИНЛАЙНОВЫЙ ОТСТУП СНИЗУ ТОЖЕ УХОДИТ. Комментарий выше говорил, что
     44px «оставлены стоять» — они резервировали место под плавающий
     переключатель ролей, которого на телефоне нет вовсе, и на экране это
     полсантиметра пустоты после последней ссылки, за которой ничего. */
  .foot-bot { margin-top: 22px; gap: 10px; padding-bottom: 6px !important; }
  .foot-bot .wrapg { gap: 4px 16px !important; }
  .foot-bot .wrapg a { padding: 10px 0; }

  /* the palette is what the burger opens, so on a phone it stops being a
     centred dialog and becomes a bottom sheet: the list starts under the
     thumb, the keyboard hints go (there is no keyboard), and the strip of
     backdrop above the sheet carries a handle so it reads as "tap to close" —
     tapping it hits .pal-back itself, which is the existing dismiss target. */
  .pal-back { padding-top: 0; }
  .pal-back.on { display: flex; flex-direction: column; justify-content: flex-end; }
  .pal-back::before {
    content: ''; flex: none; width: 44px; height: 4px; border-radius: 2px;
    margin: 0 auto 12px; background: rgba(255,255,255,.62);
  }
  .pal {
    width: 100%; max-width: none; border-radius: var(--r3) var(--r3) 0 0;
    max-height: 88dvh; display: flex; flex-direction: column;
  }
  /* the burger opens this, so on a phone the palette's field is the site's
     search box. It was a 24.8px input floating in a 56px bar, so half the bar
     was dead: the padding goes down and the input takes the height instead. */
  .pal-in { padding: 8px 16px; }
  .pal-in input { height: 42px; }
  .pal-list { max-height: none; flex: 1 1 auto; padding: 6px 6px 14px; }
  .pal-item { padding: 9px 10px; min-height: 48px; }
  .pal-foot { display: none; }

  /* app.js positions this popover arithmetically against a hard-coded 272px,
     so the width must stay 272 or the right edge lands off screen; the clamp
     only bites below a 296px viewport, which nothing here supports */
  .pop { width: min(272px, calc(100vw - 24px)); }
  .pop-b { max-height: 46dvh; }
  .pop-b .check { padding: 8px 0; min-height: 42px; }
  /* the one field in the popover — naming a new list — was 31px */
  .pop-f input { height: 40px; }
}

/* ---------- the narrowest phones ------------------------------------------
   The wordmark is kept for as long as it fits and clamped exactly where it
   stops. Measured rather than guessed: the row is the mark, the word, a
   flexible spacer and five 44px controls at an 8px gap, which lands the burger
   at 368px. So it clears a 375pt phone with 7px to spare and overruns a 360pt
   one by 8 — 366 is the width between them, and below it a mark that still
   links home beats a word clipped mid-letter. */
@media (max-width: 366px) {
  .logo { width: 24px; overflow: hidden; }
}

/* ---------- 1a. INDIGO in daylight ----------------------------------------
   The same structure the Notion scheme settled on — it took four drafts to
   find and there is no reason to find it twice — with violet where that one
   puts blue.

   The structure, in one line: a light neutral canvas with WHITE cards on it, a
   white header band, ink dark enough to carry the page (#1F2328 headings,
   #59636E body), and a border you can see (#DCDCDC). Cards separate by sitting
   above the canvas rather than by being outlined on white, and the page keeps
   its light from the header and the cards rather than from an all-white
   background, which on a layout this dense reads as a wall.

   The skills line used to keep its own ink-blue here, on the grounds that a
   blue against a violet reads as two roles rather than two moods. It does —
   and it still reads as two websites when both appear in one header, which is
   why the second accent is gone from every scheme. ---------------------- */
[data-theme="light"][data-dark="indigo"] {
  --paper:      #F7F7F7;
  --surface:    #FFFFFF;
  --surface-2:  #EFEFEF;
  --surface-3:  #E7E7E7;
  --line:       #DCDCDC;
  --line-2:     #C8C8C8;

  --ink:        #1F2328;
  --ink-2:      #59636E;
  --ink-3:      #848E98;

  --accent:      #6A5AF9;
  --accent-2:    #5646E0;
  --accent-soft: #EFECFE;
  --accent-line: #D5CEFB;

  --risk:       #C4554D;
  --risk-soft:  #FCEEED;
  --risk-line:  #F1D2CF;
  --warn:       #9A6700;
  --warn-soft:  #FBF3DF;
  --warn-line:  #EADFB6;
  --info:       #2A4FBF;
  --info-soft:  #EDF0FB;
  --info-line:  #CBD5F0;

  --panel:      #1F2328;
  --panel-2:    #2A2F36;
  --panel-line: #39414A;
  --panel-ink:  #F6F8FA;
  --panel-ink2: #A2ABB4;

  --on-accent:    #FFFFFF;
  --panel-accent: #9B90FF;

  --sh1: 0 1px 0 rgba(31,35,40,.03), 0 1px 3px rgba(66,74,83,.08);
  --sh2: 0 8px 24px -10px rgba(31,35,40,.18), 0 1px 3px rgba(66,74,83,.08);
  --sh3: 0 20px 48px -16px rgba(31,35,40,.24), 0 3px 10px rgba(66,74,83,.10);
}

/* ---------- 1a-2. LIME in daylight ----------------------------------------
   Lime is a fill colour, not an ink one. #BEF264 on white is about 1.4:1 —
   invisible as a link, a tick or a label — so a light theme cannot simply
   reuse the dark scheme's accent the way indigo can. It gets two:

     --accent      a deep lime-olive that carries TEXT: links, the spec tick,
                   grades, the "included" marks. Readable on white (~5:1).
     the button    the bright lime itself, with near-black type on it. That is
                   the one place the colour is a surface rather than a mark,
                   and it is where the scheme's identity actually lives.

   Two rules is the whole exception, and they are below rather than in a page
   stylesheet so the scheme stays one block. Everything else is a token, and
   every surface is strictly neutral (R=G=B) — white paper, white cards, grey
   rules — with the only tint outside the accent in --accent-soft, the chip
   that marks an accented state.
   ------------------------------------------------------------------------- */
[data-theme="light"][data-dark="lime"] {
  --paper:      #FFFFFF;
  --surface:    #FFFFFF;
  --surface-2:  #F5F5F5;
  --surface-3:  #EAEAEA;
  --line:       #E4E4E4;
  --line-2:     #D2D2D2;

  --ink:        #101010;
  --ink-2:      #505050;
  --ink-3:      #878787;

  --accent:      #4C7A0B;
  --accent-2:    #3C6108;
  --accent-soft: #F2F8E4;
  --accent-line: #D7E8AE;

  --risk:       #C0402A;
  --risk-soft:  #FCEFEC;
  --risk-line:  #F2D3CB;
  --warn:       #8A6410;
  --warn-soft:  #FAF2E2;
  --warn-line:  #EBDCB8;
  --info:       #2A4FBF;
  --info-soft:  #EEF1FB;
  --info-line:  #CBD5F0;

  --panel:      #141414;
  --panel-2:    #1E1E1E;
  --panel-line: #2C2C2C;
  --panel-ink:  #F5F5F5;
  --panel-ink2: #A6A6A6;

  --on-accent:    #FFFFFF;
  --panel-accent: #BEF264;

  --sh1: 0 1px 1px rgba(0,0,0,.04), 0 1px 3px rgba(0,0,0,.05);
  --sh2: 0 10px 30px -14px rgba(0,0,0,.26), 0 2px 6px rgba(0,0,0,.05);
  --sh3: 0 24px 60px -20px rgba(0,0,0,.32), 0 4px 12px rgba(0,0,0,.08);
}
/* the exception the comment above argues for: the primary action is the bright
   lime with near-black type — 14:1, and the only saturated surface on the page */
[data-theme="light"][data-dark="lime"] .btn-primary,
[data-theme="light"][data-dark="lime"] .btn-skill {
  background: #BEF264; color: #16200A;
  box-shadow: inset 0 -1px 0 rgba(0,0,0,.10);
}
[data-theme="light"][data-dark="lime"] .btn-primary:hover,
[data-theme="light"][data-dark="lime"] .btn-skill:hover { background: #AEE84A; }

/* ---------- 1a-3. NOTION --------------------------------------------------
   White, grey and one blue. Three drafts got here, and what each one taught is
   worth keeping, because the fix is never the obvious one.

   Warm beige surfaces and brown-black ink were Notion's EDITOR palette, not
   the palette of their site, which is neutral. All-white was worse on a page
   this dense: white cards on a white page have nothing to stand on, so every
   hairline does the work of a surface. A grey canvas under white cards fixed
   that and drained the page instead — mid-grey behind pale-grey text is a
   screen of low contrast however clean the geometry.

   So: the canvas is grey, the cards are white, and the page is kept from going
   flat by the two things the all-grey draft was missing rather than by taking
   the grey away — ink that is genuinely dark (#1F2328 headings, #59636E body)
   and a border you can actually see (#DCDCDC). The header is white: it is the
   one band that runs the full width of every page, and in white it puts the
   light back at the top where the eye starts.

   The grey itself is #F7F7F7 — light, and strictly neutral: R=G=B in every
   surface here. A few points of blue in the canvas is what GitHub and Stripe
   do, and it does read fresher next to a blue accent, but on a full screen it
   is visible as a cast rather than as grey, which is not what was asked for.
   Lightness does the same job here: at 247 the canvas is a shade, not a slab.

   --skill is purple, the hue the systems that do white-grey-blue put next to
   their blue. ------------------------------------------------------------- */
[data-theme="light"][data-dark="notion"] {
  --paper:      #F7F7F7;
  --surface:    #FFFFFF;
  --surface-2:  #EFEFEF;
  --surface-3:  #E7E7E7;
  --line:       #DCDCDC;
  --line-2:     #C8C8C8;

  --ink:        #1F2328;
  --ink-2:      #59636E;
  --ink-3:      #848E98;

  --accent:      #1868D6;
  --accent-2:    #1256B4;
  --accent-soft: #E7F1FD;
  --accent-line: #BFD9F7;

  --risk:       #C4554D;
  --risk-soft:  #FCEEED;
  --risk-line:  #F1D2CF;
  --warn:       #9A6700;
  --warn-soft:  #FBF3DF;
  --warn-line:  #EADFB6;
  --info:       #1868D6;
  --info-soft:  #E7F1FD;
  --info-line:  #BFD9F7;

  --panel:      #1F2328;
  --panel-2:    #2A2F36;
  --panel-line: #39414A;
  --panel-ink:  #F6F8FA;
  --panel-ink2: #A2ABB4;

  --on-accent:    #FFFFFF;
  --panel-accent: #58A6FF;

  --sh1: 0 1px 0 rgba(31,35,40,.03), 0 1px 3px rgba(66,74,83,.08);
  --sh2: 0 8px 24px -10px rgba(31,35,40,.18), 0 1px 3px rgba(66,74,83,.08);
  --sh3: 0 20px 48px -16px rgba(31,35,40,.24), 0 3px 10px rgba(66,74,83,.10);
}
/* the header is the page's white band; the grey starts under it. Shared by the
   two schemes built on this structure. */
[data-theme="light"][data-dark="notion"] .nav,
[data-theme="light"][data-dark="indigo"] .nav {
  background: color-mix(in srgb, #FFFFFF 92%, transparent);
}

/* the same scheme after dark: Notion's own night palette — #191919 canvas,
   #202020 surfaces, the lighter blue that only works on a dark ground */
[data-theme="dark"][data-dark="notion"] {
  --paper:      #191919;
  --surface:    #202020;
  --surface-2:  #262626;
  --surface-3:  #2F2F2F;
  --line:       #2C2C2C;
  --line-2:     #3D3D3D;

  --ink:        #E9E9E7;
  --ink-2:      #A0A09B;
  --ink-3:      #7A7A76;

  --accent:      #529CCA;
  --accent-2:    #6BB3E0;
  --accent-soft: #1B2733;
  --accent-line: #2C4256;

  --risk:       #E08D82;
  --risk-soft:  #2A1B18;
  --risk-line:  #472B25;
  --warn:       #D9A44B;
  --warn-soft:  #262016;
  --warn-line:  #453820;
  --info:       #529CCA;
  --info-soft:  #1B2733;
  --info-line:  #2C4256;

  --panel:      #141414;
  --panel-2:    #1E1E1E;
  --panel-line: #2C2C2C;
  --panel-ink:  #E9E9E7;
  --panel-ink2: #A0A09B;

  --on-accent:    #0F1419;
  --panel-accent: #529CCA;

  --sh1: 0 1px 2px rgba(0,0,0,.4);
  --sh2: 0 8px 24px -12px rgba(0,0,0,.7), 0 1px 3px rgba(0,0,0,.4);
  --sh3: 0 20px 48px -18px rgba(0,0,0,.8), 0 3px 10px rgba(0,0,0,.5);
}

/* ---------- 1a-3b. NOTION BLUE — the same page, the Datasheet blue -----------
   Notion's structure without argument: the grey canvas, the white cards, its
   greys and its two night values. One family is different — the accent.

   Notion's own blue is #1868D6, a blue with a little violet in it that sits
   comfortably beside its purple skill colour. The Datasheet blue is #0B62F4:
   the same lightness, further round toward true blue and considerably more
   saturated. On a white card it reads as a product blue rather than as a
   document link, which is what this variant is for — the same quiet page with
   a louder place to press.

   Everything else is copied, deliberately and completely: an accent swap that
   also nudges a grey stops being a comparison between two accents. The night
   half takes Blue's own #3B82F6 rather than lightening #0B62F4, because a
   saturated blue at that lightness on a #191919 ground is a vibration rather
   than a colour. --------------------------------------------------------- */
[data-theme="light"][data-dark="notionblue"] {
  --paper:      #F7F7F7;
  --surface:    #FFFFFF;
  --surface-2:  #EFEFEF;
  --surface-3:  #E7E7E7;
  --line:       #DCDCDC;
  --line-2:     #C8C8C8;

  --ink:        #1F2328;
  --ink-2:      #59636E;
  --ink-3:      #848E98;

  --accent:      #0B62F4;
  --accent-2:    #0952CE;
  --accent-soft: #EBF2FE;
  --accent-line: #C9DDFD;

  --risk:       #C4554D;
  --risk-soft:  #FCEEED;
  --risk-line:  #F1D2CF;
  --warn:       #9A6700;
  --warn-soft:  #FBF3DF;
  --warn-line:  #EADFB6;
  --info:       #0B62F4;
  --info-soft:  #EBF2FE;
  --info-line:  #C9DDFD;

  --panel:      #1F2328;
  --panel-2:    #2A2F36;
  --panel-line: #39414A;
  --panel-ink:  #F6F8FA;
  --panel-ink2: #A2ABB4;

  --on-accent:    #FFFFFF;
  --panel-accent: #6C9DFB;

  --sh1: 0 1px 0 rgba(31,35,40,.03), 0 1px 3px rgba(66,74,83,.08);
  --sh2: 0 8px 24px -10px rgba(31,35,40,.18), 0 1px 3px rgba(66,74,83,.08);
  --sh3: 0 20px 48px -16px rgba(31,35,40,.24), 0 3px 10px rgba(66,74,83,.10);
}
/* the header is the white band here too */
[data-theme="light"][data-dark="notionblue"] .nav {
  background: color-mix(in srgb, #FFFFFF 92%, transparent);
}

[data-theme="dark"][data-dark="notionblue"] {
  --paper:      #191919;
  --surface:    #202020;
  --surface-2:  #262626;
  --surface-3:  #2F2F2F;
  --line:       #2C2C2C;
  --line-2:     #3D3D3D;

  --ink:        #E9E9E7;
  --ink-2:      #A0A09B;
  --ink-3:      #7A7A76;

  --accent:      #3B82F6;
  --accent-2:    #60A5FA;
  --accent-soft: #12203A;
  --accent-line: #1E3A6B;

  --risk:       #E08D82;
  --risk-soft:  #2A1B18;
  --risk-line:  #472B25;
  --warn:       #D9A44B;
  --warn-soft:  #262016;
  --warn-line:  #453820;
  --info:       #3B82F6;
  --info-soft:  #12203A;
  --info-line:  #1E3A6B;

  --panel:      #141414;
  --panel-2:    #1E1E1E;
  --panel-line: #2C2C2C;
  --panel-ink:  #E9E9E7;
  --panel-ink2: #A0A09B;

  --on-accent:    #061024;
  --panel-accent: #3B82F6;

  --sh1: 0 1px 2px rgba(0,0,0,.4);
  --sh2: 0 8px 24px -12px rgba(0,0,0,.7), 0 1px 3px rgba(0,0,0,.4);
  --sh3: 0 20px 48px -18px rgba(0,0,0,.8), 0 3px 10px rgba(0,0,0,.5);
}

/* ---------- 1a-3c. NOTION LIME — the same page, the terminal green ---------
   The third of the Notion family, and the one that needed a decision rather
   than a substitution.

   A lime at Notion's accent lightness does not exist: #BEF264 on white is a
   highlighter, unreadable as text and worse as a button label. So the two
   halves take the accent from opposite ends of the Lime scheme's own logic —
   the day half is #4C7A0B, an olive dark enough to carry white text at 13px;
   the night half is the lime itself, #BEF264, which on #191919 is the colour
   the scheme is named after. They are not the same hue lightened, and they
   should not be: one is ink on paper and the other is light in a dark room.

   The one place a plain copy would have been wrong is `--info`. Notion's
   information colour mirrors its accent, which works when the accent is a
   blue; a green notice reads as a completed thing rather than as a thing worth
   knowing. So info stays blue by day, like the Lime scheme's, and by night
   becomes a desaturated sage — near enough to the neutrals to sit quietly, far
   enough from #BEF264 to never be mistaken for the accent. ---------------- */
[data-theme="light"][data-dark="notionlime"] {
  --paper:      #F7F7F7;
  --surface:    #FFFFFF;
  --surface-2:  #EFEFEF;
  --surface-3:  #E7E7E7;
  --line:       #DCDCDC;
  --line-2:     #C8C8C8;

  --ink:        #1F2328;
  --ink-2:      #59636E;
  --ink-3:      #848E98;

  --accent:      #4C7A0B;
  --accent-2:    #3C6108;
  --accent-soft: #F2F8E4;
  --accent-line: #D7E8AE;

  --risk:       #C4554D;
  --risk-soft:  #FCEEED;
  --risk-line:  #F1D2CF;
  --warn:       #9A6700;
  --warn-soft:  #FBF3DF;
  --warn-line:  #EADFB6;
  --info:       #2A4FBF;
  --info-soft:  #EEF1FB;
  --info-line:  #CBD5F0;

  --panel:      #1F2328;
  --panel-2:    #2A2F36;
  --panel-line: #39414A;
  --panel-ink:  #F6F8FA;
  --panel-ink2: #A2ABB4;

  --on-accent:    #FFFFFF;
  --panel-accent: #BEF264;

  --sh1: 0 1px 0 rgba(31,35,40,.03), 0 1px 3px rgba(66,74,83,.08);
  --sh2: 0 8px 24px -10px rgba(31,35,40,.18), 0 1px 3px rgba(66,74,83,.08);
  --sh3: 0 20px 48px -16px rgba(31,35,40,.24), 0 3px 10px rgba(66,74,83,.10);
}
/* the header is the white band here too */
[data-theme="light"][data-dark="notionlime"] .nav {
  background: color-mix(in srgb, #FFFFFF 92%, transparent);
}

[data-theme="dark"][data-dark="notionlime"] {
  --paper:      #191919;
  --surface:    #202020;
  --surface-2:  #262626;
  --surface-3:  #2F2F2F;
  --line:       #2C2C2C;
  --line-2:     #3D3D3D;

  --ink:        #E9E9E7;
  --ink-2:      #A0A09B;
  --ink-3:      #7A7A76;

  --accent:      #BEF264;
  --accent-2:    #D2F98C;
  --accent-soft: #1E230F;
  --accent-line: #3F521F;

  --risk:       #E08D82;
  --risk-soft:  #2A1B18;
  --risk-line:  #472B25;
  --warn:       #D9A44B;
  --warn-soft:  #262016;
  --warn-line:  #453820;
  --info:       #8C9C78;
  --info-soft:  #1C1F17;
  --info-line:  #333A29;

  --panel:      #141414;
  --panel-2:    #1E1E1E;
  --panel-line: #2C2C2C;
  --panel-ink:  #E9E9E7;
  --panel-ink2: #A0A09B;

  --on-accent:    #0E1206;
  --panel-accent: #BEF264;

  --sh1: 0 1px 2px rgba(0,0,0,.4);
  --sh2: 0 8px 24px -12px rgba(0,0,0,.7), 0 1px 3px rgba(0,0,0,.4);
  --sh3: 0 20px 48px -18px rgba(0,0,0,.8), 0 3px 10px rgba(0,0,0,.5);
}

/* The publisher strip is the accent at 12%, which is the right idea in every
   scheme but this one: lime at 12% on black is #1E230F, and a 38px band of it
   across the full width does not read as a tinted bar, it reads as olive drab.
   A badge can carry that colour because it is 60px wide and next to white
   text; a band cannot.

   So the band alone steps off the accent and onto a cool slate. It is still
   dark, still obviously a bar rather than the page, and it is the one pairing
   that lime has always been good with — the accent inside it keeps its full
   #BEF264 and gains contrast rather than losing it. Nothing else in the scheme
   moves: this is a background, not a token. ------------------------------- */
[data-theme="dark"][data-dark="notionlime"][data-role="publisher"] .rstrip {
  background: #20242B;
  border-bottom-color: #333A45;
}

/* The same band, the same reason, in the two terminal schemes. Carbon has no
   hue to borrow, so it takes a step up the graphite ramp — one value above the
   customer strip, which is all a band needs to read as a different bar. Slate
   has a ground with a cast already in it and simply uses more of it. */
[data-theme="dark"][data-dark="carbon"][data-role="publisher"] .rstrip {
  background: #262626;
  border-bottom-color: #3A3A3A;
}
[data-theme="dark"][data-dark="slate"][data-role="publisher"] .rstrip {
  background: #1C2836;
  border-bottom-color: #2F4055;
}
[data-theme="dark"][data-dark="darklime"][data-role="publisher"] .rstrip {
  background: #242424;
  border-bottom-color: #363636;
}
[data-theme="dark"][data-dark="midnight"][data-role="publisher"] .rstrip {
  background: #1B2739;
  border-bottom-color: #33455E;
}
[data-theme="dark"][data-dark="charcoal"][data-role="publisher"] .rstrip {
  background: #2A2A2A;
  border-bottom-color: #3B3B3B;
}
[data-theme="dark"][data-dark="stone"][data-role="publisher"] .rstrip {
  background: #302C25;
  border-bottom-color: #423D34;
}
[data-theme="dark"][data-dark="carbon2"][data-role="publisher"] .rstrip {
  background: #262626;
  border-bottom-color: #3D3D3D;
}
[data-theme="dark"][data-dark="slate2"][data-role="publisher"] .rstrip {
  background: #1C2836;
  border-bottom-color: #3A4A59;
}
[data-theme="dark"][data-dark="stone2"][data-role="publisher"] .rstrip {
  background: #302C25;
  border-bottom-color: #4C473C;
}
[data-theme="dark"][data-dark="charcoal2"][data-role="publisher"] .rstrip {
  background: #2A2A2A;
  border-bottom-color: #3F3F3F;
}

/* ---------- 1a-4. INK -----------------------------------------------------
   Black type on a white page, greys that are only black at a lower volume, and
   one blue — the flat product blue every SaaS front end has settled on. The
   page is monochrome; the blue appears exactly where something is meant to be
   pressed, followed or chosen, and nowhere else.

   That split is the whole idea of the scheme. Emphasis on this page comes from
   weight and size, so the accent does not have to shout to be found: black
   headings carry the hierarchy and #2563EB carries the actions — the primary
   button, links, the focus ring, the spec tick, a selected filter, the library
   count. Nothing decorative is coloured.

   --skill, the second product line, stays graphite rather than taking a second
   hue: with one accent in the palette, two would each mean less.

   The state colours are the exception and stay coloured. A scan grade, a
   failing call and a figure that says money is being wasted are the three
   things on this site that must not read as ordinary text, and monochrome has
   no way to say "wrong" — so those keep their red and amber, and nothing else
   does. ------------------------------------------------------------------- */
[data-theme="light"][data-dark="ink"] {
  --paper:      #FFFFFF;
  --surface:    #FFFFFF;
  --surface-2:  #F5F5F5;
  --surface-3:  #EBEBEB;
  --line:       #E0E0E0;
  --line-2:     #C9C9C9;

  --ink:        #0A0A0A;
  --ink-2:      #4A4A4A;
  --ink-3:      #7A7A7A;

  --accent:      #2563EB;
  --accent-2:    #1D4FD8;
  --accent-soft: #EFF4FF;
  --accent-line: #C7DAFB;

  --risk:       #B3392A;
  --risk-soft:  #FBEDEA;
  --risk-line:  #EFCFC8;
  --warn:       #8A6410;
  --warn-soft:  #FAF3DF;
  --warn-line:  #EADFB6;
  --info:       #2563EB;
  --info-soft:  #EFF4FF;
  --info-line:  #C7DAFB;

  --panel:      #0F0F0F;
  --panel-2:    #1A1A1A;
  --panel-line: #2A2A2A;
  --panel-ink:  #FAFAFA;
  --panel-ink2: #A3A3A3;

  --on-accent:    #FFFFFF;
  --panel-accent: #7EB0FF;

  --sh1: 0 1px 1px rgba(0,0,0,.04), 0 1px 3px rgba(0,0,0,.06);
  --sh2: 0 8px 24px -10px rgba(0,0,0,.22), 0 1px 3px rgba(0,0,0,.06);
  --sh3: 0 20px 48px -16px rgba(0,0,0,.28), 0 3px 10px rgba(0,0,0,.09);
}

/* after dark it is the mono palette: the same idea with the values inverted */
[data-theme="dark"][data-dark="ink"] {
  --paper:      #0A0A0A;
  --surface:    #121212;
  --surface-2:  #1A1A1A;
  --surface-3:  #232323;
  --line:       #262626;
  --line-2:     #383838;

  --ink:        #FAFAFA;
  --ink-2:      #A3A3A3;
  --ink-3:      #737373;

  --accent:      #FFFFFF;
  --accent-2:    #E5E5E5;
  --accent-soft: #1C1C1C;
  --accent-line: #3D3D3D;

  --risk:       #E8836B;
  --risk-soft:  #241713;
  --risk-line:  #40251E;
  --warn:       #D9A63F;
  --warn-soft:  #221B0E;
  --warn-line:  #3C3117;
  --info:       #9CA3AF;
  --info-soft:  #17181A;
  --info-line:  #2E3033;

  --panel:      #171717;
  --panel-2:    #202020;
  --panel-line: #303030;
  --panel-ink:  #FAFAFA;
  --panel-ink2: #A3A3A3;

  --on-accent:    #0A0A0A;
  --panel-accent: #FAFAFA;

  --sh1: 0 1px 1px rgba(0,0,0,.5), 0 1px 3px rgba(0,0,0,.5);
  --sh2: 0 10px 30px -14px rgba(0,0,0,.85), 0 2px 6px rgba(0,0,0,.5);
  --sh3: 0 24px 60px -20px rgba(0,0,0,.9), 0 4px 12px rgba(0,0,0,.6);
}

/* ---------- 1a-5. PAPER ---------------------------------------------------
   Mono with the green taken out. The Datasheet light theme unchanged — warm
   paper, hairline rules, mono spec labels — and black where it puts emerald,
   so the page reads as ink on paper rather than as a product with a brand
   colour. It is the daylight half of the mono scheme, which until now borrowed
   the default palette and therefore its emerald.

   Black as an accent behaves differently from a coloured one and the palette
   has to allow for it: a filled black button is the heaviest thing that can be
   put on a page, so the soft chip under an accented state goes warm grey
   rather than tinted — there is no hue to tint it with — and --skill takes a
   warm graphite for the same reason the mono schemes do. The state colours
   stay: "wrong" cannot be said in ink alone.
   ------------------------------------------------------------------------- */
[data-theme="light"][data-dark="paper"] {
  --paper:      #FBFAF7;
  --surface:    #FFFFFF;
  --surface-2:  #F4F3EE;
  --surface-3:  #EBE9E1;
  --line:       #E4E2D9;
  --line-2:     #D3D0C4;

  --ink:        #15171B;
  --ink-2:      #565C64;
  --ink-3:      #878C93;

  --accent:      #15171B;
  --accent-2:    #000000;
  --accent-soft: #F0EEE7;
  --accent-line: #D8D5C8;

  --risk:       #B23F2B;
  --risk-soft:  #FAEDE9;
  --risk-line:  #EDCFC7;
  --warn:       #96680B;
  --warn-soft:  #F9F1DE;
  --warn-line:  #E9DAB4;
  --info:       #565C64;
  --info-soft:  #F0EEE7;
  --info-line:  #D8D5C8;

  --panel:      #121413;
  --panel-2:    #1C1F1D;
  --panel-line: #2C302D;
  --panel-ink:  #F2F1EC;
  --panel-ink2: #9BA29C;

  --on-accent:    #FBFAF7;
  --panel-accent: #F2F1EC;

  --sh1: 0 1px 1px rgba(21,23,27,.035), 0 1px 3px rgba(21,23,27,.045);
  --sh2: 0 10px 30px -14px rgba(21,23,27,.28), 0 2px 6px rgba(21,23,27,.05);
  --sh3: 0 24px 60px -20px rgba(21,23,27,.35), 0 4px 12px rgba(21,23,27,.08);
}

/* ---------- 1b. the second dark scheme ------------------------------------
   The dark theme ships in two colour schemes and a control at the foot of the
   page switches between them; the switcher only exists while the dark theme is
   on, because in daylight there is nothing for it to change.

   MONO (the default, above) is the strict version: black canvas, white type,
   white accent. INDIGO is the same theme with the two complaints about pure
   black answered — the canvas lifts off #0A0A0A so a card has a surface rather
   than only a hairline, and one saturated colour comes back for the things
   that are actually interactive. Everything below is a token; no layout, no
   component and no page knows which scheme is on.
   ------------------------------------------------------------------------- */
[data-theme="dark"][data-dark="indigo"] {
  --paper:      #14161A;
  --surface:    #1A1D22;
  --surface-2:  #22262C;
  --surface-3:  #2B3038;
  --line:       #2E333B;
  --line-2:     #414852;

  --ink:        #F3F5F8;
  --ink-2:      #A9B1BC;
  --ink-3:      #79828F;

  --accent:      #7C8CFF;
  --accent-2:    #94A1FF;
  --accent-soft: #1E2233;
  --accent-line: #3A4270;

  --risk:       #F0876F;
  --risk-soft:  #2A1A16;
  --risk-line:  #48291F;
  --warn:       #E0AC4A;
  --warn-soft:  #262016;
  --warn-line:  #453820;
  --info:       #9AA3B2;
  --info-soft:  #1C2028;
  --info-line:  #333A45;

  --panel:      #1B1F25;
  --panel-2:    #23282F;
  --panel-line: #333A43;
  --panel-ink:  #F3F5F8;
  --panel-ink2: #A9B1BC;

  --on-accent:    #0F1116;
  --panel-accent: #94A1FF;

  --sh1: 0 1px 1px rgba(0,0,0,.45), 0 1px 3px rgba(0,0,0,.45);
  --sh2: 0 10px 30px -14px rgba(0,0,0,.8), 0 2px 6px rgba(0,0,0,.45);
  --sh3: 0 24px 60px -20px rgba(0,0,0,.88), 0 4px 12px rgba(0,0,0,.55);
}
[data-theme="dark"][data-dark="indigo"] .btn-ink:hover { background: #E6E9EE; }

/* ---- LIME ----------------------------------------------------------------
   The third scheme: the same neutral greys as the other two — no hue in the
   canvas at all, so nothing but the accent is coloured — and lime as the one
   saturated thing on the page. The first draft tinted the darks a few points
   towards green to "seat" the accent; on a large surface that reads as a cast
   rather than as a neutral, so the greys here are strictly achromatic and only
   --accent-soft carries any lime, which is what an accent-tinted chip is for.
   Same rules as the other two — a token block and nothing else. */
[data-theme="dark"][data-dark="lime"] {
  --paper:      #101010;
  --surface:    #171717;
  --surface-2:  #1F1F1F;
  --surface-3:  #272727;
  --line:       #292929;
  --line-2:     #3B3B3B;

  --ink:        #F5F5F5;
  --ink-2:      #A6A6A6;
  --ink-3:      #7A7A7A;

  --accent:      #BEF264;
  --accent-2:    #D2F98C;
  --accent-soft: #1E230F;
  --accent-line: #3F521F;

  --risk:       #F0876F;
  --risk-soft:  #271713;
  --risk-line:  #44271F;
  --warn:       #E0AC4A;
  --warn-soft:  #241D11;
  --warn-line:  #42341B;
  --info:       #A3A3A3;
  --info-soft:  #1B1B1B;
  --info-line:  #333333;

  --panel:      #181818;
  --panel-2:    #212121;
  --panel-line: #303030;
  --panel-ink:  #F5F5F5;
  --panel-ink2: #A6A6A6;

  --on-accent:    #0E1206;
  --panel-accent: #BEF264;

  --sh1: 0 1px 1px rgba(0,0,0,.45), 0 1px 3px rgba(0,0,0,.45);
  --sh2: 0 10px 30px -14px rgba(0,0,0,.82), 0 2px 6px rgba(0,0,0,.45);
  --sh3: 0 24px 60px -20px rgba(0,0,0,.9), 0 4px 12px rgba(0,0,0,.55);
}
[data-theme="dark"][data-dark="lime"] .btn-ink:hover { background: #E7EAE5; }

/* ---------- 14d. the colour-scheme switcher -------------------------------
   Mounted by initDarkScheme() in app.js. A swatch the size of a full stop at
   the foot of the page on the left, which opens the list of schemes; the
   right-hand corner belongs to the role preview. It used to appear only in the
   dark theme, when a scheme was only a dark palette — indigo repaints daylight
   too, so it is shown in both and hidden only on a phone, the same rule the
   role switcher follows: a permanent floating control on a 390px screen covers
   what the reader came for. */
/* ---------- key/value money lines ------------------------------------------
   A label on the left, a figure on the right, a dashed rule between them. It
   started in the buyer's dashboard, but every shared module writes it — the
   balance panel, the referral panel, the install sheet, a ticket — and half of
   those mount on pages that never load dashboard.css. Unstyled it collapses to
   "In, all time$14,793.71", the label and the number touching, which is how the
   publisher studio printed its balance until this moved here. */
.db-line { display: flex; justify-content: space-between; gap: 12px; padding: 9px 0; border-top: 1px dashed var(--line); font-size: 13.5px; }
.db-line:first-child { border-top: 0; }
.db-line b { font: 500 13px/1.4 var(--mono); }
.db-line .k { color: var(--ink-2); }
.db-line.sum { border-top: 1px solid var(--line-2); margin-top: 6px; padding-top: 12px; }
/* a settings control sits on the right of the same line; a long option label
   must shrink the control, never widen the page */
.db-line > .k { min-width: 0; }
.db-line .select, .db-line .db-num { max-width: 100%; min-width: 0; flex: 0 1 auto; }
.db-line.sum b { font-size: 15px; }

/* ---------- an invited seat -------------------------------------------------
   member.js does the structural half — the rail, the panels, the head. The
   controls left over are the ones that only make sense to an owner, and they
   are hidden here because they belong to two page scripts this file does not
   touch.

   Inviting first: reading who else is in the org is the useful half of the
   team panel, and adding somebody to it is not a member's to do.

   Then the two product rights the seat panel already tells a member and a
   billing manager they do not have — "Close the account — the last thing on an
   account is not a billing action". Settings is a panel both seats keep, so
   until this rule the cabinet offered that button three clicks from the
   sentence saying it was the owner's. Policy follows the invite rule rather
   than the close one: the rules bind every seat, so a seat reads them, and
   each states its own on or off in words underneath. What goes is the switches
   and the spend-cap slider, not the list. */
html[data-member="yes"] #teamInvite,
html[data-member="yes"] [data-panel="team"] .db-invite,
html[data-member="yes"] [data-panel="team"] [data-invite],
html[data-member="yes"] [data-panel="staff"] #teamInvite,
html[data-member="yes"] [data-panel="settings"] .card:has(#closeBody),
html[data-member="yes"] [data-act="close-account"],
html[data-member="yes"] #policyBody .switch,
html[data-member="yes"] #policyBody .db-capset { display: none; }

/* ---------- payment methods, row actions, expanded rows --------------------
   Written by both cabinets. The buyer lists the cards their invoices are
   charged to; the publisher lists the cards their placement is charged to, and
   studio.html does not load dashboard.css — which is what left the publisher's
   Billing panel with a brand plate on one line and the actions on another. */
.db-pm {
  display: flex; align-items: center; gap: 12px;
  padding: 11px; border: 1px solid var(--line); border-radius: var(--r2);
  background: var(--surface); margin-bottom: 8px;
}
.db-pm.on { border-color: var(--line-2); background: var(--surface-2); }
.db-pm b { font-size: 13.5px; }
.db-pm .grow { min-width: 0; }
.db-pm-br {
  width: 42px; height: 28px; flex: none;
  display: grid; place-items: center;
  border: 1px solid var(--line); border-radius: 5px; background: var(--surface-2);
  font-size: 10.5px; font-weight: 600; letter-spacing: .04em; color: var(--ink-2);
}
.db-pm-empty {
  padding: 20px 16px; text-align: center;
  border: 1px dashed var(--line-2); border-radius: var(--r2); background: var(--surface-2);
}
.db-pm-empty .db-pm-br { margin: 0 auto 10px; }
.db-pm-empty b { display: block; font-size: 14px; }
.db-pm-empty p { margin: 6px auto 0; max-width: 46ch; }

/* the row-level actions live in the last column and never wrap */
.db-rowacts { display: flex; gap: 6px; justify-content: flex-end; flex-wrap: nowrap; }

/* an expanded detail row under a table row */
tr.db-open > td { background: var(--surface-2); }
.db-detail { padding: 4px 2px 8px; display: grid; gap: 12px; max-width: 74ch; position: sticky; left: 12px; }
.db-detail p { font-size: 13px; color: var(--ink-2); }

@media (max-width: 620px) {
  .db-pm { flex-wrap: wrap; }
  .db-rowacts { flex-wrap: wrap; justify-content: flex-start; }
}

/* ---------- paging a table -------------------------------------------------
   A row that is not on the page. `!important` and a class rather than the
   `hidden` attribute, because below 860px .tbl-fold sets `display: block` on
   every row, and a bare [hidden] loses to it — the rows would reappear on a
   phone, which is where paging matters most. */
.pg-off { display: none !important; }
/* the last row ON SCREEN, which is not the last row in the DOM once a page is
   hidden — without this the table rules under it and the card foot rules over
   the pager, and the two lines read as a mistake */
.tbl tbody tr.pg-last > td { border-bottom: 0; }

/* the control sits under the table, inside the card, and carries the card's
   own horizontal padding rather than the page's */
.tbl-pager { padding: 4px 16px 14px; }
.tbl-pager:empty { display: none; }
.tbl-pager .pager { margin: 0; }

@media (max-width: 720px) {
  .tbl-pager { padding: 4px 12px 12px; }
}

/* ---------- a tab strip inside a panel -------------------------------------
   One control, two cabinets: the studio files three money sections behind it
   and the buyer's Billing panel switches invoices against subscriptions with
   it. It is the site's own .seg; what is added here is only how it sits under
   a panel head and what it does on a phone. */
/* the strip needs air above it: it belongs to the tables under it, not to the
   cards above, and at 16px — the gap between two cards — it read as a third
   card in the row rather than as a control for what follows */
.mn-seg { margin: 14px 0 16px; }
@media (max-width: 720px) {
  /* a 177px strip floating in a 375px column reads as a stray control; full
     width and equal halves reads as a switch */
  .mn-seg { display: flex; width: 100%; }
  .mn-seg button { flex: 1 1 0; justify-content: center; }
}

/* ---- consent bar ---------------------------------------------------------
   Bottom centre, because both corners are already spoken for: the scheme
   switcher holds the left and the role preview holds the right. It sits above
   both (250) so a page cannot open with the question half covered, and it is
   the width of a paragraph rather than a full-bleed bar — a strip across the
   whole foot of the page reads as an interstitial and gets clicked away
   without being read, which is the opposite of consent. */
.ck {
  position: fixed; z-index: 250; bottom: 20px; left: 50%; transform: translateX(-50%);
  width: min(560px, calc(100vw - 40px));
  display: grid; gap: 12px;
  padding: 16px 18px;
  background: var(--surface); border: 1px solid var(--line-2);
  border-radius: var(--r2); box-shadow: var(--sh3);
}
.ck-t b { display: block; font-size: 14px; letter-spacing: -.012em; }
.ck-t p { margin-top: 6px; font-size: 12.5px; line-height: 1.62; color: var(--ink-2); }
.ck-a { display: flex; flex-wrap: wrap; gap: 8px; justify-content: flex-end; }
.ck-a .btn { flex: 0 0 auto; }

/* A 560px bar centred in a 760px window reaches under the role preview in the
   right corner. Measured: the two stop colliding at about 862px, so below 900
   the bar steps up over both corner widgets instead of across them. */
@media (max-width: 900px) {
  .ck { bottom: 68px; }
}

/* ---- and nothing sits under it ------------------------------------------
   THE BANNER WAS ON TOP OF THE THING THE READER WAS PRESSING. Tick two servers
   in the catalogue on a phone and the compare tray slides up from the bottom —
   into exactly the band the consent banner occupies, and the banner has the
   higher z-index. A hit test on the middle of "Compare 2" returned the "Only
   essential" button: the reader pressed compare and answered a cookie
   question. The same band covers the last row of the footer.

   z-index cannot fix that — two things want the same 115 pixels. So while the
   banner is up, everything else that floats at the bottom moves above it, and
   the page ends with room for both. `--ck-h` is written by consent.js from the
   banner's measured height, so this follows a two-line banner as well as a
   one-line one. */
body.has-consent .tray,
body.has-consent .secnav,
body.has-consent .st-bar { bottom: calc(16px + var(--ck-h, 120px)); }
body.has-consent .foot { padding-bottom: calc(24px + var(--ck-h, 120px)); }

/* and on a phone it is a bar rather than a card: one line, two buttons on the
   same row as the words where they fit, and nothing that needs scrolling */
@media (max-width: 620px) {
  .ck { bottom: 12px; padding: 12px 14px; gap: 10px; width: calc(100vw - 24px); }
  .ck-t b { font-size: 13.5px; }
  .ck-t p { margin-top: 4px; font-size: 12.5px; }
  .ck-a { justify-content: stretch; }
  .ck-a .btn { flex: 1 1 auto; justify-content: center; }
}

@media (max-width: 720px) {
  /* the floating widgets are hidden at this width, so the bar can have the
     foot of the screen to itself and the two answers can be full width — a
     40px-wide "no" beside a wide "yes" is a dark pattern by geometry */
  .ck { left: 12px; right: 12px; bottom: 12px; width: auto; transform: none; }
  .ck-a { justify-content: stretch; }
  .ck-a .btn { flex: 1 1 0; justify-content: center; }
}

.dsw {
  position: fixed; left: 20px; bottom: 20px; z-index: 240;
  display: block;
}
.dsw-dot {
  width: 30px; height: 30px; padding: 0; display: grid; place-items: center;
  border: 1px solid var(--line-2); border-radius: 50%;
  background: var(--surface); box-shadow: var(--sh2); cursor: pointer;
  transition: border-color .14s, transform .08s;
}
.dsw-dot:hover { border-color: var(--ink-3); }
.dsw-dot:active { transform: translateY(1px); }
.dsw-dot i { width: 14px; height: 14px; border-radius: 50%; display: block; }
.dsw-menu {
  /* 232 was right for eight schemes with the note on one line. The scrollbar
     takes 11 of those and the newer notes are longer, so fourteen of twenty
     rows wrapped to a second line — which turned a 47px row into 77px and made
     the list half again as tall as it had to be, the opposite of what a
     scrollable menu is for. 276 buys back the gutter and about 60px, which is
     what the longest note needs. */
  position: absolute; left: 0; bottom: 38px; width: 300px;
  padding: 8px; display: grid; gap: 2px;
  background: var(--surface); border: 1px solid var(--line-2);
  border-radius: 10px; box-shadow: var(--sh3);
  /* The list grows: at twenty schemes it is over 900px tall and the top of it
     runs off the top of a laptop screen, where there is nothing to scroll —
     the menu is anchored to its dot at the bottom of the page, so what
     overflows is unreachable rather than merely below the fold.

     58px is what the anchoring costs (20px for the dot's inset, 38px for the
     gap above it); 20px more keeps it off the top edge. The 520px cap is the
     more important half: a menu the full height of a tall monitor reads as a
     page rather than as a control, and eleven or twelve rows is already more
     than anybody scans before reaching for the scrollbar. */
  max-height: min(520px, calc(100vh - 96px));
  overflow-y: auto;
  /* the page behind it must not start scrolling when the list hits its end */
  overscroll-behavior: contain;
  scrollbar-width: thin;
  scrollbar-color: var(--line-2) transparent;
}
/* the scrollbar is the one declared in section 2b, like every other */
/* The head stays while the list moves under it. It holds the only word saying
   what these rows are and the control that decides which of them are shown, so
   losing either to a scroll would be losing the frame around the thing being
   scrolled. */
.dsw-head {
  position: sticky; top: -8px; z-index: 1;
  margin: -8px -8px 2px; padding: 12px 12px 10px;
  background: var(--surface); border-bottom: 1px solid var(--line);
}
.dsw-head > .spec { display: block; padding: 0 4px 8px; }
.dsw-pick { display: flex; align-items: center; gap: 6px; }
.dsw-sel {
  flex: 1 1 auto; min-width: 0; height: 30px; padding: 0 26px 0 9px;
  border: 1px solid var(--line-2); border-radius: var(--r1);
  background: var(--surface) url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%23878C93' stroke-width='1.6'%3E%3Cpath d='M4 6.5 8 10.5 12 6.5'/%3E%3C/svg%3E") no-repeat right 7px center/13px;
  font-size: 12.5px; color: var(--ink); -webkit-appearance: none; appearance: none; cursor: pointer;
}
.dsw-sel:focus { outline: none; border-color: var(--accent); box-shadow: 0 0 0 3px var(--accent-soft); }
.dsw-del {
  flex: none; height: 30px; padding: 0 9px;
  border: 1px solid var(--line-2); border-radius: var(--r1);
  background: var(--surface); font-size: 12px; color: var(--ink-3);
}
.dsw-del:hover { color: var(--risk); border-color: var(--risk-line); background: var(--risk-soft); }
.dsw-del[data-armed="1"] { color: var(--risk); border-color: var(--risk); background: var(--risk-soft); font-weight: 600; }

/* ---------- a row, and what it can be put in ------------------------------
   The row is two controls sharing one line: the scheme itself, which is the
   whole row so it stays an easy target, and a bookmark at its right edge that
   opens the lists underneath it. The panel is drawn inline rather than as a
   second popover — a menu floating over a menu at this size is a stack of
   boxes nobody can aim at. */
.dsw-row { position: relative; display: flex; align-items: stretch; gap: 2px; flex-wrap: wrap; }
.dsw-row > button[data-dark-set] { flex: 1 1 0; min-width: 0; }
.dsw-add {
  flex: none; width: 30px; display: grid; place-items: center; position: relative;
  border: 1px solid transparent; border-radius: 7px;
  color: var(--ink-3); transition: background .12s, color .12s;
}
.dsw-add:hover { background: var(--surface-2); color: var(--ink); }
.dsw-add.has { color: var(--accent); }
.dsw-add svg { width: 14px; height: 14px; }
.dsw-add b {
  position: absolute; right: 1px; bottom: 1px;
  font: 600 9px/1 var(--mono); color: var(--accent);
}
.dsw-add[aria-expanded="true"] { background: var(--surface-2); color: var(--ink); }

.dsw-lists {
  flex: 1 0 100%; margin: 2px 0 6px; padding: 8px;
  border: 1px solid var(--line); border-radius: 8px; background: var(--surface-2);
  display: grid; gap: 2px;
}
.dsw-lrow {
  display: flex; align-items: center; gap: 8px;
  padding: 5px 6px; border-radius: 6px; font-size: 12.5px; color: var(--ink-2); cursor: pointer;
}
.dsw-lrow:hover { background: var(--surface); color: var(--ink); }
.dsw-lrow span { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.dsw-lrow i { font: 400 11px/1 var(--mono); font-style: normal; color: var(--ink-3); }
.dsw-lrow input {
  appearance: none; width: 14px; height: 14px; flex: none;
  border: 1px solid var(--line-2); border-radius: 4px; background: var(--surface);
}
.dsw-lrow input:checked {
  background: var(--accent) url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%230C1116' stroke-width='2.4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m3.5 8.5 3 3 6-6.5'/%3E%3C/svg%3E") center/10px no-repeat;
  border-color: var(--accent);
}
.dsw-new { display: flex; gap: 4px; margin-top: 4px; }
.dsw-new input {
  flex: 1 1 auto; min-width: 0; height: 28px; padding: 0 8px;
  border: 1px solid var(--line-2); border-radius: 6px;
  background: var(--surface); font-size: 12.5px; color: var(--ink);
}
.dsw-new input:focus { outline: none; border-color: var(--accent); box-shadow: 0 0 0 3px var(--accent-soft); }
.dsw-new button {
  flex: none; width: 28px; display: grid; place-items: center;
  border: 1px solid var(--line-2); border-radius: 6px; background: var(--surface); color: var(--ink-2);
}
.dsw-new button:hover { border-color: var(--accent); color: var(--accent); }
.dsw-new svg { width: 13px; height: 13px; }

.dsw-empty { padding: 22px 10px; text-align: center; font-size: 12.5px; color: var(--ink-3); }
.dsw-empty .link-a { display: block; margin-top: 8px; font-size: 12.5px; }
.dsw-menu[hidden] { display: none; }
.dsw-menu button[data-dark-set] {
  display: flex; align-items: center; gap: 10px; width: 100%;
  padding: 8px; border: 1px solid transparent; border-radius: 7px;
  background: none; text-align: left; cursor: pointer;
  transition: background .14s, border-color .14s;
}
.dsw-menu button[data-dark-set]:hover { background: var(--surface-2); }
.dsw-menu button[data-dark-set].on { background: var(--surface-2); border-color: var(--line); }
/* the swatch carries its own canvas as a ring, so the two colours that define
   a scheme are both in the 16px it gets — and it shows the half of the scheme
   that is currently on, light or dark */
.dsw-menu button[data-dark-set] i {
  width: 16px; height: 16px; border-radius: 50%; flex: none;
  outline: 3px solid var(--paper); outline-offset: 0;
  box-shadow: 0 0 0 4px var(--surface);
}
.dsw-menu button[data-dark-set] b { display: block; font-size: 13.5px; font-weight: 550; color: var(--ink); }
.dsw-menu button[data-dark-set] em { display: block; font-style: normal; font-size: 12px; color: var(--ink-3); margin-top: 1px; }
.dsw-tick { flex: none; opacity: 0; color: var(--accent); }
.dsw-tick svg { width: 14px; height: 14px; }
.dsw-menu button[data-dark-set].on .dsw-tick { opacity: 1; }
@media (max-width: 720px) { .dsw { display: none; } }

/* ==========================================================================
   SCHEMES FROM THE v2 LANGUAGE

   Five more palettes, taken from the marketplace-v2 draft. Only the colour
   came across: none of its shape decisions — the radii, the borders, the
   header treatment, the button shadows — are here, because those are that
   design's language rather than a palette, and mixing the two would produce a
   third design nobody chose.

   Four of them repaint the accent family and nothing else, so they sit on the
   Datasheet surfaces this site already has. Mint is the exception and says so:
   a palette whose identity is its canvas and its graphite type cannot be
   expressed by an accent alone, so it brings its own surfaces and inks.

   --on-accent is what makes an accent this light usable at all: the lime and
   the ink accents need dark and white type respectively, and every filled
   surface on the site reads that token rather than a hard-coded colour.
   ========================================================================== */

/* ---------- v2-1. BLUE — the accent the v2 draft is built around ---------- */
[data-dark="blue"] {
  --accent:      #0B62F4;
  --accent-2:    #0952CE;
  --accent-soft: #EBF2FE;
  --accent-line: #C9DDFD;
  --info:        #0B62F4;
  --info-soft:   #EBF2FE;
  --info-line:   #C9DDFD;
  --on-accent:   #FFFFFF;

  /* the panel keeps its own value of the blue: #0B62F4 on a near-black panel
     is a hole rather than a highlight */
  --panel-accent: #60A5FA;
}
[data-theme="dark"][data-dark="blue"] {
  --accent:      #3B82F6;
  --accent-2:    #60A5FA;
  --accent-soft: #12203A;
  --accent-line: #1E3A6B;
  --info:        #3B82F6;
  --info-soft:   #12203A;
  --info-line:   #1E3A6B;
  --on-accent:   #061024;
}

/* ---------- dark: buttons are flat ----------------------------------------
   A filled button carries `inset 0 -1px 0 rgba(0,0,0,.16)` — a one-pixel dark
   edge along its bottom. On paper that is a bevel and reads as a raised key;
   on a dark canvas the same line has nothing to be darker than, so it reads as
   a shadow pooling under the button, and the eye keeps finding it because it
   is the highest-contrast edge in a low-contrast field.

   So the filled buttons lose it after dark, and the segmented control loses
   its drop under the selected tab for the same reason — depth there comes from
   the surface being lighter than the track, which is a difference the dark
   palettes already have.

   Scoped away from data-lang="v2": those schemes are a different design with
   its own shadow language, and flattening a Datasheet button would be editing
   somebody else's drawing rather than ours. */
html:not([data-lang="v2"])[data-theme="dark"] .btn-primary,
html:not([data-lang="v2"])[data-theme="dark"] .btn-skill,
html:not([data-lang="v2"])[data-theme="dark"] .btn-ink { box-shadow: none; }

html:not([data-lang="v2"])[data-theme="dark"] .seg button.on,
html:not([data-lang="v2"])[data-theme="dark"] .demo-clients button.on { box-shadow: none; }

/* ---------- Charcoal by day: flat there too ------------------------------
   The rule above is about the dark canvas, where an inset bottom edge has
   nothing to be darker than. Charcoal's daylight asks for the same thing for a
   different reason: its buttons are the bright lime, and a bevel drawn in 16%
   black across the foot of #BEF264 reads as a smudge on the one element the
   scheme is built around. The segmented control loses its drop for the same
   reason — the selected tab already separates by being lighter than the track.

   Scoped to the scheme rather than to daylight in general: every other light
   scheme keeps its bevel, and this is a decision about one palette. */


/* The two daylight overrides this scheme used to carry — a flat button and a
   hand-written lime fill — are gone: the fill is what --accent already is, and
   the bevel is what a raised control needs back now that the page under it is
   graphite rather than white. */

/* The one exception, and it is not the bevel coming back. Midnight's accent is
   a saturated colour on a dark navy, which in the physical world spills a
   little light onto what it sits on; the flat rule above takes away an edge
   that was pretending to be depth, and this adds the thing that edge was a
   poor imitation of. A hairline ring at 35% and a soft drop below — three
   pixels of light, no halo. Specificity has to clear the rule above, hence the
   leading html. */
html[data-theme="dark"][data-dark="midnight"] .btn-primary,
html[data-theme="dark"][data-dark="midnight"] .btn-skill {
  box-shadow: 0 0 0 1px rgba(198, 255, 0, .35), 0 6px 22px -10px rgba(198, 255, 0, .55);
}
html[data-theme="dark"][data-dark="midnight"] .btn-primary:hover,
html[data-theme="dark"][data-dark="midnight"] .btn-skill:hover {
  box-shadow: 0 0 0 1px rgba(198, 255, 0, .5), 0 8px 26px -10px rgba(198, 255, 0, .7);
}

/* ---------- DUSK — mono's night, softened ---------------------------------
   The same palette as Mono after dark, pulled off the floor.

   Two things make a dark interface tiring, and neither is "being dark". The
   first is the canvas sitting at black: at #0F1110 every surface above it has
   to be found by a hairline, because there is nowhere left to go down. The
   second is the type at full brightness — #ECEBE6 on near-black is about
   16:1, well past what anyone needs to read and far enough into glare that
   long sessions hurt.

   So the floor comes up to #1B1F22 and the type comes down to #DDE3E7, which
   lands around 11:1 — comfortably past AA for body text and short of the
   glare. What that buys is room underneath: the canvas, the card, the raised
   row and the input can each take a step of their own rather than sharing one
   value and a border. The tint is a cool blue-grey rather than Mono's faint
   green, because a neutral that leans slightly cool reads as calm at low
   luminance and slightly green reads as tired.

   The state colours move too. A #2A1712 risk tint is invisible against a
   #23282C card; every soft is lifted to sit a step above the surface it
   appears on rather than a step below where it used to.

   The four surface steps are deliberately wide — 30, 34, 38 and 42 percent of
   the way up — because separation is the thing a lifted canvas buys and a
   two-unit step throws it away again. A card should be readable as a card with
   the hairline turned off. */
[data-theme="dark"][data-dark="dusk"] {
  --paper:      #1E2226;
  --surface:    #272D32;
  --surface-2:  #2F363C;
  --surface-3:  #39424A;
  --line:       #38424A;
  --line-2:     #4B5661;

  --ink:        #DDE3E7;
  --ink-2:      #A7B0B6;
  --ink-3:      #7E888F;

  --accent:      #45C98A;
  --accent-2:    #5FDB9F;
  --accent-soft: #1D3A30;
  --accent-line: #2E5F49;
  --on-accent:   #08251A;
  --panel-accent:#5FDB9F;

  --risk:       #F0937D;
  --risk-soft:  #3A231D;
  --risk-line:  #5C382E;
  --warn:       #E0B052;
  --warn-soft:  #332818;
  --warn-line:  #55431F;
  --info:       #8AA8F7;
  --info-soft:  #222B45;
  --info-line:  #38456F;

  /* the inverted panel still has to read as inverted, so it goes below the
     canvas rather than above it — the one place this scheme goes darker */
  --panel:      #12161A;
  --panel-2:    #1B2025;
  --panel-line: #2A3138;
  --panel-ink:  #E7ECEF;
  --panel-ink2: #98A2A9;

  /* shadows do less work when the surfaces separate themselves, and a heavy
     drop on a lifted canvas reads as smudge rather than depth */
  --sh1: 0 1px 2px rgb(0 0 0 / .28);
  --sh2: 0 2px 6px rgb(0 0 0 / .32);
  --sh3: 0 8px 26px -10px rgb(0 0 0 / .45);
}

/* ---------- v2-1b. BLUE · SQUARE ------------------------------------------
   The same blue, on the same Datasheet surfaces, with this site's own corner
   radii rather than the v2 draft's. Only the colour is copied here; the radii
   are restated at the end of lang-v2.css, because that layer writes 314
   literal values on top of the three tokens and moving the tokens alone would
   round a tenth of the page. */
[data-dark="bluesq"] {
  --accent:      #0B62F4;
  --accent-2:    #0952CE;
  --accent-soft: #EBF2FE;
  --accent-line: #C9DDFD;
  --info:        #0B62F4;
  --info-soft:   #EBF2FE;
  --info-line:   #C9DDFD;
  --on-accent:   #FFFFFF;

  /* as Blue */
  --panel-accent: #60A5FA;
}
[data-theme="dark"][data-dark="bluesq"] {
  --accent:      #3B82F6;
  --accent-2:    #60A5FA;
  --accent-soft: #12203A;
  --accent-line: #1E3A6B;
  --info:        #3B82F6;
  --info-soft:   #12203A;
  --info-line:   #1E3A6B;
  --on-accent:   #061024;
}

/* ---------- v2-2. LIME ACCENT ---------------------------------------------
   The Datasheet surfaces kept exactly as they are, with the accent swapped for
   the bright lime. It is a different proposition from the Lime above it, which
   also flattens the canvas — this one changes one colour and nothing else. */
[data-dark="limeflat"] {
  --accent:      #BEF264;
  --accent-2:    #AEE84A;
  --accent-soft: #F2F8E0;
  --accent-line: #D9EFA6;
  --info:        var(--ink);
  --info-soft:   #F2F8E0;
  --info-line:   #D9EFA6;
  /* near-black on lime is 14:1; white on it is 1.4:1 and unreadable */
  --on-accent:   #16200A;

  /* the scheme is the lime and nothing else, and the panel was the one place
     still holding the emerald it replaced */
  --panel-accent: #BEF264;
}
[data-theme="dark"][data-dark="limeflat"] {
  --accent:      #BEF264;
  --accent-2:    #D2F98C;
  --accent-soft: #1E2310;
  --accent-line: #3F5220;
  --info:        #BEF264;
  --info-soft:   #1E2310;
  --info-line:   #3F5220;
  --on-accent:   #14170D;
}

/* ---------- v2-3. BLACK — the accent is the ink -------------------------- */
[data-dark="black"] {
  --accent:      #111114;
  --accent-2:    #000000;
  --accent-soft: #F0F0F2;
  --accent-line: #D8D8DE;
  --info:        #56565F;
  --info-soft:   #F0F0F2;
  --info-line:   #D8D8DE;
  --on-accent:   #FFFFFF;

  /* this scheme letters with its ink, and inside the panel the ink is white */
  --panel-accent: #FAFAFA;
}
[data-theme="dark"][data-dark="black"] {
  --accent:      #FAFAFA;
  --accent-2:    #E4E4E7;
  --accent-soft: #1C1D23;
  --accent-line: #3F3F46;
  --info:        #A1A1AA;
  --info-soft:   #1C1D23;
  --info-line:   #3F3F46;
  --on-accent:   #09090B;
}

/* ---------- v2-4. EMERALD ------------------------------------------------- */
[data-dark="emerald"] {
  --accent:      #017737;
  --accent-2:    #015E2B;
  --accent-soft: #E7F4EC;
  --accent-line: #BFE3CE;
  --info:        #017737;
  --info-soft:   #E7F4EC;
  --info-line:   #BFE3CE;
  --on-accent:   #FFFFFF;
}
[data-theme="dark"][data-dark="emerald"] {
  --accent:      #3FCB86;
  --accent-2:    #63D9A0;
  --accent-soft: #0C2A1B;
  --accent-line: #17492F;
  --info:        #3FCB86;
  --info-soft:   #0C2A1B;
  --info-line:   #17492F;
  --on-accent:   #052013;
}

/* ---------- v2-5. MINT ----------------------------------------------------
   The one scheme here that moves the canvas, because that is where its
   identity is: a faintly green paper, and graphite rather than black for type
   — headings, bold copy and every figure are lettered in #30313F, with the two
   greys under it on the same hue so headings do not read cool against warm
   body copy. #288558 leads and #32BB78 is the hover and the tint: white on the
   lighter green is 2.6:1, which is why the darker one carries the text. */
/* [data-lang="v2"] carries the v2 neutrals and is written after this block, so
   the one scheme that moves the canvas has to outrank it rather than be
   overwritten by it. Mint always travels with that language, so naming both is
   accurate as well as specific. */
[data-lang="v2"][data-dark="mint"] {
  --paper:      #F7F9F8;
  --surface:    #FFFFFF;
  --surface-2:  #E7EBE9;
  --surface-3:  #DDE3E0;
  --line:       #E1E6E3;
  --line-2:     #CBD3CF;
  --ink:        #30313F;
  --ink-2:      #5A5C6E;
  --ink-3:      #8C8E9E;
  --accent:      #288558;
  --accent-2:    #32BB78;
  --accent-soft: #E3F3EA;
  --accent-line: #BCE2CD;
  --info:        #288558;
  --info-soft:   #E3F3EA;
  --info-line:   #BCE2CD;
  --on-accent:   #FFFFFF;
}
[data-theme="dark"][data-dark="mint"] {
  --accent:      #32BB78;
  --accent-2:    #4CD08D;
  --accent-soft: #0D2A1D;
  --accent-line: #1B4A33;
  --info:        #32BB78;
  --info-soft:   #0D2A1D;
  --info-line:   #1B4A33;
  --on-accent:   #06210F;
}

/* ---------- 1c. NEUTRAL CARBON — the acid lime on plain black -------------
   A palette that belongs to a terminal. Five neutrals with no hue in them at
   all — #0A0A0A, #141414, #1F1F1F, #2E2E2E, #A1A1A1 — and one colour, #BFFF00,
   which is lime with the last of the yellow left in: brighter and more acid
   than the #BEF264 the other lime schemes use.

   That combination has a meaning already attached to it. Black chrome plus an
   acid green is what a scanner, a log tail and a security console look like,
   and this is a catalogue whose central number is a trust grade. The neutrals
   carry no argument of their own, so the accent is the only thing in the page
   with an opinion — which is the whole design.

   The daylight half splits the accent in two, because #BFFF00 is a fill and
   not an ink: on white it is 1.1:1, invisible as a link and worse as a tick.
   So the lime keeps the one job it is good at — the primary button, where it
   is a surface carrying near-black type at 14:1 — and everything lettered with
   the accent takes #4C7000, the same hue at a lightness that reads. Both are
   in the palette below: --accent is the ink half, the two rules under the
   block are the fill.

   The greys turn over around them: the night's #0A0A0A ink becomes the day's,
   and #9C9B96 is the hairline. Nothing else changes hue, because there is no
   hue to change — every surface is R≈G≈B and the only tint on the page outside
   the accent is #F3FBD6, the chip that marks an accented state.

   `--info` stays neutral in both halves. A scheme this strict has exactly one
   colour, and a blue notice would be a second. ---------------------------- */
[data-theme="light"][data-dark="carbon"] {
  --paper:      #FAFAF9;
  --surface:    #FFFFFF;
  --surface-2:  #F1F0EE;
  --surface-3:  #E7E6E3;
  --line:       #E3E2DF;
  --line-2:     #CFCEC9;

  --ink:        #1C1C1A;
  --ink-2:      #6B6A66;
  --ink-3:      #9C9B96;

  /* the ink half of the accent — links, ticks, grades, anything lettered */
  --accent:      #4C7000;
  --accent-2:    #3D5A00;
  --accent-soft: #F3FBD6;
  --accent-line: #C9E88A;

  --risk:       #DC2626;
  --risk-soft:  #FDECEC;
  --risk-line:  #F7CFCF;
  --warn:       #B45309;
  --warn-soft:  #FDF1E3;
  --warn-line:  #F2DCBD;
  --info:       #6B6A66;
  --info-soft:  #F1F0EE;
  --info-line:  #E3E2DF;

  --panel:      #0A0A0A;
  --panel-2:    #1C1C1C;
  --panel-line: #2E2E2E;
  --panel-ink:  #F2F2F2;
  --panel-ink2: #A1A1A1;

  /* white, because what is FILLED with --accent is filled with the ink. The
     bright lime is a fill of its own and carries its own label; see the two
     rules under this block. */
  --on-accent:    #FFFFFF;
  --panel-accent: #BFFF00;

  --sh1: 0 1px 0 rgba(28,28,26,.04), 0 1px 3px rgba(28,28,26,.08);
  --sh2: 0 8px 24px -10px rgba(28,28,26,.18), 0 1px 3px rgba(28,28,26,.08);
  --sh3: 0 20px 48px -16px rgba(28,28,26,.24), 0 3px 10px rgba(28,28,26,.10);
}
/* The exception every lime scheme needs: the primary action is the bright lime
   with near-black type on it, which is the one place the colour is a surface
   rather than a mark — and where the scheme's identity actually lives.
   btn-skill rides with it: the two are one accent now. */
[data-theme="light"][data-dark="carbon"] .btn-primary,
[data-theme="light"][data-dark="carbon"] .btn-skill {
  background: #BFFF00; color: #1F2E00;
  box-shadow: inset 0 -1px 0 rgba(0,0,0,.10);
}
[data-theme="light"][data-dark="carbon"] .btn-primary:hover,
[data-theme="light"][data-dark="carbon"] .btn-skill:hover { background: #AEEB00; }

[data-theme="dark"][data-dark="carbon"] {
  --paper:      #0A0A0A;
  --surface:    #141414;
  --surface-2:  #1F1F1F;
  --surface-3:  #2E2E2E;
  --line:       #242424;
  --line-2:     #333333;

  --ink:        #F2F2F2;
  --ink-2:      #A1A1A1;
  --ink-3:      #737373;

  --accent:      #BFFF00;
  --accent-2:    #D2FF4D;
  --accent-soft: #1C2400;
  --accent-line: #3A4A0A;

  --risk:       #F0837A;
  --risk-soft:  #2A1715;
  --risk-line:  #482825;
  --warn:       #E3B34E;
  --warn-soft:  #262013;
  --warn-line:  #47391D;
  --info:       #A1A1A1;
  --info-soft:  #1C1C1C;
  --info-line:  #333333;

  --panel:      #0A0A0A;
  --panel-2:    #1A1A1A;
  --panel-line: #2E2E2E;
  --panel-ink:  #F2F2F2;
  --panel-ink2: #A1A1A1;

  /* #BFFF00 is bright enough that white on it is 1.4:1 — the label has to be
     near-black, and near-black with the last of the green in it rather than a
     neutral, so the button reads as one colour rather than as two */
  --on-accent:    #0C1100;
  --panel-accent: #BFFF00;

  --sh1: 0 1px 2px rgba(0,0,0,.45);
  --sh2: 0 8px 24px -12px rgba(0,0,0,.75), 0 1px 3px rgba(0,0,0,.45);
  --sh3: 0 20px 48px -18px rgba(0,0,0,.85), 0 3px 10px rgba(0,0,0,.55);
}

/* ---------- 1d. COOL SLATE — the same lime, with the blue underneath -------
   Carbon's neutrals with a blue cast pushed through all five of them:
   #0B0F14, #111820, #1A242E, #2B3946, #94A3B8. The accent steps down a notch
   at the same time, from #BFFF00 to #A3E635 — still lime, less acid.

   The pairing is doing something specific. A cool ground is the complement of
   a warm green, so the accent gains contrast from the surface it sits on
   rather than from sheer saturation, and it can be turned down without going
   quiet. That is why this is the palette every dev tool eventually arrives at:
   long sessions, a lot of type, and one colour that stays legible at 12px
   without shouting at hour three.

   The neutrals are cool but they are not blue: at #1A242E the cast is about
   6% saturation, enough to read as slate against carbon's dead grey and not
   enough to compete with the accent for attention. The state colours keep
   that cast too — a red mixed toward the ground rather than a stock #EF4444,
   which on a blue-black canvas separates like a sticker.

   Daylight is the same split carbon makes, in this scheme's temperature: a
   #F7F9FB canvas that steps back so a table of results comes forward, ink at
   #141C24, #A3E635 kept as the button fill, and the ink itself doing every
   lettered job — a link, a tick, a grade. It is the one of these three with a
   blue --info, because a cool palette can carry a blue notice without it
   reading as a second accent. ----------------------------------------- */
[data-theme="light"][data-dark="slate"] {
  --paper:      #F7F9FB;
  --surface:    #FFFFFF;
  --surface-2:  #EDF1F6;
  --surface-3:  #E2E8F0;
  --line:       #DCE3EC;
  --line-2:     #C3CEDB;

  --ink:        #141C24;
  --ink-2:      #5A6B7D;
  --ink-3:      #8B99A8;

  /* the accent is the ink here: links, ticks and grades letter in it, and
     the bright lime is a fill only. See the note on charcoal. */
  --accent:      #141C24;
  --accent-2:    #000000;
  --accent-soft: #EDF8D9;
  --accent-line: #BFE082;

  --risk:       #DC2626;
  --risk-soft:  #FDECEC;
  --risk-line:  #F7CFCF;
  --warn:       #D97706;
  --warn-soft:  #FDF2E3;
  --warn-line:  #F5DFBE;
  --info:       #2563EB;
  --info-soft:  #EEF3FE;
  --info-line:  #C9DCFA;

  --panel:      #0B0F14;
  --panel-2:    #1A242E;
  --panel-line: #2B3946;
  --panel-ink:  #E6EDF5;
  --panel-ink2: #94A3B8;

  /* white, because what is FILLED with --accent is filled with the ink. The
     bright lime is a fill of its own and carries its own label; see the two
     rules under this block. */
  --on-accent:    #FFFFFF;
  --panel-accent: #A3E635;

  --sh1: 0 1px 0 rgba(20,28,36,.04), 0 1px 3px rgba(20,28,36,.08);
  --sh2: 0 8px 24px -10px rgba(20,28,36,.18), 0 1px 3px rgba(20,28,36,.08);
  --sh3: 0 20px 48px -16px rgba(20,28,36,.24), 0 3px 10px rgba(20,28,36,.10);
}
/* The exception every lime scheme needs: the primary action is the bright lime
   with near-black type on it, which is the one place the colour is a surface
   rather than a mark — and where the scheme's identity actually lives.
   btn-skill rides with it: the two are one accent now. */
[data-theme="light"][data-dark="slate"] .btn-primary,
[data-theme="light"][data-dark="slate"] .btn-skill {
  background: #A3E635; color: #1A2E05;
  box-shadow: inset 0 -1px 0 rgba(0,0,0,.10);
}
[data-theme="light"][data-dark="slate"] .btn-primary:hover,
[data-theme="light"][data-dark="slate"] .btn-skill:hover { background: #93D827; }

[data-theme="dark"][data-dark="slate"] {
  --paper:      #0B0F14;
  --surface:    #111820;
  --surface-2:  #1A242E;
  --surface-3:  #24303C;
  --line:       #1F2A35;
  --line-2:     #2B3946;

  --ink:        #E6EDF5;
  --ink-2:      #94A3B8;
  --ink-3:      #64748B;

  --accent:      #A3E635;
  --accent-2:    #BEF264;
  /* the tint is the accent mixed into THIS ground, not onto black: 12% of
     #A3E635 over #111820 keeps the blue underneath and stops the badge from
     reading as a green cut out of a blue page */
  --accent-soft: #1A2A18;
  --accent-line: #354C28;

  --risk:       #F2867C;
  --risk-soft:  #2A1A1E;
  --risk-line:  #4A2A30;
  --warn:       #E5B45C;
  --warn-soft:  #262110;
  --warn-line:  #453A1E;
  --info:       #7DA7DE;
  --info-soft:  #16202C;
  --info-line:  #27384D;

  --panel:      #080C11;
  --panel-2:    #111820;
  --panel-line: #24303C;
  --panel-ink:  #E6EDF5;
  --panel-ink2: #94A3B8;

  --on-accent:    #0B1400;
  --panel-accent: #A3E635;

  --sh1: 0 1px 2px rgba(0,0,0,.45);
  --sh2: 0 8px 24px -12px rgba(2,6,12,.75), 0 1px 3px rgba(2,6,12,.45);
  --sh3: 0 20px 48px -18px rgba(2,6,12,.85), 0 3px 10px rgba(2,6,12,.55);
}

/* ---------- 1e. DARK + LIME — the ordinary one ----------------------------
   Zinc and lime, the pairing every dark SaaS front end lands on: #0A0A0A,
   #141414, #1C1C1C under #F5F5F5 and #A3A3A3, with #A3E635 doing the pointing.
   Carbon's neutrals with the acid taken out of the accent — #A3E635 against
   #BFFF00 is the difference between a highlighter and a colour you can put
   behind 13px type without it buzzing.

   The hover is the one unusual note and it is deliberate: #65A30D is DARKER
   than the accent, not lighter. On paper a filled button lightens under the
   cursor because the page is bright; here the button is already the brightest
   thing in the field, so the only direction left that reads as feedback is
   down. It is still 6.5:1 on the canvas, so the same value works as a hover
   for links, where it has to be legible rather than merely visible. ------- */
[data-theme="light"][data-dark="darklime"] {
  --paper:      #FAFAFA;
  --surface:    #FFFFFF;
  --surface-2:  #F4F4F5;
  --surface-3:  #E9E9EB;
  --line:       #E4E4E7;
  --line-2:     #D4D4D8;

  --ink:        #0A0A0A;
  --ink-2:      #52525B;
  --ink-3:      #8A8A93;

  --accent:      #0A0A0A;
  --accent-2:    #000000;
  --accent-soft: #F4FAE6;
  --accent-line: #D7EBAE;

  --risk:       #C4302B;
  --risk-soft:  #FCEDEC;
  --risk-line:  #F2D0CE;
  --warn:       #8A6200;
  --warn-soft:  #FBF3DF;
  --warn-line:  #EADFB6;
  --info:       #52525B;
  --info-soft:  #F4F4F5;
  --info-line:  #DEDEE2;

  --panel:      #0A0A0A;
  --panel-2:    #1C1C1C;
  --panel-line: #2E2E2E;
  --panel-ink:  #F5F5F5;
  --panel-ink2: #A3A3A3;

  --on-accent:    #FFFFFF;
  --panel-accent: #A3E635;

  --sh1: 0 1px 0 rgba(10,10,10,.04), 0 1px 3px rgba(10,10,10,.07);
  --sh2: 0 8px 24px -10px rgba(10,10,10,.18), 0 1px 3px rgba(10,10,10,.07);
  --sh3: 0 20px 48px -16px rgba(10,10,10,.24), 0 3px 10px rgba(10,10,10,.10);
}

[data-theme="dark"][data-dark="darklime"] {
  --paper:      #0A0A0A;
  --surface:    #141414;
  --surface-2:  #1C1C1C;
  --surface-3:  #262626;
  --line:       #232323;
  --line-2:     #333333;

  --ink:        #F5F5F5;
  --ink-2:      #A3A3A3;
  --ink-3:      #737373;

  --accent:      #A3E635;
  --accent-2:    #65A30D;
  --accent-soft: #18220C;
  --accent-line: #33471A;

  --risk:       #F0837A;
  --risk-soft:  #2A1715;
  --risk-line:  #482825;
  --warn:       #E3B34E;
  --warn-soft:  #262013;
  --warn-line:  #47391D;
  --info:       #A3A3A3;
  --info-soft:  #1C1C1C;
  --info-line:  #333333;

  --panel:      #0A0A0A;
  --panel-2:    #171717;
  --panel-line: #262626;
  --panel-ink:  #F5F5F5;
  --panel-ink2: #A3A3A3;

  --on-accent:    #0B1400;
  --panel-accent: #A3E635;

  --sh1: 0 1px 2px rgba(0,0,0,.45);
  --sh2: 0 8px 24px -12px rgba(0,0,0,.75), 0 1px 3px rgba(0,0,0,.45);
  --sh3: 0 20px 48px -18px rgba(0,0,0,.85), 0 3px 10px rgba(0,0,0,.55);
}

/* ---------- 1f. MIDNIGHT — navy, and a lime that is nearly yellow ---------
   Not a grey with a cast, an actual blue: #020617 is navy-black and #0F172A is
   the card. Against a ground that saturated the accent has to answer, so it
   goes the other way from Slate — #C6FF00, lime with the last of the yellow
   still in it, which on navy is the single loudest thing any of these schemes
   do. Blue and yellow-green are close to opposite, and opposites at full
   strength is exactly the pairing that reads as neon.

   That is also its limit. This palette can carry one accent object per screen
   and no more; two full-strength #C6FF00 buttons in one view and the page
   starts to vibrate. Everything that would normally take the accent for
   emphasis — rules, ticks, hover states — sits at 12% instead.

   The buttons glow. It is three pixels of light, not a halo: a hairline ring
   at 35% and a soft drop underneath, which is what a saturated colour does on
   a dark surface in the physical world and the one place this palette earns
   the extra ink. See the rule under the flat-button block. --------------- */
[data-theme="light"][data-dark="midnight"] {
  --paper:      #F8FAFC;
  --surface:    #FFFFFF;
  --surface-2:  #F1F5F9;
  --surface-3:  #E2E8F0;
  --line:       #E2E8F0;
  --line-2:     #CBD5E1;

  --ink:        #020617;
  --ink-2:      #475569;
  --ink-3:      #7C8899;

  --accent:      #4F7A00;
  --accent-2:    #3F6212;
  --accent-soft: #F5FAE4;
  --accent-line: #D9EBAA;

  --risk:       #BE3A38;
  --risk-soft:  #FCEEED;
  --risk-line:  #F1D2CF;
  --warn:       #92650B;
  --warn-soft:  #FBF3DF;
  --warn-line:  #EADFB6;
  --info:       #2A5BD7;
  --info-soft:  #EEF2FD;
  --info-line:  #C9D8F6;

  --panel:      #020617;
  --panel-2:    #0F172A;
  --panel-line: #1E293B;
  --panel-ink:  #F1F5F9;
  --panel-ink2: #94A3B8;

  --on-accent:    #FFFFFF;
  --panel-accent: #C6FF00;

  --sh1: 0 1px 0 rgba(2,6,23,.04), 0 1px 3px rgba(2,6,23,.08);
  --sh2: 0 8px 24px -10px rgba(2,6,23,.20), 0 1px 3px rgba(2,6,23,.08);
  --sh3: 0 20px 48px -16px rgba(2,6,23,.26), 0 3px 10px rgba(2,6,23,.10);
}

[data-theme="dark"][data-dark="midnight"] {
  --paper:      #020617;
  --surface:    #0F172A;
  --surface-2:  #1E293B;
  --surface-3:  #27364B;
  --line:       #26344A;
  --line-2:     #334155;

  --ink:        #F1F5F9;
  --ink-2:      #94A3B8;
  --ink-3:      #64748B;

  --accent:      #C6FF00;
  --accent-2:    #D9FF4D;
  --accent-soft: #1E2A16;
  --accent-line: #3D5520;

  --risk:       #F2867C;
  --risk-soft:  #2A1A1E;
  --risk-line:  #4A2A30;
  --warn:       #E5B45C;
  --warn-soft:  #262110;
  --warn-line:  #453A1E;
  --info:       #7DA7DE;
  --info-soft:  #132033;
  --info-line:  #27384D;

  --panel:      #020617;
  --panel-2:    #0F172A;
  --panel-line: #26344A;
  --panel-ink:  #F1F5F9;
  --panel-ink2: #94A3B8;

  --on-accent:    #0A1400;
  --panel-accent: #C6FF00;

  --sh1: 0 1px 2px rgba(0,0,0,.5);
  --sh2: 0 8px 24px -12px rgba(1,3,12,.8), 0 1px 3px rgba(1,3,12,.5);
  --sh3: 0 20px 48px -18px rgba(1,3,12,.9), 0 3px 10px rgba(1,3,12,.6);
}

/* ---------- 1g. CHARCOAL — the quiet one ----------------------------------
   The same idea as Dark + Lime with everything turned down a step: the canvas
   comes up off the floor to #111111 so the page is charcoal rather than black,
   the cards follow to #1A1A1A and #222222, the type warms to #FAFAFA over
   #A1A1AA, and the accent softens to #BEF264.

   Nothing here is trying to be a terminal. Black canvases are legible and they
   are also relentless — an interface somebody has open for six hours is easier
   at #111111 than at #0A0A0A, because the contrast between the darkest and
   lightest thing on screen is a step shorter and the eye stops working so hard
   at the edges. This is the one of the lime family to hand somebody who has to
   read the catalogue all day rather than glance at it.

   Its daylight half is close to Dark + Lime's, and deliberately so: both are
   neutral canvases that letter in their own ink and keep the lime as a fill,
   because no step on a lime ramp letters on white at 13px without either
   buzzing or dropping under 4.5:1. Their identity is in the night, which is
   where they differ. ---------------------------------------------------- */
[data-theme="light"][data-dark="charcoal"] {
  /* ---- Lime accent, дневная половина, с усиленными границами --------------
     Холст и карточка одинаково белые, поэтому всю структуру держат линии — и
     в исходной схеме они были слишком деликатными: страница читалась как одно
     сплошное полотно, на котором ничего никуда не отделено. Здесь обе линии
     подняты на шаг:

       линейка внутри карточки  #E4E4E4 → #D8D8D8   1.27 → 1.43 к белому
       рамка вокруг карточки    #D2D2D2 → #B6B6B6   1.51 → 2.03

     Рамка теперь заметно темнее заливки, поэтому карточка, инпут и кнопка
     очерчены даже там, где под ними такой же белый; линейка осталась
     отчётливо светлее рамки — иначе таблица превращается в сетку коробок.

     Второе изменение: --ink-3 поднят с #878787 (3.6:1 — ниже AA) до #6F6F6F
     (5.0:1). Он стоит под 12px-подписями, крошками и mono в таблицах.

     Лайм по-прежнему только заливка: буквят чернила и тёмная олива #4C7A0B. */

  /* поверхности */
  --paper:      #FFFFFF;   /* фон страницы, полосы-секции, подложка шапки */
  --surface:    #FFFFFF;   /* карточка, модалка, лист, поле ввода, меню */
  --surface-2:  #F5F5F5;   /* .card-foot, шапка таблицы, чип, hover тихих кнопок */
  --surface-3:  #EAEAEA;   /* трек, аватар-заглушка, switch off, disabled */

  /* линии — единственное, что отделяет одно от другого на белом */
  --line:       #D8D8D8;   /* линейка внутри карточки: таблицы, .kv, разделители */
  --line-2:     #B6B6B6;   /* внешняя рамка: карточка, инпут, кнопка, модалка */

  /* текст */
  --ink:        #101010;   /* заголовки, основной текст, цифры KPI */
  --ink-2:      #505050;   /* описания, абзацы, подписи в метриках */
  --ink-3:      #6F6F6F;   /* кикеры, крошки, мета, ключи в .kv, mono 12px */

  /* акцент — олива буквит, лайм заливает */
  --accent:      #4C7A0B;  /* ссылки, активные состояния, тонкие иконки */
  --accent-2:    #3C6108;  /* hover */
  --accent-soft: #F2F8E4;  /* чип, выбранная строка, focus-ring, «лучший план» */
  --accent-line: #C9E294;  /* рамка вокруг подложки — тоже на шаг плотнее */
  --on-accent:   #FFFFFF;  /* текст на оливковой заливке */

  /* статусы */
  --risk:       #C0402A;  --risk-soft:  #FCEFEC;  --risk-line:  #E9BCB1;
  --warn:       #8A6410;  --warn-soft:  #FAF2E2;  --warn-line:  #DFC896;
  --info:       #2A4FBF;  --info-soft:  #EEF1FB;  --info-line:  #B7C4EB;

  /* панель — тёмная вставка, там лайм снова полноценный акцент */
  --panel:      #141414;
  --panel-2:    #1E1E1E;
  --panel-line: #2C2C2C;
  --panel-ink:  #F5F5F5;
  --panel-ink2: #A6A6A6;
  --panel-accent: #BEF264;

  /* тени */
  --sh1: 0 1px 1px rgba(0,0,0,.04), 0 1px 3px rgba(0,0,0,.05);
  --sh2: 0 10px 30px -14px rgba(0,0,0,.26), 0 2px 6px rgba(0,0,0,.05);
  --sh3: 0 24px 60px -20px rgba(0,0,0,.32), 0 4px 12px rgba(0,0,0,.08);
}

/* Первичное действие остаётся лаймовым: заливкой #BEF264 читается под
   тёмно-оливковым текстом на 12.9:1, буквами — нет. */
[data-theme="light"][data-dark="charcoal"] .btn-primary,
[data-theme="light"][data-dark="charcoal"] .btn-skill {
  background: #BEF264; color: #16200A; border-color: transparent;
  box-shadow: inset 0 -1px 0 rgba(0,0,0,.10);
}
[data-theme="light"][data-dark="charcoal"] .btn-primary:hover,
[data-theme="light"][data-dark="charcoal"] .btn-skill:hover { background: #AEE84A; }

[data-theme="dark"][data-dark="charcoal"] {
  --paper:      #111111;
  --surface:    #1A1A1A;
  --surface-2:  #222222;
  --surface-3:  #2C2C2C;
  --line:       #262626;
  --line-2:     #383838;

  --ink:        #FAFAFA;
  --ink-2:      #A1A1AA;
  --ink-3:      #71717A;

  --accent:      #BEF264;
  --accent-2:    #D2F98C;
  --accent-soft: #1E2312;
  --accent-line: #3D4A22;

  --risk:       #F0837A;
  --risk-soft:  #2A1715;
  --risk-line:  #482825;
  --warn:       #E3B34E;
  --warn-soft:  #262013;
  --warn-line:  #47391D;
  --info:       #A1A1AA;
  --info-soft:  #1E1E1E;
  --info-line:  #333333;

  --panel:      #111111;
  --panel-2:    #1E1E1E;
  --panel-line: #2C2C2C;
  --panel-ink:  #FAFAFA;
  --panel-ink2: #A1A1AA;

  --on-accent:    #101705;
  --panel-accent: #BEF264;

  --sh1: 0 1px 2px rgba(0,0,0,.4);
  --sh2: 0 8px 24px -12px rgba(0,0,0,.7), 0 1px 3px rgba(0,0,0,.4);
  --sh3: 0 20px 48px -18px rgba(0,0,0,.8), 0 3px 10px rgba(0,0,0,.5);
}


/* ---------- 1h. WARM STONE — the brown-black one -------------------------
   Every dark palette so far has been neutral or cool: carbon has no hue at
   all, slate and midnight lean blue. This one leans the other way — #0F0E0B,
   #1A1815, #26231E and #3B372F are greys with brown in them, and #A8A296 is a
   warm grey rather than a silver.

   What that does to the accent is the point of it. #C6F135 on a cool ground
   reads as neon, because a cool ground pushes a yellow-green further from
   itself; on a brown-black it lands closer to home and reads as something
   organic — moss on stone rather than a warning light. The same hex, two
   meanings, decided entirely by what is underneath it.

   The daylight half keeps the warmth rather than defaulting to white: a bone
   #FBF9F5 canvas, ink with brown in it at #211E19, hairlines at #E6E0D4. The
   accent splits the way it does in the other two — #C6F135 stays the button,
   #55760D does the lettering — and even the state colours lean warm: danger is
   #C2410C rather than a stock red, warning #A16207. `--info` is the warm grey
   again, because a palette this committed to one temperature should not have a
   blue notice in it. ----------------------------------------------------- */
[data-theme="light"][data-dark="stone"] {
  --paper:      #FBF9F5;
  --surface:    #FFFFFF;
  --surface-2:  #F3EFE7;
  --surface-3:  #E9E3D7;
  --line:       #E6E0D4;
  --line-2:     #D2CABA;

  --ink:        #211E19;
  --ink-2:      #736A5C;
  --ink-3:      #9A9184;

  /* the ink half of the accent — links, ticks, grades, anything lettered */
  --accent:      #55760D;
  --accent-2:    #445E0A;
  --accent-soft: #F2F7D5;
  --accent-line: #CBDE85;

  --risk:       #C2410C;
  --risk-soft:  #FDEEE6;
  --risk-line:  #F5D4C1;
  --warn:       #A16207;
  --warn-soft:  #FBF3E0;
  --warn-line:  #EEDFB6;
  --info:       #736A5C;
  --info-soft:  #F3EFE7;
  --info-line:  #E6E0D4;

  --panel:      #0F0E0B;
  --panel-2:    #26231E;
  --panel-line: #3B372F;
  --panel-ink:  #F2EFE8;
  --panel-ink2: #A8A296;

  /* white, because what is FILLED with --accent is filled with the ink. The
     bright lime is a fill of its own and carries its own label; see the two
     rules under this block. */
  --on-accent:    #FFFFFF;
  --panel-accent: #C6F135;

  --sh1: 0 1px 0 rgba(33,30,25,.04), 0 1px 3px rgba(33,30,25,.08);
  --sh2: 0 8px 24px -10px rgba(33,30,25,.18), 0 1px 3px rgba(33,30,25,.08);
  --sh3: 0 20px 48px -16px rgba(33,30,25,.24), 0 3px 10px rgba(33,30,25,.10);
}
/* The exception every lime scheme needs: the primary action is the bright lime
   with near-black type on it, which is the one place the colour is a surface
   rather than a mark — and where the scheme's identity actually lives.
   btn-skill rides with it: the two are one accent now. */
[data-theme="light"][data-dark="stone"] .btn-primary,
[data-theme="light"][data-dark="stone"] .btn-skill {
  background: #C6F135; color: #2A3305;
  box-shadow: inset 0 -1px 0 rgba(0,0,0,.10);
}
[data-theme="light"][data-dark="stone"] .btn-primary:hover,
[data-theme="light"][data-dark="stone"] .btn-skill:hover { background: #B6E322; }

[data-theme="dark"][data-dark="stone"] {
  --paper:      #0F0E0B;
  --surface:    #1A1815;
  --surface-2:  #26231E;
  --surface-3:  #332F28;
  --line:       #2B2822;
  --line-2:     #3B372F;

  --ink:        #F2EFE8;
  --ink-2:      #A8A296;
  --ink-3:      #7A756B;

  --accent:      #C6F135;
  --accent-2:    #D6F76B;
  --accent-soft: #21250E;
  --accent-line: #414C1C;

  --risk:       #E9887C;
  --risk-soft:  #2A1A15;
  --risk-line:  #4A2C24;
  --warn:       #DFAF57;
  --warn-soft:  #26200F;
  --warn-line:  #453A1E;
  --info:       #A8A296;
  --info-soft:  #201E19;
  --info-line:  #38342C;

  --panel:      #0F0E0B;
  --panel-2:    #1A1815;
  --panel-line: #2B2822;
  --panel-ink:  #F2EFE8;
  --panel-ink2: #A8A296;

  --on-accent:    #141A05;
  --panel-accent: #C6F135;

  --sh1: 0 1px 2px rgba(0,0,0,.45);
  --sh2: 0 8px 24px -12px rgba(0,0,0,.75), 0 1px 3px rgba(0,0,0,.45);
  --sh3: 0 20px 48px -18px rgba(0,0,0,.85), 0 3px 10px rgba(0,0,0,.55);
}

/* ---------- 1i. NEUTRAL CARBON v2 — the same idea, tuned ------------------
   A second reading of the same palette, kept beside the first rather than
   replacing it: two greys this close can only be chosen between side by side.

   What moved. The night's hairline comes up from #242424 to #2E2E2E and the
   raised surface with it, so a table's rules are visible without hover; the
   type warms a shade (#F5F5F4 over #F2F2F2) and the muted step darkens, which
   widens the gap between a heading and a caption. The accent tint nearly
   doubles in weight — #1F2E00 against #1C2400 — because a badge at 12% on
   pure black was reading as a smudge rather than as a colour.

   The bigger change is daylight. v1 used one olive for both the letters and
   the button; this one keeps the acid #BFFF00 as the fill, with #1F2E00 on it
   at 14:1, and spends #4C7000 only where the accent is ink. The scheme's
   identity moves back to the colour it is named after. ------------------- */
[data-theme="light"][data-dark="carbon2"] {
  --paper:      #FAFAF9;
  --surface:    #FFFFFF;
  --surface-2:  #F1F0EE;
  --surface-3:  #E7E6E3;
  --line:       #E3E2DF;
  --line-2:     #CFCEC9;

  --ink:        #1C1C1A;
  --ink-2:      #6B6A66;
  --ink-3:      #9C9B96;

  /* the ink half of the accent — links, ticks, grades, anything lettered */
  --accent:      #4C7000;
  --accent-2:    #3B5800;
  --accent-soft: #F3FBD6;
  --accent-line: #C9E88A;

  --risk:       #DC2626;
  --risk-soft:  #FDECEC;
  --risk-line:  #F7CFCF;
  --warn:       #B45309;
  --warn-soft:  #FDF1E3;
  --warn-line:  #F2DCBD;
  --info:       #6B6A66;
  --info-soft:  #F1F0EE;
  --info-line:  #E3E2DF;

  --panel:      #0A0A0A;
  --panel-2:    #1F1F1F;
  --panel-line: #2E2E2E;
  --panel-ink:  #F5F5F4;
  --panel-ink2: #A1A1A1;

  /* white, because what is FILLED with --accent is filled with the olive; the
     bright lime is a fill of its own and carries its own label, below */
  --on-accent:    #FFFFFF;
  --panel-accent: #BFFF00;

  --sh1: 0 1px 0 rgba(28,28,26,.04), 0 1px 3px rgba(28,28,26,.08);
  --sh2: 0 8px 24px -10px rgba(28,28,26,.18), 0 1px 3px rgba(28,28,26,.08);
  --sh3: 0 20px 48px -16px rgba(28,28,26,.24), 0 3px 10px rgba(28,28,26,.10);
}
/* the fill the palette is named after, on the one control that is a surface */
[data-theme="light"][data-dark="carbon2"] .btn-primary,
[data-theme="light"][data-dark="carbon2"] .btn-skill {
  background: #BFFF00; color: #1F2E00;
  box-shadow: inset 0 -1px 0 rgba(0,0,0,.10);
}
[data-theme="light"][data-dark="carbon2"] .btn-primary:hover,
[data-theme="light"][data-dark="carbon2"] .btn-skill:hover { background: #AEEB00; }

[data-theme="dark"][data-dark="carbon2"] {
  --paper:      #0A0A0A;
  --surface:    #141414;
  --surface-2:  #1F1F1F;
  --surface-3:  #2A2A2A;
  --line:       #2E2E2E;
  --line-2:     #3D3D3D;

  --ink:        #F5F5F4;
  --ink-2:      #A1A1A1;
  --ink-3:      #6E6E6E;

  --accent:      #BFFF00;
  --accent-2:    #D0FF3D;
  --accent-soft: #1F2E00;
  --accent-line: #4C7000;

  --risk:       #F87171;
  --risk-soft:  #2A1616;
  --risk-line:  #4A2626;
  --warn:       #F5A524;
  --warn-soft:  #2A2110;
  --warn-line:  #4A3A1B;
  --info:       #A1A1A1;
  --info-soft:  #1C1C1C;
  --info-line:  #333333;

  --panel:      #0A0A0A;
  --panel-2:    #141414;
  --panel-line: #2E2E2E;
  --panel-ink:  #F5F5F4;
  --panel-ink2: #A1A1A1;

  --on-accent:    #1F2E00;
  --panel-accent: #BFFF00;

  --sh1: 0 1px 2px rgba(0,0,0,.45);
  --sh2: 0 8px 24px -12px rgba(0,0,0,.75), 0 1px 3px rgba(0,0,0,.45);
  --sh3: 0 20px 48px -18px rgba(0,0,0,.85), 0 3px 10px rgba(0,0,0,.55);
}

/* ---------- 1j. COOL SLATE v2 — the ground steps back ---------------------
   The same blue-grey ramp, opened up. Every step sits further from its
   neighbour — #22303C for the popover, #2B3946 for the rule, #3A4A59 for the
   strong one — which is what a table of results needs: the row, the row next
   to it and the line between them all have to be told apart at 13px without
   anything being outlined.

   Its --info is a real blue rather than a grey. A cool palette can carry one
   without it reading as a second accent, and a dashboard that says "for your
   information" only in green is a dashboard nobody reads twice. ---------- */
[data-theme="light"][data-dark="slate2"] {
  --paper:      #F7F9FB;
  --surface:    #FFFFFF;
  --surface-2:  #EDF1F6;
  --surface-3:  #E2E8F0;
  --line:       #DCE3EC;
  --line-2:     #C3CEDB;

  --ink:        #141C24;
  --ink-2:      #5A6B7D;
  --ink-3:      #8B99A8;

  /* the accent is the ink here: links, ticks and grades letter in it, and
     the bright lime is a fill only. See the note on charcoal. */
  --accent:      #141C24;
  --accent-2:    #000000;
  --accent-soft: #EDF8D9;
  --accent-line: #BFE082;

  --risk:       #DC2626;
  --risk-soft:  #FDECEC;
  --risk-line:  #F7CFCF;
  --warn:       #D97706;
  --warn-soft:  #FDF2E3;
  --warn-line:  #F5DFBE;
  --info:       #2563EB;
  --info-soft:  #EEF3FE;
  --info-line:  #C9DCFA;

  --panel:      #0B0F14;
  --panel-2:    #1A242E;
  --panel-line: #2B3946;
  --panel-ink:  #E8EDF2;
  --panel-ink2: #94A3B8;

  /* white, because what is FILLED with --accent is filled with the olive; the
     bright lime is a fill of its own and carries its own label, below */
  --on-accent:    #FFFFFF;
  --panel-accent: #A3E635;

  --sh1: 0 1px 0 rgba(20,28,36,.04), 0 1px 3px rgba(20,28,36,.08);
  --sh2: 0 8px 24px -10px rgba(20,28,36,.18), 0 1px 3px rgba(20,28,36,.08);
  --sh3: 0 20px 48px -16px rgba(20,28,36,.24), 0 3px 10px rgba(20,28,36,.10);
}
/* the fill the palette is named after, on the one control that is a surface */
[data-theme="light"][data-dark="slate2"] .btn-primary,
[data-theme="light"][data-dark="slate2"] .btn-skill {
  background: #A3E635; color: #1A2E05;
  box-shadow: inset 0 -1px 0 rgba(0,0,0,.10);
}
[data-theme="light"][data-dark="slate2"] .btn-primary:hover,
[data-theme="light"][data-dark="slate2"] .btn-skill:hover { background: #93D827; }

[data-theme="dark"][data-dark="slate2"] {
  --paper:      #0B0F14;
  --surface:    #111820;
  --surface-2:  #1A242E;
  --surface-3:  #22303C;
  --line:       #2B3946;
  --line-2:     #3A4A59;

  --ink:        #E8EDF2;
  --ink-2:      #94A3B8;
  --ink-3:      #64748B;

  --accent:      #A3E635;
  --accent-2:    #B8ED5C;
  --accent-soft: #1F2E05;
  --accent-line: #4D7C0F;

  --risk:       #F87171;
  --risk-soft:  #2A1A1E;
  --risk-line:  #4A2A30;
  --warn:       #FBBF24;
  --warn-soft:  #26210F;
  --warn-line:  #453A1E;
  --info:       #60A5FA;
  --info-soft:  #132033;
  --info-line:  #27384D;

  --panel:      #0B0F14;
  --panel-2:    #111820;
  --panel-line: #2B3946;
  --panel-ink:  #E8EDF2;
  --panel-ink2: #94A3B8;

  --on-accent:    #1A2E05;
  --panel-accent: #A3E635;

  --sh1: 0 1px 2px rgba(0,0,0,.45);
  --sh2: 0 8px 24px -12px rgba(0,0,0,.75), 0 1px 3px rgba(0,0,0,.45);
  --sh3: 0 20px 48px -18px rgba(0,0,0,.85), 0 3px 10px rgba(0,0,0,.55);
}

/* ---------- 1k. WARM STONE v2 — the brown ground, opened up ---------------
   Same temperature, more room in it: a fourth surface at #312D26 that v1 did
   not have, a strong rule at #4C473C, and the muted type lifted to #7A7468 so
   a caption on a card is legible rather than merely present.

   The accent tint is the telling one. #26300A is a brown-green rather than the
   olive-on-black the other two use, because a tint is the accent seen through
   the ground beneath it and this ground has brown in it. The state colours
   follow the same rule — a yellow warning at #EAB308, a danger nearer orange
   at #EF6461 — so nothing on the page argues with the temperature. ------- */
[data-theme="light"][data-dark="stone2"] {
  --paper:      #FBF9F5;
  --surface:    #FFFFFF;
  --surface-2:  #F3EFE7;
  --surface-3:  #E9E3D7;
  --line:       #E6E0D4;
  --line-2:     #D2CABA;

  --ink:        #211E19;
  --ink-2:      #736A5C;
  --ink-3:      #9A9184;

  /* the ink half of the accent — links, ticks, grades, anything lettered */
  --accent:      #55760D;
  --accent-2:    #445E0A;
  --accent-soft: #F2F7D5;
  --accent-line: #CBDE85;

  --risk:       #C2410C;
  --risk-soft:  #FDEEE6;
  --risk-line:  #F5D4C1;
  --warn:       #A16207;
  --warn-soft:  #FBF3E0;
  --warn-line:  #EEDFB6;
  --info:       #736A5C;
  --info-soft:  #F3EFE7;
  --info-line:  #E6E0D4;

  --panel:      #0F0E0B;
  --panel-2:    #26231E;
  --panel-line: #3B372F;
  --panel-ink:  #F2EFE8;
  --panel-ink2: #A8A296;

  /* white, because what is FILLED with --accent is filled with the olive; the
     bright lime is a fill of its own and carries its own label, below */
  --on-accent:    #FFFFFF;
  --panel-accent: #C6F135;

  --sh1: 0 1px 0 rgba(33,30,25,.04), 0 1px 3px rgba(33,30,25,.08);
  --sh2: 0 8px 24px -10px rgba(33,30,25,.18), 0 1px 3px rgba(33,30,25,.08);
  --sh3: 0 20px 48px -16px rgba(33,30,25,.24), 0 3px 10px rgba(33,30,25,.10);
}
/* the fill the palette is named after, on the one control that is a surface */
[data-theme="light"][data-dark="stone2"] .btn-primary,
[data-theme="light"][data-dark="stone2"] .btn-skill {
  background: #C6F135; color: #2A3305;
  box-shadow: inset 0 -1px 0 rgba(0,0,0,.10);
}
[data-theme="light"][data-dark="stone2"] .btn-primary:hover,
[data-theme="light"][data-dark="stone2"] .btn-skill:hover { background: #B6E322; }

[data-theme="dark"][data-dark="stone2"] {
  --paper:      #0F0E0B;
  --surface:    #1A1815;
  --surface-2:  #26231E;
  --surface-3:  #312D26;
  --line:       #3B372F;
  --line-2:     #4C473C;

  --ink:        #F2EFE8;
  --ink-2:      #A8A296;
  --ink-3:      #7A7468;

  --accent:      #C6F135;
  --accent-2:    #D3F55C;
  --accent-soft: #26300A;
  --accent-line: #55760D;

  --risk:       #EF6461;
  --risk-soft:  #2A1A16;
  --risk-line:  #4A2C26;
  --warn:       #EAB308;
  --warn-soft:  #28220E;
  --warn-line:  #493D1C;
  --info:       #A8A296;
  --info-soft:  #201E19;
  --info-line:  #38342C;

  --panel:      #0F0E0B;
  --panel-2:    #1A1815;
  --panel-line: #3B372F;
  --panel-ink:  #F2EFE8;
  --panel-ink2: #A8A296;

  --on-accent:    #2A3305;
  --panel-accent: #C6F135;

  --sh1: 0 1px 2px rgba(0,0,0,.45);
  --sh2: 0 8px 24px -12px rgba(0,0,0,.75), 0 1px 3px rgba(0,0,0,.45);
  --sh3: 0 20px 48px -18px rgba(0,0,0,.85), 0 3px 10px rgba(0,0,0,.55);
}

/* ---------- 1l. CHARCOAL v2 — off the floor, one lime ---------------------
   The fourth of the v2 set and the least dramatic of them, which is the point.
   #111111 rather than black, #1A1A1A and #222222 above it, and the type warm
   at #FAFAFA over #A1A1AA — a zinc ramp with the contrast between the darkest
   and the lightest thing on screen one step shorter than carbon's. An
   interface somebody has open for six hours is easier here.

   Against that ground the accent goes DOWN rather than up: #A3E635 instead of
   the acid #BFFF00, with #84CC16 as its hover in both halves — the same green
   pressed rather than a lighter one, which on a mid-dark canvas reads as the
   button taking the press rather than as it lighting up.

   Daylight keeps the bright fill and letters with the ink, the way the rest of
   the v2 set does. Two values in its light half are the palette's own choices
   rather than derivations: --bg-accent #F0FDE7 is a hair cooler than the other
   three, and --border-accent #BBF7D0 is a mint rather than an olive, which is
   the one place this scheme steps outside the lime family on purpose. ----- */
[data-theme="light"][data-dark="charcoal2"] {
  --paper:      #FAFAF9;
  --surface:    #FFFFFF;
  --surface-2:  #F4F4F2;
  /* the palette gives white for the popover; this is the ramp step below it,
     which is what the site spends on wells, tracks and empty states */
  --surface-3:  #EAEAE7;
  --line:       #E4E4E1;
  --line-2:     #D1D0CC;

  --ink:        #1C1C1A;
  --ink-2:      #6B6A66;
  --ink-3:      #9C9B96;

  /* the accent is the ink here: links, ticks and grades letter in it, and
     the bright lime is a fill only. See the note on charcoal. */
  --accent:      #1C1C1A;
  --accent-2:    #000000;
  --accent-soft: #F0FDE7;
  --accent-line: #BBF7D0;

  --risk:       #DC2626;
  --risk-soft:  #FDECEC;
  --risk-line:  #F7CFCF;
  --warn:       #B45309;
  --warn-soft:  #FDF1E3;
  --warn-line:  #F2DCBD;
  --info:       #6B6A66;
  --info-soft:  #F4F4F2;
  --info-line:  #E4E4E1;

  --panel:      #111111;
  --panel-2:    #222222;
  --panel-line: #2E2E2E;
  --panel-ink:  #FAFAFA;
  --panel-ink2: #A1A1AA;

  --on-accent:    #FFFFFF;
  --panel-accent: #A3E635;

  --sh1: 0 1px 0 rgba(28,28,26,.04), 0 1px 3px rgba(28,28,26,.08);
  --sh2: 0 8px 24px -10px rgba(28,28,26,.18), 0 1px 3px rgba(28,28,26,.08);
  --sh3: 0 20px 48px -16px rgba(28,28,26,.24), 0 3px 10px rgba(28,28,26,.10);
}
/* the fill the palette is named after, on the one control that is a surface */
[data-theme="light"][data-dark="charcoal2"] .btn-primary,
[data-theme="light"][data-dark="charcoal2"] .btn-skill {
  background: #A3E635; color: #1A2E05;
  box-shadow: inset 0 -1px 0 rgba(0,0,0,.10);
}
[data-theme="light"][data-dark="charcoal2"] .btn-primary:hover,
[data-theme="light"][data-dark="charcoal2"] .btn-skill:hover { background: #84CC16; }

[data-theme="dark"][data-dark="charcoal2"] {
  --paper:      #111111;
  --surface:    #1A1A1A;
  --surface-2:  #222222;
  --surface-3:  #2A2A2A;
  --line:       #2E2E2E;
  --line-2:     #3F3F3F;

  --ink:        #FAFAFA;
  --ink-2:      #A1A1AA;
  --ink-3:      #71717A;

  --accent:      #A3E635;
  /* darker on hover, not lighter: the accent is already the brightest thing in
     the field, so the only direction left that reads as a press is down */
  --accent-2:    #84CC16;
  --accent-soft: #1A2E05;
  --accent-line: #4D7C0F;

  --risk:       #F87171;
  --risk-soft:  #2A1616;
  --risk-line:  #4A2626;
  --warn:       #F5A524;
  --warn-soft:  #2A2110;
  --warn-line:  #4A3A1B;
  --info:       #A1A1AA;
  --info-soft:  #1E1E1E;
  --info-line:  #333333;

  --panel:      #111111;
  --panel-2:    #1A1A1A;
  --panel-line: #2E2E2E;
  --panel-ink:  #FAFAFA;
  --panel-ink2: #A1A1AA;

  --on-accent:    #1A2E05;
  --panel-accent: #A3E635;

  --sh1: 0 1px 2px rgba(0,0,0,.4);
  --sh2: 0 8px 24px -12px rgba(0,0,0,.7), 0 1px 3px rgba(0,0,0,.4);
  --sh3: 0 20px 48px -18px rgba(0,0,0,.8), 0 3px 10px rgba(0,0,0,.5);
}

/* ==========================================================================
   THE v2 SHAPE

   The five schemes above came from the marketplace-v2 draft, and a palette is
   only half of what makes that draft look like itself: the other half is the
   shape — 12/14/20px radii instead of 6/9/13, a taller header, shadows that
   sit under a card rather than around it, a flat primary button with a border
   instead of an inset highlight, and Inter over Instrument Sans.

   So the shape travels with the palette. app.js stamps data-lang="v2" on the
   root whenever one of those five is chosen, which means every rule below is
   written once rather than five times, and the Datasheet schemes are untouched
   by any of it. Switching back to Mono restores this site exactly.
   ========================================================================== */
[data-lang="v2"] {
  /* THE SURFACES ARE THE LANGUAGE

     This was the missing half. The v2 draft is a grey canvas with white cards
     floating on it — depth comes from the gap and the drop shadow, not from a
     hairline border on a warm paper. Taking only its accent left the palette
     sitting inside Datasheet's bordered strips, which is why the two lime
     pages did not look like the same design at all. */
  --paper:      #F2F2F4;
  --surface:    #FFFFFF;
  --surface-2:  #FAFAFB;
  --surface-3:  #F0F0F3;
  --line:       #E9E9ED;
  --line-2:     #DCDCE2;
  --ink:        #111114;
  --ink-2:      #56565F;
  --ink-3:      #8C8C97;
  /* the inverted panel and the state colours belong to the same palette */
  --panel:      #15151A;
  --panel-2:    #1E1E25;
  --panel-line: #2C2C35;
  --panel-ink:  #D7D7E0;
  --panel-ink2: #8C8C97;
  --risk:       #D0303A;
  --risk-soft:  #FDECEC;
  --risk-line:  #F6CFD1;
  --warn:       #B26A05;
  --warn-soft:  #FDF2E2;
  --warn-line:  #F2DEBB;

  --r1: 12px;
  --r2: 14px;
  --r3: 20px;
  /* depth from a soft drop under the element, not a ring around it */
  --sh1: 0 1px 2px rgb(17 17 20 / .045), 0 10px 26px -18px rgb(17 17 20 / .22);
  --sh2: 0 2px 4px rgb(17 17 20 / .05), 0 22px 42px -24px rgb(17 17 20 / .28);
  --sh3: 0 4px 8px rgb(17 17 20 / .06), 0 32px 64px -28px rgb(17 17 20 / .34);
  --nav-h: 64px;
  --sans: 'Inter', ui-sans-serif, system-ui, -apple-system, 'Segoe UI', Roboto, Arial, sans-serif;
  --mono: 'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
}

/* ---------- v2: what a light accent may not letter ------------------------
   Lime is a fill, not a text colour: #BEF264 on white is about 1.5:1. The
   Datasheet schemes never hit this because every accent they carry is dark
   enough to read. This is the v2 draft's own answer, kept selector for
   selector — anything that used the accent as INK is given the ink back, and
   a link keeps the lime as its underline instead of its colour. The fills —
   buttons, badges, the dot on a chart — are untouched.

   :not(.x) on every line is theirs too, and it is load-bearing: a selector
   list where one member is the bare attribute matches <html> itself, and
   text-decoration inherits from there into the whole document.
   ------------------------------------------------------------------------ */
/* a link: ink, with the lime kept as its underline */
[data-dark="limeflat"]:not(.x) .link-a,
[data-dark="limeflat"]:not(.x) .link:hover,
[data-dark="limeflat"]:not(.x) .strip a:hover,
[data-dark="limeflat"]:not(.x) .rstrip-acts a,
[data-dark="limeflat"]:not(.x) .bl-lead-t a:hover,
[data-dark="limeflat"]:not(.x) .fp-foot-note a,
[data-dark="limeflat"]:not(.x) .st-srv a:hover,
[data-dark="limeflat"]:not(.x) .db-nm a:hover,
[data-dark="limeflat"]:not(.x) .co-item-n:hover,
[data-dark="limeflat"]:not(.x) .pl-name:hover,
[data-dark="limeflat"]:not(.x) .new-list:hover,
[data-dark="limeflat"]:not(.x) .auth-mini:hover {
  color: var(--ink);
  text-decoration: underline; text-decoration-thickness: 2px;
  text-underline-offset: 3px; text-decoration-color: var(--accent);
}
/* everything else the accent used to letter */
[data-dark="limeflat"]:not(.x) .bl-tag.on,
[data-dark="limeflat"]:not(.x) .bl-tag.on .n,
[data-dark="limeflat"]:not(.x) .bl-row:hover .bl-row-t,
[data-dark="limeflat"]:not(.x) .bl-toc nav a.on span,
[data-dark="limeflat"]:not(.x) a.bl-nav-c:hover .bl-nav-t,
[data-dark="limeflat"]:not(.x) .co-client svg,
[data-dark="limeflat"]:not(.x) .co-state[data-s="owned"],
[data-dark="limeflat"]:not(.x) .co-alert.ok .ic,
[data-dark="limeflat"]:not(.x) .co-pickitem:hover b,
[data-dark="limeflat"]:not(.x) .btn.on,
[data-dark="limeflat"]:not(.x) .btn.on:hover,
[data-dark="limeflat"]:not(.x) .field:focus-within,
[data-dark="limeflat"]:not(.x) .badge-accent,
[data-dark="limeflat"]:not(.x) .verified,
[data-dark="limeflat"]:not(.x) .grade,
[data-dark="limeflat"]:not(.x) .nsheet a.on,
[data-dark="limeflat"]:not(.x) .pop-f input:focus,
[data-dark="limeflat"]:not(.x) .db-good,
[data-dark="limeflat"]:not(.x) .db-need.ok .ic,
[data-dark="limeflat"]:not(.x) .db-num:focus,
[data-dark="limeflat"]:not(.x) .db-form input[type="text"]:focus,
[data-dark="limeflat"]:not(.x) .db-tkin:focus,
[data-dark="limeflat"]:not(.x) .dx-rail a.on svg,
[data-dark="limeflat"]:not(.x) .fp-mast h1 em,
[data-dark="limeflat"]:not(.x) .fp-check li svg,
[data-dark="limeflat"]:not(.x) .jb-opt.on .n,
[data-dark="limeflat"]:not(.x) a.jb-row:hover .jb-title,
[data-dark="limeflat"]:not(.x) a.jb-row:hover .jb-title .go,
[data-dark="limeflat"]:not(.x) .nt.ok .nt-ic,
[data-dark="limeflat"]:not(.x) .pay-in:focus,
[data-dark="limeflat"]:not(.x) .pay-done-ic,
[data-dark="limeflat"]:not(.x) .pr-mast h1 em,
[data-dark="limeflat"]:not(.x) .pr-c-y,
[data-dark="limeflat"]:not(.x) .pr-c-g.is-same .spec,
[data-dark="limeflat"]:not(.x) .pr-cmp tbody tr.is-same td.pr-c-c::before,
[data-dark="limeflat"]:not(.x) .nav-acct-av,
[data-dark="limeflat"]:not(.x) .pl-node:hover .tile,
[data-dark="limeflat"]:not(.x) .pl-scope.ok,
[data-dark="limeflat"]:not(.x) .pl-swap-side:hover b,
[data-dark="limeflat"]:not(.x) #planRelated .stack-card h3 a:hover,
[data-dark="limeflat"]:not(.x) .stk-card.picked,
[data-dark="limeflat"]:not(.x) .stk-spec .h.free,
[data-dark="limeflat"]:not(.x) .stk-can svg,
[data-dark="limeflat"]:not(.x) .stk-lede-more,
[data-dark="limeflat"]:not(.x) .st-banner-ic,
[data-dark="limeflat"]:not(.x) .st-comp-state,
[data-dark="limeflat"]:not(.x) .st-form-note.done,
[data-dark="limeflat"]:not(.x) .input:focus,
[data-dark="limeflat"]:not(.x) .st-pick-btn > svg:first-child,
[data-dark="limeflat"]:not(.x) .st-pick-btn [data-icon]:first-child,
[data-dark="limeflat"]:not(.x) .st-nav-i.on svg,
[data-dark="limeflat"]:not(.x) .scard.picked,
[data-dark="limeflat"]:not(.x) .hero h1 em,
[data-dark="limeflat"]:not(.x) .big-field:focus-within,
[data-dark="limeflat"]:not(.x) .statbar .d,
[data-dark="limeflat"]:not(.x) .frow .ic,
[data-dark="limeflat"]:not(.x) .chip,
[data-dark="limeflat"]:not(.x) .plan.best,
[data-dark="limeflat"]:not(.x) .plan li svg,
[data-dark="limeflat"]:not(.x) .lib-title-input:focus,
[data-dark="limeflat"]:not(.x) .lib-desc-input:focus,
[data-dark="limeflat"]:not(.x) .rail a.on svg,
[data-dark="limeflat"]:not(.x) .kpi .d.up b,
[data-dark="limeflat"]:not(.x) .alert.ok .ic,
[data-dark="limeflat"]:not(.x) .payout-total .v,
[data-dark="limeflat"]:not(.x) .pick-card.on,
[data-dark="limeflat"]:not(.x) .sk-new-check svg,
[data-dark="limeflat"]:not(.x) .pub-done-ic,
[data-dark="limeflat"]:not(.x) .rw-in:focus,
[data-dark="limeflat"]:not(.x) .rw-body:focus,
[data-dark="limeflat"]:not(.x) .db-in:focus { color: var(--ink); }

/* ---------- v2 AT NIGHT ---------------------------------------------------
   The schemes above carry their own dark accents, but a palette is more than
   its accent after dark: v2's night is a neutral zinc — #09090B canvas, cool
   greys, hard shadows — and ours is a warm green-black. Taking only the accent
   left the v2 schemes sitting on the Datasheet night, which is the same class
   of mistake as taking the colour without the shape in daylight.

   The accent family is deliberately absent here: that belongs to whichever
   scheme is selected, and repeating it would let this block silently win. */
[data-lang="v2"][data-theme="dark"] {
  --ink:         #FAFAFA;
  --ink-2:       #A1A1AA;
  --ink-3:       #71717A;
  --line:        #27272A;
  --line-2:      #3F3F46;
  --panel:       #0B0B0E;
  --panel-2:     #141519;
  --panel-ink:   #FAFAFA;
  --panel-ink2:  #A1A1AA;
  --panel-line:  #27272A;
  --paper:       #09090B;
  --risk:        #F87171;
  --risk-line:   #7F1D1D;
  --risk-soft:   #451A1A;
  --sh1:         0 1px 3px rgb(0 0 0 / .5);
  --sh2:         0 8px 30px rgb(0 0 0 / .55);
  --sh3:         0 20px 50px rgb(0 0 0 / .6);
  --surface:     #141519;
  --surface-2:   #1C1D23;
  --surface-3:   #24252C;
  --warn:        #FACC15;
  --warn-line:   #713F12;
  --warn-soft:   #423208;
}

/* the three component decisions their night makes on top of the tokens */
[data-lang="v2"][data-theme="dark"] ::selection { background: var(--accent-soft); color: var(--ink); }
[data-lang="v2"][data-theme="dark"] .btn-ink:hover { background: #E4E4E7; }
[data-lang="v2"][data-theme="dark"] .card { border-color: var(--line); }

/* ---------- v2: lime is legible at night ---------------------------------
   The daylight rules above take the accent away from anything that letters
   with it, because #BEF264 on white is 1.5:1. On #09090B it is 16:1, so every
   one of those selectors is handed back — and the link loses the underline it
   was given as a substitute for colour. This is their own undo, selector for
   selector. */
[data-theme="dark"][data-dark="limeflat"]:not(.x) .link-a,
[data-theme="dark"][data-dark="limeflat"]:not(.x) .link:hover,
[data-theme="dark"][data-dark="limeflat"]:not(.x) .strip a:hover,
[data-theme="dark"][data-dark="limeflat"]:not(.x) .rstrip-acts a,
[data-theme="dark"][data-dark="limeflat"]:not(.x) .bl-lead-t a:hover,
[data-theme="dark"][data-dark="limeflat"]:not(.x) .fp-foot-note a,
[data-theme="dark"][data-dark="limeflat"]:not(.x) .st-srv a:hover,
[data-theme="dark"][data-dark="limeflat"]:not(.x) .db-nm a:hover,
[data-theme="dark"][data-dark="limeflat"]:not(.x) .co-item-n:hover,
[data-theme="dark"][data-dark="limeflat"]:not(.x) .pl-name:hover,
[data-theme="dark"][data-dark="limeflat"]:not(.x) .new-list:hover,
[data-theme="dark"][data-dark="limeflat"]:not(.x) .auth-mini:hover,
[data-theme="dark"][data-dark="limeflat"]:not(.x) .bl-tag.on,
[data-theme="dark"][data-dark="limeflat"]:not(.x) .bl-tag.on .n,
[data-theme="dark"][data-dark="limeflat"]:not(.x) .bl-row:hover .bl-row-t,
[data-theme="dark"][data-dark="limeflat"]:not(.x) .bl-toc nav a.on span,
[data-theme="dark"][data-dark="limeflat"]:not(.x) a.bl-nav-c:hover .bl-nav-t,
[data-theme="dark"][data-dark="limeflat"]:not(.x) .co-client svg,
[data-theme="dark"][data-dark="limeflat"]:not(.x) .co-state[data-s="owned"],
[data-theme="dark"][data-dark="limeflat"]:not(.x) .co-alert.ok .ic,
[data-theme="dark"][data-dark="limeflat"]:not(.x) .co-pickitem:hover b,
[data-theme="dark"][data-dark="limeflat"]:not(.x) .btn.on,
[data-theme="dark"][data-dark="limeflat"]:not(.x) .btn.on:hover,
[data-theme="dark"][data-dark="limeflat"]:not(.x) .field:focus-within,
[data-theme="dark"][data-dark="limeflat"]:not(.x) .badge-accent,
[data-theme="dark"][data-dark="limeflat"]:not(.x) .verified,
[data-theme="dark"][data-dark="limeflat"]:not(.x) .grade,
[data-theme="dark"][data-dark="limeflat"]:not(.x) .nsheet a.on,
[data-theme="dark"][data-dark="limeflat"]:not(.x) .pop-f input:focus,
[data-theme="dark"][data-dark="limeflat"]:not(.x) .db-good,
[data-theme="dark"][data-dark="limeflat"]:not(.x) .db-need.ok .ic,
[data-theme="dark"][data-dark="limeflat"]:not(.x) .db-num:focus,
[data-theme="dark"][data-dark="limeflat"]:not(.x) .db-form input[type="text"]:focus,
[data-theme="dark"][data-dark="limeflat"]:not(.x) .db-tkin:focus,
[data-theme="dark"][data-dark="limeflat"]:not(.x) .dx-rail a.on svg,
[data-theme="dark"][data-dark="limeflat"]:not(.x) .fp-mast h1 em,
[data-theme="dark"][data-dark="limeflat"]:not(.x) .fp-check li svg,
[data-theme="dark"][data-dark="limeflat"]:not(.x) .jb-opt.on .n,
[data-theme="dark"][data-dark="limeflat"]:not(.x) a.jb-row:hover .jb-title,
[data-theme="dark"][data-dark="limeflat"]:not(.x) a.jb-row:hover .jb-title .go,
[data-theme="dark"][data-dark="limeflat"]:not(.x) .nt.ok .nt-ic,
[data-theme="dark"][data-dark="limeflat"]:not(.x) .pay-in:focus,
[data-theme="dark"][data-dark="limeflat"]:not(.x) .pay-done-ic,
[data-theme="dark"][data-dark="limeflat"]:not(.x) .pr-mast h1 em,
[data-theme="dark"][data-dark="limeflat"]:not(.x) .pr-c-y,
[data-theme="dark"][data-dark="limeflat"]:not(.x) .pr-c-g.is-same .spec,
[data-theme="dark"][data-dark="limeflat"]:not(.x) .pr-cmp tbody tr.is-same td.pr-c-c::before,
[data-theme="dark"][data-dark="limeflat"]:not(.x) .nav-acct-av,
[data-theme="dark"][data-dark="limeflat"]:not(.x) .pl-node:hover .tile,
[data-theme="dark"][data-dark="limeflat"]:not(.x) .pl-scope.ok,
[data-theme="dark"][data-dark="limeflat"]:not(.x) .pl-swap-side:hover b,
[data-theme="dark"][data-dark="limeflat"]:not(.x) #planRelated .stack-card h3 a:hover,
[data-theme="dark"][data-dark="limeflat"]:not(.x) .stk-card.picked,
[data-theme="dark"][data-dark="limeflat"]:not(.x) .stk-spec .h.free,
[data-theme="dark"][data-dark="limeflat"]:not(.x) .stk-can svg,
[data-theme="dark"][data-dark="limeflat"]:not(.x) .stk-lede-more,
[data-theme="dark"][data-dark="limeflat"]:not(.x) .st-banner-ic,
[data-theme="dark"][data-dark="limeflat"]:not(.x) .st-comp-state,
[data-theme="dark"][data-dark="limeflat"]:not(.x) .st-form-note.done,
[data-theme="dark"][data-dark="limeflat"]:not(.x) .input:focus,
[data-theme="dark"][data-dark="limeflat"]:not(.x) .st-pick-btn > svg:first-child,
[data-theme="dark"][data-dark="limeflat"]:not(.x) .st-pick-btn [data-icon]:first-child,
[data-theme="dark"][data-dark="limeflat"]:not(.x) .st-nav-i.on svg,
[data-theme="dark"][data-dark="limeflat"]:not(.x) .scard.picked,
[data-theme="dark"][data-dark="limeflat"]:not(.x) .hero h1 em,
[data-theme="dark"][data-dark="limeflat"]:not(.x) .big-field:focus-within,
[data-theme="dark"][data-dark="limeflat"]:not(.x) .statbar .d,
[data-theme="dark"][data-dark="limeflat"]:not(.x) .frow .ic,
[data-theme="dark"][data-dark="limeflat"]:not(.x) .chip,
[data-theme="dark"][data-dark="limeflat"]:not(.x) .plan.best,
[data-theme="dark"][data-dark="limeflat"]:not(.x) .plan li svg,
[data-theme="dark"][data-dark="limeflat"]:not(.x) .lib-title-input:focus,
[data-theme="dark"][data-dark="limeflat"]:not(.x) .lib-desc-input:focus,
[data-theme="dark"][data-dark="limeflat"]:not(.x) .rail a.on svg,
[data-theme="dark"][data-dark="limeflat"]:not(.x) .kpi .d.up b,
[data-theme="dark"][data-dark="limeflat"]:not(.x) .alert.ok .ic,
[data-theme="dark"][data-dark="limeflat"]:not(.x) .payout-total .v,
[data-theme="dark"][data-dark="limeflat"]:not(.x) .pick-card.on,
[data-theme="dark"][data-dark="limeflat"]:not(.x) .sk-new-check svg,
[data-theme="dark"][data-dark="limeflat"]:not(.x) .pub-done-ic,
[data-theme="dark"][data-dark="limeflat"]:not(.x) .rw-in:focus,
[data-theme="dark"][data-dark="limeflat"]:not(.x) .rw-body:focus,
[data-theme="dark"][data-dark="limeflat"]:not(.x) .db-in:focus { color: var(--accent); text-decoration: none; }


/* ==========================================================================
   THE SHAPE OF WHAT IS NOT THERE YET.

   A cabinet on its first day is mostly empty: no calls, no invoices, no
   payouts, no reviews. An empty card with a sentence in it reads as a page
   that failed — the reader cannot tell "nothing has happened yet" from "this
   did not load". A shape can: an outline of the table, the chart or the rows
   that will fill, with one line over it saying what fills them.

   NOTHING HERE ANIMATES, and that is the point rather than an omission. A
   pulsing skeleton means "loading, wait"; nothing on these screens is loading
   and waiting will not change it. The shape sits still, and the sentence over
   it says what would put data in it.

   The studio has drawn its first run this way since it was built — these are
   its own rules, moved out of studio.css so that the buyer's cabinet, the
   money screens and the lists can say the same thing the same way.
   ========================================================================== */
/* the bar keeps `display: block`; the pill sets its own a few lines down, and
   declaring one here as well left two rules in the same block disagreeing
   about the same property */
.bars .b span.st-sk-bar { display: block; }
.bars .b span.st-sk-bar,
.st-sk-pill {
  background: var(--surface-3); border-radius: 3px;
}
.st-sk-bar { border-radius: 3px 3px 0 0; }
.st-sk .b { cursor: default; }
/* INLINE-BLOCK, because most of the slots a pill now stands in are inline. It
   was written for the catalogue skeleton, where every pill is a flex item and
   the browser blockifies it anyway; it is also what the studio's header line,
   the right-aligned balance cells and the counts inside a badge hold while
   their figures are being fetched, and a block there breaks the sentence or
   walks out of the alignment its cell was given. Flex items are unaffected. */
.st-sk-pill { height: 11px; border-radius: 6px; display: inline-block; vertical-align: middle; }
.st-sk-row { pointer-events: none; }
/* The skeleton is dimmed under the sentence rather than competing with it —
   and dimmed on its own too, where there is no sentence over it: a lone pill
   waiting for one figure is quieter than the number that replaces it, which is
   how a reader tells a slot that is filling from one that is filled. */
.st-sk .b span.st-sk-bar, .st-sk-pill { opacity: .5; }

/* A COLOUR IS A CLAIM, AND A SHAPE HAS NOTHING TO CLAIM YET. Two badges in the
   studio hold a pill while their word is fetched, and both were drawn in the
   accent green: the payout chip said the run was clear and the reviews chip
   said the rating was good before either had been read. Neither survives
   contact with the answer — a held account turns the first one amber
   (paintMoney in studio.js), and an account with no reviews has the second
   removed outright — so the green was a guess that flashed and went.
   The class stays on the element, because the script that fills the chip finds
   it by that class; only the colour waits with the shape. */
.badge:has(> .st-sk-pill) {
  background: var(--surface-2); border-color: var(--line); color: var(--ink-2);
}

/* THE WORDS BETWEEN TWO SLOTS ARE NOT A SENTENCE ON THEIR OWN. The studio's
   header line reads "<name> · paid through <method>", and while both ends are
   shapes what is left is the join, hanging between two grey bars — on a narrow
   screen on a line of its own. It waits with them, keeping its width so that
   nothing moves when the two figures arrive.

   Written as "hide it while a pill is there" rather than "show it once they
   are gone", so a browser that does not know :has() shows the join — which is
   what this line said before — instead of hiding it for ever. */
.st-head-meta:has(.st-sk-pill) .st-sk-join { visibility: hidden; }

.st-sk-host { position: relative; }
.st-sk-note {
  position: absolute; inset: 0; z-index: 1;
  display: grid; place-content: center; text-align: center;
  padding: 0 16px; pointer-events: none;
}
/* the sentence sits on its own ground rather than directly on the bars — over
   a skeleton, grey text on grey shapes is the one arrangement that reads as
   broken rather than as empty */
.st-sk-chip {
  display: grid; gap: 4px; justify-items: center;
  padding: 12px 16px; border-radius: var(--r2);
  background: var(--surface); border: 1px solid var(--line);
  box-shadow: var(--sh1); max-width: 46ch;
}
.st-sk-chip b { font-size: 14px; font-weight: 550; letter-spacing: -.014em; }
.st-sk-chip > span { font-size: 12.5px; line-height: 1.5; color: var(--ink-3); }

/* ---- the shapes the helper draws (mmSkeleton in app.js) ------------------
   Four of them, because four are what this product's empty screens are: a
   chart, a table, a list of rows and a grid of cards. Each is built out of the
   same dimmed block, so a screen with two of them still reads as one idea. */
.mm-sk { position: relative; min-height: 120px; }
.mm-sk-block { background: var(--surface-3); border-radius: 4px; opacity: .5; }
/* the host is often a flex row (`.bars`) or a grid, so the outline says how
   much room it wants rather than letting the parent size it to its content —
   a 66px-wide chart skeleton under a full-width sentence reads as breakage */
.mm-sk-shapes { display: grid; gap: 10px; padding: 4px 0; width: 100%; flex: 1 1 100%; }
.mm-sk-shapes[data-shape="table-rows"] > div,
.mm-sk-shapes[data-shape="list-rows"] > div {
  display: grid; grid-template-columns: 1fr 1fr 1fr auto; gap: 12px; align-items: center;
}
.mm-sk-shapes[data-shape="kv-rows"] > div { display: grid; grid-template-columns: minmax(0,1fr) 90px; gap: 12px; }
.mm-sk-shapes[data-shape="card-grid"] {
  grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
}
.mm-sk-shapes[data-shape="card-grid"] > div { display: grid; gap: 8px; }
.mm-sk-shapes .mm-sk-block { height: 11px; }
.mm-sk-shapes[data-shape="card-grid"] .mm-sk-tall { height: 46px; }
/* the chart's own floor, not a box inside it: the bars stand on the bottom of
   the plot area, because a skeleton floating above a strip of empty card is
   the one arrangement that reads as a broken chart rather than an empty one */
.mm-sk-shapes[data-shape="chart-bars"] {
  grid-auto-flow: column; grid-auto-columns: 1fr; align-items: stretch;
  height: 100%; min-height: 140px; gap: 6px; padding: 0;
  position: absolute; inset: 0;
}
/* each column is the full height of the chart and the bar sits on its floor:
   a percentage height needs a parent with a height to be a percentage OF,
   which `align-items: end` on the track does not give it */
.mm-sk-shapes[data-shape="chart-bars"] > div {
  height: 100%; display: flex; align-items: flex-end; min-width: 0;
}
.mm-sk-shapes[data-shape="chart-bars"] .mm-sk-block { width: 100%; border-radius: 3px 3px 0 0; }
/* a phone shows the shape rather than a table: four columns of eleven pixels
   is four grey specks, and one bar per row reads as the row it stands for */
@media (max-width: 720px) {
  .mm-sk-shapes[data-shape="table-rows"] > div,
  .mm-sk-shapes[data-shape="list-rows"] > div { grid-template-columns: 1fr; gap: 6px; }
}

/* an empty table's outline fills the cell it stands in */
.db-empty-sk { position: relative; min-height: 132px; padding: 16px 18px; }
.db-empty-sk .st-sk-chip { pointer-events: auto; }

/* ---- ТОЛЬКО СЛОВА, БЕЗ ФОРМ ПОД НИМИ -------------------------------------
   Здесь стояло высветление, которым фраза отделялась от серых рядов скелетона
   под ней. Рядов больше нет (распоряжение владельца от 14 авг 2026: на широком
   экране они растягиваются во всю карточку и страница читается недогруженной),
   а значит нечего и высветлять: пустая карточка — это карточка с фразой
   посередине и кнопкой под ней, и ничего за ними. */
.db-empty-sk .st-sk-note { z-index: 1; }
.db-empty-sk .st-sk-chip {
  background: none; border: 0; box-shadow: none; max-width: 62ch;
}
.db-empty-sk .st-sk-chip b, .st-empty-words .h4 { font-size: 15px; }
.db-empty-sk .st-sk-chip > span, .st-empty-words .small { color: var(--ink-2); }

/* an empty panel in the studio: the outline behind, the words in front */
/* THE WORDS SIT IN THE MIDDLE OF THE SHAPE, and they used to sit at its left
   edge: `.st-empty` is a grid with `justify-items: start` and `max-width: 88ch`,
   so on a wide panel the sentence and its button ended up in the left third of
   a very wide card with an outline running past them. The skeleton version
   overrides both — the block is as wide as the panel and everything in it is
   centred, which is what a "there is nothing here yet" screen should be. */
.st-empty.st-empty-sk {
  position: relative; display: grid; justify-items: center; max-width: none;
  padding: 30px 22px; min-height: 148px; align-content: center;
}
.st-empty-shape { position: absolute; inset: 18px; }
.st-empty-words {
  position: relative; z-index: 1; display: grid; justify-items: center; gap: 8px;
  text-align: center; padding: 14px 16px; margin-inline: auto; max-width: 62ch;
}
.st-empty-words .row { justify-content: center; }
.st-empty-words .ic { color: var(--ink-3); }
/* правило про формы под фразой снято вместе с самими формами — см. выше */


/* ==========================================================================
   16px ON A PHONE, WHEREVER SOMETHING IS TYPED.

   iOS Safari zooms the page when a field it focuses is set below 16px, and it
   does not zoom back out — the reader is left on a page 1.3× too wide, panning
   sideways to find the button. It happened on the front page's search, on both
   sign-in fields, on sign-up, on the revenue calculator and on every select in
   the catalogue. Each of those sizes was a deliberate design choice at desk
   width and a trap at phone width, so the rule is scoped to the phone.

   `font-size` only: nothing else about the field moves, and 16px inside a 40px
   control is still a 40px control. `text-size-adjust` is not a substitute —
   it does not affect the focus-zoom behaviour. */
@media (max-width: 620px) {
  /* `:root` in front of each one, because auth.css, views.css and docs.css all
     load after this file and set their own sizes at class specificity: an
     accessibility floor that loses the cascade is not a floor. */
  :root input[type="text"], :root input[type="email"], :root input[type="password"],
  :root input[type="search"], :root input[type="number"], :root input[type="tel"],
  :root input[type="url"], :root textarea, :root select,
  :root .field, :root .field input, :root .pay-in, :root .db-in, :root .auth-in,
  :root .rw-in, :root .rw-body, :root .st-in, :root .lib-title-input, :root .lib-desc-input,
  :root .auth-main .field input, :root .card .field input, :root form input, :root form textarea,
  /* AND THE ONES WITH NO `type` AT ALL. Half the fields in this product are
     written `<input class="input">` — the wizard's first field, the two on a
     new skill, the name of a new list, the calculator's number boxes — and a
     floor written as a list of `input[type=…]` never reached them. An input
     with no type IS a text input; iOS zooms it the same way. */
  :root input:not([type]), :root .input, :root .fp-numin, :root .db-form input,
  :root .st-form input, :root .lib-new input {
    font-size: 16px;
  }

  /* И КОРОБКА ПОД ЭТОТ ШРИФТ. Правило выше поднимает текст любого поля до
     16px, иначе iOS зумит страницу на фокусе — а .db-in, общее поле кабинета,
     осталось 34px высотой: 16-й кегль впритык к рамке и цель ниже пальца.
     Рядом с ним .field уже 42 (см. выше), и это тот же 42 — не новый размер,
     а тот же шаг лестницы. Одно правило вместо заплаток по панелям: то же
     поле стоит в переменных кабинета, в визарде после покупки, в приглашении
     в команду и в смене пароля. */
  :root .db-in { height: 42px; }
}


/* ==========================================================================
   AN EMPTY BLOCK IS AN OUTLINE, NOT A PANEL.

   A filled card says "here is something"; when the something is a sentence
   explaining that there is nothing, the fill is the part that misleads. These
   keep the border — so the shape of what will be there is still drawn — and
   give up the background, which is what makes them read as a placeholder
   rather than as content.

   `:has()` rather than a class on the card, because the card is the mock's own
   markup and the emptiness is decided at runtime by the script inside it. */
.card:has(> .st-empty-sk),
.card:has(> .card-body > .st-empty-sk),
.card:has(.db-empty-sk) {
  background: transparent;
  box-shadow: none;
}
.card:has(.db-empty-sk) .card-head { background: transparent; }

/* ---- a strip that scrolls says so ----------------------------------------
   The tab strip on a listing page is 634px of tabs in a 343px phone: three of
   the seven are visible, the other four — Pricing, Security, Versions — are
   off the right edge with no scrollbar (hidden above), no shadow and no
   arrow. A reader who does not think to swipe a row of tabs concludes the page
   has three tabs.

   Two signals, both cheap: the strip fades at the edge it continues past, and
   a swipe lands on a tab rather than between two. */
@media (max-width: 720px) {
  .tabs {
    scroll-snap-type: x proximity;
    -webkit-overflow-scrolling: touch;
    mask-image: linear-gradient(to right, #000 calc(100% - 28px), transparent 100%);
  }
  .tabs button { scroll-snap-align: start; }
  /* scrolled to the end there is nothing more to promise */
  .tabs.at-end { mask-image: none; }
}


/* ==========================================================================
   THE FOOTER SITS ON THE FLOOR OF THE SCREEN.

   A short page — notifications with two items, an empty library, a 404, a
   receipt — ended with the footer halfway up and a band of empty paper under
   it, which reads as a page that failed to finish loading rather than as a
   page with little on it.

   `position: sticky; top: 100vh` is the whole fix and it changes nothing else:
   the footer stays in normal flow, so no margin collapses differently and no
   page's layout is rewritten into a flex column. On a short page the sticky
   box is pushed to the bottom of the viewport; on a long one it behaves like
   any other block and scrolls away with the content.

   `100dvh` on top, because a phone's toolbar makes 100vh taller than the
   screen and the footer would hide under it. */
/* the sticky footer can only travel inside its parent, so the parent has to
   reach the bottom of the screen — otherwise a 950px page leaves the footer at
   950px with the viewport 1400 tall, which is the exact gap this fixes. No
   display change: `min-height` alone, so no margin collapses differently and
   no page's layout is rewritten. */
body { min-height: 100vh; min-height: 100dvh; }
body > .foot {
  position: sticky;
  top: 100vh;
  top: 100dvh;
}


/* ==========================================================================
   WHILE STRIPE'S OWN FORM IS ON ITS WAY.

   Three places hand a box over to Stripe: the card form on /payment-method,
   the payment element in the checkout, and the onboarding component on
   /stripe-connect. Each of those waits on a script from js.stripe.com, a
   round trip to our server for a client secret, and then Stripe's own render —
   and until all three finish, the box was empty. On a slow connection that is
   several seconds of nothing where somebody has come to type a card number.

   THIS ONE ANIMATES, and it is the only thing on the site that does. The rule
   everywhere else is that a static shape means "there is nothing here yet";
   here there IS something coming, from a third party, and the pulse is the
   difference between "wait" and "broken". It stops the moment Stripe mounts,
   because the slot's content is replaced. */
.mm-stripe-wait { display: grid; gap: 12px; padding: 4px 0; }
.mm-stripe-wait .row-sk { display: grid; gap: 8px; }
.mm-stripe-wait .bar {
  height: 42px; border-radius: var(--r1);
  background: var(--surface-3); opacity: .55;
  animation: mm-breathe 1.6s ease-in-out infinite;
}
.mm-stripe-wait .bar.short { width: 46%; }
.mm-stripe-wait .bar.thin { height: 11px; width: 30%; }
.mm-stripe-wait .say {
  display: flex; align-items: center; gap: 8px;
  font-size: 12.5px; color: var(--ink-3);
}
.mm-stripe-wait .say .dot {
  width: 6px; height: 6px; border-radius: 50%; background: var(--accent);
  animation: mm-breathe 1.6s ease-in-out infinite;
}
@keyframes mm-breathe { 0%, 100% { opacity: .35 } 50% { opacity: .8 } }
/* somebody who asked for less motion gets the shape and the sentence, still */
@media (prefers-reduced-motion: reduce) {
  .mm-stripe-wait .bar, .mm-stripe-wait .say .dot { animation: none; }
}
