/* Shared marketing styles. Everything resolves to tokens.css; this file adds
   layout for the pages the generator produces and nothing else. */

/* --- nav ------------------------------------------------------------------ */

/* One nav element, two layouts.

   This used to be `.nav { display: none }` below 62rem with a `.navstrip`
   waiting to take over underneath. The strip was styled but never rendered by
   build.py or index.html, so for every visitor on a phone the masthead was a
   wordmark and nothing else: six pages with no way to reach them.

   Fixed by wrapping rather than duplicating. Below the breakpoint the nav takes
   a row of its own under the masthead and scrolls sideways; above it, it sits
   between the wordmark and the controls. Two copies of the links would have
   been the smaller diff and would have put every page in the accessibility tree
   twice, or behind an aria-hidden that leaves them focusable but unannounced. */
.nav {
  display: flex;
  gap: var(--s-4);
  order: 3;
  flex: 1 0 100%;
  overflow-x: auto;
  overscroll-behavior-x: contain;
  padding-bottom: var(--s-2);
  margin-top: calc(var(--s-2) * -1);
  scrollbar-width: none;
  /* The strip is a scroll container, so the focus ring on the last link needs
     somewhere to land or it is clipped by the overflow. */
  scroll-padding-inline: var(--s-2);
}
.nav::-webkit-scrollbar { display: none; }
.nav .nav__link { white-space: nowrap; }

@media (min-width: 62rem) {
  .nav {
    order: 0;
    flex: 0 1 auto;
    gap: var(--s-5);
    margin-inline: auto;
    margin-top: 0;
    padding-bottom: 0;
    overflow: visible;
  }
}

.nav__link {
  font-size: 0.92rem;
  color: var(--ink-muted);
  text-decoration: none;
  /* 44px of vertical target on touch, from a 0.92rem line box plus this
     padding. Under the old value the links were 30px high, which fails the
     WCAG 2.5.8 minimum and is the single most-missed touch rule. */
  padding-block: var(--s-3);
  border-bottom: 2px solid transparent;
  transition: color var(--t-fast) var(--ease), border-color var(--t-fast) var(--ease);
}
.nav__link:hover { color: var(--ink); border-bottom-color: var(--rule-strong); }
.nav__link--on { color: var(--ink); border-bottom-color: var(--mark); }

@media (min-width: 62rem) {
  .nav__link { padding-block: var(--s-2); }
}

/* --- generic page --------------------------------------------------------- */

.page { padding-block: var(--s-8) var(--s-5); max-width: 58rem; }

.page__title {
  margin-top: var(--s-5);
  font-family: var(--f-display);
  font-weight: var(--w-display);
  font-size: clamp(2rem, 1.3rem + 3.2vw, 3.8rem);
  line-height: 1.07;
  letter-spacing: -0.02em;
  text-wrap: balance;
}

.page__deck {
  margin-top: var(--s-5);
  max-width: 44rem;
  font-size: clamp(1.02rem, 0.98rem + 0.3vw, 1.2rem);
  line-height: 1.65;
  color: var(--ink-secondary);
  text-wrap: pretty;
}

.page__body { padding-bottom: var(--s-9); max-width: 58rem; }

/* --- prose ---------------------------------------------------------------- */

.page__body p, .prose__body p {
  margin-top: var(--s-4);
  max-width: 40rem;
  line-height: 1.72;
  color: var(--ink-secondary);
}
.page__body ul, .page__body ol,
.prose__body ul, .prose__body ol {
  margin-top: var(--s-4);
  max-width: 40rem;
  padding-left: var(--s-5);
  color: var(--ink-secondary);
  line-height: 1.68;
}
.page__body li, .prose__body li { margin-top: var(--s-2); }
.page__body strong, .prose__body strong { color: var(--ink); font-weight: var(--w-strong); }
.page__body a, .prose__body a { color: var(--accent); text-underline-offset: 2px; }

/* An anchor styled as a button keeps the button's ink.

   The rule above is (0,1,1) and .btn in styles.css is (0,1,0), so inside a page
   body the link colour won and every <a class="btn"> got color: var(--accent) on
   background: var(--accent). Same value, so the label was invisible: a solid
   teal rectangle with nothing written on it.

   Seventeen pages carried one, including the order page, the quote page and
   every page in the account area, which means the primary action on each of them
   was a blank block. Nothing failed and no test caught it, because the element
   was present, focusable, clickable and correctly labelled for a screen reader.
   Only a person looking at it could tell. */
.page__body a.btn, .prose__body a.btn { color: var(--accent-ink); }

.prose__h2 {
  margin-top: var(--s-7);
  font-family: var(--f-display);
  font-weight: var(--w-display);
  font-size: clamp(1.4rem, 1.2rem + 0.8vw, 1.9rem);
  line-height: 1.2;
  letter-spacing: -0.012em;
  color: var(--ink);
}
.prose__h3 {
  margin-top: var(--s-6);
  font-size: 1.06rem;
  font-weight: var(--w-strong);
  color: var(--ink);
}
.prose__h4 { margin-top: var(--s-5); font-size: 0.96rem; font-weight: var(--w-strong); }

.prose__rule { margin-top: var(--s-6); border: 0; border-top: var(--rule-w) solid var(--rule); }

/* A short ochre rule above rather than a full-height stripe down the left.
   A coloured 2px left border on a block is a measured tell; the same mark set
   as a rule above says the same thing and matches the eyebrow. */
blockquote {
  margin-top: var(--s-6);
  padding-top: var(--s-4);
  max-width: 40rem;
  position: relative;
}
blockquote::before {
  content: '';
  position: absolute;
  top: 0;
  left: 0;
  width: var(--s-6);
  height: 2px;
  background: var(--mark);
}
blockquote p { color: var(--ink-muted); font-size: 0.96rem; }

code {
  font-family: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
  font-size: 0.88em;
  background: var(--surface-sunk);
  padding: 0.1em 0.3em;
  border-radius: var(--r-sm);
}

/* --- article -------------------------------------------------------------- */

.prose { padding-block: var(--s-8) var(--s-9); max-width: 52rem; }

.prose__title {
  margin-top: var(--s-5);
  font-family: var(--f-display);
  font-weight: var(--w-display);
  font-size: clamp(1.9rem, 1.3rem + 2.8vw, 3.4rem);
  line-height: 1.08;
  letter-spacing: -0.02em;
  text-wrap: balance;
}

.prose__meta {
  margin-top: var(--s-4);
  font-size: 0.84rem;
  color: var(--ink-faint);
  font-variant-numeric: tabular-nums;
}

.prose__lede {
  margin-top: var(--s-5);
  padding-bottom: var(--s-5);
  border-bottom: var(--rule-w) solid var(--rule);
  max-width: 40rem;
  font-size: 1.12rem;
  line-height: 1.6;
  color: var(--ink);
}

.prose__back { margin-top: var(--s-8); padding-top: var(--s-4);
               border-top: var(--rule-w) solid var(--rule); font-size: 0.9rem; }

/* --- cards ---------------------------------------------------------------- */

.cards { margin-top: var(--s-6); padding: 0; list-style: none;
         border-top: 2px solid var(--ink); }

.card { border-bottom: var(--rule-w) solid var(--rule); }

.card__link {
  display: block;
  padding-block: var(--s-5);
  text-decoration: none;
  color: inherit;
  transition: background var(--t-fast) var(--ease);
}
.card__link:hover { background: var(--surface-sunk); }
.card__link:hover .card__title { text-decoration: underline;
                                 text-decoration-color: var(--accent);
                                 text-underline-offset: 4px; }

.card__meta { font-size: 0.78rem; color: var(--ink-faint); font-variant-numeric: tabular-nums; }

.card__title {
  margin-top: var(--s-2);
  font-family: var(--f-display);
  font-weight: var(--w-display);
  font-size: clamp(1.2rem, 1.05rem + 0.7vw, 1.6rem);
  line-height: 1.2;
  letter-spacing: -0.012em;
}

