:root {
  --fg: #16181D;
  --bg: #FBFBFD;
  --primary: #1E1B4B;
  --accent: #4F46E5;
  --grad-from: #EEF0FF;
  --grad-to: #FBFBFD;
  --muted: #64748b;
  --border: #e5e7eb;
  --surface: #f8fafc;
  --font-heading: Manrope, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
  --font-body: Inter, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
  --text-xs: 0.8125rem;
  --text-sm: 0.9375rem;
  --text-base: 1.0625rem;
  --text-lg: 1.1875rem;
  --text-xl: 1.375rem;
  --text-2xl: 1.75rem;
  --text-3xl: 2.25rem;
  --text-4xl: 2.875rem;
  --leading-tight: 1.15;
  --leading-snug: 1.35;
  --leading-normal: 1.65;
  --space-1: 0.25rem;
  --space-2: 0.5rem;
  --space-3: 0.75rem;
  --space-4: 1rem;
  --space-5: 1.5rem;
  --space-6: 2rem;
  --space-7: 3rem;
  --space-8: 4rem;
  --radius-sm: 6px;
  --radius-md: 10px;
  --radius-lg: 16px;
  --radius-xl: 20px;
  --radius-pill: 999px;
  --shadow-sm: 0 1px 2px rgba(15, 23, 42, 0.06);
  --shadow-md: 0 2px 8px rgba(15, 23, 42, 0.08);
  --shadow-lg: 0 8px 28px rgba(15, 23, 42, 0.10);
  --measure: 68ch;
}
* { box-sizing: border-box; }
html, body { margin: 0; padding: 0; }
body {
  font-family: var(--font-body);
  font-size: var(--text-base);
  line-height: var(--leading-normal);
  color: var(--fg);
  background: var(--bg);
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  -webkit-font-smoothing: antialiased;
}
h1, h2, h3, h4 {
  font-family: var(--font-heading);
  font-weight: 700;
  line-height: var(--leading-tight);
  letter-spacing: -0.015em;
  color: var(--fg);
  text-wrap: balance;
  /* A site named konferencjalogopedyczna.pl put a 26-character unbroken word
     in the hero h1. At 30px on a 375px phone it measured 432px and ran off
     the right edge — the page did not even scroll to reveal it, the text was
     simply cut. break-word breaks only a word that cannot fit on a line of
     its own, so ordinary headings are untouched. */
  overflow-wrap: break-word;
  margin: var(--space-7) 0 var(--space-3);
}
h1 { font-size: var(--text-4xl); margin-top: 0; }
h2 { font-size: var(--text-2xl); }
h3 { font-size: var(--text-xl); }
h4 { font-size: var(--text-lg); }
p { margin: 0 0 var(--space-4); }
a { color: var(--accent); text-decoration: none; }
a:hover { text-decoration: underline; text-underline-offset: 2px; }
a:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: var(--radius-sm); }
img { max-width: 100%; height: auto; border-radius: var(--radius-md); }
ul, ol { margin: 0 0 var(--space-4); padding-left: var(--space-5); }
li { margin-bottom: var(--space-2); }
blockquote {
  border-left: 3px solid var(--accent);
  padding: var(--space-1) 0 var(--space-1) var(--space-4);
  margin: var(--space-5) 0;
  color: var(--muted);
}
/* Tables: dark header, own scroll container, restacked on mobile by the
   shared rule further down. */
/* The table is a raised card, not a grid of 1px rules: a shadow to lift it
   off the page, a gradient header so the head reads as a bar, and rows that
   respond to the cursor. border-collapse:separate is what lets the rounded
   corners survive — with collapse the header's corners square off. */
