/* Commerce - The buying-guide default: white cards on a light page, warm CTA, system sans.
   Compiled from sites.theme by services/renderer/src/theme.js. Do not edit. */

:root {
  --paper: #ffffff;
  --paper-alt: #f8f9fb;
  --paper-sunk: #f1f3f6;
  --ink: #1f2430;
  --ink-strong: #0f1420;
  --muted: #4a5360;
  --border: #e3e7ec;
  --accent: #10519e;
  --cta: #9f350a;
  --cta-hover: #892e09;
  --cta-ink: #ffffff;
  --font-body: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
  --font-display: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
  --font-mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, "Liberation Mono", monospace;
  --text-hero: clamp(34px, 1.5357rem + 2.619vw, 56px);
  --text-h1: clamp(28px, 1.375rem + 1.6667vw, 42px);
  --text-h2: clamp(22px, 1.1607rem + 0.9524vw, 30px);
  --text-h3: clamp(18px, 1.0446rem + 0.3571vw, 21px);
  --text-body: clamp(16px, 0.9732rem + 0.119vw, 17px);
  --text-small: 14px;
  --text-caption: 12px;
  --leading-body: 1.6;
  --leading-heading: 1.15;
  --tracking-heading: -0.02em;
  --caption-tracking: 0.08em;
  --caption-transform: uppercase;
  --weight-heading: 700;
  --measure: 68ch;
  --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;
  --gutter: clamp(1rem, 4vw, 2rem);
  --section-y: 2.5rem;
  --card-pad: 1.25rem;
  --card-min: 17rem;
  --max-width: 72rem;
  --cell-pad-y: 0.75rem;
  --cell-pad-x: 0.875rem;
  --radius-sm: 8px;
  --radius-md: 12px;
  --radius-lg: 16px;
  --radius-pill: 999px;
  --shadow-1: 0 1px 3px rgba(15, 20, 32, 0.07);
  --shadow-2: 0 8px 24px rgba(15, 20, 32, 0.12);
  --dur: 180ms;
  --ease: cubic-bezier(0.4, 0, 0.2, 1);
  --chart-min: 26rem;
  color-scheme: light dark;
}

@media (prefers-color-scheme: dark) {
  :root {
    --paper: #12151c;
    --paper-alt: #191d26;
    --paper-sunk: #212632;
    --ink: #d6dbe4;
    --ink-strong: #f4f6fa;
    --muted: #a9b1c0;
    --border: #2a3140;
    --accent: #7db9f5;
    --cta: #ef8249;
    --cta-hover: #f19462;
    --cta-ink: #1a1204;
  }
}

*,
*::before,
*::after {
  box-sizing: border-box;
}
* {
  margin: 0;
}
html {
  font-size: 100%;
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
}
body {
  max-width: var(--max-width);
  margin-inline: auto;
  padding: var(--space-6) var(--gutter) var(--space-8);
  background: var(--paper);
  color: var(--ink);
  font-family: var(--font-body);
  font-size: var(--text-body);
  line-height: var(--leading-body);
  /* A generated price table or a long product name must not be able to widen the page. */
  overflow-wrap: break-word;
}
/* A compiled page is one section wrapping one section per block, so the outer one must not
   add a second helping of vertical rhythm on top of the body padding.

   "main >", not "body >". renderPage emits
     <body><a class="skip-link">…</a><main id="main"><section>…
   so a section has never been a child of body — the skip-link and the <main> landmark were
   added for the a11y work after this rule was written, and it has been dead ever since.
   Found on 2026-08-03 by rendering a page and reading the markup, not by a test: nothing
   fails when a selector stops matching, the element simply loses its styling, which reads
   as a design decision. selectors.test.mjs compares class NAMES and cannot see this. */
main > section {
  padding-block: 0;
}
h1,
h2,
h3,
h4,
h5,
h6 {
  color: var(--ink-strong);
  font-family: var(--font-display);
  font-weight: var(--weight-heading);
  line-height: var(--leading-heading);
  letter-spacing: var(--tracking-heading);
  text-wrap: balance;
  margin-block: var(--space-5) var(--space-3);
}
h1 {
  font-size: var(--text-h1);
}
/* The first heading on the page is the hero. Both shapes the renderer produces are covered:
   a hand-built tree puts the h1 one level down, a compiled SiteSpec two.

   Same correction as above — these were "body > section:first-child" and could never match,
   so --text-hero has never been applied to any published page. The h1 fell back to --text-h1
   everywhere. */
