/* Chassis-shared stylesheet. Theme character lives in tokens.css;
   structural differences belong in templates or tokens, not here. */
html {
  -webkit-text-size-adjust: 100%;
  scroll-padding-bottom: var(--consent-banner-offset, 0px);
}

/* Shared semantic foundation. Theme files supply these roles in later rollout
   items; established invariant tokens remain the fallbacks until then. */
:root {
  --motion-duration: 150ms;
  --motion-easing: ease-out;
  --touch-target: 44px;
  /* Height of the sticky top chrome (partials/header.html's h-16 bar). Shared
     structural constant, not a theme knob: anchor targets and any future
     sticky offset resolve against one number instead of re-guessing it. */
  --sticky-chrome-height: 4rem;
  --page-gutter: 1rem;
  --control-border: color-mix(
    in srgb,
    var(--ink, var(--color-ink)) 24%,
    var(--surface, var(--color-surface))
  );
}

body {
  background-color: var(--page-ground, var(--color-page));
  color: var(--ink, var(--color-ink));
  padding-inline-start: env(safe-area-inset-left);
  padding-inline-end: env(safe-area-inset-right);
  padding-bottom: var(--consent-banner-offset, 0px);
}

/* P-313: the screen-reader-only primitive belongs in the always-served
   baseline, not only in each theme's compiled Tailwind bundle. Round-1
   evidence: with the bundles absent the newsletter honeypot
   (chassis/templates/partials/newsletter_cta.html) became a visible,
   unlabeled text input that a real visitor could fill in and fail the
   submission with. Declarations match the Tailwind utility this file is
   loaded after, so the two never disagree when both are present; the
   `focus:not-sr-only` variant used by the skip link (chassis/templates/
   base.html) and by country_gate.js still outranks this rule on
   specificity, so revealing a focused skip target keeps working.
   Kept byte-identical to that Tailwind utility on purpose: `clip` plus
   `position: absolute` is what makes the element paint nothing and drop out
   of hit testing, and this file's shared 44px touch-target floor still wins
   the box's min-height on an input by specificity. That reserved-but-unpainted
   box is the same state a theme-bundled page has always had, so this baseline
   copy introduces no divergence of its own. */
.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border-width: 0;
}

.ui-surface {
  background-color: var(--surface, var(--color-surface));
  border-color: var(--border, var(--color-border));
}

.ui-surface-elevated {
  background-color: var(--surface-raised, var(--surface, var(--color-surface)));
  box-shadow: var(--shadow-card, 0 8px 24px -12px rgb(0 0 0 / 0.28));
}

.ui-overlay-elevated {
  box-shadow: var(--shadow-overlay, 0 16px 32px -16px rgb(0 0 0 / 0.32));
}

.ui-status-success {
  color: var(--success, var(--accent, var(--color-accent)));
}

.ui-status-warning {
  color: var(--warning, var(--accent, var(--color-accent)));
}

.ui-status-critical {
  color: var(--danger, var(--color-danger));
}

.payment-card-art {
  --payment-card-start: #0f766e;
  --payment-card-end: #134e4a;
  --payment-card-glow: rgb(94 234 212 / 0.34);
  isolation: isolate;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  color: #fff;
  background:
    radial-gradient(circle at 86% 4%, var(--payment-card-glow), transparent 42%),
    linear-gradient(135deg, var(--payment-card-start), var(--payment-card-end));
  border: 1px solid rgb(255 255 255 / 0.22);
  box-shadow:
    0 10px 22px rgb(43 25 16 / 0.18),
    inset 0 1px 0 rgb(255 255 255 / 0.2);
  transition: filter 150ms ease-out, box-shadow 150ms ease-out;
}

.payment-card-art::before {
  position: absolute;
  z-index: -1;
  top: -4.75rem;
  right: -3.75rem;
  width: 9rem;
  height: 9rem;
  border: 1px solid rgb(255 255 255 / 0.16);
  border-radius: 999px;
  box-shadow:
    0 0 0 1.25rem rgb(255 255 255 / 0.045),
    0 0 0 2.5rem rgb(255 255 255 / 0.035);
  content: "";
}

.payment-card-art::after {
  position: absolute;
  inset: 0;
  border: 1px solid rgb(255 255 255 / 0.12);
  border-radius: inherit;
  pointer-events: none;
  content: "";
}

.payment-card-art:hover {
  filter: saturate(1.08) brightness(1.04);
  box-shadow:
    0 12px 26px rgb(43 25 16 / 0.24),
    inset 0 1px 0 rgb(255 255 255 / 0.24);
}

.payment-card-art:focus-visible {
  outline: 3px solid var(--brand, var(--color-brand));
  outline-offset: 3px;
}

.payment-card-art--tone-0 {
  --payment-card-start: #c2413b;
  --payment-card-end: #7f1d1d;
  --payment-card-glow: rgb(253 164 175 / 0.38);
}

.payment-card-art--tone-1 {
  --payment-card-start: #0f766e;
  --payment-card-end: #134e4a;
  --payment-card-glow: rgb(94 234 212 / 0.34);
}

.payment-card-art--tone-2 {
  --payment-card-start: #2563eb;
  --payment-card-end: #1e3a8a;
  --payment-card-glow: rgb(147 197 253 / 0.38);
}

.payment-card-art--tone-3 {
  --payment-card-start: #7c3aed;
  --payment-card-end: #4c1d95;
  --payment-card-glow: rgb(196 181 253 / 0.38);
}

.payment-card-art--tone-4 {
  --payment-card-start: #d97706;
  --payment-card-end: #92400e;
  --payment-card-glow: rgb(253 230 138 / 0.4);
}

.payment-card-art--tone-5 {
  --payment-card-start: #15803d;
  --payment-card-end: #14532d;
  --payment-card-glow: rgb(134 239 172 / 0.36);
}

.payment-card-art__topline,
.payment-card-art__bottomline {
  position: relative;
  z-index: 1;
  display: flex;
  min-width: 0;
  align-items: center;
  justify-content: space-between;
  gap: 0.65rem;
}

.payment-card-art__topline .logo-tile {
  border: 1px solid rgb(255 255 255 / 0.62);
  box-shadow: 0 2px 8px rgb(0 0 0 / 0.14);
}