.card__lede { margin-top: var(--s-2); max-width: 46rem; color: var(--ink-secondary);
              font-size: 0.96rem; line-height: 1.6; }

/* --- pricing tiers -------------------------------------------------------- */

.tiers { margin-top: var(--s-6); display: grid; gap: var(--s-5); }
@media (min-width: 54rem) { .tiers { grid-template-columns: repeat(3, 1fr); align-items: start; } }

.tier { border-top: 2px solid var(--ink); padding-top: var(--s-4); }
.tier--mark { border-top-color: var(--accent); }

.tier__name { font-weight: var(--w-strong); font-size: 1.02rem; }
.tier__for { margin-top: 0.15rem; font-size: 0.82rem; color: var(--ink-faint); }
.tier__price {
  margin-top: var(--s-4);
  font-family: var(--f-display);
  font-weight: var(--w-display);
  font-size: 1.7rem;
  font-variant-numeric: tabular-nums;
  line-height: 1.1;
}
.tier__unit { font-family: var(--f-text); font-size: 0.8rem; color: var(--ink-muted); }
.tier__list { margin-top: var(--s-4); padding-left: var(--s-4); font-size: 0.92rem;
              color: var(--ink-secondary); line-height: 1.6; }
.tier__list li { margin-top: var(--s-2); }
.tier__note { margin-top: var(--s-3); font-size: 0.82rem; color: var(--ink-faint); }

/* --- section illustrations ------------------------------------------------ */

/* Drawn, not sourced. Stock photography and unDraw-style figures are measured
   tells and the design gate rejects them, so density comes from real diagrams
   built out of the same hairlines and tabular numbers as the rest of the site.

   Everything below resolves to a token. An SVG that hardcodes a hex is an SVG
   that stays light when the page goes dark, which is the whole reason these are
   inline markup rather than image files. */

.illus-band { padding-block: var(--s-6) var(--s-7); border-top: var(--rule-w) solid var(--rule); }
.illus-band--tight { padding-block: var(--s-5) var(--s-6); }
.illus { margin: 0; }
.illus__svg { display: block; width: 100%; height: auto; max-width: 76rem;
              margin-inline: auto; overflow: visible; }
.illus__cap { font-size: 0.78rem; font-weight: var(--w-strong); letter-spacing: 0.02em;
              color: var(--ink-muted); margin-bottom: var(--s-4); }
.page--cont { padding-block: var(--s-5) var(--s-8); }

/* Wide composition by default, narrow twin below the breakpoint. The figures
   used to be hidden outright here; a diagram nobody on a phone can see is a
   diagram that does not exist for most of the traffic. */
/* Wide by default, the column composition below the breakpoint. Every one of
   these figures is authored at 1240 units; in a phone's content column that is
   a scale of about 0.28, at which the labels are gone rather than small. */
.illus--flow-narrow, .illus--market-narrow, .illus--stack-narrow,
.illus--levels-narrow, .illus--payment-narrow,
.illus--rfq-narrow, .illus--assistant-narrow, .illus--library-narrow { display: none; }

@media (max-width: 52rem) {
  .illus--flow, .illus--market, .illus--stack, .illus--levels,
  .illus--payment, .illus--rfq, .illus--assistant,
  .illus--library { display: none; }
  .illus--flow-narrow, .illus--market-narrow, .illus--stack-narrow,
  .illus--levels-narrow, .illus--payment-narrow,
  .illus--rfq-narrow, .illus--assistant-narrow, .illus--library-narrow { display: block; }
  .illus__svg { max-width: 30rem; }

  /* The narrow viewBox is 420 units into roughly 330 CSS pixels, so type set
     for the wide figure lands about a fifth smaller than it reads here. These
     are the sizes measured back up to the same optical size as the body text
     beside them, not a guess. Text inside an SVG does not reflow, so getting
     this wrong is not a small mistake: it is unreadable rather than tight. */
  .illus--flow-narrow .fl__n { font-size: 20px; }
  .illus--flow-narrow .fl__s { font-size: 15px; }
  .illus--flow-narrow .fl__i { font-size: 13px; }
  .illus--flow-narrow .fl__lbl { font-size: 14px; }
}

/* Shared marks. Stroke weights vary on purpose: a drawing where every line is
   the same width reads as machine output, and the eye uses the difference to
   sort structure from content. */
.illus svg text { font-family: var(--f-text); }
.fl__box, .st__box, .lb__box {
  fill: var(--surface-raised); stroke: var(--rule); stroke-width: 1.5;
}
.fl__rule, .lb__rule { stroke: var(--rule-strong); stroke-width: 1.5; stroke-linecap: round; }
.fl__rule--faint, .lb__rule { stroke: var(--rule); }
.illus__hatch, .lb__hatch { stroke: var(--rule); stroke-width: 1; }

/* flow */
.fl__n { font-size: 17px; font-weight: 620; fill: var(--ink); }
.fl__s { font-size: 12.5px; fill: var(--ink-muted); }
.fl__i { font-size: 11.5px; font-weight: 620; fill: var(--mark); letter-spacing: 0.08em;
         font-variant-numeric: tabular-nums; }
.fl__lbl { font-size: 12px; fill: var(--ink-muted); }
.fl__lbl--mark { fill: var(--mark); font-weight: 620; }
.fl__hash { font-size: 10.5px; fill: var(--ink-faint); font-variant-numeric: tabular-nums; }
.fl__num { font-size: 11px; fill: var(--ink-faint); font-variant-numeric: tabular-nums; }
.fl__caret { fill: var(--mark); }
.fl__dot { fill: var(--rule-strong); }
.fl__dot--keep { fill: var(--accent); }
.fl__scan { stroke: var(--accent); stroke-width: 1.5; }
.fl__doc { fill: var(--surface-sunk); stroke: var(--rule); stroke-width: 1; }
.fl__tick { fill: none; stroke: var(--accent); stroke-width: 1.8;
            stroke-linecap: round; stroke-linejoin: round; }
.fl__tick--big { stroke-width: 2.4; }
.fl__bar { fill: var(--accent); }
.fl__axis { stroke: var(--ink); stroke-width: 1.5; }
.fl__seal { fill: none; stroke: var(--accent); stroke-width: 2; stroke-linejoin: round; }
.fl__arrow line, .fl__arrow path { stroke: var(--rule-strong); stroke-width: 1.5; fill: none;
                                   stroke-linecap: round; stroke-linejoin: round; }
.fl__spine { stroke: var(--rule-strong); stroke-width: 1.5; }
.fl__lane { stroke-width: 1; }
.fl__lane--ai { stroke: var(--rule-strong); stroke-dasharray: 3 4; }
.fl__lane--be { stroke: var(--ink); }
.fl__mk--ai { fill: var(--surface); stroke: var(--rule-strong); stroke-width: 1.5; }
.fl__mk--be { fill: var(--ink); }
.fl__mk--decide { fill: var(--mark); }

/* market band */
.mk__v { font-family: var(--f-display); font-size: 42px; fill: var(--ink);
         font-variant-numeric: tabular-nums; }
.mk__u { font-family: var(--f-text); font-size: 18px; fill: var(--ink-muted); }
.mk__l { font-size: 13.5px; fill: var(--ink-secondary); }
.mk__s { font-size: 11.5px; fill: var(--ink-faint); }
.mk__div { stroke: var(--rule); stroke-width: 1; }
.mk__base { stroke-width: 2.5; }

/* pricing stack */
.st__n { font-size: 16px; font-weight: 620; fill: var(--ink); }
.st__w { font-size: 12.5px; fill: var(--ink-muted); }
.st__f { fill: var(--rule-strong); }
.st__tick { font-size: 11px; fill: var(--ink-faint); font-variant-numeric: tabular-nums; }
.st__unit { font-size: 11.5px; fill: var(--ink-faint); }
.st__val { font-size: 12px; fill: var(--ink-secondary); font-variant-numeric: tabular-nums; }
.st__grid { stroke: var(--rule); stroke-width: 1; }

