/* GENERATED by build.py from css/base.css (shared) + css/site.css (this site).
   Do not edit this file; your change will be overwritten.
   Edit those two files, then run: python3 build.py */

/* css/base.css: SHARED, byte-identical across Jerzy's sites (rustedvino-www is
   canonical; tools/check-shared-sync.py compares it). Components only, class-scoped,
   reading only the token contract: --ground --raised --edge --edge-control --accent
   --ink --ink-dim --sans --mono --measure --gutter --focus. build.py puts it FIRST in
   the shipped style.css; each site's css/site.css follows and defines the tokens.
   Plan: dev/plans/css-standardisation-review.md (DECISIONS 2026-09-30). */

/* ── reset ──────────────────────────────────────────────────────────── */
*, *::before, *::after { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; }
img { max-width: 100%; }
/* A visitor who asks the OS for less motion gets none, whatever a site adds. */
@media (prefers-reduced-motion: reduce) {
  * { transition-duration: .01ms !important; animation-duration: .01ms !important; }
}

/* ── skip link: hidden until focused ─────────────────────────────────── */
/* The mechanism is shared; the look (colours, padding, corners) is each site's.
   Clipped rather than parked off-screen: it stays where it will appear, and a
   far-left position can widen the page in right-to-left layouts. */
.skip {
  position: absolute; width: 1px; height: 1px;
  overflow: hidden; clip-path: inset(50%); z-index: 20;
}
.skip:focus {
  width: auto; height: auto; clip-path: none;
  left: var(--gutter); top: var(--gutter);
}

/* ── page container ─────────────────────────────────────────────────── */
/* Centred with the site's side gutter. Its max-width is each site's (a reading
   measure on one, a wider shell on the other), set in css/site.css. */
.wrap { width: 100%; margin-inline: auto; padding-inline: var(--gutter); }

/* ── theme button: which icon shows ─────────────────────────────────── */
/* The mechanism is shared (the head script sets data-theme on <html>); size,
   border, colours and hover are each site's. */
#theme { display: grid; place-items: center; cursor: pointer; }
#theme svg { display: block; }
#theme .moon { display: none; }
:root[data-theme="light"] #theme .sun { display: none; }
:root[data-theme="light"] #theme .moon { display: block; }

/* THE list component for every index page (/guides/, /releases/, and any
   future one): li.post-item > a.post-link > .post-title (+ optional .post-sub),
   then .post-meta and .post-summary. Link hover/focus styling lives HERE only:
   reuse these classes, never give a new list its own link styles. */
.post-list { list-style: none; margin: 0; padding: 0; }
.post-item { padding-block: 1.25rem; border-top: 1px solid var(--edge); }
.post-item:first-child { border-top: 0; padding-top: .25rem; }
.post-link { text-decoration: none; }
.post-title { margin: 0 0 .3rem; font-size: 1.1875rem; line-height: 1.3; color: var(--ink); }
.post-sub { margin: 0 0 .3rem; font-size: 1rem; color: var(--ink); }
.post-link:hover :is(.post-title, .post-sub),
.post-link:focus-visible :is(.post-title, .post-sub) { color: var(--accent); }
.post-meta { margin: 0 0 .4rem; font-size: .875rem; color: var(--ink-dim);
             font-variant-numeric: tabular-nums; }
.post-summary { margin: 0; color: var(--ink-dim); }

.post-header { margin-bottom: 2rem; }
.post-header h1 { font-size: 2rem; }

/* Imported article body */
.post h2 { font-size: 1.25rem; line-height: 1.35; text-transform: none; letter-spacing: normal;
           font-family: var(--sans); color: var(--ink); margin: 2.5rem 0 .75rem; }
.post h3 { font-size: 1.0625rem; margin: 2rem 0 .5rem; }
.post p { margin: 0 0 1.25rem; }
/* A long URL or token in running text breaks rather than widening the page:
   at 390px one pasted link made the whole page scroll sideways (2026-09-30). */
.post :is(p, li, blockquote, figcaption, td) { overflow-wrap: break-word; }
/* Body lists only: the header's tag list is a .post ul too, and at this
   specificity these rules used to override the card block's padding:0. */
.post ul:not(.post-tags), .post ol { margin: 0 0 1.25rem; padding-inline-start: 1.5rem; }
.post :is(ul:not(.post-tags), ol) > li { margin-bottom: .4rem; }
.post blockquote {
  margin: 1.5rem 0; padding: .25rem 0 .25rem 1.25rem;
  border-inline-start: 3px solid var(--accent); color: var(--ink-dim);
}
.post hr { border: 0; border-top: 1px solid var(--edge); margin: 2.5rem 0; }

.post code {
  font: 0.9em/1.5 var(--mono);
  background: color-mix(in srgb, var(--ink) 10%, transparent);
  padding: .12em .35em; border-radius: .2rem;
  overflow-wrap: break-word;
}
/* Code blocks (tools/md-to-post.py). The imported posts never had one. A long
   line scrolls inside the block, never the page: 390px must not scroll sideways. */