main > section:first-child > h1:first-child,
main > section:first-child > section:first-child > h1:first-child {
  font-size: var(--text-hero);
}
h2 {
  font-size: var(--text-h2);
}
h3 {
  font-size: var(--text-h3);
}
h4,
h5,
h6 {
  font-size: var(--text-body);
}
:is(h1, h2, h3, h4, h5, h6):first-child {
  margin-top: 0;
}
p {
  max-width: var(--measure);
  margin-block: var(--space-3);
}
a {
  color: var(--accent);
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: 0.15em;
  transition: text-decoration-thickness var(--dur) var(--ease);
}
a:hover {
  text-decoration-thickness: 0.14em;
}
/* Focus. :focus-visible on interactive elements, and a plain :focus on the two things that
   are focused programmatically rather than by tabbing — main, which the skip link targets,
   and the skip link's own landing. A 3px outline plus a contrasting ring clears WCAG 2.4.11
   Focus Appearance against any surface: the paper-coloured ring guarantees the indicator is
   visible even where the accent sits close to the background it is drawn on. */
a:focus-visible,
.button:focus-visible,
select:focus-visible,
[tabindex]:focus-visible {
  outline: 3px solid var(--accent);
  outline-offset: 2px;
  box-shadow: 0 0 0 5px var(--paper);
}
/* The skip link. Off-screen until focused, then a real, visible control at the top left.
   Hidden with a clip rather than display:none or visibility:hidden, because both of those
   remove it from the focus order and the link stops working at all. */
.skip-link {
  position: absolute;
  left: -9999px;
  top: 0;
  z-index: 100;
  padding: var(--space-3) var(--space-4);
  background: var(--cta);
  color: var(--cta-ink);
  border-radius: 0 0 var(--radius-md) 0;
  font-weight: 600;
  text-decoration: none;
}
.skip-link:focus {
  left: 0;
}
/* main takes focus from the skip link but is not a control, so it gets no ring of its own —
   the ring would sit around the whole page. */
main:focus {
  outline: none;
}
/* The site header. Added 2026-08-05 — before that the renderer emitted no header and no
   navigation on any page it had ever published, which is most of why its output read as a
   document rather than a site. */
/* The full-bleed hero: text over the picture, edge to edge.
   No inline style and no background-image address - both would put an operator-controlled string
   into a style attribute. The picture is a real <img> and CSS stacks the copy on top, which also
   keeps it an image to a screen reader and to a crawler.
   The scrim is not decoration. White text on an arbitrary photograph has no guaranteed contrast
   ratio, and every other colour pair in this file is solved to 7:1. A near-opaque gradient is the
   only way to make that promise when the operator chooses the photo. */
.section--hero {
  position: relative;
  isolation: isolate;
  overflow: hidden;
  padding: 0;
  border: 0;
  /* Edge to edge. body carries the max-width and the gutter, so cancelling the gutter is all
     it takes — no 100vw, which overflows by the width of the scrollbar and gives every page a
     horizontal scroll on Windows. */
  margin-inline: calc(-1 * var(--gutter));
  /* A dark floor under everything. The copy is white, and a hero whose image is missing or slow
     would otherwise be white on the pack's near-white paper — which is what the first version of
     this rule shipped, because negative z-index put the picture and the scrim BEHIND the
     section's own background rather than above it. */
  background: #0b1018;
}
.section--hero > img {
  position: absolute;
  inset: 0;
  z-index: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  border-radius: 0;
  margin: 0;
}
.section--hero::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 1;
  background: linear-gradient(105deg, rgba(8, 12, 20, 0.92) 0%, rgba(8, 12, 20, 0.72) 45%, rgba(8, 12, 20, 0.35) 100%);
}
.section--hero > .column {
  position: relative;
  z-index: 2;
  max-width: var(--max-width);
  margin-inline: auto;
  padding: var(--space-8) var(--space-4);
  gap: var(--space-3);
  /* The cards-layout packs make .column a white panel with a border and a shadow. Inside the
     hero that panel paints straight over the photograph and the scrim, which is exactly what the
     first version shipped: white copy on a white card, with the picture underneath it and
     invisible. The column here is a positioning box, not a card. */
  background: none;
  border: 0;
  box-shadow: none;
  border-radius: 0;
}
/* Fixed light values, not the pack tokens: the surface underneath is a photograph, so the
   pack's ink would be unreadable on half of them. */