/* narrow row twin, shared by market, stack and levels */
.nr__i { font-family: var(--f-display); font-size: 26px; fill: var(--mark);
         font-variant-numeric: tabular-nums; }
.nr__t { font-size: 23px; font-weight: 620; fill: var(--ink); }
.nr__s { font-size: 17px; fill: var(--ink-secondary); }
.nr__m { font-size: 15px; fill: var(--ink-faint); }

/* corpus coverage */
.lb__shelf { stroke: var(--ink); stroke-width: 2; }
.lb__v { font-family: var(--f-display); font-size: 20px; fill: var(--ink);
         font-variant-numeric: tabular-nums; }
.lb__l { font-size: 13px; fill: var(--ink-muted); }

/* levels of evidence */
.lv__box { fill: var(--surface-raised); stroke: var(--rule); stroke-width: 1.5; }
.lv__box--soft { fill: none; stroke-dasharray: 5 4; }
.lv__hatch { stroke: var(--rule); stroke-width: 1; }
.lv__i { font-family: var(--f-display); font-size: 20px; fill: var(--mark);
         font-variant-numeric: tabular-nums; }
.lv__n { font-size: 15.5px; font-weight: 620; fill: var(--ink); }
.lv__w { font-size: 13px; fill: var(--ink-muted); }
.lv__gateline { stroke: var(--mark); stroke-width: 2; }
.lv__gatetxt { font-size: 12.5px; font-weight: 620; fill: var(--mark); }

/* --- entry ---------------------------------------------------------------- */

/* Staged only when reveal.js has marked the document. Without it none of these
   selectors match, every figure renders finished, and the page is whole. That
   is also what happens under prefers-reduced-motion: reveal.js returns before
   setting the class, so there is nothing here to disable a second time. */
.reveal-js [data-reveal] .fl__stage,
.reveal-js [data-reveal] .fl__arrow,
.reveal-js [data-reveal] .fl__lanes,
.reveal-js [data-reveal] .mk__col,
.reveal-js [data-reveal] .st__row,
.reveal-js [data-reveal] .st__axis,
.reveal-js [data-reveal] .lb__doc,
.reveal-js [data-reveal] .nr__row,
.reveal-js [data-reveal] .lv__row { opacity: 0; }

.reveal-js [data-reveal].is-revealed .fl__stage,
.reveal-js [data-reveal].is-revealed .fl__arrow,
.reveal-js [data-reveal].is-revealed .fl__lanes,
.reveal-js [data-reveal].is-revealed .mk__col,
.reveal-js [data-reveal].is-revealed .st__row,
.reveal-js [data-reveal].is-revealed .st__axis,
.reveal-js [data-reveal].is-revealed .lb__doc,
.reveal-js [data-reveal].is-revealed .nr__row,
.reveal-js [data-reveal].is-revealed .lv__row {
  animation: rise 320ms var(--ease) both;
}

/* A figure the visitor arrived below, rather than scrolled down to, is shown
   finished with no animation and no delay. Playing a 900ms entrance on
   something that is already behind them would only be visible if they
   scrolled back up, where it would read as the page redrawing itself. */
.is-revealed--immediate * { animation: none !important; opacity: 1 !important; }
.is-revealed--immediate .fl__bar { transform: none !important; }
.is-revealed--immediate .fl__scan { opacity: 0 !important; }

/* Delays keyed on the index the generator writes, so a figure resolves left to
   right rather than all at once. */
.is-revealed .fl__stage[data-i="0"], .is-revealed .mk__col[data-i="0"],
.is-revealed .st__row[data-i="0"], .is-revealed .nr__row[data-i="0"], .is-revealed .lb__doc[data-i="0"],
.is-revealed .lv__row[data-i="0"] { animation-delay: 40ms; }
.is-revealed .fl__stage[data-i="1"], .is-revealed .mk__col[data-i="1"],
.is-revealed .st__row[data-i="1"], .is-revealed .nr__row[data-i="1"], .is-revealed .lb__doc[data-i="1"],
.is-revealed .lv__row[data-i="1"] { animation-delay: 110ms; }
.is-revealed .fl__stage[data-i="2"], .is-revealed .mk__col[data-i="2"],
.is-revealed .st__row[data-i="2"], .is-revealed .nr__row[data-i="2"], .is-revealed .lb__doc[data-i="2"],
.is-revealed .lv__row[data-i="2"] { animation-delay: 180ms; }
.is-revealed .fl__stage[data-i="3"], .is-revealed .mk__col[data-i="3"],
.is-revealed .nr__row[data-i="3"], .is-revealed .lb__doc[data-i="3"], .is-revealed .lv__row[data-i="3"] { animation-delay: 250ms; }
.is-revealed .fl__stage[data-i="4"], .is-revealed .nr__row[data-i="4"], .is-revealed .lb__doc[data-i="4"],
.is-revealed .lv__row[data-i="4"] { animation-delay: 320ms; }
.is-revealed .lb__doc[data-i="5"] { animation-delay: 390ms; }
.is-revealed .lb__doc[data-i="6"] { animation-delay: 460ms; }
.is-revealed .lb__doc[data-i="7"] { animation-delay: 530ms; }
.is-revealed .lb__doc[data-i="8"] { animation-delay: 600ms; }
.is-revealed .fl__arrow[data-i="0"] { animation-delay: 90ms; }
.is-revealed .fl__arrow[data-i="1"] { animation-delay: 160ms; }
.is-revealed .fl__arrow[data-i="2"] { animation-delay: 230ms; }
.is-revealed .fl__arrow[data-i="3"] { animation-delay: 300ms; }
.is-revealed .fl__lanes { animation-delay: 420ms; }
.is-revealed .st__axis  { animation-delay: 300ms; }

/* Two marks inside the stages draw rather than fade, because a line that draws
   is read as a process and a line that fades is read as an image appearing.
   Only these two: any more and the figure becomes a performance. */
.reveal-js [data-reveal] .fl__scan { opacity: 0; }
.reveal-js [data-reveal].is-revealed .fl__scan {
  animation: scanpass 700ms var(--ease) 520ms both;
}
@keyframes scanpass {
  0%   { opacity: 0; transform: translateY(-26px); }
  15%  { opacity: 1; }
  85%  { opacity: 1; }
  100% { opacity: 0; transform: translateY(26px); }
}

/* transform-box is not optional here. Without it an SVG element's
   transform-origin resolves against the viewBox origin, not the element, so
   "left center" is the left edge of the whole diagram and every bar grows from
   the wrong place. fill-box makes the percentage keywords mean what they say. */
.reveal-js [data-reveal] .fl__bar {
  transform: scaleX(0);
  transform-box: fill-box;
  transform-origin: left center;
}
.reveal-js [data-reveal].is-revealed .fl__bar {
  animation: growx 460ms var(--ease) 640ms both;
}
@keyframes growx { to { transform: scaleX(1); } }

/* --- commerce forms -------------------------------------------------------- */

/* Same underlined field as the front page signup, at the density a fourteen
   field form needs. The one addition is an error slot under every field: a
   summary at the top of a form this long means reading a list, then hunting for
   the field it names. */

.orderform { margin-top: var(--s-6); max-width: 40rem; }

.orderform .field + .field,
.orderform .orderform__pair,
.orderform .field--check { margin-top: var(--s-5); }

.orderform__pair { display: grid; gap: var(--s-5); }
@media (min-width: 34rem) {
  .orderform__pair { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: var(--s-4); }
  .orderform__pair .field + .field { margin-top: 0; }
}

.field__opt {
  font-weight: var(--w-body);
  letter-spacing: 0;
  text-transform: none;
  color: var(--ink-faint);
}

.field__note { margin-top: var(--s-2); font-size: 0.84rem; color: var(--ink-muted); }
.field__note a { color: var(--accent); }

/* Reserves no space until it has something to say, without ever leaving the
   accessibility tree. :empty on display:none is the bug this file already
   carries a note about on .signup__status. */
.field__err { font-size: 0.84rem; color: var(--error); }
.field__err:not(:empty) { margin-top: var(--s-2); }