.post pre {
  margin: 1.5rem 0; padding: .9rem 1rem; overflow-x: auto;
  background: color-mix(in srgb, var(--ink) 7%, transparent); border-radius: .3rem;
}
.post pre code { background: none; padding: 0; border-radius: 0; overflow-wrap: normal; white-space: pre; }

/* Figures break out of the measure a little — the images are the evidence in
   these posts, and 40 of them at column width reads as a contact sheet. */
.post figure { margin: 2rem 0; }
.post figure img {
  display: block; width: 100%; height: auto;
  border: 1px solid var(--edge); border-radius: .3rem; background: var(--raised);
}
.post figcaption {
  margin-top: .5rem; font-size: .875rem; line-height: 1.5; color: var(--ink-dim);
}

/* Tables came from markdown inside <pre>. Real tables reflow; the wrapper
   catches the wide ones rather than letting the page scroll sideways. */
.table-wrap { overflow-x: auto; margin: 1.5rem 0; }
.post table { border-collapse: collapse; width: 100%; font-size: .9375rem; }
.post th, .post td {
  text-align: left; padding: .5rem .7rem; border-bottom: 1px solid var(--edge);
  vertical-align: top;
}
.post th { font-weight: 600; color: var(--ink); white-space: nowrap; }
.post td { color: var(--ink-dim); }
.post td code { font-size: .85em; }

.post-origin {
  margin-top: 2.5rem; padding-top: 1.25rem; border-top: 1px solid var(--edge);
  font-size: .9375rem; color: var(--ink-dim);
}
.post-origin:empty { display: none; }

.post-nav { display: flex; gap: 1rem; flex-wrap: wrap; margin-top: 2rem; }
.post-nav a {
  flex: 1 1 14rem; padding: .875rem 1rem; border: 1px solid var(--edge);
  border-radius: .3rem; text-decoration: none; color: var(--ink); font-size: .9375rem;
  line-height: 1.35;
}
.post-nav a:hover { border-color: var(--accent); }
.post-nav span { display: block; font-size: .8125rem; color: var(--ink-dim); margin-bottom: .2rem; }
.post-nav .next { text-align: right; }

@media (max-width: 40rem) {
  .post-header h1 { font-size: 1.625rem; }
  .post-title { font-size: 1.125rem; }
  .post-meta, .post-summary, .post figcaption, .post-origin { font-size: 1rem; }
  .post table { font-size: 1rem; }
}
/* ── end: post/list block (shared with hyperfocus-bear; keep verbatim above) ── */

/* ── blog index: cards with a 16:9 lead picture ────────────────────────── */
/* A reusable component (DECISIONS 2026-09-28): markup from build.py's
   post_list_html, pictures from tools/make-leads.py. It uses only the theme
   tokens --ink, --ink-dim, --ground, --edge, --accent and --mono, so another
   site restyles it by its tokens, not by editing this block.
   Two columns do not fit the 38rem reading column; the index breaks out to the
   same 54rem as the front-page tiles, and for the same reason. */
@media (min-width: 40.0625rem) {
  .blog-index { max-width: 54rem; }
}
.post-cards {
  display: grid; grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 2.25rem 1.5rem;
}
.post-cards .post-item,
.post-cards .post-item:first-child { border-top: 0; padding: 0; }
.lead {
  display: block; aspect-ratio: 16 / 9; overflow: hidden;
  margin-bottom: .875rem;
  border: 1px solid var(--edge); border-radius: .3rem;
  background: color-mix(in srgb, var(--ink) 6%, var(--ground));
  transition: border-color .12s ease;
}
.lead img { display: block; width: 100%; height: 100%; object-fit: cover; }
.post-link:hover .lead { border-color: var(--accent); }
/* No picture: the wordmark on a quiet panel, so the grid never has a hole. */
.lead-none { display: grid; place-items: center; }
.lead-none span { font: 600 1.125rem/1 var(--mono); letter-spacing: .02em; color: var(--ink-dim); }
@media (max-width: 40rem) {
  .post-cards { grid-template-columns: 1fr; gap: 2rem; }
}
/* Tags: a row of links above the cards (the page you are on is marked, not
   unlinked) and a row of pills on each card and post. Static pages at
   /blog/tag/<tag>/, so filtering needs no script. */