.section--hero h1 {
  color: #ffffff;
  font-size: var(--text-hero);
  max-width: 18ch;
}
.section--hero p {
  color: #eef1f6;
  max-width: 46ch;
}
/* The first paragraph is the eyebrow when the operator wrote one. */
.section--hero .column > p:first-child {
  /* A fixed warm light, not var(--accent). Every pack accent is solved for contrast against
     PAPER, and the surface here is a dark scrim over a photograph — the commerce blue came out
     near-invisible on it. 12.4:1 against the scrim floor. */
  color: #f2c14e;
  font-size: var(--text-small);
  font-weight: 700;
  letter-spacing: var(--caption-tracking);
  text-transform: var(--caption-transform);
}
@media (max-width: 640px) {
  .section--hero > .column {
    padding: var(--space-6) var(--space-4);
  }
  .section--hero h1 {
    max-width: none;
  }
}
header {
  border-bottom: 1px solid var(--border);
  background: var(--paper);
}
.header-inner {
  display: flex;
  gap: var(--space-5);
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  max-width: var(--max-width);
  margin-inline: auto;
  padding: var(--space-3) var(--space-4);
}
.brand {
  display: inline-flex;
  gap: var(--space-2);
  align-items: center;
  font-weight: 700;
  color: var(--ink);
  text-decoration: none;
}
.brand-mark {
  display: grid;
  place-items: center;
  width: 1.9rem;
  height: 1.9rem;
  border-radius: var(--radius-sm);
  background: var(--accent);
  color: var(--accent-ink);
  font-size: var(--text-small);
  font-weight: 700;
}
nav {
  display: flex;
  gap: var(--space-4);
  flex-wrap: wrap;
}
/* --muted is contrast-solved to 7:1 against all three surfaces by the AAA work, so these are
   readable rather than decorative grey. They go to full ink on hover and focus. */
nav a {
  color: var(--muted);
  text-decoration: none;
  font-size: var(--text-small);
  font-weight: 600;
}
nav a:hover,
nav a:focus-visible {
  color: var(--ink);
  text-decoration: underline;
}
img {
  display: block;
  max-width: 100%;
  height: auto;
  border-radius: var(--radius-md);
  background: var(--paper-sunk);
}
/* Optional image presentation.
   The Image element returns a bare <img> unless the operator picked an alignment, a size or
   spacing, or wrote a caption — only then does it wrap the picture in a <figure>. So none of
   these rules touch an existing page, a hero, or an imported one.
   max-width on the figure, not just the img: the size presets below are fixed widths, and a
   22rem figure inside a 360px phone column would otherwise push the page sideways. That exact
   fault has already shipped once here, on a comparison table. */
.figure {
  margin-block: var(--space-5);
  max-width: 100%;
}
.figure img {
  margin-block: 0;
}
/* Alignment is margin, not text-align, because the thing being moved is the figure box. It is
   only visible once a size preset has constrained that box — a full-width figure has nowhere
   to go, which is the correct outcome rather than a bug. */
.figure--align-left {
  margin-inline: 0 auto;
}
.figure--align-center {
  margin-inline: auto;
}
.figure--align-right {
  margin-inline: auto 0;
}
.figure--size-small {
  width: 12rem;
}
.figure--size-medium {
  width: 22rem;
}
.figure--size-large {
  width: 34rem;
}
.figure--size-full {
  width: 100%;
}
.figure--space-none {
  margin-block: 0;
}
.figure--space-small {
  margin-block: var(--space-3);
}
.figure--space-medium {
  margin-block: var(--space-5);
}
.figure--space-large {
  margin-block: var(--space-8);
}
/* --muted is contrast-solved to 7:1 against all three surfaces by the AAA work, so a caption
   is readable rather than decorative grey. */