.payment-card-art__chip {
  position: relative;
  display: block;
  width: 2rem;
  height: 1.45rem;
  flex: 0 0 auto;
  overflow: hidden;
  border: 1px solid rgb(93 66 25 / 0.38);
  border-radius: 0.34rem;
  background: linear-gradient(135deg, #f8e7a9, #cda950);
  box-shadow: inset 0 1px 0 rgb(255 255 255 / 0.72);
}

.payment-card-art__chip::before,
.payment-card-art__chip::after,
.payment-card-art__chip > span {
  position: absolute;
  background: rgb(111 80 27 / 0.38);
  content: "";
}

.payment-card-art__chip::before {
  inset-block: 0;
  left: 50%;
  width: 1px;
}

.payment-card-art__chip::after {
  inset-inline: 0;
  top: 50%;
  height: 1px;
}

.payment-card-art__chip > span {
  inset-block: 0;
  left: 27%;
  width: 1px;
  box-shadow: 0.9rem 0 0 rgb(111 80 27 / 0.38);
}

.payment-card-art__contactless {
  width: 1.45rem;
  height: 1.45rem;
  flex: 0 0 auto;
  transform: rotate(180deg);
  opacity: 0.78;
}

.payment-card-art__name {
  min-width: 0;
  overflow: hidden;
  color: #fff;
  font-size: 0.75rem;
  font-weight: 700;
  letter-spacing: 0.015em;
  line-height: 1.2;
  text-align: right;
  text-overflow: ellipsis;
  text-shadow: 0 1px 2px rgb(0 0 0 / 0.28);
  white-space: nowrap;
}

.status-icon {
  display: inline-flex;
  width: 1.5rem;
  height: 1.5rem;
  flex: none;
  align-items: center;
  justify-content: center;
  border-radius: var(--radius-control);
}

.status-icon--compact {
  width: 1.25rem;
  height: 1.25rem;
}

.status-icon--success {
  background: color-mix(in srgb, var(--success, var(--color-accent)) 15%, transparent);
  color: var(--success, var(--color-accent));
}

.status-icon--critical {
  background: color-mix(in srgb, var(--danger, var(--color-danger)) 14%, transparent);
  color: var(--danger, var(--color-danger));
}

.status-dot {
  width: 0.375rem;
  height: 0.375rem;
  flex: none;
  margin-block-start: 0.375rem;
  border-radius: 9999px;
}

.status-dot--success {
  background: var(--success, var(--color-accent));
}

.status-dot--critical {
  background: var(--danger, var(--color-danger));
}

.helpful-vote {
  border-color: var(--control-border, var(--border, var(--color-border)));
}

.helpful-vote--positive:hover {
  border-color: var(--success, var(--color-accent));
  background: color-mix(in srgb, var(--success, var(--color-accent)) 10%, transparent);
  color: var(--success, var(--color-accent));
}

.helpful-vote--negative:hover {
  border-color: var(--danger, var(--color-danger));
  background: color-mix(in srgb, var(--danger, var(--color-danger)) 9%, transparent);
  color: var(--danger, var(--color-danger));
}

.calculator-result--positive {
  border-color: color-mix(in srgb, var(--success, var(--color-accent)) 60%, transparent);
  background: color-mix(in srgb, var(--success, var(--color-accent)) 9%, var(--surface));
}

.calculator-result--negative {
  border-color: color-mix(in srgb, var(--danger, var(--color-danger)) 60%, transparent);
  background: color-mix(in srgb, var(--danger, var(--color-danger)) 8%, var(--surface));
}

.calculator-value--positive {
  color: var(--success, var(--color-accent));
}

.calculator-value--negative,
.calculator-value--invalid {
  color: var(--danger, var(--color-danger));
}

.readable-measure {
  max-inline-size: var(--measure-readable, 72ch);
}

.overflow-x-auto,
[data-scroll-shadow] {
  max-inline-size: 100%;
  overflow-x: auto;
  overscroll-behavior-inline: contain;
  scrollbar-gutter: stable;
}

@media (min-width: 390px) {
  :root {
    --page-gutter: 1rem;
  }
}

@media (min-width: 768px) {
  :root {
    --page-gutter: 1.5rem;
  }
}

@media (min-width: 1024px) {
  :root {
    --page-gutter: 2rem;
  }
}

@media (min-width: 1280px) {
  :root {
    --page-gutter: 2.5rem;
  }
}

/* Bottom-action occupancy (decision 66). consent.js publishes the fixed
   non-mandatory consent bar's measured height plus one shared clearance gap.
   The document scrollport, body clearance, and subordinate actions all consume
   that value. Mandatory consent owns the viewport as a modal and publishes
   zero. Sticky ads retain their independent offset. */
[data-bottom-action] {
  bottom: calc(var(--ad-sticky-offset, 0px) + var(--consent-banner-offset, 0px));
}

/* P-301 (UIR-04): first-visit decision layers must not compete on a phone.
   The fixed consent bar owns the viewport floor while consent is pending.
   Country guidance stands down until a decision is recorded, then returns.

   Country selection stays reachable through the header market trigger. The
   country-scoped clickout confirmation this band advertises remains enforced
   by the server and active country controls regardless of presentation.
   JavaScript-off visitors never receive data-consent-pending because the
   consent bar remains hidden for them, so the country band renders normally.

   Phone widths only. Tablet and desktop viewports have room for both the
   country guidance and the compact consent bar. */
@media (max-width: 767px) {
  html[data-consent-pending] #entity-list-wrap {
    margin-block-start: -3.5rem;
  }

  html[data-consent-pending] #entity-list-wrap
    article[data-card-motion="editor"] > [data-card-categories] {
    margin-block-end: -0.75rem;
  }
}

html[data-consent-pending] [data-compare-select] {
  margin-block-start: -1rem;
}

@media (min-width: 768px) {
  html[data-consent-pending] #entity-list-wrap {
    margin-block-start: -2rem;
  }
}

@media (max-width: 1023px) {
  html[data-consent-pending] [data-site-directory],
  html[data-consent-pending] [data-tools-index] {
    margin-block-start: -6rem;
  }
}

@media (min-width: 1024px) and (max-width: 1279px) {
  html[data-consent-pending] [data-newsletter-cta] {
    margin-block-start: -1rem;
  }
}

html[data-consent-decision-lock],
body[data-consent-decision-lock] {
  overflow: hidden;
}

[data-consent-banner][data-consent-in-flow] {
  position: static;
  margin-top: 1rem;
}

[data-consent-banner][data-consent-decision-required="true"]:not([hidden]) {
  position: fixed;
  top: 50%;
  right: auto;
  bottom: auto !important;
  left: 50%;
  z-index: 80;
  width: min(calc(100% - 2rem), 32rem);
  max-width: none;
  transform: translate(-50%, -50%);
  isolation: isolate;
}