.tag-nav { display: flex; flex-wrap: wrap; gap: .5rem; margin: 0 0 2rem; }
.tag-nav a {
  display: inline-flex; align-items: center; min-height: 2.25rem; padding: 0 .8rem;
  border: 1px solid var(--edge); border-radius: 999px;
  font: 600 .875rem/1 var(--mono); color: var(--ink-dim); text-decoration: none;
  transition: color .12s ease, border-color .12s ease;
}
.tag-nav a:hover { color: var(--ink); border-color: var(--ink-dim); }
.tag-nav a[aria-current="page"] { color: var(--ink); border-color: var(--accent); }
.tag-nav a:focus-visible, .post-tags a:focus-visible { outline: 2px solid var(--focus); outline-offset: 2px; }
.post-tags {
  display: flex; flex-wrap: wrap; gap: .4rem;
  list-style: none; margin: .75rem 0 0; padding: 0;
}
.post-tags a {
  display: inline-flex; align-items: center; min-height: 1.75rem; padding: 0 .65rem;   /* ≥24px tap target */
  border: 1px solid color-mix(in srgb, var(--ink) 28%, var(--ground)); border-radius: 999px;
  background: color-mix(in srgb, var(--ink) 7%, var(--ground));
  font: 600 .8125rem/1 var(--mono); color: var(--ink-dim); text-decoration: none;
  transition: color .12s ease, border-color .12s ease;
}
.post-tags a:hover { color: var(--ink); border-color: var(--accent); }
.post-header .post-tags { margin-top: .5rem; }
/* ── end: blog index cards (shared with hyperfocus-bear; keep verbatim above) ── */

/* hyperfocusbear.com — holding page.
   Deliberately plain: the visual identity is not decided yet, so this commits
   to nothing beyond legible type. No image assets are referenced, because none
   exist (tools/make-og.py and make-mark.py are deferred).

   Two traps from the rustedvino build are respected here by construction:
   widths are in rem, never ch (ch resolves against each element's own font
   size, so one "reading column" silently becomes several), and nothing uses
   background-clip:text, whose failure mode under a theme toggle is invisible
   text. */

:root {

  /* Boundary for things you can press or type into. --edge is for decorative
     separators and measures 1.4:1 on the dark top bar and 1.14:1 on the light
     one — invisible, and far under the 3.0 WCAG asks for a control boundary.
     One expression covers both themes because it mixes the theme's own ink
     with its own ground: 5.4:1 dark, 3.26:1 light. */
  --edge-control: color-mix(in srgb, var(--ink) 55%, var(--ground));

  --ground: #101113;
  --raised: #17191c;
  --edge:   #2a2e33;
  --ink:    #e8eaec;
  --ink-dim:#a4abb2;
  --accent: #d8a24a;
  --focus: var(--accent);      /* focus rings (shared token, CSS standardisation step 3); follows the theme */

  --measure: 38rem;          /* the reading column, in rem on purpose */
  --gutter:  1.5rem;
  --sans: ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto,
          "Helvetica Neue", Arial, sans-serif;
  --mono: ui-monospace, SFMono-Regular, Menlo, Consolas, "Liberation Mono", monospace;
}

:root[data-theme="light"] {
  --ground: #fbfaf8;
  --raised: #ffffff;
  --edge:   #dcd8d1;
  --ink:    #16181a;
  --ink-dim:#54595f;          /* 7.0:1 on --ground, and 6.6:1 on the topbar mix below */
  --accent: #8a5a12;          /* 5.8:1 on --ground */
}



body {
  margin: 0;
  background: var(--ground);
  color: var(--ink);
  font: 400 1.0625rem/1.65 var(--sans);
  display: flex;
  flex-direction: column;
  min-height: 100vh;
}

/* The skip link's look only; how it hides and appears is in css/base.css
   (CSS standardisation step 5b, 2026-09-30). */
.skip { background: var(--accent); color: var(--ground); padding: .5rem .875rem; border-radius: .25rem; }

/* Centring and gutter are shared in css/base.css (CSS step 5d); the width is this site's. */
.wrap { max-width: var(--measure); }
/* The blog index IS a .wrap here (<div class="wrap blog-index">); rustedvino-www
   nests .blog-index inside one. base.css widens .blog-index to 54rem on desktop,
   but base comes FIRST in style.css (CSS step 4), so .wrap above would win the
   tie and shrink the index to --measure. Higher specificity, so order can't matter.
   Found by a fresh-profile pixel check (2026-09-30). */
@media (min-width: 40.0625rem) { .wrap.blog-index { max-width: 54rem; } }

/* ── top bar ───────────────────────────────────────────────────────────── */
/* The bar is darker than --ground, so every colour used on it is checked
   against the mix, not against --ground. That pairing is what put rustedvino's
   light-theme nav links at 4.05:1. */
.topbar {
  background: color-mix(in srgb, var(--ground) 88%, black);
  border-bottom: 1px solid var(--edge);
  padding-block: .75rem;
}
:root[data-theme="light"] .topbar {
  background: color-mix(in srgb, var(--ground) 92%, black);
}
.topbar-in { display: flex; align-items: center; justify-content: space-between; gap: 1rem; }

.brand {
  font: 600 1rem/1 var(--mono);
  letter-spacing: .02em;
  color: var(--ink);
  text-decoration: none;
}
.brand:hover { color: var(--accent); }

/* Sun and moon inherit currentColor, so they follow the theme without a
   second asset or a filter. The icon names the ACTION, like the label: the sun
   (switch to light) shows on the dark theme, the moon on the light one. Both
   are in the markup and CSS picks one, so the right icon is there before JS. */