figcaption {
  margin-top: var(--space-2);
  color: var(--muted);
  font-size: var(--text-small);
}
ul,
ol {
  max-width: var(--measure);
  margin-block: var(--space-3);
  padding-left: var(--space-6);
}
li {
  margin-block: var(--space-1);
}
li::marker {
  color: var(--muted);
}
figure {
  margin-block: var(--space-5);
}
figcaption {
  color: var(--muted);
  font-size: var(--text-small);
}

/* Tables.
   The renderer wraps every table in .table-scroll, so the wrapper is the scroll box and the
   table is a real table: it fills the column it is given, and only scrolls when its own
   columns cannot fit. The previous version made the table itself the scroll box
   (display:block; width:max-content), which works but sizes the table to its content — a
   five-column comparison table then sat at 450px in a 1200px column.

   min-width keeps the columns legible while scrolling rather than crushing them to fit a
   360px phone, which is the failure this is really guarding against. */
.table-scroll {
  margin-block: var(--space-5);
  max-width: 100%;
  overflow-x: auto;
}
table {
  width: 100%;
  min-width: 28rem;
  border-collapse: collapse;
  font-size: var(--text-small);
  /* Digits line up in a price column without a font override. */
  font-variant-numeric: tabular-nums;
}
thead {
  background: var(--paper-sunk);
}
th,
td {
  padding: var(--cell-pad-y) var(--cell-pad-x);
  border-bottom: 1px solid var(--border);
  text-align: left;
  vertical-align: top;
}
th {
  color: var(--ink-strong);
  font-weight: 600;
  white-space: nowrap;
}
tbody tr:nth-child(even) {
  background: var(--paper-alt);
}
tbody tr:last-child td {
  border-bottom: 0;
}
/* The data grid is a panel in its own right, not prose furniture: it is the unique data
   the page is built around, and framing it says so. overflow-x on the table above already
   clips, so the corners stay round. */
.datagrid {
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
}

/* Button. 44px is the smallest tap target that does not need a second attempt on a phone,
   and it is a link element, so nothing here depends on JavaScript. */
.button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: 44px;
  padding: var(--space-2) var(--space-6);
  background: var(--cta);
  color: var(--cta-ink);
  border-radius: var(--radius-pill);
  font-family: var(--font-display);
  font-size: var(--text-body);
  font-weight: 650;
  letter-spacing: 0.01em;
  text-decoration: none;
  border: 0;
  /* A CTA that sits flat on the page reads as a coloured label. One soft shadow tinted with the
     button's own colour is what makes it read as pressable without a gradient or a border. */
  box-shadow: 0 1px 2px rgba(9, 12, 18, 0.16), 0 6px 16px -6px rgba(9, 12, 18, 0.4);
  transition:
    background var(--dur) var(--ease),
    transform var(--dur) var(--ease),
    box-shadow var(--dur) var(--ease);
}
.button:hover {
  transform: translateY(-1px);
  box-shadow: 0 2px 4px rgba(9, 12, 18, 0.18), 0 10px 22px -8px rgba(9, 12, 18, 0.45);
  background: var(--cta-hover);
  text-decoration: none;
}

/* The affiliate disclosure. The one section that has to read as an aside rather than as
   content: it is a legal statement about the page, not part of the argument the page makes.

   Deliberately NOT muted into invisibility. The FTC test is "clear and conspicuous", and the
   cheapest way to fail it is styling the disclosure so honestly small that nobody reads it.
   So: --text-small and --muted, both of which clear 7:1 against every surface because
   assertContrast gates the packs at compile time, plus a left rule in the accent colour and
   a sunk background so the block is visibly a block. Smaller than body copy, never quieter
   than the rest of the page. */
.section--disclosure {
  max-width: var(--measure);
  margin-block: var(--space-4);
  padding: var(--space-3) var(--space-4);
  background: var(--paper-sunk);
  border-left: 3px solid var(--accent);
  border-radius: var(--radius-sm);
}
.section--disclosure p {
  margin: 0;
  color: var(--muted);
  font-size: var(--text-small);
}

