@charset "UTF-8";
/* One stylesheet for every surface of this site. Premium by subtraction: one type scale, one
   spacing rhythm, one accent, no gradients, no stacked shadows, no decorative icons.
   Nothing here hides a layout fault - there is deliberately no overflow-x on <html>, because
   clipping the page would make the no-horizontal-scroll measurement pass while the fault stays.
   Anything that can be wider than its column (tables, code, diagrams) scrolls INSIDE its own box. */

/* ---------- tokens ---------- */
:root {
  color-scheme: dark;
  --bg: #0e1621;
  --surface: #17212b;
  --surface-hover: #202d3a;
  --ink: #f2f5f7;
  --muted: #9aa9b7;
  --accent: #2aabee;
  /* THE JOIN GREEN (CEO 2026-10-03: every join step is a high-contrast button of ONE colour, and
     the website's join buttons must not differ from the bot's, whose buttons are solid green via
     Bot API 9.4 style "success"). One family, ONE value in both themes: the bot's green does not
     change with a theme, so neither does this. Telegram does not publish the exact hex, so this is
     the same hue family, not a sampled copy. MEASURED (WCAG 2.x relative luminance):
       #fff on #1a7f37 = 5.08:1 (hover #146c2e = 6.53:1)  - text floor 4.5:1, both themes
       #1a7f37 on the dark page #0e1621 = 3.58:1, on the light page #f4f7f9 = 4.72:1
                                                       - non-text floor 3:1, both themes
     The cookie banner's accept and reject buttons deliberately do NOT use it: they stay
     identical to each other (equal prominence is a GDPR requirement, see the consent block). */
  --join-bg: #1a7f37;
  --join-bg-hover: #146c2e;
  --join-ink: #fff;
  /* THE FRONT DOOR'S OWN COLOUR (CEO 2026-10-04: "menu Žinutės must be as well Bold and a bit
     different color when not selected ... in dark model could be yellow bold"). A dark gold, not
     a bright one: the same page already rejected a 10.11:1 gold as "too bright" for the
     #AiOpsCatalyst marker and took a 6.11:1 orange, so the loudness here is carried by WEIGHT
     and hue separation rather than by maximum luminance.
     MEASURED (WCAG 2.x relative luminance, text floor 4.5:1):
       #d9a327 on the dark page #0e1621 = 7.98:1, on --surface #17212b = 7.15:1
       (the sticky header paints --footer-bg, rgba(14,22,33,.94) over --bg, so #0e1621 is the
        background this label really sits on - not --surface) */
  --home-ink: #d9a327;
  --line: #2a3a49;
  --line-soft: #223140;
  --field: #17212b;
  --tag: #213345;
  --disabled: #71808d;
  --focus: #62b7ee;
  --notice-bg: #2d291c;
  --notice-line: #6d6034;
  --notice-text: #f1d985;
  --diagram-bg: #f6f9fb;
  --footer-bg: rgba(14, 22, 33, .94);
  --space: 8px;
  --radius: 8px;
  --measure: 68ch;
}
html[data-theme="light"] {
  color-scheme: light;
  --bg: #f4f7f9;
  --surface: #fff;
  --surface-hover: #eaf3f8;
  --ink: #1d2b36;
  --muted: #5d6e7d;
  /* MEASURED 2026-09-07 (visual_consistency_core.cjs contrastRatio): #0b78b5 on --bg #f4f7f9
     is 4.47:1, a hair under the 4.5:1 WCAG AA floor. #0a6fa8 clears it at 5.07:1 on --bg and
     5.45:1 on --surface (#fff) - the worse-case background is the binding one, both pass with a
     real margin. Same lesson as the #AiOpsCatalyst marker fixed earlier today: a 10.11:1 gold on
     dark bg was rejected by the CEO as "too bright" and replaced with a 6.11:1 orange - more
     contrast is not automatically better, so this stays close to the original hue rather than
     chasing a much higher ratio. */
  --accent: #0a6fa8;
  /* THE FRONT DOOR, IN THE LIGHT THEME (CEO 2026-10-04: "in white dark blue Dark green").
     GREEN, not the other option he offered: this theme's --accent is already a dark blue
     (#0a6fa8) and that is the colour a SELECTED tab wears - an unselected Žinutės painted in it
     would be indistinguishable from the state the CEO asked it to stand out against.
     MEASURED (WCAG 2.x relative luminance, text floor 4.5:1):
       #0f6b3f on the light page #f4f7f9 = 6.11:1, on --surface #fff = 6.57:1
     It is not --join-bg (#1a7f37, 4.72:1 here): that is a BUTTON background for the join flow
     and is deliberately never used as text colour anywhere in the shell. */
  --home-ink: #0f6b3f;
  --line: #d7e1e8;
  --line-soft: #e6edf2;
  --field: #fff;
  --tag: #e7f3fa;
  --disabled: #84919a;
  --focus: #0b78b5;
  --notice-bg: #fff8dc;
  --notice-line: #d7c16b;
  --notice-text: #62531c;
  --diagram-bg: #fff;
  --footer-bg: rgba(244, 247, 249, .94);
}

/* ---------- base ---------- */
* { box-sizing: border-box; min-width: 0; }
/* CEO verbatim: "across in every page the same logic footer and top row with logo and menus
   sticky". scroll-padding-top keeps every in-page #fragment jump (an edition permalink, a Wiki
   section) landing BELOW the now-fixed header instead of hidden behind it - the one failure mode
   a sticky header adds silently, because a browser scrolls the target to y=0 with no awareness
   that a real header now occupies that space. 72px clears the header's own ~53px (52px min-height
   + 1px border, border-box) with headroom for its own drift across breakpoints; this stays a
   single global rule rather than an annotation on every anchorable element. */
html { background: var(--bg); scroll-padding-top: 72px; }
body {
  margin: 0; min-height: 100vh; display: flex; flex-direction: column;
  background: var(--bg); color: var(--ink);
  font: 15px/1.55 system-ui, -apple-system, "Segoe UI", sans-serif;
  -webkit-text-size-adjust: 100%;
}
a { color: var(--accent); text-underline-offset: 3px; }
button, input, select { font: inherit; }
button, a, input, select, summary { touch-action: manipulation; }
button, summary { cursor: pointer; }
img, svg { max-width: 100%; }
a:focus-visible, button:focus-visible, input:focus-visible, select:focus-visible,
summary:focus-visible, [role="tabpanel"]:focus-visible { outline: 2px solid var(--focus); outline-offset: 2px; }
[hidden] { display: none !important; }
.sr-only {
  position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0;
  overflow: hidden; clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap; border: 0;
}
.skip { position: absolute; top: -100px; left: 12px; padding: 10px; background: var(--surface); z-index: 6; }
.skip:focus { top: 8px; }

/* ---------- header ----------
   ONE HEADER, ONE POSITION, ON EVERY PAGE. MEASURED 2026-09-18 across eight live pages at
   1280px: the logo sat at x=16 on /bendra/chat/, /members/ and the home page (full-bleed bar)
   and at x=136 on /llm/, /lt/wiki/, /editions.html and /privacy/ (the bar was capped at 1040
   and centred) - a 120px jump between pages of one site, which is what the CEO reported
   ("alignment in every page must be the same ... whole website must have the same header at
   least position of logo and the next menus the same position from left side").
   The bar is now full-bleed everywhere, exactly like .site-footer, so the logo starts at the
   page gutter on every page and every control after it ends at the same right edge. `main`
   keeps its 1040 reading column; a full-width bar over a centred column is the same shape the
   footer has always had here. */
main, .site-footer > * { max-width: 1040px; margin-inline: auto; }
.site-header {
  position: sticky; top: 0; z-index: 5;
  display: flex; align-items: center; gap: 12px; min-height: 52px;
  padding: 6px 16px; width: 100%; border-bottom: 1px solid var(--line);
  background: var(--footer-bg); backdrop-filter: saturate(1.1) blur(6px);
}
.brand { display: flex; align-items: center; gap: 9px; text-decoration: none; white-space: nowrap; margin-right: auto; }
.brand img { display: block; width: 28px; height: 28px; border-radius: 7px; }
.brand span { color: var(--ink); font-size: .9375rem; font-weight: 600; letter-spacing: -.01em; }
/* THE HEADER JOIN LINK IS A GREEN BUTTON, the same one the bot shows (see --join-bg above). It was a
   plain-weight word "so it never competes with the section bar"; a newcomer who could not find the
   join control is the cost the CEO measured, so it now reads as the one button in the row. */
.header-link {
  display: inline-flex; align-items: center; flex: 0 0 auto;
  padding: 6px 14px; border-radius: 999px;
  background: var(--join-bg); color: var(--join-ink);
  font-size: .8125rem; font-weight: 600; line-height: 1.2; text-decoration: none; white-space: nowrap;
}
.header-link:hover { background: var(--join-bg-hover); color: var(--join-ink); text-decoration: none; }
/* THE BIG GREEN BUTTON at the top of /prisijungti/ (CEO 2026-10-03): the same --join-bg as every
   other join control, larger because it is the ONE thing that page asks a newcomer to do. */