/* How the icon switches is shared in css/base.css (CSS step 5c); the look is here. */
#theme {
  width: 2.75rem; height: 2.75rem;         /* same 44px tap target as the nav links */
  color: var(--ink-dim);
  background: transparent;
  border: 1px solid var(--edge-control);
  border-radius: .3rem;
  transition: color .12s ease, border-color .12s ease;
}
#theme svg { width: 1.25rem; height: 1.25rem; }
#theme:hover { color: var(--ink); border-color: var(--ink); }
#theme:focus-visible { outline: 2px solid var(--focus); outline-offset: 2px; }

/* ── 404 ──────────────────────────────────────────────────────────────── */
/* The same frame as a post figure (base.css .post figure), on the 404 page. */
.nf-figure { margin: 2rem 0; max-width: 38rem; }
.nf-figure img {
  display: block; width: 100%; height: auto;
  border: 1px solid var(--edge); border-radius: .3rem; background: var(--raised);
}
.nf-figure figcaption { margin-top: .5rem; font-size: .875rem; line-height: 1.5; color: var(--ink-dim); }

/* ── main ──────────────────────────────────────────────────────────────── */
main { flex: 1; padding-block: 3.5rem 4rem; }

h1 {
  font-size: 1.875rem;
  line-height: 1.2;
  letter-spacing: -.01em;
  margin: 0 0 1rem;
}

.lede { font-size: 1.125rem; color: var(--ink-dim); margin: 0 0 2.5rem; }

section { margin-block: 2.5rem; }

h2 {
  font: 600 .8125rem/1.3 var(--mono);
  letter-spacing: .09em;
  text-transform: uppercase;
  color: var(--accent);
  margin: 0 0 .75rem;
}

p { margin: 0 0 1rem; }
p:last-child { margin-bottom: 0; }

/* Links carry an accent underline, not just a colour. Measured on the live
   page: with the underline set to --edge, an inline link computed to exactly
   the same colour as the paragraph around it (rgb(232,234,236) both) and the
   underline sat at 1.3:1 against the ground — so the link was invisible as a
   link. Colour alone would also fail WCAG 1.4.1; the underline is the
   non-colour cue. */
a {
  color: var(--ink);
  text-decoration-color: var(--accent);
  text-decoration-thickness: 1px;
  text-underline-offset: .18em;
}
a:hover { color: var(--accent); text-decoration-thickness: 2px; }
a:focus-visible { outline: 2px solid var(--focus); outline-offset: 2px; border-radius: 2px; }

.card {
  background: var(--raised);
  border: 1px solid var(--edge);
  border-radius: .4rem;
  padding: 1.125rem 1.25rem;
}

.note { font-size: .9375rem; color: var(--ink-dim); }

/* ── footer ────────────────────────────────────────────────────────────── */
footer {
  border-top: 1px solid var(--edge);
  padding-block: 1.5rem 2.5rem;
  font-size: .9375rem;
  color: var(--ink-dim);
}
footer a { color: var(--ink-dim); }

/* ── phones ────────────────────────────────────────────────────────────── */
/* Small text scales UP here, never down: no running text below 16px and no
   uppercase label below 14px. */
@media (max-width: 40rem) {
  :root { --gutter: 1.125rem; }
  body { font-size: 1rem; }
  main { padding-block: 1.5rem 2.5rem; }
  h1 { font-size: 1.625rem; }
  .lede { font-size: 1.0625rem; }
  h2 { font-size: .875rem; }
  .note, footer { font-size: 1rem; }

}

@media (prefers-reduced-motion: no-preference) {
  a, #theme { transition: color .12s ease, border-color .12s ease; }
}

/* ── blog ──────────────────────────────────────────────────────────────── */
/* Post bodies are wider than the front page's reading column: they carry
   figures, code and tables that a 38rem measure strangles. */
.post { max-width: 44rem; }

.primary { margin-inline-start: auto; margin-inline-end: 1rem; font-size: .9375rem; }
.primary a { color: var(--ink-dim); text-decoration: none; }
.primary a:hover, .primary a[aria-current="page"] { color: var(--ink); }

/* This site's own, outside the shared list block (CSS standardisation step 1,
   2026-09-30): brand, not component. Same rules, same cascade order as before. */
.more { font-size: .9375rem; }
@media (max-width: 40rem) {
  .primary { font-size: 1rem; margin-inline-end: .75rem; }
}

/* ── Ruvi ──────────────────────────────────────────────────────────────── */
.visually-hidden {
  position: absolute; width: 1px; height: 1px; overflow: hidden;
  clip-path: inset(50%); white-space: nowrap;
}
.chat { margin: 1.5rem 0 2rem; }
/* Closes the heading, above the conversation rather than inside it. */
.chat-rule { border: 0; border-top: 1px solid var(--edge); margin: 0; }
/* Nothing reserved before the first answer arrives. */
.chat-log:empty { margin: 0; }