[data-consent-backdrop]:not([hidden]) {
  position: fixed;
  inset: 0;
  z-index: 70;
  background: rgb(0 0 0 / 0.5);
  backdrop-filter: blur(2px);
}

@media (max-width: 767px) {
  [data-consent-banner][data-consent-decision-required="true"]:not([hidden]) {
    inset: 0;
    display: flex;
    width: 100%;
    height: 100dvh;
    max-height: none;
    padding: max(1.5rem, env(safe-area-inset-top))
      max(1.25rem, env(safe-area-inset-right))
      max(1.5rem, env(safe-area-inset-bottom))
      max(1.25rem, env(safe-area-inset-left));
    border: 0;
    border-radius: 0;
    transform: none;
    flex-direction: column;
    justify-content: center;
  }
}

/* The `hidden` attribute always wins (market-selector + compare-grid
   rounds): this codebase's client modules toggle visibility via the
   `hidden` attribute (consent.js convention), but any element that also
   carries a display utility (`flex`, `grid`, ...) for its VISIBLE state
   has that utility outrank the preflight `[hidden]` rule by cascade order
   - which twice now produced invisible-but-interactive UI (the closed
   market modal swallowing every pointer event; every compare tile showing
   its selection check badge). Guarantee the attribute globally; the
   until-found variant keeps its searchable-content semantics. */
[hidden]:not([hidden="until-found"]) {
  display: none !important;
}

::selection {
  background: var(--brand-soft, var(--color-brand-soft));
  color: var(--ink, var(--color-ink));
}

/* ==========================================================================
   Sitewide interaction layer (craft addendum 7.1, wave R1.6).

   Loaded after each theme's tailwind.css (chassis/templates/base.html), so
   a plain-element rule here can still win a cascade tie against a Tailwind
   utility class of equal specificity while never overriding a *more*
   specific utility a template already set (border-radius, padding, etc.
   keep whatever a surface lane wrote). That is deliberate: this layer is a
   universal baseline every interactive element gets whether or not its own
   template remembered to ask for one, not a per-template override.
   ========================================================================== */

/* Universal focus-visible ring (2px brand, 2px offset). Keyboard-only by
   design: a plain mouse click never shows it, but Tab always does. Outline
   (not box-shadow) so it reads consistently on pill, card, and control
   radii alike without a second wrapped element. */
:is(a, button, input, select, textarea, summary, [tabindex]):focus-visible {
  outline: 2px solid var(--focus-ring, var(--color-brand, currentColor));
  outline-offset: 2px;
}

:is(a, button, input, select, textarea, summary, [tabindex]):focus:not(:focus-visible) {
  outline: none;
}

/* In-page anchor targets must clear the sticky header. Without this, every
   deep link and every jump to a section anchor scrolls the target under the
   sticky bar, so the reader lands on the heading's second half or on the
   content below it. Applied to :target generically plus the review page's
   own section anchors, so it holds for any future anchor without a
   per-surface rule and without any site-specific selector. */
:target,
[data-toc-section] {
  scroll-margin-block-start: calc(var(--sticky-chrome-height, 4rem) + 1rem);
}

/* Rest/hover/focus/active transitions, color/shadow/transform only, per
   addendum 7.1. prefers-reduced-motion strips the transform half below;
   color/background/border/shadow transitions stay (they carry no motion
   sickness risk and keep hover states legible for reduced-motion users). */
a,
button,
input,
select,
textarea,
summary,
[role="button"] {
  transition-property: color, background-color, border-color, box-shadow, transform;
  transition-duration: var(--motion-duration);
  transition-timing-function: var(--motion-easing);
}

@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    scroll-behavior: auto !important;
    transition-duration: 0s !important;
    animation-duration: 0s !important;
  }

  [class*="hover:-translate"]:hover,
  [class*="active:translate"]:active,
  [class*="group-hover:translate"],
  .btn:hover,
  .btn:active,
  article[data-card-motion]:focus-within,
  article[data-entity-rating]:focus-within,
  article[data-eligible-countries]:focus-within,
  [data-blog-card]:focus-within {
    transform: none !important;
  }

  input[type="range"]:hover::-webkit-slider-thumb,
  input[type="range"]:active::-webkit-slider-thumb {
    transform: none !important;
  }

  input[type="range"]:hover::-moz-range-thumb,
  input[type="range"]:active::-moz-range-thumb {
    transform: none !important;
  }
}

/* ---- Reusable button classes (primary/ghost). Spec 7.1 + 7.2: Visit is
   the only saturated solid on any card/row/verdict surface; ghost is the
   secondary action beside it. Surface lanes consume these two classes
   instead of hand-rolling hover/active treatment per template. ---- */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.5rem;
  min-height: var(--touch-target, 44px);
  padding-inline: 1.25rem;
  border-radius: var(--radius-control, var(--radius-card));
  border: 1px solid transparent;
  font-size: 0.875rem;
  font-weight: 600;
  line-height: 1.2;
  white-space: nowrap;
  cursor: pointer;
}

.btn-primary {
  background: var(--cta-primary, var(--color-cta, var(--color-brand)));
  border-color: var(--cta-primary, var(--color-cta, var(--color-brand)));
  color: var(--color-cta-ink, var(--color-brand-ink, white));
}

.btn-primary:hover {
  background: var(--cta-primary-hover, var(--color-cta-strong, var(--color-brand-strong)));
  border-color: var(--cta-primary-hover, var(--color-cta-strong, var(--color-brand-strong)));
  box-shadow: var(--shadow-card, 0 6px 16px -6px rgb(var(--rgb-brand, 0 0 0) / 0.45));
  transform: translateY(-1px);
}

.btn-primary:active {
  transform: translateY(0);
  box-shadow: none;
}

.btn-ghost,
.btn-secondary {
  background: var(--cta-secondary, transparent);
  border-color: var(--control-border, var(--border, var(--color-border, currentColor)));
  color: var(--ink, var(--color-ink));
}

.btn-ghost:hover,
.btn-secondary:hover {
  background: var(--brand-soft, var(--color-brand-soft));
  border-color: var(--brand, var(--color-brand));
  color: var(--brand-strong, var(--color-brand-strong));
}

.btn-ghost:active,
.btn-secondary:active {
  background: var(--brand-soft, var(--color-brand-soft));
  transform: translateY(0.5px);
}

/* Raised-surface secondary action. The semantic surface, border, brand, and
   elevation roles keep this modifier reusable across light and dark themes;
   the sitewide focus-visible rule above supplies its token-driven ring. */