.table-scroll {
  overflow-x: auto; -webkit-overflow-scrolling: touch; margin: var(--space-5) 0;
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-md);
  border: 1px solid var(--border);
}
table { border-collapse: separate; border-spacing: 0; width: 100%; margin: 0; font-size: var(--text-sm); background: var(--bg); }
th, td { padding: var(--space-3) var(--space-4); text-align: left; vertical-align: top; border-bottom: 1px solid var(--border); }
th {
  background: linear-gradient(180deg,
    color-mix(in srgb, var(--fg) 92%, #fff),
    var(--fg));
  color: var(--bg);
  font-family: var(--font-heading);
  font-weight: 700;
  letter-spacing: 0.02em;
  border-bottom: none;
  position: sticky; top: 0;
}
tbody tr:last-child td { border-bottom: none; }
tbody tr:nth-child(even) td { background: var(--surface); }
tbody tr { transition: background .12s ease; }
tbody tr:hover td { background: color-mix(in srgb, var(--accent) 7%, var(--bg)); }
.callout {
  border-left: 3px solid;
  padding: var(--space-4);
  margin: var(--space-5) 0;
  border-radius: var(--radius-md);
  font-size: var(--text-base);
}
.callout--info    { border-color: #2563eb; background: #eff6ff; }
.callout--warning { border-color: #d97706; background: #fffbeb; }
.callout--pro-tip { border-color: #10b981; background: #ecfdf5; }
.callout--note    { border-color: #6b7280; background: var(--surface); }
.pros-cons { display: grid; grid-template-columns: 1fr 1fr; gap: 1rem; }
.pros li::marker { content: '✔  '; color: #10b981; }
.cons li::marker { content: '✗  '; color: #ef4444; }
.faq dt { font-weight: 600; margin-top: 1rem; }
.faq dd { margin-left: 0; color: var(--muted); }
/* Steps. The number is the browser's own <ol> counter, drawn as a disc so a
   step has an edge for the eye to land on — a numbered list where the number
   looks like text reads as prose that happens to be indented. */
.steps { margin: 1.25rem 0; }
.steps__intro { margin: 0 0 0.75rem; color: var(--muted); }
.steps__list { counter-reset: step; list-style: none; padding: 0; margin: 0;
  display: flex; flex-direction: column; gap: 0.75rem; }
.steps__step { counter-increment: step; position: relative; padding-left: 2.5rem; }
.steps__step::before { content: counter(step); position: absolute; left: 0; top: 0;
  width: 1.75rem; height: 1.75rem; border-radius: 50%;
  background: var(--accent); color: #fff;
  display: flex; align-items: center; justify-content: center;
  font-size: 0.85em; font-weight: 600; }
.steps__text { display: block; }
.steps__detail { display: block; margin-top: 0.2rem; color: var(--muted); font-size: 0.92em; }
/* Icon list. A fixed icon column rather than a float: the text of a wrapping
   item lines up under itself instead of under the icon. */
.icon-list { list-style: none; padding: 0; margin: 1.25rem 0;
  display: flex; flex-direction: column; gap: 0.5rem; }
.icon-list__item { display: grid; grid-template-columns: 1.5rem 1fr; gap: 0.5rem;
  align-items: start; }
.icon-list__icon { line-height: 1.5; }
.missing-operator {
  border: 1px dashed #f59e0b; background: #fffbeb;
  padding: 0.5rem 0.75rem; border-radius: 4px;
  color: #92400e; font-family: monospace; font-size: 0.85em;
}
/* Sticky header. CSS-only — position: sticky keeps the header pinned
   to the viewport while content scrolls, with a translucent backdrop
   so brand keeps visible without obscuring text below. Both the
   pre-Wave-R fallback (.site-header) and the section-block slot
   wrapper (.layout-slot--header) get the treatment so layout-builder
   and default sites look consistent. */
.site-header,
.layout-slot--header {
  position: sticky; top: 0; z-index: 50;
  /* --header-bg is set per-site (layout editor → headerBackground). When
     unset (NULL-config fallback sites) it's the translucent-white sticky
     look. */
  background: var(--header-bg, rgba(255, 255, 255, 0.92));
  -webkit-backdrop-filter: saturate(180%) blur(8px);
  backdrop-filter: saturate(180%) blur(8px);
}
.site-header {
  display: flex; align-items: center; justify-content: space-between; gap: 1rem;
  /* Full-bleed bar, but inset the CONTENT so the logo + nav line up with
     the page column below. The column is the page-max / page-gutter pair
     declared in the block sheet; the inset adds the gutter on top of the
     centring, otherwise the header content sits one gutter wider on each side
     than the column. max() keeps the gutter on narrow viewports where the
     calc goes negative. */
  padding: 0.85rem max(var(--page-gutter), calc((100% - var(--page-max)) / 2 + var(--page-gutter)));
  border-bottom: 1px solid var(--border);
}
.site-name {
  display: flex; align-items: center; gap: 0.75rem;
  font-family: var(--font-heading); font-weight: 700;
  font-size: 1.25rem; color: var(--header-fg, var(--primary)); text-decoration: none;
}
.site-name .logo {
  height: 40px; width: 40px; object-fit: contain; border-radius: 6px;
  /* white chip so a white-background logo stays visible on any header */
  background: #fff; padding: 3px; border: 1px solid rgba(0,0,0,0.08);
  box-shadow: 0 1px 3px rgba(0,0,0,0.12);
}
.article {
  max-width: 760px; margin: 2.5rem auto 4rem; padding: 0 1.5rem;
}
/* Default index-page layout (rendered when the site has no pageBody
   slot blocks defined — operator left it bare). The slot wrapper
   .layout-slot--pageBody already enforces max-width when section
   blocks ARE configured; this rule covers the fallback path. */
.article-index {
  max-width: 880px; margin: 3rem auto 4rem; padding: 0 1.5rem;
}
.article-index > h1 {
  font-size: 2.5rem; margin: 0 0 0.4rem;
  letter-spacing: -0.01em;
}
.article-index > h1::after {
  content: ''; display: block; width: 3rem; height: 3px;
  background: var(--accent); border-radius: 2px; margin-top: 0.75rem;
}
.article-list { list-style: none; padding: 0; }
.article-card {
  display: flex; gap: 1rem; align-items: flex-start;
  padding: 1.25rem 0; border-bottom: 1px solid var(--border);
}
/* Hover = highlight only, no layout shift (a padding/transform shift made
   the card content visibly jump). Title link colour-shifts via the rule
   below; card-variant border/shadow highlight lives in EXTRA_BLOCK_CSS. */
.article-card__body { flex: 1 1 auto; min-width: 0; }
.article-card h2 { margin: 0 0 0.35rem; font-size: 1.35rem; }
.article-card h2 a { color: var(--primary); }
.article-card h2 a:hover { color: var(--accent); text-decoration: none; }
.article-card p { margin: 0; color: var(--muted); font-size: 0.95em; }
/* article-card thumbnails (shared: index list + article-list-grid block) */
.article-card__thumb {
  flex: 0 0 auto; width: 168px; aspect-ratio: 16 / 10;
  border-radius: 8px; overflow: hidden; background: var(--surface, #eee);
}
.article-card__thumb img { width: 100%; height: 100%; object-fit: cover; display: block; }
.article-card__thumb--ph {
  display: flex; align-items: center; justify-content: center;
  background: linear-gradient(135deg, hsl(var(--thumb-h, 220) 52% 42%), hsl(calc(var(--thumb-h, 220) + 38) 58% 52%));
}
.article-card__thumb--ph span { font-size: 2.25rem; font-weight: 800; color: rgba(255,255,255,0.9); line-height: 1; }
/* Entity logo in a review hub row. A wordmark has to fit inside the tile;
   the cover crop above is for an article's hero photo and would cut it. */
/* Square and small. The 168px 16:10 tile above is sized for an article's hero
   photo; a brand mark stretched across it showed every soft edge it had. */
.article-card__thumb--logo {
  width: 5rem; aspect-ratio: 1; background: #fff; border: 1px solid var(--border);
}
.article-card__thumb--logo img { object-fit: contain; padding: 0.5rem; box-sizing: border-box; }
/* The brand's score in the site's rating, on the rows that have one. */
.review-hub__score {
  flex: 0 0 auto; align-self: center;
  font-weight: 700; font-size: 1.15rem; color: var(--accent);
}
@media (max-width: 560px) {
  .article-card { flex-direction: column; }
  .article-card__thumb { width: 100%; }
  /* Except a brand mark. Full width is right for an article's hero photo and
     ruinous for a 180px favicon: stretched across the column it filled half a
     phone screen with its own soft edges. The row keeps the mark beside the
     name instead of stacking under it. */
  .article-card--logo { flex-direction: row; align-items: center; }
  .article-card__thumb--logo { width: 4.5rem; flex: 0 0 auto; }

  /* Restack a labelled table as one card per row (S-10). Squeezing 5+
     columns into a phone width made comparison tables unreadable; each row
     becomes a stack of "Label: value" pairs instead. Only applies to tables
     that have headers to label with (.table--stacked). */
  .table--stacked { border: 0; }
  /* The wrapper's own frame comes off once the table is a stack of cards.
     Each row already draws its own border, so the wrapper's added a second
     one down both sides — and two tables in a row then read as one long
     bordered column instead of two separate tables. The gap between them is
     what separates them now. The class is put on the wrapper by the renderer
     rather than matched with :has(), which is not on every phone browser. */
  .table-scroll--stacked {
    border: 0; box-shadow: none; border-radius: 0; overflow: visible;
    margin: var(--space-6) 0;
  }
  .table--stacked thead { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }
  .table--stacked tbody tr {
    display: block; margin: 0 0 0.75rem; border: 1px solid var(--border);
    border-radius: 10px; overflow: hidden; background: var(--surface);
  }
  .table--stacked tbody tr:last-child { margin-bottom: 0; }
  .table--stacked tbody td, .table--stacked tbody th {
    display: flex; gap: 0.75rem; align-items: baseline; justify-content: space-between;
    border: 0; border-bottom: 1px solid var(--border); padding: 0.55rem 0.8rem; background: none;
    /* The value sits on the right of the row, so its own lines line up on the
       right too. Wrapped values used to start back at the left of their box —
       "Curaçao 8048/JAZ2021-" over "184" — which read as a second column that
       was not there. */
    text-align: right;
  }
  .table--stacked tbody tr > *:last-child { border-bottom: 0; }
  .table--stacked tbody td[data-label]::before {
    /* The label leads the row, so it is the heavier of the two. It used to be
       lighter AND smaller than the value it labels — muted grey at 0.85em
       against full-size body text — which put the emphasis on the answer and
       left the reader hunting for the question. */
    content: attr(data-label); font-weight: 700; color: var(--fg);
    flex: 0 0 42%; font-size: 1.02em; text-align: left;
  }
  /* The value, now the quieter half. */
  .table--stacked tbody td[data-label] { font-size: 0.94em; color: var(--muted); }
  /* A row that carries its own label as a th — the fact tables on brand, game
     and provider pages. The label used to render as a full-width band above
     the value, AND the value printed it again from data-label: the same word
     twice, the first one washed out in a surface-coloured strip. The td no
     longer carries data-label at all, and the two sit side by side.
     Adjacent-sibling, not :has() — this has to hold on every phone browser. */
  .table--stacked tbody th[scope="row"] {
    display: inline-block; width: 42%; vertical-align: top;
    font-weight: 700; font-size: 1.02em; color: var(--fg);
    background: none; text-align: left;
    /* No rule under it: side by side with the value, the cell border drew a
       line across 42% of the card and read as a stray underline. Each row is
       its own card here, so nothing needs separating inside one. */
    border-bottom: 0;
  }
  /* A FACT table is one table, not a stack of them. Every row here is a single
     label/value pair, and the card treatment above turned five facts into five
     separate bordered boxes — the operator read them as five tables. The
     wrapper keeps its frame and the rows sit inside it, separated by a rule. */
  .table--facts tbody tr {
    display: block; margin: 0; border: 0; border-radius: 0;
    background: none; border-bottom: 1px solid var(--border);
  }
  .table--facts tbody tr:last-child { border-bottom: 0; }
  .table--stacked tbody th[scope="row"] + td {
    display: inline-block; width: 57%; vertical-align: top;
    text-align: right; font-size: 0.94em; color: var(--muted);
  }
}
.site-footer {
  /* Left, not centred, and thin: the footer is a strip under the page, not a
     closing section of it. Inset to the page column like the header, so the
     first character of the footer sits under the first character of the
     article rather than out at the screen edge. */
  padding: 1rem max(var(--page-gutter), calc((100% - var(--page-max)) / 2 + var(--page-gutter)));
  border-top: 1px solid var(--border);
  color: var(--muted); font-size: 0.8rem; text-align: left;
  /* auto top-margin pins the footer to the bottom of the flex-column
     body on short pages; on long pages there's no free space so it sits
     right after the content. */
  margin-top: auto;
}
.article__title { font-size: 2rem; margin-top: 0; }
.operator-row { border: 1px solid var(--border); border-radius: 6px; padding: 0.75rem; margin: 0.75rem 0; }
.operator-row .logo { height: 40px; width: auto; }
/* hero-operator = a featured operator card: logo + name on the left, the
   bonus CTA pushed to the right. Sits in the centred hero/content column. */
.hero-operator { display: flex; align-items: center; flex-wrap: wrap; gap: 1rem; border: 1px solid var(--border); border-radius: 10px; padding: 1.25rem 1.5rem; margin: 0.75rem 0; background: var(--surface); }
.hero-operator .logo { height: 56px; width: auto; border-radius: 8px; }
.hero-operator .name { margin: 0; font-size: 1.5rem; }
.hero-operator .rating { color: var(--muted); font-size: 0.95rem; }
.hero-operator .cta--hero { margin-left: auto; padding: 0.6em 1.4em; font-size: 1.05em; }
.podium { display: grid; grid-template-columns: 1fr 1fr 1fr; gap: 0.75rem; padding: 0; list-style: none; }
.podium__slot { border: 1px solid var(--border); border-radius: 6px; padding: 0.75rem; text-align: center; }
.podium__slot .logo { height: 40px; width: auto; }
.cta { display: inline-block; background: var(--accent); color: white; padding: 0.4em 0.9em; border-radius: 4px; text-decoration: none; font-weight: 600; }
.ai-image figcaption, .image figcaption { font-size: 0.85em; color: var(--muted); margin-top: 0.25rem; }
.lede { color: var(--muted); font-style: italic; }

/* ===== Wave R section blocks — emitted by render-section-blocks.ts =====
 * Active only on sites with sites.layout_config set. When unset, the
 * pre-Wave-R fallback chrome (.site-header / .site-name / .site-footer
 * from baseSiteCss) renders instead.
 */

/* One content column for the whole page.
   Header, hero, article, sidebar and footer each used to carry their own
   width: the bars were centred on 1100px, the article grid on 1140px, and the
   hero escaped both and ran to the viewport edge. Measured at 1440px the three
   left edges were 194, 174 and 154 and the right edges 1246, 1266 and 1408 —
   nothing lined up with anything, which is how it was reported. These two
   tokens are the column; everything that draws a page-level box uses them.
   The rail formula below is for FULL-BLEED bars (header, footer, hero band):
   the background spans the viewport and only the content is inset. */
:root { --page-max: 1140px; --page-gutter: 1.5rem; }
/* How much of the viewport the sticky header covers. Everything that sticks
   below it has to start past this line, or it sticks UNDERNEATH the bar and
   gets its top cut off — which is what the rail did: the offer card's heading
   was hidden behind the header the moment the page scrolled.
   Measured 73px on a live site; the token is rounded up so a header whose nav
   wraps to a second line still clears. */
:root { --header-h: 4.5rem; }
/* Anchors too: a link to a heading used to scroll it exactly under the bar. */
html { scroll-padding-top: calc(var(--header-h) + 1rem); }
/* Same breakpoint as the article grid, so the gutter narrows for every
   element at the same moment the sidebar drops under the article. */
@media (max-width: 900px) { :root { --page-gutter: 1rem; } }

/* slot wrappers */
.layout-slot { width: 100%; }
.layout-slot--header { display: flex; align-items: center; justify-content: space-between; flex-wrap: wrap; gap: 1rem; padding: 1rem max(var(--page-gutter), calc((100% - var(--page-max)) / 2 + var(--page-gutter))); border-bottom: 1px solid var(--border); background: var(--header-bg, var(--surface)); }
.layout-slot--footer { padding: 2rem max(var(--page-gutter), calc((100% - var(--page-max)) / 2 + var(--page-gutter))); border-top: 1px solid var(--border); background: var(--surface); color: var(--muted); margin-top: auto; }
/* The top gap belongs to the GRID, not to the article column. On the column
   it was a 2.5rem margin the rail did not have, so the first card of the rail
   sat 40px above the first line of the article and the two columns began on
   different lines. */
.article-layout { display: grid; grid-template-columns: minmax(0, 1fr) 320px; gap: 2rem; max-width: var(--page-max); margin: 0 auto; padding: 2.5rem var(--page-gutter) 0; align-items: start; }
/* Inside the two-column layout the article column owns its width, so drop
   the standalone centring/padding it uses on sidebar-less pages. */
.article-layout > .article,
.article-layout > .article-index,
.article-layout > .layout-slot--pageBody { max-width: none; margin: 0 0 4rem; padding: 0; }
/* The rail follows the reader down the page (the spec's "фиксированный блок
   справа от контента при скроле"). align-self:start + sticky is what makes a
   grid child stick rather than stretch to the row height. */
.article-layout > .layout-slot--sidebar { position: sticky; top: calc(var(--header-h) + 1rem); align-self: start; max-height: calc(100vh - var(--header-h) - 2rem); overflow-y: auto; padding-top: 0; }
/* The rail sticks; the cards inside it do not. A sticky child of a scrolling
   box is positioned against that box's scrollport, so an 88px offset pushed
   the offer card 88px DOWN from where it belongs even at rest. Measured at
   1440:
   the card started at y=670 against a rail that started at 566, and its
   bottom ran 64px into the author card below it. Two sticky boxes, one inside
   the other, is one too many. */
.article-layout > .layout-slot--sidebar .top-offer--sticky { position: static; }
/* Both columns start with their first card on the same line. The grid's own
   top padding is the gap; a block that adds its own margin on top of it puts
   one column lower than the other — the rating table's 2rem did exactly that,
   and the rail's first card sat 32px above the article's first heading. */
.article-layout > .layout-slot--pageBody > :first-child,
.article-layout > .article > :first-child,
.article-layout > .article-index > :first-child { margin-top: 0; }
@media (max-width: 900px) {
  /* Not enough width for a rail — stack it under the article and let it
     scroll with the page again. */
  .article-layout { grid-template-columns: 1fr; }
  .article-layout > .article,
  .article-layout > .article-index,
  .article-layout { padding-top: 2rem; }
  .article-layout > .layout-slot--pageBody { margin: 0 0 2rem; }
  .article-layout > .layout-slot--sidebar { position: static; max-height: none; overflow: visible; }
}
/* No horizontal padding. The rail is a column of cards, not a panel holding
   them: its own 1.5rem inset put every card 24px inside the column, so the
   offer card's right edge landed on 1242 against a hero picture and a footer
   that both end on 1266. The surface colour behind it is within three points
   of the page background, so nothing is lost by dropping the frame. */
.layout-slot--sidebar { padding: 1rem 0; background: var(--surface); border-radius: 6px; }
/* Hero slot is centred to the content column like pageBody, so card-style
   blocks (hero-operator, podium, ai-image) sit in the column instead of
   sprawling edge-to-edge. A full-bleed hero-banner escapes this via the
   negative-margin trick below. */
.layout-slot--hero { padding: 0 var(--page-gutter); max-width: var(--page-max); margin: 0 auto 2rem; }
.layout-slot--pageBody { padding: 0 var(--page-gutter); max-width: var(--page-max); margin: 0 auto; }
/* A review page (brand / game / provider) prints its own <h1> and then nests
   the pageBody slot INSIDE main.article, so the slot's gutter applies on top
   of the column the heading already sits in: measured at 1440px, the heading
   started at x=174 and every line of the body at x=198. The heading looked
   detached from its own page. Article pages are unaffected — there the slot
   is a direct child of .article-layout, which already resets it above. */
.article > .layout-slot--pageBody { max-width: none; margin: 0; padding: 0; }

/* site-logo (4 variants) */
.site-logo { display: inline-flex; align-items: center; gap: 0.75rem; text-decoration: none; font-family: var(--font-heading); font-weight: 700; font-size: 1.25rem; color: var(--header-fg, var(--primary)); }
/* White chip behind the logo so a logo generated on a white background
   (the common case) stays visible on ANY header colour — framed on light
   headers, a clean badge on dark/branded ones. */
.site-logo__img { height: 40px; width: 40px; object-fit: contain; border-radius: 6px; background: #fff; padding: 3px; border: 1px solid rgba(0,0,0,0.08); box-shadow: 0 1px 3px rgba(0,0,0,0.12); }
.site-logo--logo-only .site-logo__name { display: none; }
.site-logo--logo-only .site-logo__img { height: 48px; width: 48px; }
.site-logo--name-only .site-logo__img { display: none; }
.site-logo--name-below { flex-direction: column; align-items: flex-start; gap: 0.25rem; }
.site-logo--name-below .site-logo__name { font-size: 1rem; }

/* site-nav */
.site-nav ul { display: flex; gap: 1.25rem; list-style: none; margin: 0; padding: 0; flex-wrap: wrap; }
.site-nav a { color: var(--header-fg, var(--fg)); text-decoration: none; font-weight: 500; padding: 0.25rem 0; border-bottom: 2px solid transparent; transition: border-color 120ms ease; }
.site-nav a:hover { border-bottom-color: var(--accent); text-decoration: none; }
/* The nav and its burger travel together, so the header can put the logo and
   the button on one row. display:contents keeps the wrapper out of the
   header's flex layout while still grouping the two for the sibling selectors
   below. */
.site-nav-bar { display: contents; }
.site-nav__burger {
  display: none; background: none; border: 0; padding: 0.5rem; margin-left: auto;
  cursor: pointer; color: var(--header-fg, var(--fg)); line-height: 0;
}
/* Three bars drawn from one element: the middle is the box, the outer two are
   its pseudo-elements. Nothing to keep in sync with markup. */
.site-nav__burger-bars,
.site-nav__burger-bars::before,
.site-nav__burger-bars::after {
  display: block; width: 22px; height: 2px; border-radius: 2px;
  background: currentColor; content: '';
}
.site-nav__burger-bars { position: relative; }
.site-nav__burger-bars::before { position: absolute; top: -7px; }
.site-nav__burger-bars::after { position: absolute; top: 7px; }
/* An author display declaration beats the UA rule behind [hidden], so both of these
   have to say it themselves — otherwise a scriptless visitor gets a burger
   that does nothing, and a closed menu that is not closed. */
.site-nav__burger[hidden] { display: none; }
.site-nav ul[hidden] { display: none; }

/* On a phone the header used to be 397px of an 812px screen — four menu items
   wrapped to three lines each, and the first word of content sat at y=942.
   Two shapes, and which one applies is decided by whether the script is
   running (ADR-016 rule 1: the page is whole without it).
     - script off: the burger stays [hidden] and the list is a one-line
       scrolling rail, so every link is still reachable;
     - script on:  the burger appears and the list becomes a stacked panel
       that it opens and closes. */
@media (max-width: 640px) {
  .site-nav__burger:not([hidden]) { display: block; }
  .site-nav ul { flex-wrap: nowrap; overflow-x: auto; gap: 1rem; scrollbar-width: none; -webkit-overflow-scrolling: touch; }
  .site-nav ul::-webkit-scrollbar { display: none; }
  .site-nav li { flex: 0 0 auto; }
  .site-nav a { white-space: nowrap; font-size: 0.9rem; }

  .site-nav__burger:not([hidden]) ~ .site-nav { flex-basis: 100%; }
  .site-nav__burger:not([hidden]) ~ .site-nav ul {
    flex-wrap: wrap; flex-direction: column; overflow: visible; gap: 0;
    padding: 0.25rem 0 0.5rem;
  }
  .site-nav__burger:not([hidden]) ~ .site-nav li { width: 100%; }
  .site-nav__burger:not([hidden]) ~ .site-nav a {
    display: block; padding: 0.6rem 0; font-size: 0.95rem;
    border-bottom: 1px solid var(--border); white-space: normal;
  }
}

/* hero-banner */
/* Full-bleed: escape the centred hero slot so the banner image spans the
   whole viewport width (the deliberate full-width hero look). */
/* 100vw, not 100%: the band is pulled out of the hero slot by the negative
   margins below, but a percentage padding still resolves against that slot's
   1092px content box, not against the viewport — which inset the hero content
   by one bare gutter and left it 150px to the left of the article column. */
.hero-banner { position: relative; padding: 4rem max(var(--page-gutter), calc((100vw - var(--page-max)) / 2 + var(--page-gutter))); text-align: center; color: var(--fg); overflow: hidden; width: 100vw; max-width: 100vw; margin-left: calc(50% - 50vw); margin-right: calc(50% - 50vw); }
.hero-banner--bg-primary { background: var(--primary); color: white; }
.hero-banner--bg-accent { background: var(--accent); color: white; }
.hero-banner--bg-primary .hero-banner__title,
.hero-banner--bg-accent .hero-banner__title { color: white; }
/* Flat ground (A3/B2). One solid colour under the hero, the way the
   reference sites do it — a gradient under an offer card reads as noise. */
/* The flat ground was flat on purpose once — a gradient under a card read as
   noise while the card was a thin strip. Now the band carries a heading, a
   full-height offer block and a square picture, and a single mixed colour
   under all three reads as unfinished. Kept shallow: both stops come from the
   theme's own pair, so it is depth rather than a second colour. */
.hero-banner--bg-solid {
  background: linear-gradient(168deg, var(--grad-from), var(--grad-to));
  color: var(--fg);
}
/* The footer closes the page the way the hero opens it, mirrored. */
.site-footer {
  background: linear-gradient(12deg, var(--grad-from), var(--grad-to));
}
.hero-banner--bg-solid .hero-banner__title { color: var(--fg); }
/* Hero with an illustration beside the text. Single column below 900px:
   two columns on a phone leaves neither readable. */
/* The two halves of the band, 2rem apart like the article grid, and both
   ending on the page column's right edge. The picture gets a wide basis on
   purpose: squeezed into the sidebar's 320px it was a thumbnail of a scene,
   and cropped to a square it lost 43% of its width — "не видно сюжета". */
.hero-banner--split .hero-banner__inner { flex: 1 1 26rem; min-width: 0; max-width: none; }
.hero-banner--split .hero-banner__aside { flex: 0 1 30rem; }
/* stretch, not centre. Centred, the picture floated in the middle of its own
   half: its top sat 37px below the headline and its bottom 37px above the
   offer card, and the band had no line either side agreed on. Stretched, the
   two halves start and end together whatever the headline runs to. */
.hero-banner--split { display: flex; flex-wrap: wrap; gap: 2rem; align-items: stretch; }
.hero-banner--split .hero-banner__aside { position: relative; display: block; min-height: 15rem; }
/* Taken OUT of flow, so the picture cannot set the band's height — it can only
   fill whatever the copy beside it takes. In flow with height:100% the
   percentage had nothing definite to resolve against, the picture fell back to
   its own intrinsic height, and being the taller of the two it became the
   height: the copy column stretched to the picture and the offer card ended
   five pixels short of the picture's bottom edge. Reported as a red line under
   both. */
.hero-banner--split .hero-banner__aside img {
  position: absolute; inset: 0;
  width: 100%; height: 100%; aspect-ratio: auto; object-fit: cover;
}
/* And nothing hanging below the last thing in the copy column: the offer
   card's own 1.5rem bottom margin sat inside the column's box, so the picture
   stretched 24px past the card it is supposed to line up with. */
.hero-banner__inner > :last-child { margin-bottom: 0; }
/* Left-aligned only where the two columns actually stand side by side. The
   hero's own centred text is right for a plain band and wrong next to a
   picture: centred lines beside a right-hand image leave a ragged gutter down
   the middle. Below the breakpoint the picture is gone and the centred band is
   back, which is what the phone already looked like. */
@media (min-width: 900px) {
  .hero-banner--split { text-align: left; }
}
/* And not centred on a phone either. A four-line centred headline over a
   centred card is the look the operator sent back: every line starts in a
   different place, so the eye has nowhere to return to. The hero's centring
   was written for a band of one short line, which is not what it holds now. */
@media (max-width: 899px) {
  .hero-banner { text-align: left; }
}
/* Justified body copy on a phone, at the operator's request. Hyphenation is
   not optional with it: a 375px column of Polish has words long enough to open
   rivers of white space between the few that fit on a line. The page carries a
   lang attribute, which is what the hyphenation dictionary keys on. */
@media (max-width: 899px) {
  .article-body p,
  .home-content p,
  .article-index .lede {
    text-align: justify;
    hyphens: auto;
  }
}
.hero-banner__aside { flex: 0 1 340px; }
/* The source's own shape, not a square. These are generated 3:2, and a square
   box with object-fit:cover keeps only the middle 57% of the frame —
   on a wide scene that is the part with nothing in it, which is how it was
   reported. Shown whole, at a width that makes the scene readable, the
   picture is the right-hand half of the band rather than a stamp on it. */
.hero-banner__aside img {
  width: 100%; aspect-ratio: 3 / 2; height: auto; display: block;
  object-fit: cover; object-position: center;
  border-radius: var(--radius-lg, 16px);
}
/* Not on a phone. Stacked under the offer card the picture is decoration
   costing a full screen of scroll before the first line of the page —
   reported with the image crossed out. The hero still has its heading and its
   offer, so nothing is lost but the ornament. */
@media (max-width: 899px) {
  .hero-banner--split .hero-banner__aside { display: none; }
}
/* A hero that is ONLY a picture. Capping its height turned it into a 380px
   square floating in a 1440px band — a stamp in the middle of an empty page,
   which is how it was reported. A hero is a band: the picture spans the
   content width and is CROPPED to a banner shape, so a square illustration
   fills it instead of sitting in the middle of it. */
.hero-banner:not(.hero-banner--split) {
  /* No padding: the picture IS the band. With the section's own 64px/32px the
     image sat as a rounded rectangle floating in a taller coloured box —
     reported twice as "странный хиро", and rightly. */
  padding: 0;
}
.hero-banner:not(.hero-banner--split) .hero-banner__aside { max-width: none; margin: 0; }
.hero-banner:not(.hero-banner--split) .hero-banner__aside img {
  /* A little taller now that the source is 16:9 rather than square: the crop
     from 1.78:1 to roughly 4:1 costs the top and bottom of a picture composed
     to keep nothing important there. */
  width: 100%; height: clamp(200px, 24vw, 340px); object-fit: cover;
  object-position: center;
  /* Edge to edge, so it reads as a band rather than as a picture placed on
     one. Corners rounded on a full-bleed strip look like a mistake. */
  border-radius: 0;
}
/* The top-1 offer card. Data-bound to sites.top_offer_rating_brand_id. */
/* The card has to separate from the band it sits on, and on a light theme it
   does not do that on its own: measured on a live site, the hero's gradient
   ends on rgb(250,252,251) and the card's own background IS rgb(250,252,251).
   At the bottom of the band the card was literally the colour behind it, held
   together by a hairline border and an 8% shadow.
   So: an accent bar along the top edge, a border tinted with the same accent
   and a shadow that carries its colour. None of it depends on the theme being
   light or dark — all three are mixed from --accent. */
.hero-offer {
  display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-4, 1rem);
  background: var(--bg); color: var(--fg);
  border: 1px solid color-mix(in srgb, var(--accent) 30%, var(--border));
  border-radius: var(--radius-lg, 16px);
  /* One number for the card's padding and the badge's pull-out, so a
     breakpoint that grows one grows the other. */
  --offer-pad: var(--space-4, 1rem);
  padding: var(--offer-pad);
  margin: var(--space-5, 1.5rem) 0;
  /* overflow:hidden so the header bar below is cropped by the card's own
     corners instead of squaring them off. The accent line the bar replaces is
     gone from the shadow — one accent edge, not two. */
  overflow: hidden;
  box-shadow: 0 10px 30px color-mix(in srgb, var(--accent) 20%, transparent),
              0 2px 6px rgba(15,23,42,.12);
  max-width: 560px;
}
.hero-offer__logo {
  flex: 0 0 auto;
  width: 56px; height: 56px; object-fit: contain;
  border-radius: var(--radius-md, 10px); background: #fff; padding: 4px;
  border: 1px solid var(--border);
}
/* The card reads left, whatever the band around it does. Inheriting the hero's
   centring gave a centred brand name over a centred five-line paragraph with
   the mark stranded on a line of its own — sent from a phone as "лого криво". */
.hero-offer { text-align: left; }
/* Zero basis, not auto. Flex decides whether to wrap from the item's
   hypothetical size, and with basis:auto that is the bonus line's max-content
   width — so the row wrapped and left the mark alone on a line before
   min-width:0 ever got a chance to shrink anything. */
.hero-offer__body { flex: 1 1 0; min-width: 0; }
/* Two lines, then stop. The bonus line in the database is the operator's full
   terms — "100 Free Spins bez depozytu na Sweet Rush Bonanza (za rejestrację i
   weryfikację telefonu + e-maila); Pakiet powitalny: 100% do €1,000..." — and
   printed whole it turns a promo card into a wall of text taller than the
   phone. The rating table below is where the full terms belong. */
.hero-offer__bonus {
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
/* The card's own name, on its own line above the brand. Same words and same
   treatment as the rail's card, so the two read as one thing in two places. */
/* A bar across the top of the card, white on the accent.
   The operator picked it out of five: a line of accent-coloured text still
   read as a caption, and a corner ribbon reads as an advertisement — this one
   is seen out of the corner of the eye without looking like a sticker.
   The negative margins pull it out to the card's edges by the card's own
   padding variable; the cropping rule up there trims it to the corners. On the
   desktop hero they were a fixed 1rem against 1.5rem of padding, and the bar
   floated inside the card with a white frame round it. */
.hero-offer__label {
  flex: 1 0 100%;
  margin: calc(-1 * var(--offer-pad)) calc(-1 * var(--offer-pad)) 0.6rem;
  padding: 0.5rem var(--offer-pad);
  background: var(--accent); color: #fff;
  font-size: 0.75rem; letter-spacing: .06em; text-transform: uppercase;
  font-weight: 700;
}
/* Brand on the left, score on the right, one line. The rail's copy of this
   card has carried the number since it existed; the hero's said only who the
   offer was from, so the same offer read as rated in one place and unrated in
   the other. */
.hero-offer__eyebrow {
  font-size: 0.8125rem; color: var(--muted);
  display: flex; align-items: baseline; justify-content: space-between; gap: 0.5rem;
}
.hero-offer__score { font-weight: 700; color: var(--fg); white-space: nowrap; font-size: 0.9375rem; }
.hero-offer__score-max { color: var(--muted); font-weight: 400; font-size: 0.8em; }
.hero-offer__bonus { margin: 0.15rem 0 0; font-weight: 600; }
/* The loudest control on the page, and deliberately so: this is the one the
   site is paid for. Three things do the work — a vertical gradient so the
   button has a top and a bottom, a halo in the accent's own colour so it lifts
   off the card, and a sheen that crosses it every few seconds so the eye comes
   back to it. Everything is mixed from --accent, so a site's own palette
   drives it and nothing here is a fixed colour.
   Shared with the rail's copy of the card, one rule for both. */
.hero-offer__cta,
.top-offer__cta {
  position: relative; overflow: hidden;
  background: linear-gradient(180deg,
    color-mix(in srgb, var(--accent) 80%, #fff),
    var(--accent) 55%,
    color-mix(in srgb, var(--accent) 78%, #000));
  color: #fff;
  border-radius: var(--radius-pill, 999px);
  font-weight: 700; letter-spacing: 0.01em; text-decoration: none;
  box-shadow: 0 1px 0 rgba(255,255,255,0.4) inset,
              0 0 0 3px color-mix(in srgb, var(--accent) 18%, transparent),
              0 8px 22px color-mix(in srgb, var(--accent) 50%, transparent);
  transition: transform .12s ease, box-shadow .12s ease, filter .12s ease;
}
.hero-offer__cta { flex: 0 0 auto; padding: 0.8rem 1.5rem; white-space: nowrap; }
.top-offer__cta { display: block; text-align: center; padding: 0.75rem 1rem; }
.hero-offer__cta::after,
.top-offer__cta::after {
  content: ''; position: absolute; top: 0; left: -60%; width: 40%; height: 100%;
  background: linear-gradient(100deg, transparent, rgba(255,255,255,0.45), transparent);
  animation: cta-sheen 3.4s ease-in-out infinite;
  pointer-events: none;
}
@keyframes cta-sheen {
  0% { left: -60%; }
  55% { left: 130%; }
  100% { left: 130%; }
}
/* A moving highlight is exactly what a reader who asked for less motion asked
   to be rid of. The button keeps its gradient and its halo. */
@media (prefers-reduced-motion: reduce) {
  .hero-offer__cta::after,
  .top-offer__cta::after { animation: none; opacity: 0; }
}
.hero-offer__cta:hover,
.top-offer__cta:hover {
  filter: brightness(1.05); text-decoration: none; transform: translateY(-1px);
  box-shadow: 0 1px 0 rgba(255,255,255,0.45) inset,
              0 0 0 4px color-mix(in srgb, var(--accent) 22%, transparent),
              0 12px 28px color-mix(in srgb, var(--accent) 58%, transparent);
}
.hero-offer__cta:active,
.top-offer__cta:active {
  transform: translateY(1px);
  box-shadow: 0 2px 8px color-mix(in srgb, var(--accent) 35%, transparent);
}
/* On a phone the mark stays beside the name and only the button drops to its
   own line. The body needs min-width:0 for that: without it the long bonus
   string sets a min-content width that pushes the mark onto a line by itself,
   which is what the phone screenshot showed. */
@media (max-width: 560px) {
  .hero-offer { flex-wrap: wrap; }
  .hero-offer__cta { width: 100%; text-align: center; }
}
/* The money card, at the size it deserves on a desktop hero — taller, not
   wider. As one flat row it was a thin strip beside a 340px picture; giving
   the button its own full-width line stacks the card into a block that holds
   its half of the band, and puts the thing being clicked on its own line. */
@media (min-width: 900px) {
  .hero-banner--split .hero-offer {
    flex-wrap: wrap;
    max-width: 34rem;
    gap: var(--space-4, 1rem);
    --offer-pad: var(--space-5, 1.5rem);
  }
  .hero-banner--split .hero-offer__logo { width: 72px; height: 72px; border-radius: var(--radius-lg, 16px); }
  .hero-banner--split .hero-offer__body { flex: 1 1 12rem; }
  .hero-banner--split .hero-offer__eyebrow { font-size: 0.875rem; }
  .hero-banner--split .hero-offer__bonus { font-size: 1.0625rem; line-height: 1.4; }
  .hero-banner--split .hero-offer__cta {
    flex: 1 0 100%;
    text-align: center;
    padding: 0.9rem 1.5rem;
    font-size: 1.0625rem;
  }
}
.hero-banner--bg-gradient { background: linear-gradient(135deg, var(--grad-from), var(--grad-to)); color: white; }
.hero-banner--bg-gradient .hero-banner__title { color: white; }
.hero-banner--bg-hero-image { color: white; }
.hero-banner--bg-hero-image::before { content: ''; position: absolute; inset: 0; background: rgba(0,0,0,0.45); pointer-events: none; }
.hero-banner--bg-hero-image .hero-banner__inner { position: relative; z-index: 1; }
.hero-banner--bg-hero-image .hero-banner__title { color: white; }
/* hero image used as the default background (any mode except solid). The
   overlay keeps text legible over an arbitrary photo; gradient mode tints
   the photo with the brand gradient instead of flat black. */
.hero-banner--has-image { color: white; }
.hero-banner--has-image::before { content: ''; position: absolute; inset: 0; background: rgba(0,0,0,0.45); pointer-events: none; }
.hero-banner--has-image .hero-banner__inner { position: relative; z-index: 1; }
.hero-banner--has-image .hero-banner__title { color: white; }
.hero-banner--bg-gradient.hero-banner--has-image::before { background: linear-gradient(135deg, color-mix(in srgb, var(--grad-from) 65%, transparent), color-mix(in srgb, var(--grad-to) 55%, transparent)); }
.hero-banner__inner { max-width: 760px; margin: 0 auto; }
.hero-banner__title { font-size: 2.75rem; margin: 0 0 0.5rem; }
.hero-banner__sub { font-size: 1.125rem; margin: 0 0 1.5rem; opacity: 0.9; }
.hero-banner__cta { display: inline-block; background: var(--accent); color: white; padding: 0.65rem 1.5rem; border-radius: 6px; font-weight: 600; text-decoration: none; transition: transform 120ms ease; }
.hero-banner__cta:hover { transform: translateY(-1px); text-decoration: none; }
.hero-banner--bg-gradient .hero-banner__cta,
.hero-banner--bg-hero-image .hero-banner__cta,
.hero-banner--has-image .hero-banner__cta,
.hero-banner--bg-primary .hero-banner__cta,
.hero-banner--bg-accent .hero-banner__cta { background: white; color: var(--primary); }

/* article-list-grid (multiple variants — grid / list / cards) */
/* variants match the schema enum: compact | card | full (default card).
   'card' = grid of bordered cards (thumb on top); 'compact' = tight list
   (thumb left, base row); 'full' = spacious stacked (thumb on top). */
.article-list--card { display: grid; grid-template-columns: repeat(auto-fill, minmax(260px, 1fr)); gap: 1.5rem; padding: 0; }
.article-list--compact, .article-list--full { padding: 0; }
.article-card--card { background: var(--surface); border: 1px solid var(--border); border-radius: 8px; padding: 1.25rem; transition: border-color 140ms ease, box-shadow 140ms ease; flex-direction: column; align-items: stretch; }
.article-card--card:hover { border-color: var(--accent); box-shadow: 0 4px 16px rgba(0,0,0,0.10); }
.article-card--card .article-card__thumb { width: 100%; }
.article-card--compact { padding: 0.75rem 0; border-bottom: 1px solid var(--border); }
.article-card--compact .article-card__thumb { width: 96px; aspect-ratio: 1 / 1; }
.article-card--full { padding: 1.5rem 0; border-bottom: 1px solid var(--border); flex-direction: column; align-items: stretch; }
.article-card--full .article-card__thumb { width: 100%; aspect-ratio: 16 / 9; }
.article-card--card h3, .article-card--compact h3, .article-card--full h3 { margin: 0 0 0.4rem; font-size: 1.125rem; }
.article-card--card h3 a, .article-card--compact h3 a, .article-card--full h3 a { color: var(--primary); }
.article-card--card p, .article-card--compact p, .article-card--full p { margin: 0; font-size: 0.9rem; color: var(--muted); }

/* featured-article — bigger spotlight card */
.featured-article { background: linear-gradient(135deg, var(--surface) 0%, var(--bg) 100%); border: 1px solid var(--border); border-radius: 12px; padding: 2rem; margin: 1.5rem 0; }
.featured-article h2 { margin: 0 0 0.5rem; font-size: 1.75rem; }
.featured-article h2 a { color: var(--primary); }
.featured-article p { margin: 0; font-size: 1.05rem; color: var(--fg); }

/* newsletter-cta */
.newsletter-cta { background: var(--surface); border: 1px solid var(--border); border-radius: 8px; padding: 1.5rem; margin: 1.5rem 0; text-align: center; }
.newsletter-cta h3 { margin: 0 0 1rem; }
.newsletter-cta__form { display: flex; gap: 0.5rem; max-width: 460px; margin: 0 auto; flex-wrap: wrap; justify-content: center; }
.newsletter-cta__form input[type="email"] { flex: 1 1 220px; padding: 0.65rem 0.9rem; border: 1px solid var(--border); border-radius: 6px; font: inherit; }
.newsletter-cta__form button { background: var(--accent); color: white; border: none; padding: 0.65rem 1.25rem; border-radius: 6px; font-weight: 600; cursor: pointer; }
.newsletter-cta__form button:hover { filter: brightness(1.05); }

/* The site's pinned top-1 offer, as a card (sidebar rail + between an
   article's sections). Vertical: it is written for a 320px column, and the
   hero's horizontal card does not survive that width. */
.top-offer {
  display: block; background: var(--bg); color: var(--fg);
  border: 1px solid color-mix(in srgb, var(--accent) 30%, var(--border));
  border-radius: var(--radius-lg, 16px);
  padding: 1rem; margin: 0 0 1rem;
  /* Same reason the hero's card crops: the heading bar bleeds to the edges. */
  overflow: hidden;
  box-shadow: 0 8px 22px color-mix(in srgb, var(--accent) 16%, transparent),
              0 2px 6px rgba(15,23,42,.10);
}
/* The rail's copy of the card wears the same bar: the two are one thing in
   two places, and a bar in the hero with a grey caption in the rail reads as
   two different offers. */
.top-offer__heading {
  margin: -1rem -1rem 0.75rem;
  padding: 0.45rem 1rem;
  background: var(--accent); color: #fff;
  font-size: 0.75rem; letter-spacing: .06em; text-transform: uppercase; font-weight: 700;
}
.top-offer__head { display: flex; align-items: center; gap: 0.6rem; }
.top-offer__logo { width: 44px; height: 44px; object-fit: contain; background: #fff; border: 1px solid var(--border); border-radius: var(--radius-md, 10px); padding: 3px; flex: 0 0 auto; }
.top-offer__name { font-family: var(--font-heading); font-weight: 700; flex: 1 1 auto; min-width: 0; }
.top-offer__score { font-weight: 700; white-space: nowrap; }
.top-offer__score-max { color: var(--muted); font-weight: 400; font-size: 0.8em; }
.top-offer__bonus { margin: 0.6rem 0 0.8rem; font-size: 0.9rem; color: var(--muted); }

/* Only in the rail, and only where there is a viewport to stick to: on a
   phone the sidebar is stacked under the article, and a sticky card there
   parks itself over the text the reader is trying to finish. */
@media (min-width: 861px) {
  .top-offer--sticky { position: sticky; top: calc(var(--header-h) + 1rem); }
}
/* The copy of the card the article body carries between its sections. Full
   width of the column, and never sticky — it is part of the text's flow. */
.article-body > .top-offer { margin: 1.75rem 0; position: static; }

/* author-bio. Compact in the rail: beside the offer card it is a credential,
   not a section — the 64px portrait and the full bio made it the tallest
   thing in the column. */
.layout-slot--sidebar .author-bio { gap: 0.6rem; font-size: 0.9rem; }
.layout-slot--sidebar .author-bio__avatar { width: 40px; height: 40px; font-size: 0.85rem; }
.layout-slot--sidebar .author-bio h4 { font-size: 0.95rem; }
.layout-slot--sidebar .author-bio p { margin: 0.15rem 0 0.25rem; font-size: 0.85rem; }
/* author-bio */
.author-bio { display: grid; grid-template-columns: auto 1fr; gap: 1rem; align-items: start; background: var(--surface); border: 1px solid var(--border); border-radius: 8px; padding: 1.25rem; margin: 1.5rem 0; }
.author-bio__avatar { grid-row: span 3; width: 64px; height: 64px; border-radius: 50%; object-fit: cover; }
.author-bio h4 { margin: 0; font-size: 1.05rem; }
.author-bio p { margin: 0.25rem 0 0.5rem; font-size: 0.95rem; color: var(--muted); }
.author-bio__socials { display: flex; gap: 0.75rem; flex-wrap: wrap; }
.author-bio__social { font-size: 0.85rem; padding: 0.2rem 0.5rem; border-radius: 4px; background: var(--bg); border: 1px solid var(--border); color: var(--fg); text-transform: capitalize; text-decoration: none; }
.author-bio__social:hover { background: var(--accent); color: white; border-color: var(--accent); text-decoration: none; }

/* footer-links
   A thin strip, not a sitemap. Two legal links stacked under a centred LEGAL
   heading took a third of a phone screen and read as a section of the page;
   they are a footer. One row, left-aligned, small — the column heading sits
   inline with its links and stops being a title. */
.footer-links {
  display: flex; flex-wrap: wrap; align-items: baseline; gap: 0.4rem 1.25rem;
  max-width: var(--page-max); margin: 0 auto; text-align: left;
}
.footer-links__col { display: flex; flex-wrap: wrap; align-items: baseline; gap: 0.4rem 1rem; }
.footer-links__col h5 {
  margin: 0; font-size: 0.72rem; text-transform: uppercase; letter-spacing: 0.06em;
  color: var(--muted); font-weight: 600;
}
.footer-links__col ul { list-style: none; padding: 0; margin: 0; display: flex; flex-wrap: wrap; gap: 0.3rem 1rem; }
.footer-links__col li { margin: 0; }
.footer-links__col a { color: var(--muted); font-size: 0.8rem; }
.footer-links__col a:hover { color: var(--accent); }

/* footer-disclaimer */
/* Left, with the links, and tighter: it is the same strip. */
.footer-disclaimer { max-width: var(--page-max); margin: 0.75rem auto 0; padding-top: 0.75rem; border-top: 1px solid var(--border); font-size: 0.75rem; color: var(--muted); text-align: left; }
.footer-disclaimer strong { color: var(--fg); }

/* rich fallback footer (sites with no configured footer slot) */
.site-footer--rich { display: flex; flex-direction: column; align-items: center; gap: 0.25rem; }
.site-footer--rich .site-footer__name { font-weight: 700; color: var(--fg); font-size: 1.05rem; }
.site-footer--rich .site-footer__tagline { margin: 0.25rem 0 0; color: var(--muted); font-size: 0.85rem; }
.site-footer--rich .footer-disclaimer { width: 100%; }
.site-footer--rich .site-footer__legal { margin: 1rem 0 0; font-size: 0.8rem; color: var(--muted); }

/* bonus-badges */
.bonus-badges { display: flex; flex-wrap: wrap; gap: 1rem; list-style: none; padding: 0; margin: 1.5rem 0; }
.bonus-badge { display: flex; flex-direction: column; align-items: center; gap: 0.15rem; min-width: 120px; padding: 1rem 1.25rem; border: 1px solid var(--border); border-radius: 12px; background: var(--surface); }
.bonus-badge__icon { font-size: 1.5rem; line-height: 1; }
.bonus-badge__value { font-family: var(--font-heading); font-weight: 800; font-size: 1.4rem; color: var(--primary); }
.bonus-badge__label { font-size: 0.8rem; color: var(--muted); text-transform: uppercase; letter-spacing: 0.04em; }

/* offer-hero */
.offer-hero { border: 1px solid var(--border); border-radius: 16px; padding: 2.5rem 2rem; margin: 1.5rem 0; text-align: center; background: linear-gradient(135deg, var(--surface) 0%, var(--bg) 100%); }
.offer-hero__inner { max-width: 640px; margin: 0 auto; }
.offer-hero__eyebrow { text-transform: uppercase; letter-spacing: 0.08em; font-size: 0.8rem; color: var(--accent); margin: 0 0 0.5rem; font-weight: 700; }
.offer-hero__headline { font-size: 2.5rem; margin: 0 0 0.75rem; color: var(--primary); }
.offer-hero__sub { font-size: 1.1rem; color: var(--muted); margin: 0 0 1.5rem; }
.offer-hero__cta { display: inline-block; background: var(--accent); color: #fff; padding: 0.8rem 2rem; border-radius: 8px; font-weight: 700; font-size: 1.05rem; text-decoration: none; }
.offer-hero__cta:hover { filter: brightness(1.05); text-decoration: none; }
.offer-hero__fine { font-size: 0.75rem; color: var(--muted); margin: 1rem 0 0; }

/* operator-tiles */
.operator-tiles { display: grid; grid-template-columns: repeat(auto-fill, minmax(160px, 1fr)); gap: 1rem; margin: 1.5rem 0; }
.operator-tile { display: flex; flex-direction: column; align-items: center; gap: 0.5rem; padding: 1.25rem; border: 1px solid var(--border); border-radius: 12px; background: var(--surface); text-decoration: none; transition: border-color 140ms ease, box-shadow 140ms ease; }
.operator-tile:hover { border-color: var(--accent); box-shadow: 0 4px 16px rgba(0,0,0,0.10); text-decoration: none; }
.operator-tile__logo { height: 48px; width: auto; max-width: 100%; object-fit: contain; }
.operator-tile__bonus { font-size: 0.8rem; color: var(--muted); text-align: center; }

/* operator-placeholder — stub for Phase 3.2 operator binding */
.operator-placeholder { padding: 1rem; border: 1px dashed var(--muted); border-radius: 6px; color: var(--muted); font-style: italic; text-align: center; margin: 1rem 0; }

/* rating-table (S-03) — the site's assigned rating, ordered brand cards */
.rating-table { margin: 1.5rem 0; }
.rating-table__heading { font-size: 1.5rem; margin: 0 0 1rem; color: var(--primary); }
.rating-table__list { display: flex; flex-direction: column; gap: 0.75rem; }
.rating-card { display: grid; grid-template-columns: auto 56px 1fr auto auto; align-items: center; gap: 1rem; padding: 0.9rem 1.1rem; border: 1px solid var(--border); border-radius: 12px; background: var(--surface); }
.rating-card__rank { font-weight: 700; color: var(--accent); font-size: 1.05rem; min-width: 1.2em; text-align: center; }
.rating-card__logo { height: 40px; width: 56px; object-fit: contain; }
.rating-card__logo--ph { display: inline-flex; align-items: center; justify-content: center; border-radius: 8px; background: var(--bg); color: var(--muted); font-weight: 700; }
.rating-card__name { font-weight: 600; color: var(--fg); }
.rating-card__bonus { color: var(--muted); font-size: 0.9rem; }
/* Second in the hierarchy, and it has to look it. Same family as the top-1
   button — the accent's own gradient, a highlight along the top edge — but
   the depth comes from a hard bottom edge rather than a halo, and there is no
   sheen. Next to a rating card the top-1 button still wins the eye; on its own
   this reads as a solid, pressable control instead of a flat rectangle. */
.rating-card__cta {
  display: inline-block;
  background: linear-gradient(180deg,
    color-mix(in srgb, var(--accent) 86%, #fff),
    var(--accent));
  color: #fff; padding: 0.55rem 1.2rem; border-radius: 8px;
  font-weight: 700; font-size: 0.9rem; text-decoration: none; white-space: nowrap;
  box-shadow: 0 1px 0 rgba(255,255,255,0.35) inset,
              0 3px 0 color-mix(in srgb, var(--accent) 60%, #000),
              0 5px 12px color-mix(in srgb, var(--accent) 26%, transparent);
  transition: transform .12s ease, box-shadow .12s ease, filter .12s ease;
}
.rating-card__cta:hover {
  filter: brightness(1.05); text-decoration: none; transform: translateY(-1px);
  box-shadow: 0 1px 0 rgba(255,255,255,0.4) inset,
              0 4px 0 color-mix(in srgb, var(--accent) 60%, #000),
              0 9px 18px color-mix(in srgb, var(--accent) 34%, transparent);
}
/* Pressed, the button sits down ON its own edge — the edge shrinks with it. */
.rating-card__cta:active {
  transform: translateY(2px);
  box-shadow: 0 1px 0 color-mix(in srgb, var(--accent) 60%, #000),
              0 2px 6px color-mix(in srgb, var(--accent) 24%, transparent);
}
.rating-card__score { font-weight: 700; color: var(--primary); white-space: nowrap; }
@media (max-width: 640px) {
  .rating-card { grid-template-columns: auto 44px 1fr; row-gap: 0.5rem; }
  .rating-card__cta, .rating-card__score { grid-column: 2 / -1; justify-self: start; }
}

/* site-type chrome (spec: Типы Сайтов) */
/* Scroll-snap rail — the swipe feel of a carousel with no runtime JS. */
.rail { margin: 1.5rem 0; }
.rail__heading { font-size: 1.35rem; margin: 0 0 0.75rem; color: var(--primary); }
.rail__track { display: flex; gap: 0.75rem; overflow-x: auto; scroll-snap-type: x mandatory; -webkit-overflow-scrolling: touch; padding-bottom: 0.5rem; }
.rail__item { flex: 0 0 auto; scroll-snap-align: start; width: 132px; display: flex; flex-direction: column; gap: 0.4rem; align-items: center; text-align: center; text-decoration: none; color: var(--fg); }
.rail__item img { width: 132px; height: 132px; object-fit: cover; border-radius: 12px; border: 1px solid var(--border); background: var(--surface); }
.rail__item span { font-size: 0.8rem; color: var(--muted); }
.rail__item--logo { width: auto; }
.rail__item--logo img { width: auto; height: 44px; padding: 0.4rem 0.8rem; object-fit: contain; }

/* game-stats — reuses the responsive table rules from S-10a */
.game-stats { margin: 1.5rem 0; }
.game-stats__heading { font-size: 1.35rem; margin: 0 0 0.75rem; color: var(--primary); }
.game-stats th[scope="row"] { width: 40%; background: var(--surface); }

/* game-demo */
.game-demo { margin: 1.5rem 0; border: 1px solid var(--border); border-radius: 14px; overflow: hidden; background: var(--surface); }
.game-demo iframe { display: block; width: 100%; aspect-ratio: 16 / 10; border: 0; }
.game-demo--placeholder { position: relative; display: flex; align-items: center; justify-content: center; aspect-ratio: 16 / 10; }
.game-demo__poster { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
.game-demo__label { position: relative; z-index: 1; background: var(--accent); color: #fff; padding: 0.7rem 1.6rem; border-radius: 999px; font-weight: 700; }

/* header CTA (Login / PLAY) */
.header-cta { background: var(--accent); color: #fff; padding: 0.5rem 1.2rem; border-radius: 8px; font-weight: 700; font-size: 0.9rem; text-decoration: none; white-space: nowrap; }
.header-cta:hover { filter: brightness(1.05); text-decoration: none; }

/* Sticky navbar — the spec asks for a header that stays on scroll. */
.layout-slot--header { position: sticky; top: 0; z-index: 50; }

/* cta-button (S-10): operator-placed CTA inside the article body */
.cta-block { margin: 1.5rem 0; }
.cta-block--center { text-align: center; }
.cta-block--full .cta { display: block; text-align: center; }
.cta-block__fine { margin: 0.5rem 0 0; font-size: 0.75rem; color: var(--muted); }

/* author-bio extras (S-10): job title + last-updated line */
/* The role in bold — it is the reason the name is on the page at all, and in
   plain muted grey it read as a second line of the bio. */
.author-bio__role { margin: 0.1rem 0 0; font-size: 0.85rem; color: var(--muted); font-weight: 700; }
/* The date, last and small. The class alone loses to the rail's compact
   paragraph rule inside the sidebar slot, so the selector names the element
   as well — same weight, and later in the sheet. */
.author-bio p.author-bio__updated { margin: 0.75rem 0 0; font-size: 0.7rem; color: var(--muted); }
/* No pill. Highlighted, a two-line date was the loudest thing in a box whose
   subject is a person — and in a 320px rail it wrapped, so the tint broke
   across two ragged rows. */
.author-bio__updated time { background: none; padding: 0; }

/* home-content (S-08) — article body published into the home page */
.home-content { max-width: var(--page-max); margin: 0 auto 2rem; padding: 0 var(--page-gutter); }

/* offer-bar — the site's pinned offer, along the bottom of the window.
   Painted in the accent rather than the surface: it is a promo, and a strip
   the colour of the page reads as part of the page. The cookie bar sits above
   it while both are up — consent first, promo second — which is why this one
   carries the lower z-index of the two. */
.offer-bar {
  position: fixed; left: 0; right: 0; bottom: 0; z-index: 9990;
  display: flex; align-items: center; gap: 0.85rem;
  padding: 0.6rem max(var(--page-gutter), calc((100vw - var(--page-max)) / 2 + var(--page-gutter)));
  background: linear-gradient(180deg, color-mix(in srgb, var(--accent) 86%, #fff), var(--accent));
  color: #fff;
  box-shadow: 0 -4px 18px rgba(0,0,0,0.18);
}
.offer-bar[hidden] { display: none; }
.offer-bar__logo {
  flex: 0 0 auto; width: 40px; height: 40px; object-fit: contain;
  background: #fff; border-radius: 8px; padding: 3px;
}
.offer-bar__name { font-weight: 700; white-space: nowrap; }
/* One line, and the first to give up its width. The bonus line in the
   database is the operator's full terms, and a strip is not where they fit. */
.offer-bar__bonus {
  flex: 1 1 0; min-width: 0;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  font-size: 0.95rem;
}
.offer-bar__cta {
  flex: 0 0 auto; background: #fff; color: var(--accent);
  padding: 0.55rem 1.4rem; border-radius: var(--radius-pill, 999px);
  font-weight: 700; text-decoration: none; white-space: nowrap;
}
.offer-bar__cta:hover { filter: brightness(0.97); text-decoration: none; }
.offer-bar__close {
  flex: 0 0 auto; background: none; border: 0; color: #fff;
  font-size: 1.4rem; line-height: 1; cursor: pointer; padding: 0 0.2rem;
  opacity: 0.75;
}
.offer-bar__close:hover { opacity: 1; }
/* On a phone the brand name goes: the logo says who it is, and the terms and
   the button are what the strip is for. */
@media (max-width: 560px) {
  .offer-bar { gap: 0.6rem; padding-inline: 0.75rem; }
  .offer-bar__name { display: none; }
  .offer-bar__bonus { font-size: 0.85rem; }
  .offer-bar__cta { padding: 0.5rem 1rem; font-size: 0.9rem; }
}

/* cookie-banner (S-15) — fixed consent bar, dismissed via localStorage */
/* --bg, not --surface: this banner is fixed over arbitrary page content,
   and on a dark theme --surface is white at 5% opacity — a pane of glass.
   The text was landing unreadable on top of the hero image. A surface token
   meant for a card inside the page cannot be reused by something that floats
   over one. */
.cookie-banner { position: fixed; left: 0; right: 0; bottom: 0; z-index: 9999; display: flex; align-items: center; justify-content: center; gap: 1rem; flex-wrap: wrap; padding: 0.85rem 1.25rem; background: var(--bg); color: var(--fg); border-top: 1px solid var(--border); box-shadow: 0 -2px 12px rgba(0,0,0,0.18); }
.cookie-banner[hidden] { display: none; }
.cookie-banner__text { margin: 0; font-size: 0.85rem; color: var(--muted); }
.cookie-banner__link { color: var(--accent); }
.cookie-banner__accept { background: var(--accent); color: #fff; border: 0; padding: 0.5rem 1.4rem; border-radius: 8px; font-weight: 700; font-size: 0.85rem; cursor: pointer; }
.cookie-banner__accept:hover { filter: brightness(1.05); }

/* floating-banner (S-12) — promo, bottom-right desktop / bottom mobile */
.floating-banner { position: fixed; right: 1.25rem; bottom: 1.25rem; z-index: 9998; max-width: 320px; padding: 1rem 1.1rem; background: var(--surface); border: 1px solid var(--border); border-radius: 14px; box-shadow: 0 8px 28px rgba(0,0,0,0.16); }
.floating-banner[hidden] { display: none; }
.floating-banner__close { position: absolute; top: 0.35rem; right: 0.55rem; background: none; border: 0; font-size: 1.15rem; line-height: 1; color: var(--muted); cursor: pointer; }
.floating-banner__heading { margin: 0 0 0.35rem; font-weight: 700; color: var(--primary); font-size: 0.95rem; }
.floating-banner__text { margin: 0 0 0.75rem; font-size: 0.85rem; color: var(--muted); }
.floating-banner__cta { display: inline-block; background: var(--accent); color: #fff; padding: 0.55rem 1.2rem; border-radius: 8px; font-weight: 700; font-size: 0.85rem; text-decoration: none; }
.floating-banner__cta:hover { filter: brightness(1.05); text-decoration: none; }
@media (max-width: 640px) {
  .floating-banner { left: 0.75rem; right: 0.75rem; bottom: 0.75rem; max-width: none; }
}

/* mobile */
@media (max-width: 640px) {
  /* Vertical only — the gutter is the page token now, the same one the
     article grid uses, so the bars cannot drift from the column again. */
  .layout-slot--header { padding-block: 0.75rem; }
  .hero-operator { flex-direction: column; align-items: flex-start; }
  .hero-operator .cta--hero { margin-left: 0; }
  .hero-banner { padding-block: 2.5rem; }
  .hero-banner__title { font-size: 1.875rem; }
  .author-bio { grid-template-columns: 1fr; text-align: center; }
  .author-bio__avatar { grid-row: auto; margin: 0 auto; }
}

/* ----- rating cards (A3b) ----------------------------------------------- */
.rating-table { margin: var(--space-6) 0; }
.rating-table__heading { font-size: var(--text-2xl); margin: 0 0 var(--space-4); color: var(--fg); }
.rating-table__list { display: flex; flex-direction: column; gap: var(--space-3); }
.rating-card {
  display: grid;
  grid-template-columns: 3.25rem 6.5rem 1fr auto;
  align-items: center;
  gap: var(--space-4);
  padding: var(--space-4) var(--space-5);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  background: var(--bg);
  box-shadow: var(--shadow-sm);
  transition: box-shadow .15s ease, border-color .15s ease, transform .15s ease;
}
.rating-card:hover {
  box-shadow: var(--shadow-lg);
  border-color: color-mix(in srgb, var(--accent) 35%, var(--border));
  transform: translateY(-2px);
}
/* The top slot is the one the page is selling. It carries the accent along
   its edge and a tinted shadow, so the eye lands there before it reads. */
.rating-card--top {
  border-color: color-mix(in srgb, var(--accent) 45%, var(--border));
  background: linear-gradient(180deg, color-mix(in srgb, var(--accent) 6%, var(--bg)), var(--bg) 60%);
  box-shadow: 0 10px 30px color-mix(in srgb, var(--accent) 22%, transparent);
}
.rating-card__rankcol { display: flex; flex-direction: column; align-items: center; gap: 2px; }
.rating-card__rank {
  display: grid; place-items: center;
  width: 1.9rem; height: 1.9rem;
  border-radius: var(--radius-md);
  background: var(--surface); color: var(--fg);
  font-family: var(--font-heading); font-weight: 700; font-size: var(--text-sm);
}
.rating-card--top .rating-card__rank { background: var(--accent); color: #fff; }
.rating-card__rank-label {
  font-size: 0.625rem; letter-spacing: .06em; text-transform: uppercase;
  color: var(--muted); text-align: center; line-height: 1.1;
}
/* The entity's mark on its own review page. It used to render at its natural
   size — a 256px favicon stretched across the column, where every soft edge in
   it showed. A mark is an identifier, not an illustration: small, beside the
   title's block, in the same white tile the rating cards use. */
.review-logo { margin: 0 0 var(--space-4); }
.review-logo img {
  display: block; width: 4rem; height: 4rem; object-fit: contain;
  background: #fff; border: 1px solid var(--border);
  border-radius: var(--radius-md); padding: var(--space-2);
}

.rating-card__logobox {
  display: grid; place-items: center;
  height: 5rem; background: #fff;
  border: 1px solid var(--border); border-radius: var(--radius-md);
  padding: var(--space-2);
  /* The mark is a square with its own corners, and the tile has rounded ones.
     Without this the corners poke past the rounded edge and the logo reads as
     sitting ON the tile rather than in it — reported as "лого вылезают из
     рамки". Nothing inside can escape now, whatever shape it is. */
  overflow: hidden;
}
/* The mark fills the tile's content box exactly and is letterboxed inside it.
   Measured, not assumed: with max-height:100% a 180px mark came out 86x86 in
   a tile whose content area is 88x64 — it hung 15px below, and the tile's
   overflow:hidden then CUT it. Percentages resolve against the grid area and
   did not clamp; 4rem is the tile's 5rem minus its 0.5rem padding top and
   bottom, and an absolute length cannot be misresolved. */
.rating-card__logo { max-width: 100%; max-height: 4rem; width: auto; height: auto; object-fit: contain; }
.rating-card__logo--ph { display: grid; place-items: center; width: 100%; height: 100%; background: transparent; color: var(--muted); font-family: var(--font-heading); font-weight: 700; }
.rating-card__main { min-width: 0; }
.rating-card__name { display: block; font-family: var(--font-heading); font-weight: 700; font-size: var(--text-lg); color: var(--fg); }
.rating-card__bonus { display: block; color: var(--muted); font-size: var(--text-sm); margin-top: 2px; }
.rating-card__actions { display: flex; flex-direction: column; align-items: flex-end; gap: var(--space-2); }
.rating-card__score { font-family: var(--font-heading); font-weight: 700; font-size: var(--text-xl); color: var(--fg); line-height: 1; }
.rating-card__score-max { font-size: var(--text-xs); color: var(--muted); font-weight: 500; }
.rating-card__cta {
  background: var(--accent); color: #fff;
  padding: 0.6rem 1.25rem; border-radius: var(--radius-pill);
  font-weight: 600; font-size: var(--text-sm); text-decoration: none; white-space: nowrap;
}
.rating-card__cta:hover { filter: brightness(1.08); text-decoration: none; }
@media (max-width: 700px) {
  /* Three rows, not three columns.
     The card kept its desktop shape on a phone: rank, mark and a 1fr text
     column, which on a 375px screen is about 200px. The bonus line — the
     longest string on the card and the reason anyone reads it — wrapped into
     six ragged lines beside the mark, reported from a phone as exactly that.
     Now the top row identifies the brand, the bonus gets the full width, and
     the score and button close the card.
     The text wrapper becomes display:contents, which is what allows it: the
     name and the bonus are its children, and they have to land in different
     rows of the card's own grid. */
  .rating-card { grid-template-columns: 2.5rem 4rem 1fr; row-gap: var(--space-3); align-items: center; }
  .rating-card__logobox { height: 4rem; }
  /* Same arithmetic as above: tile height minus its padding. */
  .rating-card__logo { max-height: 3rem; }
  .rating-card__main { display: contents; }
  .rating-card__name { grid-column: 3; align-self: center; font-size: var(--text-base); }
  .rating-card__bonus { grid-column: 1 / -1; margin-top: 0; }
  .rating-card__actions { grid-column: 1 / -1; flex-direction: row; align-items: center; justify-content: space-between; width: 100%; }
  .rating-card__cta { flex: 0 0 auto; }
}

/* ----- rating controls (B10) -------------------------------------------- */
.rating-controls { display: flex; flex-wrap: wrap; gap: var(--space-3); margin: 0 0 var(--space-4); }
.rating-controls__search {
  flex: 1 1 14rem; min-width: 0;
  border: 1px solid var(--border); border-radius: var(--radius-pill);
  padding: 0.55rem 1rem; font: inherit; font-size: var(--text-sm);
  background: var(--bg); color: var(--fg);
}
.rating-controls__chip {
  border: 1px solid var(--border); border-radius: var(--radius-pill);
  padding: 0.55rem 1.1rem; font: inherit; font-size: var(--text-sm); font-weight: 600;
  background: var(--bg); color: var(--fg); cursor: pointer;
}
.rating-controls__chip[aria-pressed='true'] { background: var(--accent); border-color: var(--accent); color: #fff; }
.rating-controls__search:focus-visible, .rating-controls__chip:focus-visible {
  outline: 2px solid var(--accent); outline-offset: 2px;
}

/* ----- article body ----------------------------------------------------- */
.article { max-width: var(--measure); }
.article-index { max-width: var(--measure); }
.callout--info    { border-color: var(--accent); background: transparent; }
.callout--warning { border-color: #B45309; background: transparent; }
.callout--pro-tip { border-color: #0F766E; background: transparent; }
.callout--note    { border-color: var(--border); background: var(--surface); }
/* No background on content cards or callouts — the operator asked for that
   explicitly, and on a light ground a tinted box is noise, not emphasis.
   pros-cons keeps one, because there the two halves have to read apart. */
.pros-cons { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--space-4); margin: var(--space-5) 0; }
.pros-cons > * { background: var(--surface); border-radius: var(--radius-md); padding: var(--space-4); }
@media (max-width: 640px) { .pros-cons { grid-template-columns: 1fr; } }

/* ----- in-article CTA --------------------------------------------------- */
.cta-button { margin: var(--space-6) 0; text-align: center; }
/* A flat accent rectangle is what read as "2000s". A CTA gets a gradient
   along its own accent, a shadow tinted with that accent rather than grey,
   and it moves when pressed — the three things that make a button look
   pressable rather than printed. */
.cta-button a, a.cta {
  display: inline-block;
  background: linear-gradient(160deg,
    color-mix(in srgb, var(--accent) 88%, #fff),
    var(--accent) 55%,
    color-mix(in srgb, var(--accent) 82%, #000));
  color: #fff;
  padding: 0.85rem 2rem; border-radius: var(--radius-pill);
  font-weight: 700; letter-spacing: 0.01em; text-decoration: none;
  border: 1px solid color-mix(in srgb, var(--accent) 70%, #000);
  box-shadow: 0 1px 0 rgba(255,255,255,0.25) inset,
              0 6px 16px color-mix(in srgb, var(--accent) 35%, transparent);
  transition: transform .12s ease, box-shadow .12s ease, filter .12s ease;
}
.cta-button a:hover, a.cta:hover {
  filter: brightness(1.06); text-decoration: none; transform: translateY(-1px);
  box-shadow: 0 1px 0 rgba(255,255,255,0.3) inset,
              0 10px 22px color-mix(in srgb, var(--accent) 45%, transparent);
}
.cta-button a:active, a.cta:active {
  transform: translateY(1px);
  box-shadow: 0 2px 6px color-mix(in srgb, var(--accent) 30%, transparent);
}

/* ----- author box ------------------------------------------------------- */
.author-bio {
  border: 1px solid var(--border); border-radius: var(--radius-lg);
  padding: var(--space-5); background: var(--bg); box-shadow: var(--shadow-sm);
}
/* The old rule (site-bundle.ts) sets a two-column grid for a portrait beside
   the text. Every Review site ships without one, so the name
   landed in column 1 and the role was squeezed into column 2 — one word per
   line. One column unless the box actually carries a portrait. */
.author-bio { grid-template-columns: 1fr; }
.author-bio--portrait { grid-template-columns: auto 1fr; }
.author-bio__initials {
  display: grid; place-items: center;
  width: 56px; height: 56px; border-radius: 50%;
  background: var(--accent); color: #fff;
  font-family: var(--font-heading); font-weight: 600;
  font-size: var(--text-lg); letter-spacing: 0.02em;
  /* Its own row: the portrait variant spans three, and an initials circle
     stacked above the name is the arrangement the reference sites use. */
  grid-row: auto;
}
.author-bio h4 { margin: 0 0 var(--space-1); font-size: var(--text-lg); }
.author-bio__role { margin: 0 0 var(--space-3); color: var(--muted); font-size: var(--text-sm); }
.author-bio__updated { font-size: var(--text-xs); color: var(--muted); }

/* ----- footer ----------------------------------------------------------- */
.site-footer { border-top: 1px solid var(--border); margin-top: var(--space-8); padding-top: var(--space-6); }

:root { --header-bg: var(--surface); --header-fg: var(--fg); }