/* A select needs the same underline as a text field, and it needs its own
   appearance reset or the platform draws a rounded grey box that belongs to a
   different design. */
select.field__input {
  appearance: none;
  padding-right: var(--s-6);
  /* The chevron is two gradients, not an SVG data URI.
     `img-src 'self'` does not cover the data: scheme, so the first version of
     this was blocked outright and the control lost its only affordance while
     the console said nothing a visitor could see. A gradient is CSS and falls
     under style-src, which is already 'self', and it inherits currentColor so
     it does not need a second value per theme. */
  background-image:
    linear-gradient(45deg, transparent 50%, currentColor 50%),
    linear-gradient(135deg, currentColor 50%, transparent 50%);
  background-position: right 0.75rem center, right 0.4rem center;
  background-size: 0.35rem 0.35rem;
  background-repeat: no-repeat;
  color: var(--ink);
}
/* The arrow is drawn in the text colour, so a muted select would fade it too. */
select.field__input:hover { border-bottom-color: var(--ink-muted); }

.field__input--area {
  border: 2px solid var(--rule-strong);
  border-radius: var(--r-sm);
  padding: var(--s-3);
  resize: vertical;
  min-height: 5rem;
}
.field__input--area:focus { border-color: var(--accent); }

/* The checkbox rows carry the two consents. The label wraps the control so the
   whole sentence is a hit target, which on a phone is the difference between a
   16px square and a line of text. */
.check {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  gap: var(--s-3);
  align-items: start;
  font-size: 0.94rem;
  line-height: 1.55;
  color: var(--ink-secondary);
  cursor: pointer;
  /* 44px of target height around a 1.1rem control. */
  padding-block: var(--s-2);
}
.check input {
  width: 1.15rem;
  height: 1.15rem;
  margin-top: 0.15rem;
  accent-color: var(--accent);
}
.check a { color: var(--accent); text-underline-offset: 2px; }

.orderform .btn { margin-top: var(--s-6); }

.orderform__status { margin-top: var(--s-5); font-size: 0.95rem; line-height: 1.6; }
.orderform__status:empty { margin-top: 0; }
.orderform__status:focus { outline: none; }
.orderform__status[data-state='error'] { color: var(--error); }
.orderform__status[data-state='ok'] {
  padding-top: var(--s-4);
  border-top: 2px solid var(--accent);
  color: var(--ink);
  max-width: 34rem;
}

/* --- the assistant --------------------------------------------------------- */

/* Anchored bottom right, which is where people look for it, and that is the
   only thing it takes from the hosted-widget house style. No bubble, no avatar,
   no circle with a smiling face: the panel is a ruled sheet like the rest of
   the page, and the turns are ruled rows rather than rounded speech bubbles.
   Bubbles are the tell tokens.css warns about, and a bot that looks like every
   other bot invites being treated like every other bot. */

.asst {
  position: fixed;
  right: var(--s-4);
  bottom: var(--s-4);
  z-index: 40;
  display: grid;
  justify-items: end;
  gap: var(--s-3);
  /* Never wider than the viewport minus its own margins, so it cannot be the
     thing that gives the document a horizontal scrollbar on a small phone. */
  max-width: calc(100vw - var(--s-6));
}

.asst__open {
  display: inline-flex;
  align-items: center;
  gap: var(--s-2);
  padding: var(--s-3) var(--s-4);
  min-height: 2.75rem;
  background: var(--ink);
  color: var(--ink-inverse);
  font-size: 0.9rem;
  font-weight: var(--w-strong);
  border-radius: var(--r-md);
  border: var(--rule-w) solid var(--ink);
  transition: background var(--t-fast) var(--ease);
}
.asst__open:hover { background: var(--ink-secondary); }
.asst__icon { width: 1.1rem; height: 1.1rem; flex: none; }

.asst[data-open='true'] .asst__open { display: none; }

/* The hidden attribute is set by the script and has to win. A class selector
   is specificity (0,1,0) and the browser's own [hidden] { display: none } is
   (0,1,0) too, so the later of the two decides, and the stylesheet is always
   later than the UA sheet. Without this line the panel was open on every page
   load: the attribute was set, the script agreed it was closed, and the panel
   sat there anyway. */
.asst__panel[hidden] { display: none; }

.asst__panel {
  width: min(24rem, calc(100vw - var(--s-6)));
  max-height: min(32rem, calc(100svh - var(--s-8)));
  display: grid;
  grid-template-rows: auto auto minmax(0, 1fr) auto auto;
  background: var(--surface-raised);
  border: var(--rule-w) solid var(--rule-strong);
  border-radius: var(--r-md);
  /* The one shadow on the site. It is here because the panel floats over
     arbitrary content and needs an edge that does not depend on what is behind
     it; a hairline alone disappears over a figure. */
  box-shadow: 0 8px 32px rgb(0 0 0 / 0.22);
}

.asst__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s-3);
  padding: var(--s-4) var(--s-4) var(--s-3);
  border-bottom: var(--rule-w) solid var(--rule);
}
.asst__title { font-size: 0.82rem; font-weight: var(--w-strong); letter-spacing: 0.02em; }
.asst__close {
  display: grid;
  place-items: center;
  width: 2.75rem; height: 2.75rem;
  margin: calc(var(--s-2) * -1);
  color: var(--ink-muted);
}
.asst__close:hover { color: var(--ink); }
.asst__close svg { width: 1.1rem; height: 1.1rem; }

/* The AI disclosure. Not small print, and not dismissible: Article 50 asks that
   the person be informed, and an informed person is one who can still read it
   on the tenth turn. */
.asst__notice {
  padding: var(--s-3) var(--s-4);
  font-size: 0.8rem;
  line-height: 1.5;
  color: var(--ink-secondary);
  background: var(--surface-sunk);
  border-bottom: var(--rule-w) solid var(--rule);
}

.asst__log {
  overflow-y: auto;
  overscroll-behavior: contain;
  padding: var(--s-4);
  display: grid;
  gap: var(--s-4);
  align-content: start;
}
.asst__log:focus-visible { outline-offset: -3px; }

.asst__who {
  font-size: 0.68rem;
  font-weight: var(--w-strong);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ink-faint);
}
.asst__text {
  margin-top: var(--s-2);
  font-size: 0.94rem;
  line-height: 1.6;
  color: var(--ink);
  white-space: pre-wrap;
}
.asst__turn--you { border-left: 2px solid var(--rule-strong); padding-left: var(--s-3); }
.asst__turn--bot { border-left: 2px solid var(--accent); padding-left: var(--s-3); }

/* The sources are the point. A grounded answer with its citations hidden behind
   a chevron is an ungrounded answer as far as the reader is concerned. */
.asst__sources {
  margin: 0;
  padding-left: var(--s-5);
  font-size: 0.84rem;
  line-height: 1.5;
  color: var(--ink-muted);
}
.asst__sources li + li { margin-top: var(--s-1); }
.asst__sources a { color: var(--accent); text-underline-offset: 2px; }

.asst__handoff { font-size: 0.88rem; color: var(--ink-secondary); }
.asst__handoff a { color: var(--accent); }

.asst__form {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  gap: var(--s-2);
  padding: var(--s-3) var(--s-4);
  border-top: var(--rule-w) solid var(--rule);
}
.asst__input {
  width: 100%;
  padding: var(--s-2) 0;
  background: none;
  border: 0;
  border-bottom: 2px solid var(--rule-strong);
  border-radius: 0;
  color: var(--ink);
  font-size: 0.94rem;
}
.asst__input::placeholder { color: var(--ink-faint); }
.asst__input:focus { outline: none; border-bottom-color: var(--accent); }
.asst__send { padding: var(--s-2) var(--s-4); font-size: 0.88rem; }

.asst__foot {
  padding: 0 var(--s-4) var(--s-4);
  font-size: 0.76rem;
  line-height: 1.5;
  color: var(--ink-faint);
}
.asst__foot a { color: var(--ink-muted); }