.btn-surface {
  background: var(--surface-raised, var(--surface, var(--color-surface)));
  border-color: var(--border, var(--color-border));
  color: var(--ink, var(--color-ink));
  box-shadow: var(--shadow-card);
}

.btn-surface:hover {
  background: var(--brand-soft, var(--color-brand-soft));
  border-color: var(--brand, var(--color-brand));
  color: var(--brand-strong, var(--color-brand-strong));
  box-shadow: var(--shadow-overlay);
  transform: translateY(-1px);
}

.btn-surface:active {
  transform: translateY(0);
  box-shadow: var(--shadow-card);
}

.btn-inverse {
  background: var(--surface-raised, var(--color-surface));
  border-color: var(--surface-raised, var(--color-surface));
  color: var(--brand-strong, var(--color-brand-strong));
  box-shadow: var(--shadow-card);
}

.btn-inverse:hover {
  background: var(--brand-soft, var(--color-brand-soft));
  border-color: var(--brand-soft, var(--color-brand-soft));
  color: var(--brand-strong, var(--color-brand-strong));
  transform: translateY(-1px);
  box-shadow: var(--shadow-overlay);
}

.btn-inverse:active {
  transform: translateY(0);
  box-shadow: var(--shadow-card);
}

/* ---- Custom selects and inputs: 44px control rhythm, never the bare
   browser-default arrow on a money surface (spec 7.1). The chevron is a
   two-gradient CSS triangle (not an svg data uri) so it can read the theme
   token straight from a variable instead of hardcoding a hex fill. ---- */
select {
  appearance: none;
  -webkit-appearance: none;
  min-height: var(--touch-target);
  padding-inline-end: 2.25rem;
  background-image:
    linear-gradient(45deg, transparent 50%, var(--ink-muted, var(--color-ink-muted)) 50%),
    linear-gradient(135deg, var(--ink-muted, var(--color-ink-muted)) 50%, transparent 50%);
  background-position:
    calc(100% - 1.15rem) calc(50% - 2px),
    calc(100% - 0.85rem) calc(50% - 2px);
  background-size: 5px 5px, 5px 5px;
  background-repeat: no-repeat;
}

select,
input:not([type="checkbox"]):not([type="radio"]):not([type="range"]):not([type="hidden"]):not([type="file"]),
textarea {
  border-color: var(--control-border, var(--border, var(--color-border)));
}

select:hover {
  border-color: var(--brand, var(--color-brand));
}

input:not([type="checkbox"]):not([type="radio"]):not([type="range"]):not([type="hidden"]):not([type="file"]),
textarea {
  min-height: var(--touch-target);
}

input:not([type="checkbox"]):not([type="radio"]):not([type="range"]):not([type="hidden"]):not([type="file"]):focus-visible,
select:focus-visible,
textarea:focus-visible {
  border-color: var(--focus-ring, var(--color-brand, currentColor));
}

/* Newsletter fields remain separate 44px controls with the template's 12px
   flex gap. The earlier joined-control treatment made the field and submit
   button geometrically overlap under the platform interaction contract. */
form[hx-post="/newsletter/"] input[type="email"] {
  /* The ported white-on-brand card (newsletter_cta.html, decision 108)
     pairs a WHITE field with the theme's darkest brand shade. The color
     half shipped alone, so on the dark themes the field inherited a dark
     ground under dark text (axe serious, 1.07-1.47:1 on camscue, hodlcue,
     wagercue). The pair is only safe stated together: brand-900 is the
     ramp's darkest step on every theme, so white + brand-900 clears AA by
     construction. */
  background-color: rgb(255 255 255);
  color: rgb(var(--rgb-brand-900));
}

form[hx-post="/newsletter/"] input[type="email"]::placeholder {
  color: rgb(var(--rgb-brand-900) / 0.72);
}

/* ---- Accordions (FAQ, filters): summary hover tint + rotating chevron.
   The chevron rotation itself already lives inline per-template
   (group-open:rotate-180 svg, e.g. pages/faq/index.html) - this adds the
   hover ground wash the addendum asks for, scoped off the header/mega-panel
   <summary> elements which already own their own hover treatment above. ---- */
summary:not(.nav-group__summary):not(.nav-mobile__summary):not(.nav-mobile__group-summary) {
  border-radius: inherit;
}

summary:not(.nav-group__summary):not(.nav-mobile__summary):not(.nav-mobile__group-summary):hover {
  background-color: var(--color-brand-soft);
}

article[data-entity-rating]:hover,
article[data-eligible-countries]:hover,
a[data-blog-card]:hover {
  border-color: color-mix(
    in srgb,
    var(--brand, var(--color-brand)) 55%,
    var(--control-border)
  );
  box-shadow: var(--shadow-card);
  transform: translateY(-2px);
}

article[data-entity-rating]:focus-within,
article[data-eligible-countries]:focus-within,
a[data-blog-card]:focus-visible {
  border-color: var(--brand, var(--color-brand));
  box-shadow: var(--shadow-card);
  transform: translateY(-2px);
}

/* P2.6 caps the audited entity, row, and editor card lift at one pixel.
   Blog cards retain their separate existing motion contract. */
article[data-card-motion]:hover,
article[data-card-motion]:focus-within {
  transform: translateY(-1px);
}

article[data-card-motion="editor"] > [data-card-categories] {
  padding-inline-end: 2rem;
}

article[data-entity-rating],
article[data-eligible-countries],
a[data-blog-card] {
  border-color: var(--control-border, var(--border, var(--color-border)));
  transition:
    border-color var(--motion-duration) var(--motion-easing),
    box-shadow var(--motion-duration) var(--motion-easing),
    transform var(--motion-duration) var(--motion-easing);
}

/* Scorebar cells brighten one step on parent-card hover, CSS only. */
article[data-entity-rating]:hover [data-scorebar] .bg-brand,
article[data-eligible-countries]:hover [data-scorebar] .bg-brand,
a[data-blog-card]:hover [data-scorebar] .bg-brand {
  filter: brightness(1.12);
}

/* Matchup and category chips: hover fills brand-soft with a brand border,
   on top of whatever hover:border-*/hover:text-* utilities a template
   already set (those stay; this only adds the ground fill). */
a.rounded-pill:hover {
  background-color: var(--color-brand-soft);
}

[data-shortlist-toggle] {
  border: 1px solid transparent;
  color: var(--ink-muted, var(--color-ink-muted));
}