/* Brilliant being unavailable used to be a line of small print under the tool
   buttons, which a visitor reliably found only after composing a question.
   It sits in the conversation now, at reading size, with the accent edge the
   page uses nowhere else. */
.chat-notice {
  margin: 0 0 1.25rem;
  padding: 1.125rem 1.25rem;
  font-size: 1.0625rem;
  line-height: 1.6;
  color: var(--ink);
  background: color-mix(in srgb, var(--accent) 12%, var(--raised));
  border: 1px solid color-mix(in srgb, var(--accent) 55%, var(--edge));
  border-inline-start-width: 4px;
  border-radius: .4rem;
}
.chat-notice[hidden] { display: none; }

/* And the box you cannot type into should look like it. */
.chat[data-state="stopped"] .chat-form { opacity: .45; }

@media (max-width: 40rem) { .chat-notice { font-size: 1rem; } }
.chat-log { list-style: none; margin: 0 0 1rem; padding: 0; display: flex;
            flex-direction: column; gap: .875rem; }
.msg { padding: .875rem 1rem; border-radius: .5rem; max-width: 90%; }
.msg p { margin: 0 0 .6rem; }
.msg p:last-child { margin: 0; }
.msg.brilliant { background: var(--raised); border: 1px solid var(--edge); align-self: flex-start; }
/* The greeting is a block outside the flex log, so it needs its own spacing:
   no UA paragraph margin fighting .chat's, and the same gap to the first real
   turn that two turns have between them. */
.chat-greeting { margin: 0 0 .875rem; }
.msg.you {
  background: color-mix(in srgb, var(--accent) 16%, var(--ground));
  border: 1px solid color-mix(in srgb, var(--accent) 35%, var(--edge));
  align-self: flex-end;
}
/* Send sits under the box, right-aligned, rather than beside it. Next to a
   three-row textarea it had to align to one edge or float in the middle, and
   "under, on the right" is where the eye finishes reading the message anyway. */
.chat-form { display: flex; flex-direction: column; align-items: flex-end; gap: .5rem; }
.chat-form textarea {
  width: 100%; min-width: 0; font: inherit; line-height: 1.5; color: var(--ink);
  background: var(--raised); border: 1px solid var(--edge-control);
  border-radius: .35rem; padding: .6rem .75rem;
  /* Three lines by default, and a native grabber so it can be made taller. */
  resize: vertical; min-height: 5.4rem; max-height: 60vh;
}
.chat-form textarea:focus-visible { outline: 2px solid var(--focus); outline-offset: 1px; }
.chat-form textarea:disabled { opacity: .55; }
.chat-form button {
  font: 500 1rem/1 var(--sans); color: var(--ground); background: var(--accent);
  border: 1px solid var(--accent); border-radius: .35rem; padding: .6rem 1.1rem;
  cursor: pointer; white-space: nowrap;
}
.chat-form button:hover { filter: brightness(1.08); }
.chat-form button:disabled { opacity: .5; cursor: default; }
.chat-status { margin: .75rem 0 0; font-size: .875rem; color: var(--ink-dim); }
.brilliant-about { margin-top: 2rem; }

@media (max-width: 40rem) {
  .msg { max-width: 100%; }
  .chat-status { font-size: 1rem; }
}

/* Reset / download. Secondary to the input, so quieter than the send button. */
.chat-tools { display: flex; gap: .5rem; flex-wrap: wrap; margin-top: .75rem; }
.tool {
  font: 500 .875rem/1 var(--sans); color: var(--ink-dim);
  background: transparent; border: 1px solid var(--edge-control);
  border-radius: .3rem; padding: .45rem .7rem; cursor: pointer;
}
.tool:hover:not(:disabled) { color: var(--ink); border-color: var(--ink); }
.tool:focus-visible { outline: 2px solid var(--focus); outline-offset: 2px; }
.tool:disabled { opacity: .4; cursor: default; }

@media (max-width: 40rem) { .tool { font-size: .9375rem; } }

/* The introduction (decisions-005). Until this browser agrees, brilliant.js
   hides the form, the tools and the status line, and this takes their place. */
.chat-intro[hidden], .chat-form[hidden], .chat-tools[hidden], .chat-status[hidden] { display: none; }
.chat-intro .msg { margin: 0 0 .875rem; }
.intro-tools { align-items: center; margin: 0 0 .5rem; }
#intro-yes {
  font: 500 1rem/1 var(--sans); color: var(--ground); background: var(--accent);
  border: 1px solid var(--accent); border-radius: .35rem; padding: .6rem 1.1rem; cursor: pointer;
}
#intro-yes:hover { filter: brightness(1.08); }
#intro-yes:focus-visible { outline: 2px solid var(--focus); outline-offset: 2px; }
a.tool { text-decoration: none; display: inline-block; }

/* ── icon navigation and the front-page tiles ───────────────────────── */
/* The header used to carry two word links that sat almost on top of each other
   on a phone, one of which ("Ruvi") told a first-time visitor nothing. Icons
   are compact, carry meaning without words, and give a proper tap target. */