/* While a turn is in flight the send button is disabled; say so with more than
   a greyed control, because a disabled button with no other signal reads as
   broken rather than as busy. */
.asst[data-busy='true'] .asst__input { color: var(--ink-muted); }

/* On a phone the panel takes the width it needs and sits above the safe area,
   which on iOS is the home indicator. */
@media (max-width: 30rem) {
  .asst { right: var(--s-3); left: var(--s-3); bottom: max(var(--s-3), env(safe-area-inset-bottom)); }
  .asst__panel { width: 100%; max-height: min(28rem, calc(100svh - var(--s-9))); }
}

/* Hidden until its own script has initialised. Without JavaScript the stream
   cannot run and the button would be a control that does nothing, which is
   worse than no button: the page already carries hej@rekaro.se in the footer.
   Keyed on a mark assistent.js sets rather than on reveal.js's class, because
   reveal.js deliberately returns before setting anything under
   prefers-reduced-motion, and a visitor who dislikes motion still wants the
   assistant. */
.asst { display: none; }
.asst[data-ready='true'] { display: grid; }
.asst__datanotis { display: block; margin-top: var(--s-2); color: var(--ink-muted); }

/* --- the money path -------------------------------------------------------- */

.pay__box { fill: var(--surface-raised); stroke: var(--rule); stroke-width: 1.5; }
.pay__box--shut { fill: none; stroke: var(--rule-strong); stroke-dasharray: 5 4; }
.pay__hatch { stroke: var(--rule); stroke-width: 1; }
.pay__i { font-size: 11.5px; font-weight: 620; letter-spacing: 0.08em; fill: var(--mark);
          font-variant-numeric: tabular-nums; }
.pay__n { font-size: 16px; font-weight: 620; fill: var(--ink); }
.pay__n--shut { fill: var(--ink-muted); }
.pay__s { font-size: 12.5px; fill: var(--ink-muted); }
.pay__note { font-size: 12.5px; fill: var(--ink-secondary); }
.pay__note--mark { fill: var(--mark); font-weight: 620; }
.pay__arrow line, .pay__arrow path { stroke: var(--rule-strong); stroke-width: 1.5;
                                     fill: none; stroke-linecap: round; stroke-linejoin: round; }
.pay__branch { stroke: var(--rule-strong); stroke-width: 1.5; stroke-dasharray: 4 4; }
.pay__gatebox { fill: none; stroke: var(--mark); stroke-width: 2; }
.pay__shackle { fill: none; stroke: var(--mark); stroke-width: 2; stroke-linecap: round; }

/* --- the comparison matrix ------------------------------------------------- */

.rq__box { fill: var(--surface-raised); stroke: var(--rule); stroke-width: 1.5; }
.rq__label { font-size: 11.5px; font-weight: 620; letter-spacing: 0.08em;
             text-transform: uppercase; fill: var(--ink-muted); }
.rq__meta { font-size: 11.5px; fill: var(--ink-faint); font-variant-numeric: tabular-nums; }
.rq__crit { font-size: 13.5px; font-weight: 620; fill: var(--ink); }
.rq__frozen { font-size: 11.5px; fill: var(--mark); }
.rq__wn { font-size: 13.5px; fill: var(--ink-secondary); }
.rq__wv { font-size: 13px; fill: var(--ink); font-variant-numeric: tabular-nums; }
.rq__wtrack { fill: var(--surface-sunk); }
.rq__wbar { fill: var(--mark); }
.rq__col { font-size: 12px; font-weight: 620; letter-spacing: 0.04em; fill: var(--ink-faint); }
.rq__col--total { fill: var(--ink); }
.rq__name { font-size: 13.5px; font-weight: 500; fill: var(--ink); }
.rq__cell { font-size: 13px; fill: var(--ink-muted); font-variant-numeric: tabular-nums; }
/* The best score per criterion, not per row. A reader scanning a matrix looks
   down a column, and the mark should reward that rather than the diagonal. */
.rq__cell--best { fill: var(--ink); font-weight: 620; }
.rq__ttrack { fill: var(--surface-sunk); }
.rq__tbar { fill: var(--accent); }
.rq__total { font-family: var(--f-display); font-size: 17px; fill: var(--ink);
             font-variant-numeric: tabular-nums; }
.rq__rule { stroke: var(--rule); stroke-width: 1; }
.rq__rule--head { stroke: var(--ink); stroke-width: 2; }
.rq__line path, .rq__line { fill: none; stroke: var(--rule-strong); stroke-width: 1.25; }
.rq__stamptxt { font-size: 12px; fill: var(--ink-faint); }

/* --- the boundary ---------------------------------------------------------- */

.as__who { font-size: 11px; font-weight: 620; letter-spacing: 0.08em;
           text-transform: uppercase; fill: var(--ink-faint); }
.as__text { font-size: 14px; fill: var(--ink); }
.as__line { stroke: var(--mark); stroke-width: 2.5; }
.as__btxt { font-size: 13px; font-weight: 620; fill: var(--mark); }
.as__bsub { font-size: 12px; fill: var(--ink-muted); }
.as__box { fill: var(--surface-raised); stroke: var(--rule); stroke-width: 1.5; }
.as__box--human { stroke: var(--mark); }
.as__cn { font-size: 14.5px; font-weight: 620; fill: var(--ink); }
.as__cw { font-size: 12.5px; fill: var(--ink-muted); }
.as__human { fill: var(--mark); }
.as__auto { fill: var(--ink); }
.as__cross, .as__crosshead { fill: none; stroke: var(--rule-strong); stroke-width: 1.5;
                             stroke-linecap: round; stroke-linejoin: round; }
.as__pausetxt { font-size: 11.5px; fill: var(--ink-faint); }

/* --- entry for the four new figures ---------------------------------------- */

.reveal-js [data-reveal] .pay__stage,
.reveal-js [data-reveal] .pay__arrow,
.reveal-js [data-reveal] .pay__closed,
.reveal-js [data-reveal] .rq__doc,
.reveal-js [data-reveal] .rq__weight,
.reveal-js [data-reveal] .rq__head,
.reveal-js [data-reveal] .rq__row,
.reveal-js [data-reveal] .rq__stamp,
.reveal-js [data-reveal] .as__turn,
.reveal-js [data-reveal] .as__boundary,
.reveal-js [data-reveal] .as__check,
.reveal-js [data-reveal] .as__pause { opacity: 0; }

.reveal-js [data-reveal].is-revealed .pay__stage,
.reveal-js [data-reveal].is-revealed .pay__arrow,
.reveal-js [data-reveal].is-revealed .pay__closed,
.reveal-js [data-reveal].is-revealed .rq__doc,
.reveal-js [data-reveal].is-revealed .rq__weight,
.reveal-js [data-reveal].is-revealed .rq__head,
.reveal-js [data-reveal].is-revealed .rq__row,
.reveal-js [data-reveal].is-revealed .rq__stamp,
.reveal-js [data-reveal].is-revealed .as__turn,
.reveal-js [data-reveal].is-revealed .as__boundary,
.reveal-js [data-reveal].is-revealed .as__check,
.reveal-js [data-reveal].is-revealed .as__pause {
  animation: rise 320ms var(--ease) both;
}

/* Sixteen rows at the usual 70ms stagger would run for over a second, which
   reads as a list loading rather than a diagram arriving. 26ms, and the whole
   table settles in under half a second. */

.is-revealed .pay__stage[data-i="0"] { animation-delay: 40ms; }
.is-revealed .pay__stage[data-i="1"] { animation-delay: 110ms; }
.is-revealed .pay__stage[data-i="2"] { animation-delay: 180ms; }
.is-revealed .pay__stage[data-i="3"] { animation-delay: 250ms; }
.is-revealed .pay__stage[data-i="4"] { animation-delay: 320ms; }
.is-revealed .pay__arrow[data-i="0"] { animation-delay: 90ms; }
.is-revealed .pay__arrow[data-i="1"] { animation-delay: 160ms; }
.is-revealed .pay__arrow[data-i="2"] { animation-delay: 230ms; }
.is-revealed .pay__arrow[data-i="3"] { animation-delay: 300ms; }
/* The closed branch arrives last and alone. It is the point of the figure. */
.is-revealed .pay__closed { animation-delay: 520ms; }