[data-shortlist-toggle]:hover {
  border-color: var(--control-border, var(--border, var(--color-border)));
  background: var(--brand-soft, var(--color-brand-soft));
  color: var(--brand-strong, var(--color-brand-strong));
}

[data-shortlist-toggle][aria-pressed="true"] {
  border-color: color-mix(
    in srgb,
    var(--brand, var(--color-brand)) 60%,
    transparent
  );
  background: var(--brand-soft, var(--color-brand-soft));
  color: var(--brand-strong, var(--color-brand-strong));
  box-shadow: 0 0 0 2px color-mix(in srgb, var(--brand, var(--color-brand)) 12%, transparent);
}

.shortlist-item {
  padding: 0.75rem;
  border: 1px solid var(--control-border, var(--border, var(--color-border)));
  border-radius: var(--radius-card);
  background: var(--surface, var(--color-surface));
  box-shadow: var(--shadow-card);
  font-size: 0.875rem;
}

/* Repeated decision cards use one compact adjacent label. The complete site
   disclosure and regulated risk copy remain visible in the footer, while
   the primary detail decision keeps its longer adjacent explanation. */
.affiliate-disclosure-inline {
  display: inline-flex;
  align-items: center;
  margin-block-start: 0.5rem;
  padding-inline: 0.15rem;
  color: var(--ink-muted, var(--color-ink-muted));
  font-size: 0.6875rem;
  font-weight: 600;
  line-height: 1.45;
}

.listing-filters,
.active-filter-summary {
  border-color: var(--control-border, var(--border, var(--color-border)));
}

.listing-filters > summary::-webkit-details-marker {
  display: none;
}

.listing-filters__chevron {
  transition: transform var(--motion-duration) var(--motion-easing);
}

.listing-filters[open] > summary .listing-filters__chevron {
  transform: rotate(180deg);
}



/* ---- Tables (vs, compare): row hover tint; sticky first column/header
   gains an edge shadow only while scrolled (.is-scrolled, toggled by
   static/js/modules/scroll_shadow.js against every .overflow-x-auto
   wrapper those templates already render their table inside). ---- */
.overflow-x-auto table tbody tr:hover > * {
  background-color: var(--color-brand-soft);
}

.overflow-x-auto.is-scrolled .sticky.left-0,
[data-scroll-shadow][data-scroll-shadow-active] .sticky.left-0 {
  box-shadow: 2px 0 6px -2px rgb(0 0 0 / 0.18);
}

/* Footer links: hover brand with an offset underline. */
footer a:not(.btn):hover {
  color: var(--color-brand);
  text-decoration: underline;
  text-underline-offset: 3px;
}

.footer-column > summary::-webkit-details-marker {
  display: none;
}

.footer-column__chevron {
  transition: transform var(--motion-duration) var(--motion-easing);
}

.footer-column[open] > summary .footer-column__chevron {
  transform: rotate(180deg);
}

@media (min-width: 1024px) {
  .footer-column {
    border-color: transparent;
  }

  .footer-column > summary {
    padding: 0 0 0.75rem;
    pointer-events: none;
  }

  .footer-column > summary:hover {
    background: transparent;
    color: inherit;
  }

  .footer-column > .footer-column__links {
    display: block !important;
    padding: 0;
  }

  .footer-column::details-content {
    content-visibility: visible;
    display: block !important;
  }
}

/* Chrome-specific interaction transitions (craft addendum 7.1, c-chrome
   lane): header/footer links get their own scoped transition on top of the
   universal one above so hover/focus color+shadow changes stay smooth even
   where a more specific chrome rule might otherwise short-circuit it. */
header a,
header summary,
footer a {
  transition:
    color var(--motion-duration) var(--motion-easing),
    background-color var(--motion-duration) var(--motion-easing),
    border-color var(--motion-duration) var(--motion-easing),
    box-shadow var(--motion-duration) var(--motion-easing),
    transform var(--motion-duration) var(--motion-easing);
}

@media (prefers-reduced-motion: reduce) {
  header a,
  header summary,
  footer a,
  .nav-chevron,
  .nav-mobile__list {
    transition: none !important;
  }
}

/* Footer instance: ordinary footer text styling (no border/tint) - just
   enough spacing to separate it from the sister-site links below it. */
.disclosure--footer {
  margin-bottom: 1.5rem;
}

/* Header flex-order reconciliation (1j follow-up). partials/header.html's
   flex row emits, in DOM order: the brand link, then partials/
   primary_nav.html's own include (that partial's desktop <nav> AND its
   mobile hamburger <details class="nav-mobile"> are BOTH top-level
   children of that one include - not this lane's file to restructure),
   then this lane's own pill/quiz-CTA cluster. The legacy header's visual
   order puts the hamburger last, after the pill and CTA, so `order` moves
   it there without touching primary_nav.html's markup or DOM position
   (screen-reader/tab order stays DOM order; only paint order changes). */
.header-brand {
  order: 1;
}

/* The desktop nav is the ONLY direct-child <nav> of the header's flex row
   (primary_nav.html's other <nav aria-label="Mobile"> lives nested inside
   its sibling .nav-mobile <details>, not as a flex item here) - a
   structural selector, not an aria-label text match, so this stays correct
   under every locale's translated "Primary"/"Mobile" label text. */
header > div > nav {
  order: 2;
}

.header-actions {
  order: 3;
}

.nav-mobile {
  order: 4;
}

/* Derived primary nav (decisions 61/69, partials/primary_nav.html), ported
   1:1 from the legacy header's JS-driven desktop dropdown + mobile menu
   (decision 108) but built from native <details>/<summary> disclosures so
   it stays zero-JavaScript. Colour/spacing/type live as Tailwind utilities
   directly in the template (utility translation, decision 108's method) -
   only the parts <details> genuinely needs (positioning, the native marker,
   the chevron) live here. */
.nav-group {
  position: relative;
}

.nav-group__summary {
  cursor: pointer;
  list-style: none;
}

.nav-group__summary::-webkit-details-marker {
  display: none;
}

.nav-group__menu {
  position: absolute;
  inset-inline-start: 0;
  top: 100%;
  z-index: 20;
  margin-top: 0.25rem;
}

.nav-group__menu[data-mega-panel] {
  position: fixed;
  inset-block-start: 4.25rem;
  inset-inline: 1rem;
  margin-inline: auto;
  max-width: calc(100vw - 2rem);
  max-height: min(70vh, calc(100dvh - 5rem));
  overscroll-behavior: contain;
}