.join-button {
  display: inline-flex; align-items: center; justify-content: center;
  min-height: 48px; padding: 12px 28px; border-radius: 999px;
  background: var(--join-bg); color: var(--join-ink);
  font-size: 1.0625rem; font-weight: 700; line-height: 1.2; text-decoration: none;
}
.join-button:hover { background: var(--join-bg-hover); color: var(--join-ink); text-decoration: none; }
button { border: 1px solid var(--line); border-radius: 6px; background: var(--surface); color: var(--accent); padding: 6px 10px; }
.theme-toggle { display: inline-flex; align-items: center; justify-content: center; width: 34px; height: 34px; padding: 0; flex: 0 0 auto; }
.theme-toggle .icon { width: 18px; height: 18px; fill: none; stroke: currentColor; stroke-width: 1.7; stroke-linecap: round; stroke-linejoin: round; }
.theme-toggle .icon-moon { fill: currentColor; stroke: none; }
.theme-toggle .icon-moon, html[data-theme="light"] .theme-toggle .icon-sun { display: none; }
html[data-theme="light"] .theme-toggle .icon-moon { display: block; }

/* ---------- main + section bar ---------- */
/* The sticky footer overlays the last ~36px of the page, so the last line of content
   needs room to clear it. */
main { width: 100%; flex: 1 0 auto; padding: 0 16px calc(var(--space) * 7); }
.view-tabs {
  display: flex; align-items: flex-end; gap: 18px; flex-wrap: wrap;
  border-bottom: 1px solid var(--line); margin-bottom: calc(var(--space) * 2);
}
.tab-group { display: flex; gap: 18px; }
/* nav.lang-switch shares these declarations by SELECTOR, never by copy: it must look like a tab
   without wearing .view-tabs, which is the section bar's identity (site_shell_dom_test.cjs
   asserts exactly one .view-tabs per page). A second copy of these five lines is how the next
   change to a tab's look would reach one of them and not the other. */
.view-tabs a, nav.lang-switch a {
  border-bottom: 2px solid transparent; color: var(--muted); text-decoration: none;
  padding: 12px 0 9px; font-size: .875rem; font-weight: 600; white-space: nowrap;
}
.view-tabs a:hover, nav.lang-switch a:hover { color: var(--ink); }
.view-tabs a[aria-selected="true"], .view-tabs a[aria-current="page"] { color: var(--accent); border-bottom-color: var(--accent); }
/* THE MACHINE-ACCESS TAB IS THE LOUDEST THING IN THE ROW (CEO 2026-10-01, from a 375px
   screenshot): "LLM & MCP ... almost invidible; this is most important to be vizible ... keep it
   white but bold and to fit in the screen".
   --ink, NOT a literal white: in the light theme it is #1d2b36, so a hard #fff would be a white
   tab on a white page. --ink is the theme's own strongest text colour - the white the CEO asked
   for on the dark theme he was looking at.
   FIT IS A REQUIREMENT, NOT A HOPE: bolding widens the label, and a nav that overflows hides the
   very item being promoted. `white-space: nowrap` keeps it on one line, and the row already
   wraps (style.css .view-tabs) / scrolls (archive.css), so a bold label costs a second line at
   worst - never a clipped or unreachable link. The transition is colour-only, so no motion is
   added. */
.view-tabs a.view-link--llm { color: var(--ink); font-weight: 700; }
.view-tabs a.view-link--llm:hover { color: var(--ink); }
.view-tabs a.view-link--llm[aria-current="page"] { color: var(--accent); }
/* THE FRONT DOOR IS THE LOUDEST TAB IN THE ROW (CEO 2026-10-04, verbatim: "menu Žinutės must be as
   well Bold and a bit different color when not selected (to get more attention in the menu) - for
   example in dark model could be yellow bold; in white dark blue Dark green. when selected and
   normal").
   BOLD + ITS OWN COLOUR WHILE NOT SELECTED, AND EXACTLY THE NORMAL SELECTED TAB WHILE IT IS. The
   two halves are the request: the tab that opens the conversation is the one people are looking
   for, and it must not shout while you are already reading the page it opened.
   IT IS A CLASS, NOT A COLOUR, for the reason the --llm rule above gives - and both renderers
   emit it (shell.py's section_bar and reader.php's public_reader_section_bar), which is what
   keeps the static pages and the live reader showing the same nav.
   The colour itself lives in --home-ink (style.css :root / [data-theme=light]) with its measured
   contrast ratios recorded there; `currentColor` is not used because the two themes need two
   different values, not one that happens to work. */
.view-tabs a.view-link--home { color: var(--home-ink); font-weight: 700; }
.view-tabs a.view-link--home:hover { color: var(--home-ink); }
/* NORMAL means normal: back to --accent, and back to the 600 every other selected tab wears, so
   the selected Žinutės is the same weight as the selected Apžvalgos beside it. */
.view-tabs a.view-link--home[aria-current="page"] { color: var(--accent); font-weight: 600; }
.view-tabs span { margin-left: 4px; font-weight: 400; font-size: .6875rem; }
.view-note { margin: 0 0 calc(var(--space) * 1.5); font-size: .75rem; color: var(--muted); }
/* ---------- the language switch ----------
   Two forms, one rule set. The Wiki pair renders `p.lang-switch` (a plain link, unchanged since
   2026-09-08). The /llm/ pair renders `nav.view-tabs.lang-switch`, so it inherits every tab
   rule above and reads as a tab in the same row - CEO 2026-09-18 asked for the English version
   as a tab with an icon, and the cheapest honest way to look like a tab is to BE one.
   It carries no bottom border of its own: the section bar directly above already draws that
   line across the page, and a second one 34px below it reads as an empty second row. */
p.lang-switch { margin: calc(var(--space) * -1) 0 calc(var(--space) * 2); font-size: .8125rem; }
nav.lang-switch {
  display: flex; justify-content: flex-end;
  margin: calc(var(--space) * -2.25) 0 calc(var(--space) * 1.5);
}
nav.lang-switch a { display: inline-flex; align-items: center; gap: 6px; padding: 0; }
/* ---------- the switch INSIDE the sticky header (CEO 2026-09-18) ----------
   It lives beside the day/night toggle now, so it must sit on the header's centre line rather
   than hang off the body's baseline grid - which is what the negative margins above do. The
   selector is scoped to .site-header so the two placements cannot fight; nothing about the
   body form changes. */
.site-header nav.lang-switch { margin: 0; flex: 0 0 auto; }
.site-header nav.lang-switch a {
  border-bottom: 0; padding: 6px 8px; border: 1px solid var(--line); border-radius: 6px;
  font-size: .8125rem; min-height: 34px; box-sizing: border-box;
}
.site-header nav.lang-switch a:hover { color: var(--ink); border-color: var(--accent); }
/* Both labels ship in the HTML; width decides which is read. The code is aria-hidden, so a
   screen reader always hears the language's own name, never "EN". */
.lang-code { display: none; }
/* ---------- a two-option comparison table that FITS a phone ----------
   A three-column table (label + option A + option B) cannot fit 375px, and layout_overflow_test
   measures the page with every scroll box UN-CLIPPED on purpose: a table that only scrolls
   inside its own box is still content a thumb has to chase sideways. Under 700px each row
   becomes a small block - the row label, then the two options each under their own heading -
   so the whole comparison reads top to bottom with nothing off-screen. */
@media (max-width: 700px) {
  table.compare, table.compare tbody, table.compare tr, table.compare th, table.compare td { display: block; width: auto; }
  table.compare thead { display: none; }
  table.compare tr { border-bottom: 1px solid var(--line); padding: 8px 0; }
  table.compare tr > th:first-child { padding: 0 0 4px; border: 0; font-size: .8125rem; }
  table.compare td { border: 0; padding: 2px 0 2px 12px; }
  table.compare td::before { content: attr(data-option); display: block; font-size: .6875rem; color: var(--muted); text-transform: uppercase; letter-spacing: .04em; }
}
@media (max-width: 700px) {
  .lang-name { display: none; }
  .lang-code { display: inline; font-weight: 700; letter-spacing: .02em; }
  .site-header nav.lang-switch a { padding: 6px; gap: 4px; }
}
/* Same stroke weight and same box as the theme toggle's sun and moon, so the two controls in
   the page chrome read as one family rather than two borrowed icon sets. */
.icon-language { width: 15px; height: 15px; fill: none; stroke: currentColor; stroke-width: 1.7; stroke-linecap: round; stroke-linejoin: round; }
h1 { font-size: 1.1875rem; font-weight: 650; margin: 0; }
h2 { font-size: 1.0625rem; font-weight: 600; margin: calc(var(--space) * 3.5) 0 var(--space); letter-spacing: -.01em; }
h3 { font-size: .9375rem; font-weight: 600; margin: 0 0 6px; }
p { max-width: var(--measure); }
.lead { max-width: var(--measure); margin: 0 0 calc(var(--space) * 2); color: var(--muted); font-size: .9375rem; }
.filter-note, .secondary-note, .privacy-note, .view-share, .back-link { font-size: .8125rem; color: var(--muted); }
.secondary-note { margin: 10px 0 18px; }
.privacy-note { margin: calc(var(--space) * 2) 0 0; font-size: .75rem; }
.text-button { padding: 0; border: 0; background: transparent; text-decoration: underline; }
.synthetic-notice {
  max-width: none; padding: 9px 12px; margin: 0 0 calc(var(--space) * 2);
  border: 1px solid var(--notice-line); border-radius: 6px;
  background: var(--notice-bg); color: var(--notice-text); font-size: .8125rem; line-height: 1.5;
}