.is-revealed .rq__weight[data-i="0"] { animation-delay: 90ms; }
.is-revealed .rq__weight[data-i="1"] { animation-delay: 140ms; }
.is-revealed .rq__weight[data-i="2"] { animation-delay: 190ms; }
.is-revealed .rq__weight[data-i="3"] { animation-delay: 240ms; }
.is-revealed .rq__head { animation-delay: 300ms; }
.is-revealed .rq__row[data-i="0"] { animation-delay: 340ms; }
.is-revealed .rq__row[data-i="1"] { animation-delay: 390ms; }
.is-revealed .rq__row[data-i="2"] { animation-delay: 440ms; }
.is-revealed .rq__row[data-i="3"] { animation-delay: 490ms; }
.is-revealed .rq__row[data-i="4"] { animation-delay: 540ms; }
.is-revealed .rq__row[data-i="5"] { animation-delay: 590ms; }
.is-revealed .rq__stamp { animation-delay: 680ms; }

.is-revealed .as__turn[data-i="0"] { animation-delay: 40ms; }
.is-revealed .as__turn[data-i="1"] { animation-delay: 160ms; }
.is-revealed .as__turn[data-i="2"] { animation-delay: 280ms; }
.is-revealed .as__turn[data-i="3"] { animation-delay: 400ms; }
.is-revealed .as__boundary { animation-delay: 120ms; }
.is-revealed .as__check[data-i="0"] { animation-delay: 200ms; }
.is-revealed .as__check[data-i="1"] { animation-delay: 260ms; }
.is-revealed .as__check[data-i="2"] { animation-delay: 320ms; }
.is-revealed .as__check[data-i="3"] { animation-delay: 380ms; }
/* The proposal reaching the boundary, then waiting. The delay after the last
   check is the pause, and the pause is the message. */
.is-revealed .as__pause { animation-delay: 900ms; }

/* The sideways corpus chart, for a column. */
.lb__nl { font-size: 17px; fill: var(--ink); }
.lb__nv { font-family: var(--f-display); font-size: 20px; fill: var(--ink);
          font-variant-numeric: tabular-nums; }
.lb__ntrack { fill: var(--surface-sunk); }
.lb__nbar { fill: var(--accent); }
/* A month too thin to compare is marked, not just short. */
.lb__nbar--thin { fill: var(--mark); }

.reveal-js [data-reveal] .lb__nrow { opacity: 0; }
.reveal-js [data-reveal].is-revealed .lb__nrow { animation: rise 320ms var(--ease) both; }
.is-revealed .lb__nrow[data-i="0"] { animation-delay: 40ms; }
.is-revealed .lb__nrow[data-i="1"] { animation-delay: 110ms; }
.is-revealed .lb__nrow[data-i="2"] { animation-delay: 180ms; }
.is-revealed .lb__nrow[data-i="3"] { animation-delay: 250ms; }
.is-revealed .lb__nrow[data-i="4"] { animation-delay: 320ms; }

/* --- the agent roster, as a table ------------------------------------------ */

/* The figure above this is aria-hidden and the table is the content. On a phone
   the figure is gone entirely and this stands alone, which is the right way
   round: sixteen rows of two sentences each is a table, not a drawing. */

.agt__wrap { margin-top: var(--s-5); overflow-x: auto; }
.agt { width: 100%; border-collapse: collapse; font-size: 0.94rem; text-align: left; }
.agt thead th {
  padding-bottom: var(--s-2);
  border-bottom: 2px solid var(--ink);
  font-size: 0.7rem;
  font-weight: var(--w-strong);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ink-faint);
}
.agt tbody tr { border-bottom: var(--rule-w) solid var(--rule); }
.agt td, .agt th { padding-block: var(--s-3); padding-right: var(--s-4); vertical-align: baseline; }
.agt__i { color: var(--mark); font-variant-numeric: tabular-nums; font-size: 0.82rem; width: 2.5rem; }
.agt__n { font-weight: var(--w-strong); color: var(--ink); white-space: nowrap; }
.agt__w { color: var(--ink-secondary); }
.agt__a { color: var(--ink-muted); font-size: 0.86rem; white-space: nowrap; }
/* The ochre is reserved for the one row-type that acts without being asked, so
   the eye finds it before reading a word. */
.agt__a[data-autonomy='acts'] { color: var(--mark); }

/* The figure duplicates the table, so below the breakpoint only the table runs.
   Above it the figure carries the shape and the table carries the detail. */
@media (max-width: 52rem) {
  .agt__n { white-space: normal; }
  .agt__a { white-space: normal; }
  .agt thead { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); }
  .agt tbody tr { display: grid; grid-template-columns: 2.5rem minmax(0, 1fr); padding-block: var(--s-3); }
  .agt td, .agt th { padding-block: 0; }
  .agt__i { grid-row: 1 / span 3; }
  .agt__w { margin-top: var(--s-1); }
  .agt__a { margin-top: var(--s-2); font-size: 0.82rem; }
}

/* The assistant button is fixed, so on a phone it sits over whatever happens to
   be at the bottom of the viewport. Mid-scroll that is unavoidable and expected
   of a chat control. At the end of the document it is not: without this the
   last line of the footer is permanently under the button with nothing left to
   scroll. */
@media (max-width: 30rem) {
  .colophon { padding-bottom: calc(var(--s-9) + env(safe-area-inset-bottom)); }
}

/* --- the contact form ------------------------------------------------------ */

.fieldset { border: 0; padding: 0; margin: 0; }
.fieldset .field__note { margin-bottom: var(--s-2); }

/* Inline checkboxes wrap into rows rather than stacking one per line: six
   languages down a column pushes the rest of the form off a phone screen. */
.check--inline {
  display: inline-grid;
  grid-template-columns: auto auto;
  gap: var(--s-2);
  margin-right: var(--s-4);
  padding-block: var(--s-2);
}

/* A key shown exactly once. Monospaced, selectable, and deliberately not behind
   a copy button: a button that fails silently on a browser without clipboard
   permission leaves the visitor with nothing, and this is the only time they
   will ever see the value. */
.tokenbox {
  margin-top: var(--s-5);
  padding: var(--s-4);
  border: var(--rule-w) solid var(--mark);
  border-radius: var(--r-md);
  background: var(--surface-sunk);
  max-width: 34rem;
}
.tokenbox__label {
  font-size: 0.72rem;
  font-weight: var(--w-strong);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--mark);
}
.tokenbox__value {
  display: block;
  margin-top: var(--s-2);
  padding: var(--s-3);
  background: var(--surface-raised);
  border-radius: var(--r-sm);
  font-family: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
  font-size: 0.86rem;
  word-break: break-all;
  user-select: all;
}
.tokenbox__note { margin-top: var(--s-3); font-size: 0.84rem; color: var(--ink-muted); }

.profile-contact { max-width: 46rem; }
.profile__back { margin-top: var(--s-7); padding-top: var(--s-4);
                 border-top: var(--rule-w) solid var(--rule); font-size: 0.9rem; }

/* --- update prompt ---------------------------------------------------------
   The service worker no longer swaps itself under an open form, so a new build
   waits until somebody asks for it. This is the asking. It is inserted by
   site.js rather than written into every page, because it must not exist on a
   page where there is nothing waiting.

   Bottom of the viewport and not the top: the masthead carries the navigation
   and the theme control, and a bar that pushes those down moves a target the
   visitor was already reaching for. */