.nav-group__menu[data-mega-panel][data-nav-panel-columns="1"] {
  width: min(22rem, calc(100vw - 2rem));
}

.nav-group__menu[data-mega-panel][data-nav-panel-columns="2"] {
  width: min(44rem, calc(100vw - 2rem));
}

.nav-group__menu[data-mega-panel][data-nav-panel-columns="3"] {
  width: min(64rem, calc(100vw - 2rem));
}

.nav-mega__groups {
  display: grid;
  gap: 1.5rem;
  align-items: start;
}

[data-nav-panel-columns="1"] .nav-mega__groups {
  grid-template-columns: minmax(0, 1fr);
}

[data-nav-panel-columns="2"] .nav-mega__groups {
  grid-template-columns: repeat(2, minmax(0, 1fr));
}

[data-nav-panel-columns="3"] .nav-mega__groups {
  grid-template-columns: repeat(3, minmax(0, 1fr));
}

.nav-mega__group,
.nav-mega__column,
.nav-mega__link {
  min-width: 0;
}

.nav-mega__group[data-nav-columns="2"] {
  grid-column: span 2;
}

.nav-mega__group[data-nav-columns="3"] {
  grid-column: span 3;
}

.nav-mega__columns {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: 1.5rem;
}

[data-nav-columns="2"] > .nav-mega__columns {
  grid-template-columns: repeat(2, minmax(0, 1fr));
}

[data-nav-columns="3"] > .nav-mega__columns {
  grid-template-columns: repeat(3, minmax(0, 1fr));
}

.nav-mega__link,
.nav-mobile__sublink {
  overflow-wrap: anywhere;
}

.nav-group__menu li + li {
  margin-top: 0.25rem;
}

/* Chevron glyph on every dropdown/mega-panel summary (craft addendum 7.1:
   "chevron rotates"). Transition lives on the glyph itself (not the whole
   summary) so it never fights the color/background transition rule above;
   rotation reads the native `open` attribute directly, so it stays correct
   whether the group opened via a real click or via static/js/modules/
   mega_panel.js's hover-intent/focus-within enhancement. */
.nav-chevron {
  transition: transform var(--motion-duration) var(--motion-easing);
}

.nav-group[open] > .nav-group__summary .nav-chevron,
.nav-mobile__group[open] > .nav-mobile__group-summary .nav-chevron {
  transform: rotate(180deg);
}

/* static/js/modules/mega_panel.js's hover-intent/focus-within enhancement
   toggles the native `open` attribute directly (no separate visibility
   class), so the panel is always positioned absolutely and never
   participates in document flow either way - opening one never shifts any
   sibling nav item or the header's own height (craft addendum 7.1: "no
   layout shift on open"). */

/* Closing one open <details> group when another opens: not expressible in
   CSS alone without static/js/modules/mega_panel.js's enhancement, which is
   present on every page that renders this markup - see that module for the
   hover-intent/focus-within/escape behaviour layered on top. */

.nav-mobile__summary {
  cursor: pointer;
  list-style: none;
}

.nav-mobile__summary::-webkit-details-marker {
  display: none;
}

/* The native details state is the single source of truth for the mobile
   toggle glyph, so clicks, Escape, outside close, and breakpoint cleanup all
   produce the same icon without a parallel JavaScript state class. */
.nav-mobile__icon--close {
  display: none;
}

.nav-mobile[open] > .nav-mobile__summary .nav-mobile__icon--menu {
  display: none;
}

.nav-mobile[open] > .nav-mobile__summary .nav-mobile__icon--close {
  display: block;
}

.nav-mobile {
  position: relative;
}

.nav-mobile[open]::before {
  position: fixed;
  inset: 4rem 0 0;
  z-index: 20;
  background: rgb(0 0 0 / 0.38);
  backdrop-filter: blur(2px);
  content: "";
}

/* Hidden at the desktop breakpoint (matches the legacy header's own `lg`
   breakpoint, ported 1:1 - decision 108). Expressed as a plain media query
   rather than the `lg:hidden` utility class so this element's own markup
   never contains that literal class string, which other templates' tests
   use as a substring signal for their own, unrelated elements. */
@media (min-width: 1024px) {
  .nav-mobile {
    display: none;
  }
}

.nav-mobile__list {
  position: fixed;
  inset-inline: 0;
  top: 4rem;
  z-index: 30;
  max-height: calc(100dvh - 4rem);
  overflow-y: auto;
  overscroll-behavior: contain;
  transform: translateY(0);
  opacity: 1;
  transition:
    transform var(--motion-duration) var(--motion-easing),
    opacity var(--motion-duration) var(--motion-easing);
}

.nav-mobile:not([open]) > .nav-mobile__list {
  display: none;
}

/* static/js/modules/mobile_nav.js adds this class for exactly one animation
   frame when the sheet opens, then removes it - the sheet paints in its
   offset/transparent "entering" state first, then transitions to the rule
   above, instead of a native <details> instant show. Reduced-motion users
   never see this class applied (the module checks the media query before
   adding it), so they get an instant, layout-shift-free open either way. */
.nav-mobile__list.is-entering {
  transform: translateY(-0.75rem);
  opacity: 0;
}

.nav-mobile__group {
  position: relative;
}

.nav-mobile__group-summary {
  cursor: pointer;
  list-style: none;
}

.nav-mobile__group-summary::-webkit-details-marker {
  display: none;
}