.primary { display: flex; gap: .5rem; margin-inline-start: auto; margin-inline-end: .25rem; }
/* Words, not icons. Emoji were tried here and on the front-page tiles: they
   fixed the crowding but still asked the visitor to guess, and "Chat"/"Blog"
   do not. Both survive as loanwords in Polish and Vietnamese, so the nav stays
   legible without translation. Uppercase is CSS, so the DOM text is still
   "Chat" and a screen reader does not spell it out. */
.navlink {
  display: grid; place-items: center;
  min-width: 2.75rem; height: 2.75rem;     /* 44px: the minimum comfortable tap target */
  padding-inline: .5rem; border-radius: .4rem;
  font: 600 .8125rem/1 var(--mono); letter-spacing: .09em; text-transform: uppercase;
  color: var(--ink-dim); text-decoration: none;
  transition: color .12s ease, background-color .12s ease;
}
.navlink:hover { color: var(--ink); }
.navlink[aria-current="page"] {
  color: var(--ink); background: color-mix(in srgb, var(--ink) 8%, transparent);
}
.navlink:focus-visible { outline: 2px solid var(--focus); outline-offset: 2px; }
/* Three links (Chat, Blog, Author) plus the brand and the toggle overflowed a
   390px phone by ~27px. Spacing gives way here; the 44px tap targets do not. */
@media (max-width: 40rem) {
  .topbar-in { gap: .5rem; }
  .primary { gap: .125rem; }
  .navlink { padding-inline: .375rem; letter-spacing: .06em; }
}
/* Language switcher, after rustedvino-www's .langs (its markup, this site's look):
   a control beside the nav, not three more nav links. The bar had no room left
   on phones — even one 44px chip pushed the theme button off a 390px screen — so
   below 40rem it used to move to the footer (.langs-foot). Since 2026-10-24 the nav
   links go behind ☰ on phones instead and the switch stays on the bar (below). */
.langs { display: flex; gap: .2rem; margin-inline-end: .75rem; }
.langs a, .langs span {
  display: grid; place-items: center;
  min-width: 2.75rem; height: 2.75rem; padding-inline: .4rem;
  border: 1px solid transparent; border-radius: .4rem;
  font: 600 .8125rem/1 var(--mono); letter-spacing: .06em;
  color: var(--ink-dim); text-decoration: none;
}
.langs a:hover { color: var(--ink); }
.langs a:focus-visible { outline: 2px solid var(--focus); outline-offset: 2px; }
.langs [aria-current] { color: var(--ink); border-color: var(--edge-control); }
.langs-foot { display: none; }
@media (max-width: 40rem) {
  .topbar .langs { display: none; }
  .langs-foot { display: flex; margin: 0 0 .75rem -.4rem; }
}
/* Phones (2026-10-24): the language switch is the main control on the bar, because
   Polish and Vietnamese readers often land on the English page first and the
   footer copy is never seen. Chat / Blog / Author move behind a ☰ button. No JS:
   a visually hidden checkbox opens the menu, so it works with scripts blocked
   too. Desktop is unchanged; the checkbox and its button do not render there.
   Hierarchy (same day): ☰ is the one full-size control, far right; EN|PL is a
   smaller joined pill; the theme toggle lives inside the open menu, top right. */
.menu-toggle, .menu-btn { display: none; }
@media (max-width: 40rem) {
  .topbar { position: relative; }
  .topbar .langs {
    display: flex; order: 1; margin-inline: auto 0; gap: 0;
    border: 1px solid var(--edge-control); border-radius: 999px; padding: 2px;
  }
  .topbar .langs a, .topbar .langs span {
    min-width: 2.25rem; height: 2rem; padding-inline: .5rem;   /* 32px: over the 24px floor */
    border: 0; border-radius: 999px; font-size: .75rem;
  }
  .topbar .langs a { color: var(--ink); }
  .topbar .langs [aria-current] { background: var(--ink); color: var(--ground); }
  .menu-toggle {
    display: block; position: absolute; width: 1px; height: 1px;
    opacity: 0; pointer-events: none;
  }
  .menu-btn {
    order: 3; display: grid; place-items: center; cursor: pointer;
    width: 2.75rem; height: 2.75rem; flex-shrink: 0;
    color: var(--ink-dim); border: 1px solid var(--edge-control); border-radius: .3rem;
  }
  .menu-btn svg { width: 1.25rem; height: 1.25rem; }
  .menu-toggle:checked + .menu-btn { color: var(--ink); border-color: var(--ink); }
  .menu-toggle:focus-visible + .menu-btn { outline: 2px solid var(--focus); outline-offset: 2px; }
  /* Shown only while the menu is open, at the top right of the drop-down. */
  #theme { display: none; }
  .menu-toggle:checked ~ #theme {
    display: grid; place-items: center; position: absolute; z-index: 11;
    top: calc(100% + .25rem); right: var(--gutter);
  }
  .primary {
    display: none; position: absolute; top: 100%; left: 0; right: 0; z-index: 10;
    flex-direction: column; gap: 0; margin: 0;
    padding: .25rem var(--gutter) .75rem;
    background: color-mix(in srgb, var(--ground) 88%, black);
    border-bottom: 1px solid var(--edge);
  }
  :root[data-theme="light"] .primary { background: color-mix(in srgb, var(--ground) 92%, black); }
  .menu-toggle:checked ~ .primary { display: flex; }
  .primary .navlink { align-self: flex-start; place-items: center start; padding-inline: .5rem; font-size: .875rem; }
  /* The footer copy stays as a second way in, but the bar's switch is the main one. */
}
/* 360px Android phones are common; there the wordmark shrinks too. */
@media (max-width: 23.5rem) {
  .topbar-in { gap: .375rem; }
  .brand { font-size: .8125rem; letter-spacing: 0; }
  .navlink { padding-inline: .25rem; }
}
@media (max-width: 21rem) {   /* 320px */
  :root { --gutter: .75rem; }
  .primary { margin-inline-end: 0; }
}