.uppdatering {
  position: fixed;
  inset-inline: var(--s-4);
  bottom: var(--s-4);
  z-index: 40;
  display: flex;
  gap: var(--s-4);
  align-items: center;
  justify-content: space-between;
  max-width: 34rem;
  margin-inline: auto;
  padding: var(--s-3) var(--s-4);
  background: var(--surface-raised);
  border: var(--rule-w) solid var(--rule-strong);
  border-radius: var(--r-md);
  box-shadow: 0 6px 24px rgb(0 0 0 / 0.14);
  font-size: 0.92rem;
  /* The assistant panel opens from the same corner. Leaving room for the safe
     area keeps the bar off the home indicator on a phone. */
  padding-bottom: max(var(--s-3), env(safe-area-inset-bottom));
}
.uppdatering[hidden] { display: none; }
.uppdatering__knapp {
  flex: none;
  padding: var(--s-2) var(--s-4);
  background: var(--accent);
  color: var(--accent-ink);
  border: 0;
  border-radius: var(--r-sm);
  font: inherit;
  font-weight: var(--w-medium);
  cursor: pointer;
  transition: background var(--t-fast) var(--ease);
}
.uppdatering__knapp:hover { background: var(--accent-hover); }
.uppdatering__knapp:focus-visible { outline: 2px solid var(--focus); outline-offset: 2px; }

@media (prefers-reduced-motion: reduce) {
  .uppdatering__knapp { transition: none; }
}

/* --- fenced code in prose --------------------------------------------------
   The markdown renderer had no fence support, so a shell command in a guide was
   emitted as a paragraph and the backslashes in a regex read as typos. It has
   one now, and this is what it renders into.

   overflow-x on the block and not on the page: a long command must scroll
   itself. A code sample that widens the document gives every page on the site a
   horizontal scrollbar on a phone. */
.prose__pre {
  margin: var(--s-5) 0;
  padding: var(--s-4);
  overflow-x: auto;
  background: var(--surface-sunk);
  border: var(--rule-w) solid var(--rule);
  border-radius: var(--r-sm);
  font-family: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
  font-size: 0.86rem;
  line-height: 1.55;
  -webkit-overflow-scrolling: touch;
}
.prose__pre code {
  display: block;
  padding: 0;
  background: none;
  border: 0;
  white-space: pre;
  font: inherit;
  color: var(--ink);
}
/* A scroll container has to be reachable by keyboard, or the content past the
   right edge cannot be read without a mouse. WCAG 2.1.1. */
.prose__pre:focus-visible { outline: 2px solid var(--focus); outline-offset: 2px; }

/* --- the account link in the masthead --------------------------------------
   Authored as "Logga in" and swapped to "Konto" by site.js when a session
   answers. Sized to the theme toggle beside it so the swap between two words of
   different length does not move the toggle, which is a control people reach for
   by position. */
.masthead__konto {
  display: inline-flex;
  align-items: center;
  min-width: 5.5rem;
  justify-content: flex-end;
  padding: var(--s-2) 0;
  color: var(--ink-secondary);
  font-size: 0.88rem;
  font-weight: var(--w-medium);
  text-decoration-color: var(--rule-strong);
  text-underline-offset: 0.28em;
  white-space: nowrap;
  transition: color var(--t-fast) var(--ease);
}
.masthead__konto:hover { color: var(--accent); }
.masthead__konto:focus-visible { outline: 2px solid var(--focus); outline-offset: 3px; }

@media (max-width: 40rem) {
  /* The masthead is already carrying a wordmark, a scrolling nav and a toggle at
     this width. The account link is reachable from the footer and from every
     account page, so it is the one that gives way. */
  .masthead__konto { display: none; }
}
@media (prefers-reduced-motion: reduce) {
  .masthead__konto { transition: none; }
}

/* Fem steg i stället för tre. Grid utan fast kolumnantal, annars blir det två
   rader där den andra har två kort och ser ut som ett misstag. */
@media (min-width: 54rem) {
  .tiers--fem { grid-template-columns: repeat(auto-fit, minmax(13rem, 1fr)); }
}
.tier__jamfor {
  margin-top: var(--s-2);
  font-size: 0.82rem;
  color: var(--accent);
  line-height: 1.45;
}

/* Löftena. Fem rader som var och en svarar mot ett klagomål som återkom i minst
   tre oberoende källor när vi läste recensioner och forum för kategorin. */
.loften {
  margin-top: var(--s-7);
  display: grid;
  gap: var(--s-4);
  border-top: 1px solid var(--rule);
  padding-top: var(--s-5);
}
@media (min-width: 46rem) {
  .loften { grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr)); }
}
.lofte__namn { font-weight: var(--w-strong); font-size: 0.95rem; }
.lofte__text {
  margin-top: var(--s-2);
  font-size: 0.88rem;
  color: var(--ink-secondary);
  line-height: 1.6;
}

/* --- konkurrentsidan ------------------------------------------------------ */
/* En bred tabell ska rulla i sin egen ruta. Rullar sidan i stället går layouten
   sönder på telefon, och det är där de flesta läser en jämförelse. */
.kk__tabellyta { margin-top: var(--s-5); overflow-x: auto; }
.kk__tabell { width: 100%; border-collapse: collapse; font-size: 0.88rem;
              min-width: 44rem; }
.kk__tabell caption { text-align: left; padding-bottom: var(--s-3);
                      font-size: 0.82rem; color: var(--ink-faint); }
.kk__tabell th, .kk__tabell td {
  text-align: left;
  vertical-align: top;
  padding: var(--s-3) var(--s-3) var(--s-3) 0;
  border-bottom: 1px solid var(--rule);
  line-height: 1.55;
}
.kk__tabell thead th { border-bottom: 2px solid var(--ink); font-size: 0.8rem;
                       letter-spacing: 0.04em; text-transform: uppercase; }
.kk__kalla { font-size: 0.8rem; color: var(--ink-faint); line-height: 1.5; }
.kk__ingress { margin-top: var(--s-4); font-size: 0.9rem;
               color: var(--ink-secondary); line-height: 1.65; max-width: 42rem; }
.kk__lista { margin-top: var(--s-5); display: grid; gap: var(--s-4); }
@media (min-width: 52rem) {
  .kk__lista { grid-template-columns: repeat(auto-fit, minmax(19rem, 1fr)); }
}
.kk__rad { border-top: 2px solid var(--rule-strong); padding-top: var(--s-3); }
/* Byggt i dag får accentfärgen. Planerat får inte samma markering, eftersom en
   jämförelse där allt ser levererat ut är en jämförelse som ljuger. */
.kk__rad--byggt { border-top-color: var(--accent); }
.kk__amne { font-weight: var(--w-strong); font-size: 0.95rem; }
.kk__citat { margin-top: var(--s-2); font-size: 0.88rem;
             color: var(--ink-secondary); line-height: 1.6; }
.kk__svar { margin-top: var(--s-3); font-size: 0.88rem; line-height: 1.6; }
.kk__lage { margin-top: var(--s-2); font-size: 0.75rem; letter-spacing: 0.06em;
            text-transform: uppercase; color: var(--ink-faint); }
.kk__rad--byggt .kk__lage { color: var(--accent); }

/* Figurerna från illustrations_extra.
 *
 * De fyra reglerna nedan MÅSTE ligga här och inte i en <style> inne i SVG:en.
 * Sidan skickar `style-src 'self'` utan `unsafe-inline`, och en style-tagg i
 * dokumentet är inline-stil oavsett att den råkar stå inuti ett svg-element.
 * Webbläsaren släcker den utan ett ord, så figuren renderar, hovringen gör
 * ingenting och regeln för reducerad rörelse gäller inte. Det är ett fel som
 * ser ut som ingenting alls, och det är därför raderna står här.
 *
 * Källan är konstanten SITE_CSS i illustrations_extra.py. Ändras den ska den
 * ändras här i samma commit.
 */
.xa { max-width: 62rem; }
.xa text { font-family: var(--f-text); }
/* Interaktion utan en rad JavaScript: en osynlig träffyta per rad tänder sin
   egen etikett, på hovring och på tangentbordsfokus. */