/* Pros and cons.

   This rule deliberately does almost nothing, and that is the correction. The first version
   wrapped the row in a panel — background, border, radius — and a screenshot of the commerce
   pack showed a box inside a box: in the card layouts .column is ALREADY a panel, so the
   outer one framed two framed lists. That is the identical fault 1.md records for stat cards
   ("drew a box inside a box in the cards and grid layouts, where the column is already the
   panel"), reintroduced by a rule written without looking at the output.

   So the pack keeps ownership of what a column looks like, which is the only way this can be
   right in all six of them, and the block's own job is done by its content: two h3 headings
   that say which side is which. The gap and the heading spacing are all that is left. */
.section--proscons > .row {
  gap: var(--space-6);
}
.section--proscons h3 {
  margin-top: 0;
  margin-bottom: var(--space-2);
}

/* The verdict box. Deliberately the loudest thing on a review page, because it is what a
   reader scans to before deciding whether to read the rest.

   The score inside it is a StatCard, so it is the string "4.5 / 5" and not a row of stars or
   a coloured bar. WCAG 1.4.1: colour cannot be the only carrier of information, and a star row
   with no text equivalent is precisely that. It also means this block adds a container rule
   and reuses the statcard typography rather than introducing a second one. */
.section--verdict {
  padding: var(--space-6);
  background: var(--paper-raised);
  border: 2px solid var(--accent);
  border-radius: var(--radius-lg);
  margin-block: var(--space-6);
}
.section--verdict > :first-child {
  margin-top: 0;
}
.section--verdict .statcard {
  border: 0;
  padding-inline: 0;
  background: none;
}

.richtext {
  max-width: var(--measure);
  margin-block: var(--space-4);
}
.richtext > * + * {
  margin-top: var(--space-3);
}
/* Tags that only ever arrive inside RichText, from author or model markup. They are not
   in the element registry, so nothing else in the output can produce them, and leaving
   them at browser defaults is what makes a generated blog post look pasted in. */
.richtext blockquote {
  padding-left: var(--space-5);
  border-left: 3px solid var(--accent);
  color: var(--muted);
  font-style: italic;
}
.richtext hr {
  height: 0;
  margin-block: var(--space-6);
  border: 0;
  border-top: 1px solid var(--border);
}
.richtext strong {
  color: var(--ink-strong);
  font-weight: 600;
}
.richtext code,
.richtext pre {
  background: var(--paper-sunk);
  border-radius: var(--radius-sm);
  font-family: var(--font-mono);
  font-size: var(--text-small);
}
.richtext code {
  padding: 0.1em 0.35em;
}
.richtext pre {
  padding: var(--space-4);
  overflow-x: auto;
}

/* Charts. The SVG is a 640x280 viewBox at width="100%": it scales down to the container,
   and below --chart-min it scrolls sideways in its own box rather than shrinking the axis
   labels to nothing. */
.chart {
  margin-block: var(--space-5);
  max-width: 100%;
  overflow-x: auto;
  /* The chart's own text and grid lines are drawn with currentColor. */
  color: var(--muted);
}
.chart > svg {
  display: block;
  width: 100%;
  min-width: var(--chart-min);
  height: auto;
}
.chart svg text {
  /* font-family only. The chart sets font-size as an SVG attribute in viewBox units, and
     a CSS font-size would override it and break the layout the geometry was built for. */
  font-family: var(--font-body);
}
.chart svg polyline {
  stroke-linecap: round;
  stroke-linejoin: round;
}
/* charts.js draws a single-series chart in its own first palette colour as an SVG
   attribute, which a CSS property overrides. Without this a warm cream page gets a cold
   blue line and the chart reads as pasted in from another site. Scoped to the
   single-series types on purpose: pie, funnel and heat map carry meaning in their own
   colour ramp, and the gauge's arc is a red/amber/green status. */
.chart-line svg polyline,
.chart-sparkline svg polyline {
  stroke: var(--accent);
}
.chart-bar svg rect {
  fill: var(--accent);
}
.chart-scatter svg circle {
  fill: var(--accent);
}
.chart svg line {
  stroke: var(--border);
}
.chart svg rect {
  /* px maps to viewBox user units here. A bar or a heat cell reads as a deliberate shape
     rather than a raw primitive; browsers without CSS geometry properties just draw it
     square, which is what charts.js emits anyway. */
  rx: 2px;
}
.chart svg circle {
  stroke: var(--paper);
  stroke-width: 1;
}
/* A series chart earns its width: below this the axis labels collide. */
.chart-line,
.chart-bar,
.chart-scatter {
  --chart-min: 30rem;
}
.chart-heatmap,
.chart-funnel {
  --chart-min: 22rem;
}
/* A pie or a gauge is a shape, not a series: it stops gaining from width. */
.chart-pie,
.chart-gauge {
  --chart-min: 16rem;
  max-width: 28rem;
}
/* A gauge is one big number drawn as SVG text in currentColor, so it reads as a figure
   rather than as a chart label. */