/* ---------- footer: sticky, centred, on every page ---------- */
.site-footer {
  position: sticky; bottom: 0; z-index: 4; flex: 0 0 auto;
  display: flex; flex-wrap: wrap; align-items: center; justify-content: center;
  gap: 6px 16px; padding: 9px 16px calc(9px + env(safe-area-inset-bottom, 0px));
  border-top: 1px solid var(--line); background: var(--footer-bg);
  backdrop-filter: saturate(1.1) blur(6px);
  font-size: .75rem; color: var(--muted); text-align: center;
}
/* THE FOOTER IS INVERTED AGAINST THE PAGE (CEO 2026-09-08: "footer to have inverted colors
   (based on day/night modes) and to bold #AiOpsCatalyst with Prisijungti prie kanalo on every
   page"). Night page -> light band; day page -> dark band.

   These are the SAME values the PHP reader already ships in public-reader/archive.css
   (.archive-reader .site-footer). Copied deliberately rather than re-derived: the static pages
   and the reader render the identical footer markup, and two independently chosen palettes for
   one visual element is how the two halves of this site drift apart.

   SCOPED TO .site-footer, NEVER TO :root. `--footer-bg` is also what .site-header paints with,
   so redefining it at the root would inverted the sticky header too. Only the footer inverts;
   the header, the nav and the content keep the theme.

   The band needs its OWN text, link and FOCUS colours: values tuned for the page sit on the
   opposite background down here and fail. A focus ring in the page's colour would vanish
   against the inverted band - a keyboard accessibility failure, not a cosmetic one. */
.site-footer {
  --footer-bg: #eef3f7;      /* night page -> light band */
  --footer-ink: #10202c;
  --footer-link: #0a5c8c;
  --footer-focus: #0a5c8c;
  color: var(--footer-ink);
  border-top: 0;
}
html[data-theme="light"] .site-footer {
  --footer-bg: #10202c;      /* day page -> dark band */
  --footer-ink: #eef3f7;
  --footer-link: #79c8f2;
  --footer-focus: #79c8f2;
}
.site-footer a { color: var(--footer-link); text-decoration: none; }
.site-footer a:hover { text-decoration: underline; }
.site-footer a:focus-visible {
  outline: 3px solid var(--footer-focus);
  outline-offset: 2px;
  border-radius: 3px;
}
/* The two the CEO named: the creator marker and the join CTA are the footer's only promotional
   text, so both carry the weight. The other three links stay regular - bolding everything is
   the same as bolding nothing. */
.site-footer .footer-brand,
.site-footer .footer-join { font-weight: 600; }
/* The footer's join link is the same green button as the header's and the floating pill's. It is
   the placement a phone ALWAYS has: the band sits under the cookie banner, never behind it, so a
   first-time visitor on a 375px screen sees a real join button while the banner is still open. */
.site-footer a.footer-join {
  display: inline-flex; align-items: center;
  padding: 6px 14px; border-radius: 999px;
  background: var(--join-bg); color: var(--join-ink);
}
.site-footer a.footer-join:hover { background: var(--join-bg-hover); text-decoration: none; }

/* ---------- the two Telegram join buttons (header + footer) ----------
   CEO 2026-10-21: the floating pill is gone (one join in the header, one in the footer is
   enough), both remaining joins carry the Telegram paper plane instead of an arrow, and they wear
   TELEGRAM BLUE instead of the green. Telegram's own #2AABEE cannot carry white text: MEASURED
   (WCAG 2.x relative luminance) #fff on #2AABEE = 2.57:1 and on #0088CC = 3.89:1, both under the
   4.5:1 floor for this 13px label. #0077B5 - the same Telegram blue family, darker - gives
   #fff 4.88:1 (AA, normal text), and as a button against the page it is 3.73:1 on the dark
   #0e1621 and 4.53:1 on the light #f4f7f9 (non-text floor 3:1), so ONE value passes in both
   themes. Hover #006FA8: #fff 5.47:1.
   WHY `html body` IN THE SELECTORS: archive.css (loaded after this file by the PHP reader, and
   edited by other work) paints the same links green at 0-3-1; these win at 0-3-3 on BOTH rails
   without touching that file. Only these two links change - Registruotis, .join-button and every
   other CTA keep their colours. */
html body .site-header a.header-link.tg-join,
html body .site-footer a.footer-join.tg-join {
  /* NO `display` here: archive.css hides the reader's header join on a narrow screen to leave the
     tabs room, and a display value at this specificity would force it back (it did, 2026-10-21:
     "Žinutės Re..." at 375px). Both base rules are already inline-flex. */
  align-items: center; gap: .4em;
  background: #0077b5; color: #fff;
}
html body .site-header a.header-link.tg-join:hover,
html body .site-footer a.footer-join.tg-join:hover { background: #006fa8; color: #fff; text-decoration: none; }
.tg-join .tg-icon { flex: none; width: 1.05em; height: 1.05em; }

/* ---------- cards, tables, code, diagrams ---------- */
.archive-status, .empty-state {
  background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius); padding: 16px;
}
.archive-status h2, .empty-state h2, .empty-state h3 { margin: 0 0 8px; font-size: 1rem; font-weight: 600; }
.archive-status p, .empty-state p { margin: 0 0 10px; color: var(--muted); }
.empty-icon { display: none; }
.table-scroll { overflow-x: auto; margin: var(--space) 0 0; border: 1px solid var(--line); border-radius: var(--radius); }
table { width: 100%; border-collapse: collapse; font-size: .8125rem; }
th, td { padding: 9px 12px; text-align: left; vertical-align: top; border-bottom: 1px solid var(--line-soft); overflow-wrap: anywhere; }
tbody tr:last-child th, tbody tr:last-child td { border-bottom: 0; }
/* Headers and short value columns must never be broken mid-word: 'FORMATAS' rendered as
   'FORMAT / AS' is the exact kind of detail that makes a careful page look unfinished.
   The wide description column keeps overflow-wrap so a long identifier still cannot push
   the table past its scroll container. */
thead th { font-size: .6875rem; text-transform: uppercase; letter-spacing: .06em; color: var(--muted); font-weight: 600; white-space: nowrap; overflow-wrap: normal; }
td.nowrap, th.nowrap { white-space: nowrap; overflow-wrap: normal; }
tbody th { min-width: 120px; font-weight: 600; }
code { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: .8125em; overflow-wrap: anywhere; }
pre {
  position: relative; max-width: 100%; overflow-x: auto; padding: 12px; margin: var(--space) 0;
  background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius);
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: .75rem; line-height: 1.5;
}
pre code { font-size: 1em; overflow-wrap: normal; }
/* llm.html only (copy-code.js wires this in): a small always-reachable control, never covering
   text on a phone - it sits above the code, not floating over it, so it cannot hide a character
   the reader needs to read. Room is reserved on the right so it never overlaps a long line. */