.xa__hit { cursor: pointer; }
.xa__hit:hover .xa__hl, .xa__hit:focus-within .xa__hl { opacity: 1; }
.xa__hit:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
/* Rörelsen ligger i .xa__go ovanpå färdig geometri, så att den som stängt av
   animationer tappar dekoration och aldrig en siffra. */
@media (prefers-reduced-motion: reduce) {
  .xa__go { display: none; }
  .xa__still { display: inline; }
}

/* --- fotografier ---------------------------------------------------------- */
/* Bilderna är CC0 eller public domain och behöver ingen källrad. Vår egen
   proveniens ligger i site/img/foto/MANIFEST.json, för oss och för en
   granskare, inte som en rad under bilden som ser ut som ett krav. */
.foto { margin: 0; }
.foto__bild {
  display: block;
  width: 100%;
  height: auto;
  border-radius: var(--r-sm);
  /* Fotografierna är dokumentära och sidan är typografisk. En svag
     avmättning håller ihop dem med resten utan att göra dem till dekor. */
  filter: saturate(0.86) contrast(1.02);
}
.foto__text { margin-top: var(--s-2); font-size: 0.8rem; color: var(--ink-faint);
              line-height: 1.5; }
.fotoband { padding-block: var(--s-5); }
.fotorad { display: grid; gap: var(--s-3); grid-template-columns: 1fr 1fr; }
@media (min-width: 46rem) {
  .fotorad { grid-template-columns: repeat(4, 1fr); gap: var(--s-4); }
}
/* Bandet beskär till samma höjd. Utan det blir en stående bild dubbelt så hög
   som grannen och raden ser ut som ett fel snarare än som ett urval. */
.fotorad .foto__bild { aspect-ratio: 4 / 3; object-fit: cover; }
.foto--bred .foto__bild { aspect-ratio: 21 / 9; object-fit: cover; }

/* Språkväljaren. Tom i HTML och fylld av sprak.js, eftersom en förvald flagga
   på fel språk är det första besökaren ser. */
.sprakvaljare { display: flex; align-items: center; gap: var(--s-2); }
.sprakvaljare__label {
  font-size: 0.72rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--ink-faint);
}
.sprakvaljare__falt {
  font: inherit;
  font-size: 0.82rem;
  color: var(--ink);
  background: transparent;
  border: 1px solid var(--rule);
  border-radius: var(--r-sm);
  padding: 0.2rem 0.4rem;
}
.sprakvaljare__falt:focus-visible { outline: 2px solid var(--accent);
                                    outline-offset: 2px; }
/* På telefon tar etiketten plats som navigeringen behöver. Fältet räcker: det
   visar sitt eget språknamn, vilket är den tydligaste etiketten som finns. */
@media (max-width: 52rem) { .sprakvaljare__label { display: none; } }

/* Figurerna är ritade i 1 000 enheter. I en telefonspalt på 22 rem renderar de
   i ungefär skala 0,34, alltså en etikett på 13 enheter vid 4,4 pixlar. Det är
   inte litet, det är oläsligt.
 *
 * Tre av 47 har en handritad smal tvilling. De övriga får i stället behålla sin
 * ritade bredd och rulla i sin egen ruta: figuren blir lika läslig som på en
 * skärm, och sidan rullar fortfarande bara i höjdled. Alternativet, att skala
 * upp texten i figuren, flyttar etiketter ut ur de rutor de pekar på.
 *
 * Rullningsrutan är fokuserbar med tabindex i markupen och har en synlig ring,
 * eftersom ett område som bara går att rulla med fingret är otillgängligt för
 * den som navigerar med tangentbord. */
@media (max-width: 44rem) {
  .illus--rullbar {
    overflow-x: auto;
    overscroll-behavior-x: contain;
    scrollbar-width: thin;
  }
  .illus--rullbar .xa { min-width: 34rem; max-width: none; }
  .illus--rullbar::after {
    content: "Dra i sidled för att se hela figuren";
    display: block;
    margin-top: var(--s-2);
    font-size: 0.72rem;
    color: var(--ink-faint);
  }
  .illus--rullbar:focus-visible { outline: 2px solid var(--accent);
                                  outline-offset: 2px; }
}

/* --- Agentsidorna ------------------------------------------------------- */
/*
 * Namnen i agenttabellen leder till en sida per agent. Lanken far samma
 * understrykning som ovrig brodtext, eftersom en tabell dar bara vissa celler
 * ar klickbara maste saga vilka utan att man provar sig fram.
 */
.agt__link { color: inherit; text-decoration: underline;
             text-underline-offset: 0.18em;
             text-decoration-color: var(--rule-strong);
             transition: text-decoration-color 120ms ease, color 120ms ease; }
.agt__link:hover { color: var(--accent); text-decoration-color: var(--accent); }

.agent__niva { margin-top: var(--s-3); }
.agent__nivamark { display: inline-block; padding: 0.3rem 0.7rem;
                   border: var(--rule-w) solid var(--rule);
                   border-radius: 999px; font-size: 0.82rem;
                   color: var(--ink-secondary); }
/* Tre nivaer, tre marken. Fargen bar ingen information ensam, texten i
   market sager samma sak i klartext. */
.agent__nivamark[data-niva="proposes"] { border-color: var(--rule-strong); }
.agent__nivamark[data-niva="approval"] { border-color: var(--mark);
                                         color: var(--ink); }
.agent__nivamark[data-niva="acts"] { border-color: var(--accent);
                                     color: var(--ink); }
.agent__block { margin-top: var(--s-5); }
.agent__ater { margin-top: var(--s-6); }

/* --- Figurernas hoverlager --------------------------------------------- */
/*
 * Gäller varje figur ur illustrations_extra, alltså ett trettiotal stycken,
 * och kostar ingen ny markup. Tanken är densamma i alla: det man pekar på
 * lyfts fram och resten träder tillbaka, så att blicken hamnar där handen är.
 *
 * Allt sitter på .xa och inte på enskilda figurer. En regel per figur hade
 * blivit trettio regler som glider isär, och den trettioförsta figuren hade
 * fått ingenting.
 */
.xa .xa__f, .xa .xa__d, .xa .xa__o, .xa .xa__n, .xa .xa__l {
  transition: opacity 160ms ease, fill 160ms ease, stroke 160ms ease,
              transform 160ms ease;
}

/* Staplar och punkter reser sig nagra pixlar. transform-box och origin behovs
   for att en SVG-form annars roterar kring hela ritytans mitt och inte kring
   sig sjalv. */
.xa .xa__f { transform-box: fill-box; transform-origin: center bottom; }
.xa__hit:hover ~ .xa__f, .xa__hit:hover + .xa__f { transform: translateY(-3px); }

/* Nar en traffyta ar aktiv tonas figurens ovriga delar ned. Opaciteten stannar
   pa 0.45 och inte lagre, eftersom en nedtonad stapel fortfarande ska ga att
   jamfora med den man pekar pa. */
.xa:has(.xa__hit:hover) .xa__f,
.xa:has(.xa__hit:focus-within) .xa__f { opacity: 0.45; }
.xa .xa__hit:hover ~ .xa__f,
.xa .xa__hit:hover + .xa__f,
.xa .xa__hit:focus-within ~ .xa__f { opacity: 1; }

/* Siffrorna far full styrka nar deras rad ar aktiv. */
.xa:has(.xa__hit:hover) .xa__n { opacity: 0.5; }
.xa .xa__hit:hover ~ .xa__n, .xa .xa__hit:focus-within ~ .xa__n { opacity: 1; }

/* Hela figuren far en svag lyftning nar pekaren ar i den, sa att den laser som
   ett foremal och inte som en bild i texten. */
.illus--extra { transition: transform 200ms ease; }
.illus--extra:hover { transform: translateY(-2px); }

@media (prefers-reduced-motion: reduce) {
  .xa .xa__f, .xa .xa__d, .xa .xa__o, .xa .xa__n, .xa .xa__l,
  .illus--extra { transition: none; }
  .xa__hit:hover ~ .xa__f, .xa__hit:hover + .xa__f,
  .illus--extra:hover { transform: none; }
}