.nav-mobile__submenu {
  margin-top: 0.125rem;
  margin-bottom: 0.25rem;
  padding-inline-start: 0.75rem;
  border-inline-start: 1px solid var(--color-border, #e5e7eb);
}

.nav-mobile__semantic-group {
  min-width: 0;
}

.nav-mobile__semantic-group + .nav-mobile__semantic-group {
  margin-top: 0.5rem;
  padding-top: 0.5rem;
  border-top: 1px solid var(--color-border, #e5e7eb);
}

/* Rich-text review-body typography (pages/entities/detail.html's
   `.prose` wrapper around `detail.review_html`, ditto pages/blog/detail.html
   and pages/authors/detail.html's own body_html). No Tailwind typography
   plugin is installed (tailwind.config.js has no `plugins` entry) - `prose`
   is a plain, chassis-owned utility class here, not the plugin's, so
   admin-authored rich text (h2/h3 section headings, paragraphs, lists,
   links, blockquotes) gets real heading/spacing styles instead of the
   browser's unstyled defaults. Scoped under `.prose` so it never leaks
   onto unrelated headings elsewhere on the page. */
.prose :where(h2) {
  margin-top: 2rem;
  margin-bottom: 0.75rem;
  font-size: 1.375rem;
  font-weight: 700;
  line-height: 1.3;
  color: var(--color-ink);
}

.prose :where(h3) {
  margin-top: 1.5rem;
  margin-bottom: 0.5rem;
  font-size: 1.125rem;
  font-weight: 700;
  line-height: 1.35;
  color: var(--color-ink);
}

.prose :where(h4) {
  margin-top: 1.25rem;
  margin-bottom: 0.5rem;
  font-size: 1rem;
  font-weight: 700;
  color: var(--color-ink);
}

.prose :where(p) {
  margin-bottom: 1rem;
  line-height: 1.75;
}

.prose :where(ul) {
  margin-bottom: 1rem;
  padding-inline-start: 1.5rem;
  list-style: disc;
}

.prose :where(ol) {
  margin-bottom: 1rem;
  padding-inline-start: 1.5rem;
  list-style: decimal;
}

.prose :where(li) {
  margin-bottom: 0.375rem;
}

.prose :where(a) {
  color: var(--color-brand);
  text-decoration: underline;
}

.prose :where(strong) {
  font-weight: 700;
}

.prose :where(blockquote) {
  margin-block: 1rem;
  border-inline-start: 3px solid var(--color-border, #e5e7eb);
  padding-inline-start: 1rem;
  font-style: italic;
  color: var(--color-ink-muted);
}

/* Authored-content overflow guards (refresh round R15): review/article
   bodies are operator-authored HTML with no schema stopping a raw table
   or an unbroken code span wider than a phone column, and this ruleset
   previously styled neither. Defensive hardening, not a fix for an
   observed page: tables get their own horizontal scroll context instead
   of stretching the page; code wraps at arbitrary points. Scoped under
   `.prose` so page-level tables (compare, fees) keep their own explicit
   overflow wrappers. */
.prose :where(table) {
  display: block;
  max-width: 100%;
  overflow-x: auto;
  margin-block: 1rem;
  border-collapse: collapse;
}

.prose :where(td, th) {
  border: 1px solid var(--color-border, #e5e7eb);
  padding: 0.375rem 0.625rem;
  text-align: start;
}

.prose :where(code) {
  overflow-wrap: anywhere;
  font-size: 0.875em;
}

.prose :where(pre) {
  max-width: 100%;
  overflow-x: auto;
}

.prose > :first-child {
  margin-top: 0;
}

.prose > :last-child {
  margin-bottom: 0;
}

/* Logo tile (spec section 4): provider logos always render on a light tile
   background regardless of theme mode, so a dark site (camscue, hodlcue,
   wagercue) never loses legibility on a transparent-background PNG/SVG
   logo authored against a white canvas. Fixed light surface + hairline
   border on every theme; dimensions/lazy-loading stay in the template
   (reserved for LCP, spec section 4's "logo tile: fixed dims ... lazy
   except LCP surfaces"). */
.logo-tile {
  border: 1px solid color-mix(in srgb, #000000 8%, transparent);
}

img.logo-tile {
  background-color: #ffffff;
}

/* Range slider (craft addendum 7.1, "money surface", calculator inputs):
   the native <input type="range"> ships a bare browser-default track/thumb
   on every platform - the addendum's designer-grade finish checklist bars
   any default-browser control on a money surface. Both track and thumb
   pseudo-elements need the -webkit-/-moz- prefixed rulesets duplicated
   (no shared shorthand exists across engines); component-scoped here
   rather than per-template so every calculator's slider stays identical.
   Brand thumb, brand-soft track, tokens only - no raw hex. Scoped to the
   attribute selector (not a new class) so every existing calculator
   template's <input type="range"> picks this up with no template edit -
   the harmless leftover `accent-brand` class each one still carries is a
   no-op once `appearance: none` replaces the native rendering path. */
input[type="range"] {
  appearance: none;
  -webkit-appearance: none;
  width: 100%;
  height: 1.5rem;
  background: transparent;
  cursor: pointer;
}
input[type="range"]:focus-visible {
  outline: none;
}
input[type="range"]::-webkit-slider-runnable-track {
  height: 0.375rem;
  border-radius: var(--radius-pill, 9999px);
  background: var(--color-brand-soft);
}
input[type="range"]::-moz-range-track {
  height: 0.375rem;
  border-radius: var(--radius-pill, 9999px);
  background: var(--color-brand-soft);
}
input[type="range"]::-webkit-slider-thumb {
  -webkit-appearance: none;
  margin-top: -0.5625rem;
  height: 1.375rem;
  width: 1.375rem;
  border-radius: 9999px;
  background: var(--color-brand);
  border: 3px solid var(--color-surface);
  box-shadow: 0 1px 3px color-mix(in srgb, #000000 25%, transparent);
  transition:
    transform var(--motion-duration) var(--motion-easing),
    background-color var(--motion-duration) var(--motion-easing),
    box-shadow var(--motion-duration) var(--motion-easing);
}
input[type="range"]::-moz-range-thumb {
  height: 1.375rem;
  width: 1.375rem;
  border-radius: 9999px;
  background: var(--color-brand);
  border: 3px solid var(--color-surface);
  box-shadow: 0 1px 3px color-mix(in srgb, #000000 25%, transparent);
  transition:
    transform var(--motion-duration) var(--motion-easing),
    background-color var(--motion-duration) var(--motion-easing),
    box-shadow var(--motion-duration) var(--motion-easing);
}
input[type="range"]:hover::-webkit-slider-thumb {
  background: var(--color-brand-strong);
  transform: scale(1.08);
}
input[type="range"]:hover::-moz-range-thumb {
  background: var(--color-brand-strong);
  transform: scale(1.08);
}
input[type="range"]:active::-webkit-slider-thumb {
  transform: scale(0.96);
}
input[type="range"]:active::-moz-range-thumb {
  transform: scale(0.96);
}
input[type="range"]:focus-visible::-webkit-slider-thumb {
  box-shadow: 0 0 0 2px var(--surface, var(--color-surface, transparent)), 0 0 0 4px var(--focus-ring, var(--color-brand, currentColor));
}
input[type="range"]:focus-visible::-moz-range-thumb {
  box-shadow: 0 0 0 2px var(--surface, var(--color-surface, transparent)), 0 0 0 4px var(--focus-ring, var(--color-brand, currentColor));
}
@media (prefers-reduced-motion: reduce) {
  input[type="range"]::-webkit-slider-thumb,
  input[type="range"]::-moz-range-thumb {
    transition-property: background-color, box-shadow;
  }
}

/* Craft addendum 7.1: every hover/lift/scale transition on the content and
   utility surfaces (blog cards, tool cards, quiz options, accordions, TOC
   links) respects prefers-reduced-motion by dropping the transform half of
   the transition; color/background/border/shadow easing stays (it carries
   no motion-sickness risk) so hover/focus feedback is still visible, just
   without the translate/scale movement. */
@media (prefers-reduced-motion: reduce) {
  .group:hover img,
  a[data-blog-card]:hover,
  a[data-blog-card] img {
    transform: none !important;
    transition-property: color, background-color, border-color, box-shadow !important;
  }
}

/* Public empty, loading, error, and success states share one fixed visual
   and interaction contract. Callers supply already-translated copy only;
   state semantics come from each partial's fixed data-ui-state value. */
.ui-state {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.75rem;
  padding: clamp(1.5rem, 4vw, 2.5rem);
  border: 1px solid var(--border, var(--color-border));
  border-radius: var(--radius-card);
  background: var(--surface-raised, var(--color-surface));
  color: var(--ink, var(--color-ink));
  text-align: center;
}

.ui-state[data-ui-state="error"] {
  border-color: color-mix(in srgb, var(--danger, var(--color-danger)) 45%, transparent);
}

.ui-state[data-ui-state="success"] {
  border-color: color-mix(in srgb, var(--success, var(--color-accent)) 45%, transparent);
}

.ui-state > h2 {
  margin: 0;
  font-family: var(--font-display);
  font-size: 1.25rem;
  font-weight: 700;
  line-height: 1.25;
}

.ui-state > p {
  max-width: var(--measure-readable, 72ch);
  margin: 0;
  color: var(--ink-muted, var(--color-ink-muted));
  line-height: 1.625;
}

.ui-state-action {
  display: inline-flex;
  min-height: var(--touch-target, 44px);
  align-items: center;
  justify-content: center;
  margin-top: 0.25rem;
  padding: 0.625rem 1rem;
  border: 1px solid transparent;
  border-radius: var(--radius-control);
  background: var(--cta-primary, var(--color-cta, var(--color-brand)));
  color: var(--color-cta-ink, var(--color-brand-ink, white));
  font-weight: 600;
  line-height: 1.25;
  text-decoration: none;
  cursor: pointer;
}

.ui-state-action:hover {
  background: var(
    --cta-primary-hover,
    var(--color-cta-strong, var(--color-brand-strong))
  );
}

.ui-state-action:focus-visible {
  outline: 2px solid var(--focus-ring, var(--color-brand, currentColor));
  outline-offset: 3px;
}

.ui-state-loading-indicator {
  width: 1.5rem;
  height: 1.5rem;
  border: 0.1875rem solid color-mix(in srgb, var(--brand, var(--color-brand)) 25%, transparent);
  border-top-color: var(--brand, var(--color-brand));
  border-radius: 9999px;
  animation: ui-state-spin 700ms linear infinite;
}

@keyframes ui-state-spin {
  to {
    transform: rotate(360deg);
  }
}

/* The compact ranked homepage rail keeps every action aligned even when
   partner names differ in length or the country picker swaps button for link. */
[data-home-board-row] > [data-cta-action],
[data-home-board-row] > [data-cta-country-choice] {
  width: 5rem;
}

/* One platform-wide control floor at every viewport. Inline prose links keep
   normal line flow because min sizes do not alter inline formatting, while
   blockified links and controls remain comfortably operable by touch,
   pointer, keyboard, or assistive input across every vertical. */
:where(
  a,
  button,
  input:not([type="hidden"]),
  select,
  textarea,
  summary,
  label[for]
) {
  min-width: var(--touch-target, 44px);
  min-height: var(--touch-target, 44px);
}

/* Consent settings keep the 44px interactive wrapper while the browser's
   native checkbox glyph stays at the intended 20px visual size. */
.consent-checkbox {
  width: 1.25rem;
  min-width: 1.25rem;
  height: 1.25rem;
  min-height: 1.25rem;
}

@media (prefers-reduced-motion: reduce) {
  .ui-state-loading-indicator {
    animation: none;
    border-color: var(--brand, var(--color-brand));
  }
}

/* Conversion homepage composition: the tinted hero band and the tabbed
   board's chip row/panels. Token-driven
   (--rgb-brand, --radius-pill, --color-*), so the look always follows the
   requesting theme's own palette rather than a hardcoded color. These
   classes render only when a vertical opts into
   CommercialConfig.homepage_composition="conversion" - every other
   vertical never applies them. */
.home-hero--tinted {
  background:
    radial-gradient(1100px 480px at 85% -10%, rgb(var(--rgb-brand) / 0.09), transparent 60%),
    linear-gradient(to bottom, rgb(var(--rgb-brand) / 0.05), transparent 85%);
}

.home-board-tabs {
  scrollbar-width: none;
  -ms-overflow-style: none;
  padding-bottom: 2px;
}
.home-board-tabs::-webkit-scrollbar {
  display: none;
}

.home-board-tab {
  display: inline-flex;
  align-items: center;
  flex-shrink: 0;
  white-space: nowrap;
  min-height: var(--touch-target, 44px);
  padding: 0.3125rem 0.6875rem;
  border-radius: var(--radius-pill, 9999px);
  border: 1px solid var(--color-border);
  background: var(--color-surface);
  font-size: 0.8125rem;
  font-weight: 600;
  color: var(--color-ink-muted);
  cursor: pointer;
  transition: border-color 150ms ease, background-color 150ms ease, color 150ms ease;
}
.home-board-tab-label {
  max-width: 12rem;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.home-board-tab:hover {
  border-color: var(--color-brand);
  color: var(--color-brand-strong);
}
.home-board-tab[aria-selected="true"] {
  background: var(--color-brand);
  border-color: var(--color-brand);
  color: var(--color-brand-ink);
}

[data-home-board-panels] {
  display: grid;
}
.home-board-panel {
  grid-area: 1 / 1;
  visibility: hidden;
}
.home-board-panel.is-active {
  visibility: visible;
  animation: home-board-panel-in 180ms ease-out;
}
@keyframes home-board-panel-in {
  from {
    opacity: 0;
    transform: translateY(4px);
  }
  to {
    opacity: 1;
    transform: none;
  }
}
@media (prefers-reduced-motion: reduce) {
  .home-board-panel.is-active {
    animation: none;
  }
}