.home { padding-block: 1rem; }
/* The generic `section { margin-block: 2.5rem }` is for stacked prose sections
   and has no business above the first heading on the page. Left in, it put
   56 + 16 + 40 = 112px between the top bar and the h1 on a phone, which read
   as a missing element rather than as breathing room. */
.home > section:first-child { margin-block: 0; }
.tiles {
  display: grid; gap: 1rem; grid-template-columns: repeat(2, 1fr); margin-top: 3rem;
  /* Wider than --measure on purpose. Four tiles as a 2×2 square: Ask Brilliant,
     Learn / Try RustedVINO, Contact Bear (Learn added 2026-10-24). The two that
     stay on this site and need no technical background come first, which is also
     the order a phone stacks them in; Learn sits above Contact, so on /pl/ the
     demos lead to the training page. The blog tile went 2026-09-28, since the
     header and the Bear Way both link it. They share the 54rem
     row with the Bear Way below, so the page keeps one width under the lede.
     The heading and lede stay in the reading column. */
  width: min(54rem, 100%); margin-inline: auto;
}
@media (min-width: 40.0625rem) {
  .home { max-width: 54rem; }
}
.ext { font-size: .75em; margin-inline-start: .25em; color: var(--ink-dim); vertical-align: .08em; }
/* Text buttons rather than icons. The label carries the meaning on its own, so
   there is nothing left for a picture to disambiguate. */
.tile {
  /* Top-aligned, not centred: the notes are different lengths, so
     centring put each label at a different height and the row stopped
     scanning as a row. */
  display: flex; flex-direction: column; justify-content: flex-start;
  text-align: center; text-decoration: none; color: var(--ink);
  background: var(--raised); border: 1px solid var(--edge); border-radius: .6rem;
  padding: 1.5rem 1.25rem;
}
.tile:hover { border-color: var(--accent); }
.tile:focus-visible { outline: 2px solid var(--focus); outline-offset: 2px; }
.tile-label { display: block; font-size: 1.1875rem; font-weight: 600; margin-bottom: .45rem; }
.tile-note { display: block; font-size: .9375rem; line-height: 1.5; color: var(--ink-dim); }

/* ── /learn: the three demos as large buttons, one column ───────────────── */
/* One column on purpose: each button carries a sentence of what you will
   understand, which reads badly in a narrow grid cell, and a phone stacks
   them anyway. Same card look as the front-page tiles. */
.learn { display: grid; gap: 1rem; margin-top: 2rem; }
.learn-item {
  display: block; text-decoration: none; color: var(--ink);
  background: var(--raised); border: 1px solid var(--edge); border-radius: .6rem;
  padding: 1.25rem 1.375rem;
}
.learn-item:hover { border-color: var(--accent); }
.learn-item:focus-visible { outline: 2px solid var(--focus); outline-offset: 2px; }
.learn-part {
  display: block; margin-bottom: .4rem; font: 600 .875rem/1.4 var(--mono);
  letter-spacing: .06em; text-transform: uppercase; color: var(--ink-dim);
}
.learn-name { display: block; font-size: 1.1875rem; font-weight: 600; margin-bottom: .35rem; }
.learn-note { display: block; font-size: .9375rem; line-height: 1.5; color: var(--ink-dim); }
.learn + .note { margin-top: 1.5rem; }

/* ── Ruvi page: the chat is the main element ────────────────────────────── */
/* The heading and its "Powered by RustedVINO" line are one unit (<hgroup>),
   so the h1 itself stays just "Ask Brilliant" for a screen reader. 14px is
   the floor for uppercase labels on this site. */