pre.has-copy { padding-top: 38px; }
.copy-btn {
  position: absolute; top: 8px; right: 8px; z-index: 1;
  font-size: .6875rem; padding: 4px 9px; line-height: 1.4;
}
.copy-btn.is-copied { color: #2e9e5b; border-color: #2e9e5b; }
.copy-btn.is-failed { color: #d24b4b; border-color: #d24b4b; }
/* Off-screen clipboard scratch field for the execCommand('copy') fallback - same pattern as the
   .report-trap honeypot below, kept out of an inline style so the page's own CSP is never asked
   to allow one. */
.copy-scratch { position: absolute; left: -9999px; width: 1px; height: 1px; overflow: hidden; }
.diagram { margin: calc(var(--space) * 2) 0; }
.diagram-frame {
  overflow-x: auto; padding: 14px; background: var(--diagram-bg);
  border: 1px solid var(--line); border-radius: var(--radius);
}
/* Natural size inside a scrollable frame, never a squeezed thumbnail. A 210px-wide
   pipeline and a 740px-wide boundary map must both stay READABLE, so they are not forced
   to a shared height; the frame scrolls horizontally on a phone instead, which keeps the
   overflow inside the box and off the page. */
.diagram-frame img { display: block; margin-inline: auto; width: auto; height: auto; max-width: none; max-height: min(70vh, 620px); }
.diagram figcaption { margin-top: 6px; font-size: .8125rem; color: var(--muted); }
.diagram details { margin-top: 6px; }
.ascii-diagram { white-space: pre; }
.doc-contents { display: flex; flex-wrap: wrap; gap: 8px 14px; margin: 0 0 calc(var(--space) * 2); font-size: .8125rem; }
.changelog { display: grid; gap: calc(var(--space) * 2); }
.log-entry { padding-left: 14px; border-left: 2px solid var(--line); }
.log-entry h3 { margin: 0 0 4px; font-size: .8125rem; font-weight: 600; color: var(--muted); font-variant-numeric: tabular-nums; }
.log-entry ul { margin: 0; padding-left: 18px; }
.log-entry li { margin-bottom: 4px; font-size: .875rem; max-width: var(--measure); }
.product-documentation section { max-width: none; }

/* ---------- filters ---------- */
.filters {
  display: grid; gap: 9px; align-items: end;
  grid-template-columns: minmax(180px, 2fr) minmax(130px, 1.2fr) minmax(115px, 1fr) minmax(115px, 1fr) auto;
}
.filters label { display: block; font-size: .6875rem; margin-bottom: 4px; color: var(--muted); }
.filters input, .filters select {
  width: 100%; height: 34px; padding: 6px 8px; font-size: .75rem;
  background: var(--field); color: var(--ink); border: 1px solid var(--line); border-radius: 6px;
}
.filters button { min-height: 36px; font-size: .75rem; }
.filter-note { margin: 8px 0; }
#filter-status { font-size: .6875rem; color: var(--muted); margin: 8px 0; }

/* ---------- edition cards ---------- */
.edition {
  display: flex; flex-direction: column; padding: 12px 14px; margin: 9px 0;
  background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius);
}
.edition-top { order: 0; display: flex; justify-content: space-between; align-items: center; }
.eyebrow { font-size: .5625rem; letter-spacing: .09em; margin: 0; color: var(--muted); }
.permalink { text-decoration: none; }
.edition h3 { order: 1; font-size: 1.0625rem; margin: 3px 0; font-variant-numeric: tabular-nums; }
.period { order: 2; color: var(--muted); font-size: .6875rem; margin: 0 0 8px; }
.topic-list { order: 3; }
.topic-empty { order: 4; }
.topic { border-top: 1px solid var(--line-soft); padding: 10px 0; }
.topic h4 { font-size: .875rem; margin: 0 0 3px; }
.topic h4 span { font-size: .625rem; font-weight: 400; color: var(--muted); margin-left: 7px; white-space: nowrap; }
.topic p { font-size: .6875rem; color: var(--muted); margin: 3px 0 5px; }
summary { color: var(--accent); font-size: .75rem; padding: 5px 0; }
.sources { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 5px; margin: 5px 0; }
.source-link { padding: 7px 9px; border: 1px solid var(--line); border-radius: 4px; font-size: .75rem; text-decoration: none; overflow-wrap: anywhere; }
.source-link:hover { background: var(--surface-hover); }
.source-pager, .edition-pager { display: flex; justify-content: space-between; align-items: center; gap: 10px; font-size: .6875rem; color: var(--muted); margin-top: 8px; }
.source-pager a, .edition-pager a { padding: 7px 10px; border: 1px solid var(--line); border-radius: 5px; text-decoration: none; }
button:disabled, a[aria-disabled="true"] { color: var(--disabled); cursor: default; }
.member-note { order: 5; font-size: .6875rem; color: var(--muted); margin: 8px 0; }
.stats { order: 6; display: flex; flex-wrap: wrap; gap: 18px; font-size: .6875rem; color: var(--muted); padding-top: 8px; border-top: 1px solid var(--line-soft); }
.stats strong { font-size: .75rem; color: var(--ink); margin-right: 4px; }
.coverage { order: 7; font-size: .6875rem; color: var(--muted); margin: 6px 0; }
.accounting { order: 8; }
.accounting p, .accounting li { font-size: .6875rem; color: var(--muted); }
.edition-footer { order: 9; display: flex; flex-wrap: wrap; justify-content: space-between; gap: 6px; padding-top: 8px; margin-top: 8px; border-top: 1px solid var(--line-soft); font-size: .625rem; color: var(--muted); }

/* ---------- message reader (synthetic preview and shared message styling) ---------- */
.archive-reader .filters { grid-template-columns: minmax(180px, 2fr) repeat(4, minmax(110px, 1fr)) auto; }
.message-row { padding: 12px 14px; margin: 8px 0; border: 1px solid var(--line); border-radius: var(--radius); background: var(--surface); }
.message-meta, .message-footer { display: flex; align-items: baseline; gap: 12px; flex-wrap: wrap; color: var(--muted); font-size: .75rem; }
.message-meta { justify-content: space-between; margin-bottom: 8px; }
.message-meta a { text-decoration: none; }
.message-text { font-size: .9375rem; line-height: 1.55; white-space: pre-wrap; overflow-wrap: anywhere; }
.message-row figure { margin: 12px 0; }
.message-image { display: block; width: 100%; max-width: min(100%, 640px); height: auto; border-radius: 5px; }
.message-footer { margin-top: 12px; border-top: 1px solid var(--line-soft); padding-top: 7px; }
.message-tags { display: flex; flex-wrap: wrap; gap: 6px; margin-right: auto; }
.message-tags span { background: var(--tag); border-radius: 4px; padding: 2px 6px; }
.telegram-source { font-size: .6875rem; }
.message-heading { font-size: 1rem; font-weight: 600; margin: 0 0 8px; }
.link-preview { display: flex; align-items: center; gap: 12px; max-width: 680px; margin-top: 12px; padding: 10px; border: 1px solid var(--line); border-radius: 5px; text-decoration: none; overflow-wrap: anywhere; }
.link-preview > img { flex: 0 1 120px; width: 120px; height: auto; object-fit: contain; }
.link-preview > span { display: grid; gap: 3px; }
.link-preview small { color: var(--muted); font-size: .6875rem; }
.link-preview strong { font-size: .9375rem; }
.link-preview span span { color: var(--muted); font-size: .8125rem; }
#archive-pagination { display: flex; gap: 7px; flex-wrap: wrap; margin: 16px 0; }
#archive-pagination a { min-width: 38px; padding: 7px 10px; text-align: center; border: 1px solid var(--line); border-radius: 5px; text-decoration: none; }
#archive-pagination a[aria-current="page"] { background: var(--accent); color: #fff; border-color: var(--accent); }
#archive-status, .view-share, .back-link { margin: 8px 0; }

/* ---------- responsive ---------- */
@media (max-width: 950px) {
  .archive-reader .filters { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  .archive-reader .search-field { grid-column: 1 / -1; }
}
@media (max-width: 680px) {
  body { font-size: 15px; }
  .site-header, main, .site-footer { padding-left: 12px; padding-right: 12px; }
  .site-header { gap: 10px; }
  .brand span { font-size: .875rem; }
  /* A PHONE HEADER CARRIES THE MARK AND THE CONTROLS, NOTHING ELSE. Measured 2026-09-18 at
     375px with the language switch added: the brand word and "Prisijungti prie kanalo ↗"
     overlapped. archive.css has hidden both in the reader since it was built, so this is the
     static side catching up rather than a new idea - and it is what keeps the two headers the
     same shape at every width. 2026-10-03 (CEO: a first-time phone visitor saw no real join
     button while the cookie banner was open): the join link is a compact GREEN button here
     again. It fits because the brand word is gone - logo, link, language switch and theme
     control measured to fit at 375 and 320 - and it sits at the TOP, so the bottom cookie
     banner can never cover it. The footer's green button stays as the second placement. */
  .site-header .brand-name { display: none; }
  .site-header .header-link { padding: 5px 10px; font-size: .75rem; }
  .view-tabs { gap: 14px; margin-bottom: 12px; }
  .tab-group { gap: 14px; }
  .view-tabs a { font-size: .8125rem; padding: 10px 0 8px; }
  .filters { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: 7px; }
  .archive-reader .filters { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .search-field { grid-column: 1 / -1; }
  .filters input, .filters select { font-size: 16px; height: 38px; }
  .filters button { justify-self: start; }
  .archive-status, .empty-state { padding: 14px; }
  .edition, .message-row { padding: 11px 12px; }
  .sources { grid-template-columns: 1fr; }
  .source-link { min-height: 38px; display: flex; align-items: center; }
  .message-meta { gap: 3px; flex-direction: column; }
  .link-preview { gap: 8px; }
  .link-preview > img { flex-basis: 80px; width: 80px; }
  .site-footer { gap: 4px 12px; font-size: .6875rem; }
  /* MEASURED 2026-09-07 (visual_consistency_probe.cjs, clipping stripped): a curl example and a
     JSON snippet on llm.html reached 770px, a mermaid source block on the Wiki reached 750px,
     inside a 375px viewport - real content wider than the phone, not a hidden scrollbar. The
     desktop-only `overflow-x: auto` on `pre` above stayed exactly as designed (the CEO's own
     "scrolls inside its own box" rule for tables/code/diagrams on a real screen); on a phone the
     content now WRAPS instead, which is reachable without a sideways scroll gesture at all - a
     better outcome than a working scrollbar, not merely an equivalent one. */
  pre, pre code { white-space: pre-wrap; overflow-wrap: anywhere; }
  /* MEASURED 2026-09-07: the Wiki's data-tables list (28 rows, 4 columns) reached 423px in a
     375px viewport with the desktop table rules untouched - `tbody th{min-width:120px}` alone
     is 32% of the budget, and the "Pasiekiama" header's default `white-space:nowrap` held its
     column to its own label width even though every value under it ("Vieša"/"Privati"/"Abi")
     is short. Scoped to `.data-table` only - the existing 2-column "Kas veikia" table already
     fits at 375px and keeps its wider row-header floor on desktop. */
  .data-table tbody th { min-width: 0; }
  .data-table thead th { white-space: normal; }
  .data-table td.nowrap { white-space: normal; overflow-wrap: anywhere; }
}
/* MEASURED 2026-09-24: the "LLM" tab became "LLM & MCP" (CEO: rename the /llm/ section's title
   and tab). Before this rename, five tabs fit in one row at 320px with room to spare (measured
   on the pre-rename build: last tab right edge well inside the 296px bar). "LLM & MCP" needs a
   72px-wide fifth tab instead of "LLM"'s ~47px, pushing the row to 322.59px against that same
   296px bar; .view-tabs's own flex-wrap:wrap (by design, see the rule above) then wraps the
   fifth tab onto its own line - not a page-width overflow, but no longer "one row" either.
   Only the GAP moves, and only below 360px: font-size stays readable, and the 361-680px range
   - where five tabs already fit - is untouched. */
@media (max-width: 360px) {
  .view-tabs { gap: 6px; }
}
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { animation-duration: .001ms !important; animation-iteration-count: 1 !important; transition-duration: .001ms !important; scroll-behavior: auto !important; }
}

/* ---------------------------------------------------------------------------
   Public bug-report form (/pranesti/) and its quiet footer link.

   EVERY colour here is a THEME TOKEN, never a literal. That is the whole reason
   this block needs no `html[data-theme="light"]` counterpart: night is :root and
   day re-points the same tokens, so one rule set is correct in both. A hardcoded
   hex would look right in the default theme and wrong in the other, which is how
   this stylesheet grew its per-theme overrides in the first place.

   Compact by instruction (the CEO's standing "less empty space"): the form is
   three fields and a button, and it must not read as a wall.
   --------------------------------------------------------------------------- */

/* The spacing scale, declared HERE rather than borrowed. --s1..--s6 are defined in
   public-reader/archive.css, which this page deliberately does NOT load, so every
   var(--sN) below resolved to nothing: gaps collapsed and the submit button rendered with
   zero padding as a ~60px sliver (measured on the live page 2026-09-07). Scoping the
   scale to .bug-report makes this block correct under either stylesheet and keeps it out
   of every other page's way. Same values as the reader's, so both surfaces breathe alike. */
.bug-report {
  --s1: 4px; --s2: 8px; --s3: 12px; --s4: 16px; --s5: 24px; --s6: 32px;
}

.bug-report main {
  max-width: 46rem;
  margin: 0 auto;
  padding: var(--s4) var(--s4) var(--s6);
}
.bug-report .report-title { font-size: 1.25rem; margin: 0 0 var(--s1); }
.bug-report .report-lead { color: var(--muted); margin: 0 0 var(--s4); font-size: .9rem; }

/* The one line that tells the visitor what happened. Uses the notice tokens the
   reader already defines for its "page size was capped" note, so a success and a
   refusal look like the same system rather than two designs. */
.bug-report .report-note {
  background: var(--notice-bg);
  border: 1px solid var(--notice-line);
  color: var(--notice-text);
  border-radius: var(--radius);
  padding: var(--s2) var(--s3);
  margin: 0 0 var(--s4);
  font-size: .88rem;
}

.bug-report .report-form { display: flex; flex-direction: column; gap: var(--s3); }
/* max-width:none because each field row is a <p>, and the shared `p { max-width:
   var(--measure) }` reading-measure rule capped every input at 68ch - correct for prose,
   wrong for a control (measured: a 583px textarea inside a 704px column). */
.bug-report .field { display: flex; flex-direction: column; gap: var(--s1); margin: 0; max-width: none; }
.bug-report label { font-size: .85rem; color: var(--ink); font-weight: 600; }
.bug-report .hint { font-size: .78rem; color: var(--muted); }

.bug-report textarea,
.bug-report input[type="email"],
.bug-report input[type="file"] {
  font: inherit;
  color: var(--ink);
  background: var(--field);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: var(--s2) var(--s3);
  width: 100%;
  box-sizing: border-box;
}
.bug-report textarea { resize: vertical; min-height: 6.5rem; }

/* A VISIBLE focus ring is not decoration here - this form is the one keyboard
   interaction on the whole site, and the default ring disappears against
   --field in the dark theme. */
.bug-report textarea:focus-visible,
.bug-report input:focus-visible,
.bug-report .report-send:focus-visible {
  outline: 2px solid var(--focus);
  outline-offset: 2px;
}

.bug-report .report-send {
  font: inherit;
  font-weight: 600;
  color: var(--bg);
  background: var(--accent);
  border: 0;
  border-radius: var(--radius);
  padding: var(--s2) var(--s5);
  cursor: pointer;
  align-self: flex-start;
}
.bug-report .report-send:hover { filter: brightness(1.08); }

/* THE HONEYPOT. Hidden from people, still in the DOM for a bot to find and fill.
   `display:none` would be the obvious way and is the wrong one - the better bots
   skip fields they can see are hidden that way. Positioning it off-canvas keeps
   it "real" to a script while a person never meets it, and the markup also
   carries aria-hidden + tabindex=-1 so a screen-reader user is never offered a
   trap field to fill in. */
.bug-report .report-trap {
  position: absolute;
  left: -9999px;
  width: 1px;
  height: 1px;
  overflow: hidden;
}

.bug-report .report-back { margin: var(--s5) 0 0; font-size: .85rem; }
.bug-report .report-back a { color: var(--muted); text-decoration: none; }
.bug-report .report-back a:hover,
.bug-report .report-back a:focus { color: var(--accent); text-decoration: underline; }

@media (max-width: 640px) {
  .bug-report main { padding: var(--s3) var(--s3) var(--s6); }
  .bug-report .report-send { align-self: stretch; text-align: center; }
}

/* THE DELIVERED EDITION'S SOURCE LINKS.
   A delivered (AI-written) edition REPLACES that day's keyword edition, so it carries the
   permalinks to the messages it summarised - otherwise those messages become unreachable
   from the site and edition_link_audit refuses the publish (measured 2026-09-08: 31
   declared, 0 rendered). Rendered as one quiet wrapping row of small anchors, NOT a button
   per message: that wall is the shape the CEO rejected on the digest page. */
.delivered-sources {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 6px;
  margin: 12px 0 0;
  font-size: 0.78rem;
  line-height: 1.6;
}
.delivered-sources-label { color: var(--muted); }
.delivered-sources a {
  color: var(--muted);
  text-decoration: none;
  border: 1px solid var(--line);
  border-radius: 999px;
  padding: 1px 8px;
  font-variant-numeric: tabular-nums;
}
.delivered-sources a:hover,
.delivered-sources a:focus { color: var(--accent); border-color: currentColor; }

/* THE AI-ACT PROVENANCE LINE on the STATIC pages (editions.html and each
   /apzvalgos/<day>/<window>.html article). ai_disclosure.machine_text_block emits it
   directly under the prose it is about.

   IT NEEDS ITS OWN RULE HERE. The identical element already had a rule in
   public-reader/archive.css, but that one is scoped `.archive-reader .ai-prov` and the
   static pages load style.css instead - so without this block the disclosure inherited
   card-body styling and read as one more line of the summary, which is the opposite of
   telling a reader the summary was machine-written.

   Deliberately quieter than the prose and deliberately NOT hidden: no display:none, no
   visually-hidden, no zero size. A disclosure the stylesheet can hide is not a
   disclosure. Same reasoning, same values as the archive.css rule it mirrors. */
.ai-prov {
  margin: 0.35em 0 0;
  font-size: 0.74rem;
  line-height: 1.45;
  color: var(--muted);
  overflow-wrap: anywhere;
}
.ai-prov b { font-weight: 600; }

/* THE AUTO-FORWARDED DUPLICATE, collapsed. Telegram copies every channel post into the linked
   group, so the daily summary reached the chat page twice. The group copy now carries one
   muted line and a link instead of the whole body (CEO 2026-09-08: "just the fact that it was
   and link to it"). Deliberately quiet: the channel's own post is the one worth reading, and
   it keeps its full styling. */
.forwarded-ref p {
  margin: 0;
  color: var(--muted);
  font-size: .85rem;
  font-style: italic;
}
.forwarded-ref .text-button { font-style: normal; white-space: nowrap; }

/* ---------------------------------------------------------------------------
   THE MEDIA LIGHTBOX - CHROME SHARED BY BOTH RAILS.

   CEO 2026-09-07 asked for a basic tap-to-zoom image modal (theme.js, reader.php only). CEO
   2026-09-09 widened it: "possible enlarge images on website (mising funcionality full size
   modal with zooming and navigational funcions especially for bigger mermaid diagrams)".

   THIS FILE, not archive.css, because style.css is the ONE stylesheet BOTH rails load -
   `public_reader_assets()` links style.css before archive.css on every reader.php/members.php
   response, and every static template links it too (`{{STYLE}}` / `href="style.css"`). Putting
   the shared chrome here means the documentation pages' mermaid diagrams (`.diagram-frame img`,
   styled ABOVE - no <a>, no reader.php involved) get a real modal with zero duplication, instead
   of drifting the way a class defined on only one rail always has on this site.

   PLACED BEFORE THE CONSENT BLOCK BELOW, not after it: that block is BYTE-IDENTICAL to its
   copy in public-reader/archive.css (consent_gate_test.cjs asserts marker-to-end-of-file
   equality in both files), so nothing may follow it in this file. It first landed appended
   AFTER the consent block by mistake, which silently broke that equality - caught by running
   consent_gate_test.cjs, not assumed safe from reading the diff alone.

   TOKENS: --accent/--radius/--space/--ink/--focus/--line are declared in :root above and are
   NOT redeclared here, so this resolves identically outside the reader. Nested inside
   `.archive-reader` (public-reader/archive.css) those same names still resolve from :root -
   archive.css never overrides --accent - so the chrome looks consistent on both rails; only
   --radius picks up the reader's own (more rounded) 14px there, which is intentional per-rail
   texture, not drift.

   FOOTER RESERVE VIA FALLBACK, NOT A SECOND RULE: --footer-h is declared ONLY inside
   .archive-reader (archive.css). `var(--footer-h, 0px)` below resolves to the reader's real
   value there and to 0px everywhere else, so ONE rule correctly reserves the sticky footer
   band on the reader and reserves nothing where there is no such band - no archive.css
   duplicate needed for that part.

   TRANSITIONS NEED NO SEPARATE prefers-reduced-motion RULE: the sitewide one two screens up
   (`*, *::before, *::after { transition-duration: .001ms !important }`) already covers every
   selector below by construction - proven in lightbox_dom_test.cjs rather than assumed. */
.lightbox {
  position: fixed;
  inset: 0;
  z-index: 40;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: calc(var(--space) * 2);
  padding-bottom: calc(var(--space) * 2 + var(--footer-h, 0px));
  background: rgba(0, 0, 0, 0.88);
  overscroll-behavior: contain;
}
html[data-lightbox="open"], html[data-lightbox="open"] body { overflow: hidden; }

.lightbox-figure {
  margin: 0;
  max-width: 100%;
  max-height: 100%;
  overflow: auto;
  /* `none`, not `pinch-zoom` - theme.js runs its OWN two-pointer pinch and single-pointer
     pan (a real touch fires the same pointerdown/move/up a mouse does), and `none` hands the
     browser's touch input to that code with nothing else trying to claim the same gesture.
     `pinch-zoom` on a NESTED scrollable box (not the whole page) is unevenly supported across
     browsers - historically weak/absent in Safari - so this stopped being "the native phone
     case for free" and became a real implementation instead. CDP-driven touch pinch measured
     0 effect on --zoom-scale under `pinch-zoom`; the JS pinch below is what actually moves it. */
  touch-action: none;
  -webkit-overflow-scrolling: touch;
  text-align: center;
}
.lightbox-image, .lightbox-svg {
  display: block;
  margin: 0 auto;
  /* --radius-sm exists only inside .archive-reader (archive.css); this fallback keeps the
     reader's own smaller-corner choice on that rail and uses this file's own --radius (an
     identical 8px) everywhere else - one rule, no per-rail duplicate. */
  border-radius: var(--radius-sm, var(--radius));
  max-width: 100%;
  max-height: calc(100vh - 8rem - var(--footer-h, 0px));
  height: auto;
  width: auto;
  cursor: zoom-in;
  transform: translate(var(--pan-x, 0px), var(--pan-y, 0px)) scale(var(--zoom-scale, 1));
  transform-origin: center center;
  transition: transform .2s ease;
}
.lightbox-image[data-zoom="full"], .lightbox-svg[data-zoom="full"] { cursor: zoom-out; }
.lightbox-image:focus-visible, .lightbox-svg:focus-visible { outline: 2px solid var(--focus); outline-offset: 2px; }

.lightbox-close {
  position: absolute;
  /* Same safe-area convention the sticky footer already uses (env(safe-area-inset-bottom)) -
     a notch or Dynamic Island sits at the TOP on every phone that has one, and this is the
     one control the CEO's mobile criterion named by name as never allowed to hide under it. */
  top: calc(var(--space) + env(safe-area-inset-top, 0px));
  right: calc(var(--space) + env(safe-area-inset-right, 0px));
  width: 44px;
  height: 44px;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 1.6rem;
  line-height: 1;
  color: #fff;
  background: rgba(0, 0, 0, 0.55);
  border: 1px solid rgba(255, 255, 255, 0.35);
  border-radius: 50%;
  cursor: pointer;
}
.lightbox-close:hover { background: rgba(0, 0, 0, 0.8); }
.lightbox-close:focus-visible { outline: 2px solid #fff; outline-offset: 2px; }

/* THE TOOLBAR - zoom + navigation, one row, BELOW the image so it never competes with the
   close button's top-right corner. Deliberately absolute-positioned off the figure's own flow
   (like .lightbox-close already was) so a tall zoomed image cannot push it off screen. */
.lightbox-toolbar {
  position: absolute;
  left: 50%;
  bottom: calc(var(--space) * 1.5 + var(--footer-h, 0px) + env(safe-area-inset-bottom, 0px));
  transform: translateX(-50%);
  display: flex;
  align-items: center;
  gap: var(--space);
  padding: 6px 10px;
  border-radius: 999px;
  background: rgba(0, 0, 0, 0.55);
  border: 1px solid rgba(255, 255, 255, 0.35);
}
.lightbox-toolbar button {
  /* 44px, not a smaller glyph box: the CEO's mobile acceptance criterion is explicit (~44x44
     CSS px, thumb-reachable) - the same floor .lightbox-close already used. Five of these
     (prev/zoom-out/position/zoom-in/next) plus the toolbar's own gaps and padding still total
     well under 375px, MEASURED with the responsive check below. */
  width: 44px;
  height: 44px;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 1.1rem;
  line-height: 1;
  color: #fff;
  background: transparent;
  border: none;
  border-radius: 50%;
  cursor: pointer;
}
.lightbox-toolbar button:hover { background: rgba(255, 255, 255, 0.18); }
.lightbox-toolbar button:focus-visible { outline: 2px solid #fff; outline-offset: 2px; }
.lightbox-position {
  min-width: 3.5rem;
  text-align: center;
  color: rgba(255, 255, 255, 0.92);
  font-size: 0.8rem;
  font-variant-numeric: tabular-nums;
}

/* The note sits UNDER the image, as asked (reader.php only - a diagram carries no such note,
   and public_reader_media_note() emits nothing when no description is stored, so this element
   simply never exists on the other cases). Light-on-dark deliberately: this caption sits on a
   near-black scrim in BOTH themes, so the page's normal --muted token would be unreadable in
   light mode. */
.lightbox-note {
  margin: var(--space) auto 0;
  max-width: var(--measure);
  text-align: left;
  color: rgba(255, 255, 255, 0.92);
}
.lightbox-note .ai-note { color: rgba(255, 255, 255, 0.92); margin-top: 0; }
.lightbox-note .ai-badge {
  color: rgba(255, 255, 255, 0.92);
  background: rgba(255, 255, 255, 0.12);
  border-color: rgba(255, 255, 255, 0.35);
}
.lightbox-note .ai-prov { color: rgba(255, 255, 255, 0.7); }

/* max-width for a narrow (portrait) phone, OR max-height for a SHORT one (a phone rotated to
   landscape is wide but short - the dimension that runs out of room is height, not width, and
   a width-only breakpoint never fires there at all). MEASURED 2026-09-09 at 812x375 (a common
   landscape phone size) without the max-height arm: the fit-sized diagram's own bottom edge
   sat at y=311 while the toolbar started at y=305 - a real 6px overlap, not a hypothetical
   one, caught by boundingBox() intersection, not by eyeballing a screenshot. */
@media (max-width: 32rem), (max-height: 30rem) {
  .lightbox { padding: var(--space); padding-bottom: calc(var(--space) + var(--footer-h, 0px)); }
  .lightbox-close { top: calc(var(--space) + env(safe-area-inset-top, 0px)); right: calc(var(--space) + env(safe-area-inset-right, 0px)); }
  /* Five 44px buttons + gaps + padding still total ~260px, comfortably under a 375px
     viewport - MEASURED below, not assumed. */
  .lightbox-toolbar { bottom: calc(var(--space) + var(--footer-h, 0px) + env(safe-area-inset-bottom, 0px)); gap: 6px; padding: 6px 8px; }
  .lightbox-image, .lightbox-svg { max-height: calc(100vh - 10rem - var(--footer-h, 0px)); }
}

/* THE UPCOMING-EVENT CARD (event_card.py; CEO 2026-10-21, VVGO #4). ONE element, two shapes:
   below 1024px a one-line bar right under the sticky header (in flow, it scrolls away); from
   1024px a card in its own right-hand column. The column is REAL space, not an overlay: main and
   the card are centred as a pair, and main gives up the card's width only when the viewport has
   less than 1040px + card to spare. Gated on :has() so a browser that cannot reserve the column
   keeps the bar instead of painting the card over the text. Everything is removed by theme.js at
   the event's start, and :has() then gives main its full width back. Same file for both
   renderers: the PHP reader loads /style.css too. */
.event-card { --event-w: 15rem; --event-gap: 24px; }
.event-card a { text-decoration: none; }
.event-card-bar {
  display: flex; align-items: center; justify-content: center; gap: 10px;
  padding: 7px 16px; background: var(--surface); border-bottom: 1px solid var(--line);
  color: var(--ink); font-size: .875rem; line-height: 1.3; white-space: nowrap;
}
.event-card-bar-text { min-width: 0; overflow: hidden; text-overflow: ellipsis; }
/* The teaser line is longer and carries no button, so on a narrow phone it wraps to two centred
   lines instead of cutting off "vieta bus paskelbta". */
.event-card[data-event-kind="teaser"] .event-card-bar { white-space: normal; text-align: center; }
.event-card-bar b { color: var(--home-ink); font-weight: 700; }
.event-card-bar-cta {
  flex: none; padding: 3px 10px; border-radius: 999px;
  background: var(--join-bg); color: var(--join-ink); font-weight: 700;
}
.event-card-bar:hover .event-card-bar-cta { background: var(--join-bg-hover); }
.event-card-panel { display: none; }
/* THE PHONE BAR TUCKS AWAY ON SCROLL DOWN (theme.js toggles .event-card-tucked). It is STICKY
   right under the sticky header, so its slot in the flow never changes - the hide is a transform,
   never a height or display change, which is what keeps it free of layout shift. The header
   (z-index 5) paints over it while it slides. --header-h is the reader's declared bar height;
   the static header measures 52px. */
@media (max-width: 1023.98px) {
  .event-card {
    position: sticky; z-index: 3; top: var(--header-h, 52px);
    transition: transform .2s ease-out;
  }
  .event-card.event-card-tucked { transform: translateY(calc(-100% - var(--header-h, 52px))); }
}
@media (prefers-reduced-motion: reduce) {
  .event-card { transition: none; }
}
@media (min-width: 1024px) {
  @supports selector(body:has(.event-card)) {
    .event-card {
      position: fixed; z-index: 4; top: 68px; width: var(--event-w);
      right: max(16px, calc((100vw - 1040px - var(--event-w) - var(--event-gap)) / 2));
      max-height: calc(100vh - 68px - var(--footer-h, 64px) - 16px); overflow-y: auto;
    }
    body:has(.event-card:not([hidden])) main {
      margin-inline: max(16px, calc((100vw - 1040px - 15rem - 24px) / 2)) auto;
      max-width: min(1040px, calc(100vw - 32px - 15rem - 24px));
    }
    .event-card-bar { display: none; }
    .event-card-panel {
      display: block; padding: 12px; background: var(--surface);
      border: 1px solid var(--line); border-radius: var(--radius);
    }
  }
}
/* The designed thumbnail is a small POSTER, so its colours are fixed rather than themed - it
   reads the same in day and night mode, like the logo it carries. The image slot (card.json
   "image") replaces it with a same-origin picture. */
.event-card-thumb {
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 4px;
  aspect-ratio: 16 / 10; margin-bottom: 10px; border-radius: 6px;
  background: #0e1621; border: 1px solid #2a3a49; color: #f2f5f7; text-align: center;
}
.event-card-thumb img { width: 44px; height: 44px; }
.event-card-thumb-date { font-size: 1.375rem; font-weight: 800; letter-spacing: .02em; color: #d9a327; }
.event-card-thumb-place { font-size: .8125rem; font-weight: 600; }
/* The picture FILLS the 16:10 slot. `.event-card-thumb img` above (the 44px logo) outranks a bare
   `.event-card-img`, which once drew the cover as a 44px square; hence the two-class selector. The
   square PreVid cover keeps its title box (33-80% of its height) inside a 16:10 crop at 60%. */
.event-card-thumb .event-card-img {
  display: block; width: 100%; height: 100%; object-fit: cover; object-position: 50% 60%; border-radius: 6px;
}
.event-card-kicker {
  margin: 0 0 2px; font-size: .75rem; font-weight: 600; letter-spacing: .05em;
  text-transform: uppercase; color: var(--muted);
}
.event-card-title { margin: 0 0 6px; font-size: 1rem; font-weight: 700; line-height: 1.3; }
.event-card-title a { color: var(--ink); }
.event-card-title a:hover { text-decoration: underline; }
.event-card-meta { margin: 0 0 10px; font-size: .8125rem; line-height: 1.45; color: var(--muted); }
.event-card-meta time { color: var(--ink); font-weight: 600; }
/* The door times: latecomers are not let in, so this is the one line on the card a reader must
   not miss - the gold the date already wears, on the surface it already sits on (7.15:1). */
.event-card-note { margin: -4px 0 10px; font-size: .8125rem; font-weight: 600; line-height: 1.4; color: var(--home-ink); }
.event-card-cta {
  display: block; padding: 8px 12px; border-radius: var(--radius); text-align: center;
  background: var(--join-bg); color: var(--join-ink); font-weight: 700;
}
.event-card-cta:hover { background: var(--join-bg-hover); }
/* The series teaser: a plan, not a bookable event - a summary and a plain text link to the
   events page, never the green registration button. */
.event-card-summary { margin: -4px 0 10px; font-size: .8125rem; line-height: 1.45; color: var(--muted); }
.event-card-more { font-size: .875rem; font-weight: 600; color: var(--accent); }
.event-card-more:hover { text-decoration: underline; }
.event-card-qr {
  display: flex; align-items: center; gap: 10px; margin-top: 12px; padding-top: 10px;
  border-top: 1px solid var(--line-soft); font-size: .8125rem; color: var(--muted);
}
.event-card-qr svg { flex: none; width: 88px; height: 88px; border-radius: 4px; }

/* RENGINIAI IN THE NAV, EMPHASISED (CEO 2026-10-21: "people ignore banners"). Its own colour,
   distinct from Žinutės' gold/green and the accent blue, bold in both themes. MEASURED (WCAG 2.x):
   dark #ff8a3d on the page #0e1621 = 7.75:1, on --surface 6.95:1; light #b43c0a on #f4f7f9 =
   5.43:1, on #fff 5.85:1. Both renderers emit the same class (reader.php mirrors SECTIONS). */
:root { --events-ink: #ff8a3d; }
html[data-theme="light"] { --events-ink: #b43c0a; }
.view-tabs a.view-link--events, .view-tabs a.view-link--events:hover { color: var(--events-ink); font-weight: 700; }
/* SIX TABS STILL ONE ROW ON A PHONE. MEASURED with Renginiai added: the six labels are 316px at
   13px, so the 14px gap wrapped LLM & MCP onto a second line at 375 (351px bar). 6px gaps fit
   375 (346px); below 370px the labels drop to 12px (292px + 30px fits 360). At 320 the row wraps,
   as five tabs already did before the 2026-09-24 rename note above. */
/* RENGINIAI RIGHT AFTER ŽINUTĖS BELOW 1024px (CEO 2026-10-21: on a phone "Renginiai must be visible
   without scrolling or be the first item after Žinutės"). The reader puts the tabs in a horizontal
   scroller inside the 48px header; at 320-414px the six labels overflow it, and Renginiai - last in
   the DOM, as the CEO ordered for desktop - would sit past the right edge. A flex `order` moves it
   on narrow screens only, so the desktop row keeps his order and no markup differs per width. */
@media (max-width: 1023.98px) {
  .view-tabs .tab-group, .view-tabs a.view-link--home { order: -2; }
  .view-tabs a.view-link--events { order: -1; }
}
@media (max-width: 400px) { .view-tabs, .view-tabs .tab-group { gap: 6px; } }
@media (max-width: 370px) { .view-tabs a { font-size: .75rem; } }
.events-next a, .events-list a { text-decoration: none; }
.events-next-title a:hover, .events-list a:hover { text-decoration: underline; }

/* ---- /renginiai/ -------------------------------------------------------------------------- */
.events-page main h2 { margin: calc(var(--space) * 3) 0 var(--space); font-size: 1.125rem; }
.events-next-grid { display: grid; gap: 20px; grid-template-columns: minmax(0, 1fr); }
@media (min-width: 760px) { .events-next-grid { grid-template-columns: minmax(0, 15rem) minmax(0, 1fr); align-items: start; } }
.events-next-media .event-card-thumb { max-width: 15rem; }
.events-next-media .event-card-thumb-date { font-size: 1.75rem; }
.events-next-title { margin: 0 0 6px; font-size: 1.375rem; line-height: 1.25; }
.events-next-title a { color: var(--ink); text-decoration: none; }
.events-next-when { margin: 0 0 2px; font-weight: 700; }
.events-next-where { margin: 0 0 10px; color: var(--muted); }
.events-next-summary { margin: 0 0 14px; }
.events-next-cta { display: inline-block; min-width: 12rem; }
.events-list { list-style: none; margin: 0 0 var(--space); padding: 0; }
.events-list li { padding: 10px 0; border-bottom: 1px solid var(--line-soft); }
.events-list li b { color: var(--ink); }
.events-row-meta { font-size: .8125rem; color: var(--muted); }
/* Who runs it, at a glance: our own group in the Renginiai colour, partners and other
   organisers in plain muted outline - the page lists everyone, the badge says whose it is. */
.events-badge {
  display: inline-block; margin-left: 6px; padding: 0 8px; border: 1px solid var(--line);
  border-radius: 999px; font-size: .6875rem; font-weight: 700; line-height: 1.6; color: var(--muted);
  vertical-align: 1px;
}
.events-badge--ours { border-color: var(--events-ink); color: var(--events-ink); }
.events-also { margin: 10px 0 0; }

/* ---- the click-to-load video poster and its 9:16 lightbox ----------------------------------
   Nothing from YouTube is in the page until the click (theme.js builds the iframe then); the
   poster is our own same-origin cover. The notice under the button says what the click loads. */
.video-facade { margin: 0 0 10px; }
.video-facade-play {
  position: relative; display: block; width: 100%; padding: 0; border: 0; border-radius: 8px;
  overflow: hidden; background: #000; cursor: pointer; aspect-ratio: 9 / 16;
}
.events-next-media .video-facade-play { max-width: 15rem; }
.video-facade-cover { display: block; width: 100%; height: 100%; object-fit: cover; }
/* Low, like aimastermind.lt's .aim-yt__play: the invite poster carries its title mid-frame. */
.video-facade-icon {
  position: absolute; left: 0; right: 0; bottom: 8%; margin: 0 auto; width: 64px; height: 64px; border-radius: 50%;
  display: flex; align-items: center; justify-content: center;
  background: rgba(0, 0, 0, .62); color: #fff; border: 2px solid #fff;
}
.video-facade-play:hover .video-facade-icon, .video-facade-play:focus-visible .video-facade-icon { background: #0077b5; }
.video-facade-notice { margin: 6px 0 0; font-size: .75rem; line-height: 1.4; color: var(--muted); }
.video-lightbox {
  position: fixed; inset: 0; z-index: 50; display: flex; align-items: center; justify-content: center;
  padding: 16px; background: rgba(0, 0, 0, .86);
}
.video-lightbox-box { position: relative; height: min(88vh, calc((100vw - 32px) * 16 / 9)); aspect-ratio: 9 / 16; }
.video-lightbox-box iframe { display: block; width: 100%; height: 100%; border: 0; border-radius: 10px; background: #000; }
.video-lightbox-close {
  position: absolute; top: -12px; right: -12px; width: 40px; height: 40px; padding: 0;
  border: 2px solid #fff; border-radius: 50%; background: #111; color: #fff; font-size: 1.5rem; line-height: 1;
}
html.video-lightbox-open, html.video-lightbox-open body { overflow: hidden; }
@media (prefers-reduced-motion: no-preference) {
  .video-lightbox { animation: video-lightbox-in .18s ease-out; }
  @keyframes video-lightbox-in { from { opacity: 0; } to { opacity: 1; } }
}

/* ---- CONSENT BANNER + PRIVACY-AND-AI SETTINGS PANEL -------------------------------------
   AIM-CONSENT-UI v1 - this block is BYTE-IDENTICAL in web/style.css and
   web/public-reader/archive.css, and web/consent_gate_test.cjs (section 14) fails if the two ever
   differ. It compares from the marker above to the END of the file, so this block stays LAST:
   a new rule goes above it, never after it (an appended rule turned that test red, 2026-09-23).
   Two stylesheets because the site has two renderers and a page loads only one of them; there
   is no shared build step that could generate this from one source, so the duplication is
   deliberate and pinned rather than accidental.

   IT WAS MISSING ENTIRELY. Measured 2026-09-08: `grep -c consent` returned 0 in BOTH files
   while consent.js had been shipping a banner with class names since it landed. Nothing would
   have rendered wrong today - the gate is inert without an analytics config, so the banner has
   never been built on a live page - but the first time analytics was switched on, the consent
   dialog would have appeared as unstyled black-on-white text at the end of the document. A UI
   that only ever runs after a future config change is a UI nobody has ever looked at.

   POSITIONED FROM THE BAND, NOT FROM THE VIEWPORT. Both surfaces pin the footer to the bottom
   (`position: fixed` in archive.css, `position: sticky` in style.css) and both of those make it
   a containing block, so `bottom: 100%` puts these panels exactly on its top edge at any band
   height. The alternative - a fixed element with a hardcoded offset - is the bug this same
   change is fixing for the join button, which sat 6px on top of the footer links because the
   height it was positioned from had gone stale twice. */
.consent-banner,
.consent-settings {
  /* Mounted INSIDE the inverted .site-footer, painted in the PAGE colours: re-point the footer's
     link and focus tokens here, or `.site-footer a` paints the light-band blue on this dark panel
     (measured 2.27:1 live 2026-10-23; footer_inversion_test.cjs pins it). */
  --footer-link: var(--accent);
  --footer-focus: var(--accent);
  position: absolute;
  bottom: 100%;
  inset-inline: 0;
  z-index: 20;
  box-sizing: border-box;
  margin-bottom: 8px;
  /* A long panel must never grow taller than the screen and strand its own close button. */
  max-height: 70vh;
  overflow-y: auto;
  padding: 14px 16px;
  background: var(--surface);
  color: var(--ink);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  font-size: 0.82rem;
  line-height: 1.5;
  /* The footer centres its links; this is prose and reads left-aligned. */
  text-align: start;
}
.consent-banner__text,
.consent-settings__text { margin: 0 0 10px; color: var(--ink); }
.consent-settings__title { margin: 0 0 10px; font-size: 0.95rem; }
.consent-settings__section { margin: 0 0 14px; }
.consent-settings__heading { margin: 0 0 4px; font-size: 0.85rem; }
.consent-banner__actions,
.consent-settings__actions { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 10px; }
/* EQUAL WEIGHT IS THE WHOLE POINT, so accept and reject share ONE rule and there is no
   modifier class that could ever make one of them quieter. A reject button styled as a faint
   text link is the dark pattern EDPB guidance names, and the way to make that impossible is to
   leave nowhere to express it. consent_gate_test.cjs asserts the two nodes have identical
   tagName and className; this is the stylesheet half of the same guarantee.
   flex-basis 0, not auto: with auto the longer label won the wider box (MEASURED 2026-10-23 at
   390: Sutinku 165px, Nesutinku 183px). Basis 0 splits the row evenly whatever the words are. */
.consent-banner__button {
  flex: 1 1 0;
  min-height: 40px;
  padding: 8px 16px;
  border: 1px solid var(--accent);
  border-radius: var(--radius);
  background: var(--accent);
  color: #062033;
  font: inherit;
  font-weight: 600;
  cursor: pointer;
}
.consent-banner__button:focus-visible,
.consent-banner__more > summary:focus-visible,
.consent-settings__close:focus-visible { outline: 3px solid var(--focus, var(--accent)); outline-offset: 2px; }
.consent-banner__link { color: var(--accent); font-size: 0.78rem; }
/* ON ITS OWN LINE INSIDE THE PANEL. Left inline, the link and the close button are two
   independently-sized boxes competing for one row, and MEASURED at 1920 the button painted over
   the tail of "Visas privatumo pranesimas" - the reader saw a clipped word, not a link. The
   banner keeps the inline form because it has no close button to collide with. */
.consent-settings .consent-banner__link { display: block; margin: 0 0 10px; }
.consent-settings__close {
  min-height: 40px;
  padding: 8px 16px;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: transparent;
  color: var(--ink);
  font: inherit;
  cursor: pointer;
}
/* STAND THE FLOATING CHROME DOWN WHILE A PANEL IS OPEN. The join button and the jump controls
   rest on the same band, so without this the panel would open on top of them - moving the
   overlap up a layer instead of removing it. consent.js sets this class from what is actually
   mounted in the DOM, never from a flag. */
.site-footer.consent-open .join-sticky,
.site-footer.consent-open .jump { display: none; }

/* THE BANNER IS A SHORT FIRST LAYER, NOT A WALL. MEASURED live 2026-10-23 at 390x844 on
   /bendra/chat/: banner 248px + footer band 109px = 365px, 43.2% of the first screen, in front of
   the archive the visitor came for. Layout only - consent.js keeps every word (section 15 of
   consent_gate_test.cjs joins the two layers back into the exact text):
   - text at 14px (0.875rem), tighter padding and line-height;
   - "Daugiau" and the notice link share ONE row while the details are closed; opened, the details
     take the full width and the link drops below;
   - the two buttons always side by side and equal (flex-basis 0 above);
   - while a consent surface is open the footer band shows only the "Privatumo ir DI nustatymai"
     link (the AI Act Art. 50 disclosure) and drops its height floor; the rest of the band is one
     choice away and the banner carries its own privacy link.
   consent_gate_test.cjs section 16 measures it in chromium: <= 25% of the screen at 390x844 and
   1280x800, text >= 14px, buttons identical in size and paint, the settings link on screen. */
.consent-banner {
  display: grid;
  grid-template-columns: auto 1fr;
  align-items: baseline;
  gap: 6px 16px;
  margin-bottom: 4px;
  padding: 10px 12px;
  font-size: 0.875rem;
  line-height: 1.4;
}
.consent-banner > .consent-banner__text,
.consent-banner > .consent-banner__actions,
.consent-banner > .consent-banner__more[open] { grid-column: 1 / -1; }
.consent-banner > .consent-banner__text,
.consent-banner > .consent-banner__actions { margin: 0; }
.consent-banner > .consent-banner__actions { margin-top: 4px; }
.consent-banner > .consent-banner__link { font-size: inherit; justify-self: start; }
/* style.css's global `summary` rule is .75rem (12px); the consent layer stays at the banner's 14px,
   and 3px of padding keeps the toggle a 24px-tall target (WCAG 2.5.8). */
.consent-banner__more > summary { color: var(--accent); font-size: inherit; font-weight: 600; padding: 3px 0; cursor: pointer; }
.consent-banner__detail { margin: 6px 0 0; color: var(--ink); }
.site-footer.consent-open { min-height: 0; }
.site-footer.consent-open > :not(.consent-banner):not(.consent-settings):not([data-consent-manage]) { display: none; }
/* Wide screens: the choice sits to the RIGHT of the text instead of under it. */
@media (min-width: 48rem) {
  .consent-banner { grid-template-columns: auto 1fr auto; }
  .consent-banner > .consent-banner__text,
  .consent-banner > .consent-banner__more[open] { grid-column: 1 / 3; }
  .consent-banner > .consent-banner__actions { grid-column: 3; grid-row: 1 / span 2; align-self: center; margin: 0; }
  .consent-banner .consent-banner__button { min-width: 9rem; }
}