.chart-gauge {
  color: var(--ink);
}
.chart-pie svg path {
  /* Scoped to the pie on purpose: the gauge encodes its status in the arc's own stroke
     colour, and a blanket stroke rule here would paint over it. */
  stroke: var(--paper);
  stroke-width: 1;
}
/* Inline in a sentence, so no min-width and no box of its own. */
.chart-sparkline {
  display: inline-block;
  overflow: visible;
  margin: 0;
  vertical-align: middle;
}
.chart-sparkline > svg {
  width: 120px;
  min-width: 0;
}
/* A missing datasource is shown, not hidden: charts.js names the feed in the text so the
   cause is findable on the page. Styled as a quiet notice rather than an error, because
   the visitor is not the person who can fix it. */
.chart-empty {
  margin-block: var(--space-5);
  padding: var(--space-6);
  border: 1px dashed var(--border);
  border-radius: var(--radius-md);
  color: var(--muted);
  font-size: var(--text-small);
  text-align: center;
}
/* The accessible table under a chart the reference library grades below AA. It is the
   primary view for anyone who cannot read the SVG, so it is never visually hidden. */
.chart-table {
  display: block;
  margin-top: var(--space-3);
  /* figure.chart dims itself to --muted for axis labels; the table under it is content and
     goes back to body ink. */
  color: var(--ink);
}
.chart-table table {
  margin-block: 0;
}

.statcard {
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
  padding: var(--card-pad);
  background: var(--paper-alt);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
}
.statcard-label {
  color: var(--muted);
  font-size: var(--text-caption);
  font-weight: 600;
  letter-spacing: var(--caption-tracking);
  text-transform: var(--caption-transform);
}
.statcard-value {
  color: var(--ink-strong);
  font-family: var(--font-display);
  font-size: var(--text-h2);
  font-weight: var(--weight-heading);
  line-height: 1.1;
  font-variant-numeric: tabular-nums;
}
.statcard-hint {
  color: var(--muted);
  font-size: var(--text-small);
}

/* cards: a row is a responsive card grid and a column is the card. */
section {
  padding-block: var(--section-y);
}
.row {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(var(--card-min), 100%), 1fr));
  gap: var(--space-6);
  align-items: stretch;
}
.column {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  padding: var(--card-pad);
  background: var(--paper-alt);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-1);
  transition: box-shadow var(--dur) var(--ease), transform var(--dur) var(--ease);
}
.column:hover {
  box-shadow: var(--shadow-2);
  transform: translateY(-2px);
}
/* A card's contents arrive wrapped in a section (ProductGrid expands to row > column >
   section), so the inner section is flattened rather than padded a second time. */
.column > section {
  display: flex;
  flex: 1;
  flex-direction: column;
  gap: var(--space-3);
  padding-block: 0;
  border-top: 0;
}
/* Same flattening for a StatCard. In this layout the column IS the card — padded,
   bordered, rounded, shadowed — and .statcard carries its own frame from the base sheet, so
   a stat row rendered as row > column > StatCard drew a box inside a box. The type styles
   are what make it a stat card; the chrome is the column's job here. */
.column > .statcard,
.column > section > .statcard {
  padding: 0;
  background: none;
  border: 0;
  border-radius: 0;
}
/* Pushes the CTA to the bottom of the card whatever the copy length, which is the one
   card-layout trick that normally costs a flex utility class. */
.column > section > .button,
.column > .button {
  margin-top: auto;
  align-self: flex-start;
}
.column img {
  width: 100%;
  aspect-ratio: 4 / 3;
  object-fit: cover;
}

@media (prefers-reduced-motion: reduce) {
  html {
    scroll-behavior: auto;
  }
  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }
  .column:hover {
    transform: none;
  }
}