.brilliant-head { margin-bottom: 1rem; }
.brilliant-h1 { margin-bottom: .3rem; }
.brilliant-powered {
  margin: 0; font: 600 .875rem/1.4 var(--mono);
  letter-spacing: .06em; text-transform: uppercase; color: var(--ink-dim);
}
.brilliant-powered a { color: inherit; text-underline-offset: .2em; }
.brilliant-powered a:hover { color: var(--accent); }
/* No min-height. It was meant to stop the page looking empty before the first
   answer and did the opposite: one greeting fills about 7rem of a reserved 16,
   so it opened a void between the message and the box you type in. The log
   grows with the conversation instead. */

@media (max-width: 40rem) {
  /* Stacked full-width cards with centred contents sat under a left-aligned
     heading, so the page looked like two different layouts glued together.
     As rows — icon left, text beside it, everything aligned to the same left
     edge as the h1 — it reads as one column. It is also far shorter: the tile
     stack was 650px tall, which is a lot of scrolling for three links. */
  .tiles { grid-template-columns: 1fr; margin-top: 1.75rem; width: 100%; gap: .75rem; }
  .tile { text-align: start; padding: 1rem 1.125rem; }
  .tile-label { margin-bottom: .2rem; }
  .tile-note { font-size: .9375rem; }
  .chat-form { align-items: stretch; }   /* full-width Send is a better tap target */
  /* No min-height here either, for the reason given above. It was the last
     place the reserved-space idea survived, and once the greeting became a
     bubble it was the most visible: 12rem of nothing between what Brilliant
     just said and the box you answer in, which reads as a broken thread
     rather than a page still loading. */
}

/* ── the RustedVINO wordmark ────────────────────────────────────────────── */
/* Reproduced exactly as rustedvino.com sets it: "Rusted" in oxide, "VINO" in
   the signal blue, 700 weight, letter-spacing -0.02em. The two colours are
   that site's --brand and --signal, per theme, and are scoped here rather than
   added to the palette — they are RustedVINO's brand, not this site's.

   Deliberately NOT using background-clip:text. The handoff records that it
   fails to repaint when variables change under a theme toggle, and its failure
   state is invisible text. Two plain colours do the same job and cannot fail. */
.rv {
  /* rustedvino.com's --oxide-lit, not its --brand (#cf5c2c). Measured: that
     brand colour is 4.77:1 on THAT site's near-black --ground (#0e0f10) and
     only 4.38:1 on this site's --raised tile (#17191c), which fails AA. The
     lit variant is the same palette — it is what their own Open Graph image
     uses for "RUSTED" — and measures 5.55:1 here. Same wordmark, correct
     surface. Light theme does NOT keep their light pair (#7d3415 / #1a5570):
     8–9:1 but brown and teal on this site's white tiles — the mark read bleak
     (Jerzy, 2026-10-24). Brighter rust and blue instead, both 5.06:1 on
     --ground and 5.28:1 on --raised. */
  --rv-brand: #e2703a;
  --rv-signal: #5ab4f0;
  font-weight: 700;
  letter-spacing: -0.02em;
  white-space: nowrap;
}
:root[data-theme="light"] .rv {
  --rv-brand: #b8481a;
  --rv-signal: #0b6bcb;
}
.rv span { color: var(--rv-brand); }
.rv b { color: var(--rv-signal); font-weight: inherit; }

/* ── The Bear Way (front page, below the tiles) ─────────────────────────── */
/* Five principles, each ending in a link to where the site proves it. Below
   the tiles on purpose: the first screen is unchanged. */
.bear-way { width: min(54rem, 100%); margin: 3.5rem auto 0; }
.bear-way-list { list-style: none; margin: 0; padding: 0; }
.bear-way-list li {
  display: flex; align-items: baseline; justify-content: space-between; gap: 1.5rem;
  padding-block: .8rem; border-top: 1px solid var(--edge);
}
.bear-way-list li:first-child { border-top: 0; padding-top: .25rem; }
.bear-way-list strong { color: var(--ink); font-weight: 600; }
.bear-way-list span { color: var(--ink-dim); }
.bear-way-list a {
  flex: none; display: inline-block; padding-block: .3rem;   /* ≥24px tap target */
  font: 600 .875rem/1.4 var(--mono); color: var(--ink-dim);
  text-underline-offset: .2em; white-space: nowrap;
}
.bear-way-list a:hover { color: var(--accent); }
@media (max-width: 40rem) {
  .bear-way { margin-top: 2.5rem; }
  .bear-way-list li { flex-direction: column; gap: .3rem; }
}

/* ── /author ───────────────────────────────────────────────────────────── */
/* The page a business-card QR code opens, usually on a phone. The photo is a
   portrait; at full width it would fill the first screen by itself, so it is
   held small enough that the name and the start of the bio stay above the fold. */
.author-photo {
  display: block;
  width: 11rem; height: auto;
  margin: 0 0 1.5rem;
  border-radius: .3rem;
}
.author-list { padding-inline-start: 1.25rem; }
.author-list li { margin: .35rem 0; }


/* The course sample on /pl/learn: a second list after the demos' footnote,
   because the footnote describes the demos only. */
.note + .learn-course { margin-top: 1.5rem; }
