/* ============================================================================
   TrustGetter — marketing site styles.

   Google design language, same as the app. See DESIGN.md §11.

   Consumes tokens.css exclusively: no literal colour value belongs in this
   file except the marketing pastel ramp declared at the top, which is Google's
   published 100-series and has no equivalent in the app.

   The base rules (type roles, buttons, cards) intentionally mirror the
   dashboard's styles.css treatments so the two surfaces are indistinguishable
   — a customer moving from trustgetter.com to app.trustgetter.com should not
   feel a seam.
   ========================================================================= */

:root {
  /* ── Marketing type scale ──────────────────────────────────────────────
     Fluid, because a landing page headline that is right at 1440px is far too
     big at 380px and there is no dense UI around it to hold the size in check.
     The token scale tops out at 28px, correct for an admin surface and far too
     quiet here.

     Tracking stays 0 — Google sets type at zero tracking and never negative.
     Headings are 500: large and *light* is the signature on Google's own
     marketing pages, but at 500 rather than 400 a headline still holds its own
     against a photograph-free layout, and 700 would leave the language.

     ⚠️ The hero used to top out at 62px and the section headings at 44px.
     Both were set when the hero headline was four short words; at a sentence's
     length 62px ran to four lines, pushed the lede and the buttons under the
     fold on a 900px-tall laptop, and made the one-accent rule below impossible
     to read — a coloured phrase cannot be an emphasis if the whole line is
     already shouting. The ceiling came down; the floor did not move, because
     the phone size was never the problem. */
  --type-hero-size:     clamp(36px, 4.2vw, 52px);
  --type-hero-lh:       1.12;
  --type-section-size:  clamp(26px, 3vw, 38px);
  --type-section-lh:    1.2;
  --type-lead-size:     clamp(17px, 1.4vw, 20px);
  --type-lead-lh:       1.6;
  --type-card-size:     clamp(18px, 1.3vw, 21px);
  --type-card-lh:       1.3;

  /* ── Pastel surfaces — Google's 100-series ────────────────────────────
     Used as whole-card backgrounds, the way Google's careers and product
     pages do. These are backgrounds only: text on them always uses the
     accessible *-ink tokens from tokens.css, never a tinted colour. */
  --pastel-blue:    #E8F0FE;
  --pastel-blue-2:  #D2E3FC;
  --pastel-green:   #E6F4EA;
  --pastel-green-2: #CEEAD6;
  --pastel-yellow:  #FEF7E0;
  --pastel-yellow-2:#FEEFC3;
  --pastel-red:     #FCE8E6;
  --pastel-red-2:   #FAD2CF;
  --pastel-purple:  #F3E8FD;
  --pastel-purple-2:#E9D2FD;

  --container-text: 1040px;  /* the app's content column */
  --section-y:      clamp(56px, 7vw, 104px);
}

*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }

/* ⚠️ `hidden` must actually hide, and by default it does not survive contact
   with this stylesheet. The browser's own rule is `[hidden] { display: none }`
   — one attribute selector — so ANY class rule that sets `display` beats it.
   `.topbar-actions` is `display: flex`, which meant the signed-in header and
   the signed-out one rendered side by side: "Sign in", "Get started free" AND
   "Dashboard", all at once, on every page.
   The `!important` is the point of the rule, not a shortcut around one. */
[hidden] { display: none !important; }

html { -webkit-text-size-adjust: 100%; scroll-behavior: smooth; }
@media (prefers-reduced-motion: reduce) { html { scroll-behavior: auto; } }

/* Thin, quiet scrollbars — matching the app. */
* { scrollbar-width: thin; scrollbar-color: var(--color-hairline) transparent; }
::-webkit-scrollbar { width: 8px; height: 8px; }
::-webkit-scrollbar-track { background: transparent; }
::-webkit-scrollbar-thumb { background: var(--color-hairline); border-radius: var(--radius-full); }
::-webkit-scrollbar-thumb:hover { background: var(--color-stone); }

body {
  font-family: var(--font-sans);
  font-size: var(--type-body-lg-size);
  font-weight: var(--type-body-weight);
  line-height: var(--type-body-lg-lh);
  letter-spacing: var(--type-tracking);
  background: var(--color-canvas);
  color: var(--color-ink);
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  overflow-x: hidden;   /* a decorative element must never widen the page */
}

img, svg { max-width: 100%; }

.skip-link {
  position: absolute;
  left: -9999px;
  top: var(--space-xs);
  z-index: 100;
  background: var(--color-canvas);
  color: var(--color-primary);
  padding: var(--space-xs) var(--space-md);
  border-radius: var(--radius-full);
  border: 1px solid var(--color-hairline);
}
.skip-link:focus { left: var(--space-md); }

/* ── Type roles ────────────────────────────────────────────────────────── */
.t-hero {
  font-size: var(--type-hero-size);
  font-weight: 500;
  line-height: var(--type-hero-lh);
  color: var(--color-ink-deep);
}
.t-section {
  font-size: var(--type-section-size);
  /* 500, not 400: a section heading repeated eight times down a page needs
     enough weight to act as a stop, or the page reads as one long column. */
  font-weight: 500;
  line-height: var(--type-section-lh);
  color: var(--color-ink-deep);
}
.t-card {
  font-size: var(--type-card-size);
  font-weight: 400;
  line-height: var(--type-card-lh);
  color: var(--color-ink-deep);
}
.t-display {
  font-size: var(--type-display-size);
  font-weight: var(--type-display-weight);
  line-height: var(--type-display-lh);
  color: var(--color-ink-deep);
}
.t-headline {
  font-size: var(--type-headline-size);
  font-weight: var(--type-headline-weight);
  line-height: var(--type-headline-lh);
  color: var(--color-ink-deep);
}
.t-title {
  font-size: var(--type-title-size);
  font-weight: var(--type-title-weight);
  line-height: var(--type-title-lh);
  color: var(--color-ink-deep);
}
.t-body-lg { font-size: var(--type-body-lg-size); line-height: var(--type-body-lg-lh); }
.t-body-sm { font-size: var(--type-body-size); line-height: var(--type-body-lh); }
.t-caption { font-size: var(--type-caption-size); line-height: var(--type-caption-lh); }

/* Genuinely secondary text only. Body copy uses --color-ink. */
.muted { color: var(--color-charcoal); }
.hint {
  font-size: var(--type-caption-size);
  line-height: var(--type-caption-lh);
  color: var(--color-steel);
}
.lede {
  font-size: var(--type-lead-size);
  line-height: var(--type-lead-lh);
  color: var(--color-ink);
  max-width: 60ch;
}

/* Coloured words in a headline, Google's own device.
   Green and yellow are the 700/800 steps, not the brand 500s: at 2.9:1 and
   worse, the 500s fail even the 3:1 large-text floor. Blue and red 500s pass
   at hero sizes and keep the mark recognisably Google. */
.c-blue   { color: var(--g-blue-500); }
.c-red    { color: var(--g-red-500); }
.c-green  { color: var(--color-success); }
.c-yellow { color: var(--color-attention); }

a { color: var(--color-primary); text-decoration: none; }
a:hover { text-decoration: underline; }
a:focus-visible { outline: 2px solid var(--color-primary); outline-offset: 2px; border-radius: var(--radius-xs); }

/* ── Layout ────────────────────────────────────────────────────────────── */
.container {
  width: 100%;
  max-width: var(--container-text);
  margin: 0 auto;
  padding: 0 var(--space-lg);
}
.container--wide { max-width: var(--container-max); }
.container--narrow { max-width: 760px; }

.section { padding: var(--section-y) 0; }
.section--tight { padding: clamp(40px, 5vw, 72px) 0; }
.section--soft { background: var(--color-surface-soft); }
.section--hairline { border-top: 1px solid var(--color-hairline-soft); }

/* Widths in px, not ch. `ch` resolves against the element's OWN font-size,
   and these are containers sitting at body size — 22ch measured about 176px
   and stacked every section heading one word per line. Measure limits belong
   on the text element, never on the box around it. */
.section-head { max-width: 680px; margin-bottom: clamp(32px, 4vw, 56px); }
.section-head--center { margin-left: auto; margin-right: auto; text-align: center; max-width: 800px; }
.section-head p {
  margin-top: var(--space-md);
  color: var(--color-ink);
  font-size: var(--type-lead-size);
  line-height: var(--type-lead-lh);
  max-width: 60ch;
}
.section-head--center p { margin-left: auto; margin-right: auto; }

.eyebrow {
  display: inline-block;
  font-size: var(--type-label-size);
  font-weight: var(--type-label-weight);
  line-height: var(--type-label-lh);
  letter-spacing: var(--type-tracking-wide);
  color: var(--color-primary);
  margin-bottom: var(--space-sm);
}

.stack    { display: flex; flex-direction: column; gap: var(--space-md); }
.stack-lg { display: flex; flex-direction: column; gap: var(--space-lg); }
.row      { display: flex; align-items: center; gap: var(--space-sm); flex-wrap: wrap; }
.row--center { justify-content: center; }

/* Spacing utilities exist so no page needs a style attribute. The CSP does
   permit inline styles — the widget injects a <style> into its shadow root, so
   'unsafe-inline' has to stay in style-src — but that is a concession to the
   widget, not a licence for these pages to use it. */
.mt-md { margin-top: var(--space-md); }
.mt-lg { margin-top: var(--space-lg); }
.mt-xl { margin-top: var(--space-xl); }
.mt-xxl { margin-top: var(--space-xxl); }

/* ── The compatibility strip ───────────────────────────────────────────────
   A seamless scrolling row of the platforms the widget can be embedded on,
   directly under the hero.

   Two identical tracks side by side, translated by exactly half the pair's
   width: at -50% the second copy sits precisely where the first began, so the
   reset is invisible. This is why the markup emits the list twice — one copy
   would empty the right-hand side before snapping back.

   The mask fades both edges into the page. Without it the names are cut in
   half at the viewport edge, which reads as a broken layout rather than as
   something continuing past the frame. */
.logo-strip {
  overflow: hidden;
  /* Inside the install section now, on white, so it needs a separator from the
     code block above it rather than a box of its own. */
  margin-top: var(--space-xxl);
  padding-top: var(--space-xl);
  border-top: 1px solid var(--color-hairline-soft);
}
.logo-marquee {
  display: flex;
  width: max-content;
  animation: logo-scroll 46s linear infinite;
  -webkit-mask-image: linear-gradient(90deg, transparent, #000 10%, #000 90%, transparent);
          mask-image: linear-gradient(90deg, transparent, #000 10%, #000 90%, transparent);
}
/* Hovering stops it, so a logo that catches the eye can actually be looked at. */
.logo-marquee:hover { animation-play-state: paused; }
.logo-track {
  display: flex;
  align-items: center;
  /* Wide on purpose: seven logos average ~120px, so a ~120px gap is what puts
     four or five of them on screen at once. Tighter and the row reads as a
     crowded list; wider and it reads as empty. */
  gap: clamp(56px, 9vw, 128px);
  padding-right: clamp(56px, 9vw, 128px);
  flex: none;
}
/* Every item is the same height regardless of how tall its logo is, so the
   marks sit on one optical centre line and the row cannot wobble. The widths
   differ — that is the point; see BRAND_LOGOS for why one common height would
   make the tall marks tower over the wide ones. */
.logo-item {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  height: 44px;
  flex: none;
}
.logo-item img {
  display: block;
  /* max-* rather than fixed: the width/height attributes on the tag already set
     the intended size and reserve the space, and these stop a logo overflowing
     a narrow phone. */
  max-width: 100%;
  height: auto;
}

@keyframes logo-scroll {
  from { transform: translate3d(0, 0, 0); }
  to   { transform: translate3d(-50%, 0, 0); }
}

/* Reduced motion gets the same information with none of the movement: the
   strip becomes a centred, wrapped row and the duplicate track is removed
   outright — a static list that silently says everything twice is worse than
   no strip at all. */
@media (prefers-reduced-motion: reduce) {
  .logo-marquee {
    animation: none;
    width: auto;
    flex-wrap: wrap;
    justify-content: center;
    -webkit-mask-image: none;
            mask-image: none;
  }
  .logo-marquee .logo-track[aria-hidden='true'] { display: none; }
  .logo-track {
    /* flex: none out here keeps the track at its max-content width, which is
       wider than the container — so the row silently loses the logos at both
       ends instead of wrapping. It has to be allowed to shrink. */
    flex: 1 1 auto;
    width: 100%;
    flex-wrap: wrap;
    justify-content: center;
    gap: var(--space-xl) clamp(32px, 5vw, 64px);
    padding-right: 0;
  }
}

/* ── Buttons — pill-shaped, exactly the app's tiers ────────────────────── */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-xs);
  font-family: inherit;
  font-size: var(--type-label-size);
  font-weight: var(--type-label-weight);
  line-height: var(--type-label-lh);
  letter-spacing: var(--type-tracking-wide);
  height: 40px;
  padding: 0 24px;
  border-radius: var(--radius-full);
  border: 1px solid transparent;
  cursor: pointer;
  white-space: nowrap;
  text-decoration: none;
  transition: background var(--motion-fast), border-color var(--motion-fast),
              box-shadow var(--motion-fast), color var(--motion-fast);
}
.btn:hover { text-decoration: none; }
.btn:focus-visible { outline: 2px solid var(--color-primary); outline-offset: 2px; }

.btn--cobalt { background: var(--color-primary); color: var(--color-on-primary); }
.btn--cobalt:hover { background: var(--color-primary-deep); box-shadow: var(--shadow-subtle); }

.btn--success { background: var(--color-success); color: var(--color-on-primary); }
.btn--success:hover { background: var(--color-success-deep); box-shadow: var(--shadow-subtle); }

.btn--secondary {
  background: transparent;
  color: var(--color-primary);
  border-color: var(--color-hairline);
}
.btn--secondary:hover { background: var(--color-primary-soft); border-color: var(--color-primary-hairline); }

.btn--ghost {
  background: transparent;
  color: var(--color-primary);
  border-color: transparent;
  padding: 0 12px;
}
.btn--ghost:hover { background: var(--color-primary-soft); }

/* One size up for a marketing call to action. The app has no equivalent —
   every action there sits in a dense working UI where 40px is correct. */
.btn--lg { height: 48px; padding: 0 32px; font-size: var(--type-body-lg-size); }

/* A button whose glyph LEADS its label — the header's "Sign in" and
   "Dashboard". 16px, matching the app's inline-with-a-label icon size in
   DESIGN.md §7: the 20px used in the rail would out-weigh a 14px label and
   turn a text button into an icon with a caption.

   NO margin here. `.btn` is already `display: inline-flex` with
   `gap: var(--space-xs)`, so a margin-right adds to the gap rather than
   creating it — 6px + 8px read as an icon and a label that had drifted apart. */
.btn--icon-lead svg { width: 16px; height: 16px; flex: none; }

/* Available to a screen reader, invisible to everyone else. Used for the
   comparison table's <caption>, which describes a five-column table that is
   perfectly clear visually and completely opaque read one cell at a time.
   Not `display:none` — that removes it from the accessibility tree too,
   which would defeat the entire point. */
.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/* Green because it opens WhatsApp and because green already means "go" here.
   Deliberately --color-success rather than WhatsApp's brand green: a literal
   brand hex would be a colour outside the palette. */
.btn--whatsapp { background: var(--color-success); color: var(--color-on-primary); }
.btn--whatsapp:hover { background: var(--color-success-deep); box-shadow: var(--shadow-subtle); }
.btn--whatsapp svg { width: 18px; height: 18px; }

/* Text link with a travelling arrow — Google's standard "keep reading" affordance. */
.link-arrow {
  display: inline-flex;
  align-items: center;
  gap: var(--space-xs);
  font-size: var(--type-label-size);
  font-weight: var(--type-label-weight);
  color: var(--color-primary);
}
.link-arrow:hover { text-decoration: none; }
.link-arrow svg { width: 18px; height: 18px; transition: transform var(--motion-medium); }
.link-arrow:hover svg { transform: translateX(4px); }

/* ── Cards ─────────────────────────────────────────────────────────────── */
.card {
  background: var(--color-canvas);
  border: 1px solid var(--color-hairline-soft);
  border-radius: var(--radius-xl);
  padding: var(--space-lg);
  box-shadow: var(--shadow-none);
}

/* ── Chips ─────────────────────────────────────────────────────────────── */
.chip {
  display: inline-flex;
  align-items: center;
  gap: var(--space-xs);
  height: 36px;
  padding: 0 var(--space-md);
  border-radius: var(--radius-full);
  background: var(--color-surface-alt);
  color: var(--color-charcoal);
  font-size: var(--type-body-size);
  line-height: var(--type-body-lh);
}
.chip svg { width: 16px; height: 16px; }
.chip--primary { background: var(--color-primary-soft); color: var(--color-primary-ink); }
.chip--success { background: var(--color-success-soft); color: var(--color-success-ink); }
.chip--outline { background: transparent; border: 1px solid var(--color-hairline); }

/* ── The mark ─────────────────────────────────────────────────────────────
   Used where the full lockup is too big — the widget credit line in the hero
   mock-up. This was four coloured squares drawn with gradients, which is not
   the logo: the real one closes into a half-circle along the bottom. It is an
   <img> of the actual icon now, so it cannot drift from the file again. */
.brand-mark {
  width: 18px;
  height: 18px;
  flex: 0 0 auto;
  display: block;
  object-fit: contain;
}

/* ⚠ The sticky bar overlaps whatever an in-page link scrolls to.
   A jump to `#anything` puts the target at scroll position 0, which is UNDER
   a bar that is 76px tall and painted opaque — so the heading a reader
   followed a link to is the one thing they cannot see. `scroll-padding-top`
   on the scroll container fixes every anchor at once, including the browser's
   own back-forward restoration and find-in-page; `scroll-margin-top` would
   have to be repeated on each target and would be missed by the next section
   somebody adds. 76px of bar, plus room to not sit against the hairline. */
html {
  scroll-padding-top: 92px;
}

/* ── Topbar ────────────────────────────────────────────────────────────── */
.topbar {
  background: var(--color-canvas);
  border-bottom: 1px solid var(--color-hairline-soft);
  position: sticky;
  top: 0;
  z-index: 30;
}
.topbar-inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-md);
  /* Taller than the app's 64px bar. The app's is a working surface where every
     pixel of height is taken from the thing being worked on; here the bar is
     the first thing on the page and a 44px action needs room to sit in without
     touching the hairline. */
  min-height: 76px;
  max-width: var(--container-max);
  margin: 0 auto;
  padding: var(--space-sm) var(--space-lg);
}

.brand {
  display: flex;
  align-items: center;
  padding: var(--space-xxs) var(--space-xs);
  border-radius: var(--radius-full);
}
.brand:hover { text-decoration: none; }
/* An explicit INTEGER box, not `width: auto`. The file is 378×80 (see
   shared/resize-logo.py), so 40px tall is exactly 189px wide — auto-sizing off
   the old 600×127 master produced 188.97px, and a fractional box lands the
   image on sub-pixel boundaries, which is half of why it looked soft. */
.brand img { height: 40px; width: 189px; display: block; }

.topbar-right { display: flex; align-items: center; gap: var(--space-lg); }
.nav { display: flex; align-items: center; gap: var(--space-xxs); }

/* ⚠️ Nav items are INK, not charcoal, and they have a real height.
   They were charcoal 14px in a 4px-tall padding box, which on white is the
   treatment for a hint beside a form field — the whole bar sat quieter than
   the body copy under it and read as chrome rather than as the way around the
   site. Charcoal stays for what genuinely is secondary. */
.nav-link {
  display: inline-flex;
  align-items: center;
  height: 40px;
  padding: 0 var(--space-md);
  border-radius: var(--radius-full);
  color: var(--color-ink);
  font-size: var(--type-body-lg-size);
  font-weight: var(--type-label-weight);
  line-height: var(--type-label-lh);
}
.nav-link:hover { background: var(--color-surface-alt); text-decoration: none; }
/* Google marks the current nav item with a blue tint, never a dark fill. */
.nav-link--active { background: var(--color-primary-soft); color: var(--color-primary); }
.nav-link--active:hover { background: var(--color-primary-soft); }

/* ── The "Widgets" dropdown ───────────────────────────────────────────────
   One bar item standing in for the two product pages, the same way the
   integrations hub stands in for seven guides. The bar is capped at five
   items because past that it wraps at 1024px and pushes Pricing off the end.

   The menu is collapsed by `.nav-js .nav-menu`, and `nav-js` is a class
   nav.js puts on <html>. With script blocked the menu simply stays in flow
   and every link remains reachable — the failure direction is an untidy bar,
   never a button that does nothing above two unreachable pages. */
.nav-group { position: relative; }

.nav-link--group {
  display: inline-flex;
  align-items: center;
  height: 40px;
  gap: var(--space-xxs);
  border: 0;
  background: none;
  font-family: inherit;
  cursor: pointer;
}

.nav-caret {
  width: 16px;
  height: 16px;
  transition: transform var(--motion-fast);
}
.nav-group.is-open .nav-caret { transform: rotate(180deg); }

.nav-js .nav-menu {
  position: absolute;
  top: calc(100% + var(--space-xs));
  left: 0;
  z-index: 40;
  display: none;
  min-width: 306px;
  padding: var(--space-xs);
  background: var(--color-canvas);
  border: 1px solid var(--color-hairline-soft);
  border-radius: var(--radius-xl);
  /* The one place in the header something genuinely floats, so the one place
     a shadow is not decoration. Google's grey-blue, never black. */
  box-shadow: 0 4px 12px rgba(60, 64, 67, 0.15);
}
.nav-js .nav-group.is-open .nav-menu { display: block; }

/* A hover-opened menu with a gap under the button loses the pointer crossing
   it and shuts mid-reach. The bridge is padding on the panel's own top edge,
   drawn on nothing. */
.nav-js .nav-menu::before {
  content: '';
  position: absolute;
  inset: calc(var(--space-xs) * -1) 0 auto 0;
  height: var(--space-xs);
}

.nav-menu-item {
  display: flex;
  align-items: flex-start;
  gap: var(--space-sm);
  padding: var(--space-sm);
  border-radius: var(--radius-lg);
  color: var(--color-ink);
}
.nav-menu-item:hover { background: var(--color-surface-alt); text-decoration: none; }
.nav-menu-item--active { background: var(--color-primary-soft); }
.nav-menu-item--active .nav-menu-name { color: var(--color-primary); }

/* The marks are the platforms' own and are NOT tinted by our palette: the
   Google G carries its four published colours and the Instagram glyph is a
   currentColor outline. Sizing is ours; the artwork is not. */
.nav-menu-mark {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 32px;
  height: 32px;
  border-radius: var(--radius-circle);
  background: var(--color-surface-alt);
  color: var(--color-charcoal);
}
.nav-menu-item--active .nav-menu-mark { background: var(--color-canvas); }
/* ⚠️ The Instagram mark is a FILLED app icon, so the grey disc behind it
   became a grey ring around a coloured square — two shapes where the Google
   G, which is loose artwork on a transparent ground, needs one. The disc is
   what the G sits on; the app icon is its own disc. */
.nav-menu-mark--instagram { background: none; }
.nav-menu-mark--instagram .nav-mark { width: 32px; height: 32px; }
.nav-mark { width: 18px; height: 18px; display: block; }

.nav-menu-text { display: flex; flex-direction: column; gap: 2px; }
.nav-menu-name {
  font-size: var(--type-label-size);
  font-weight: var(--type-label-weight);
  line-height: var(--type-label-lh);
  color: var(--color-ink-deep);
}
.nav-menu-blurb {
  font-size: var(--type-caption-size);
  line-height: var(--type-caption-lh);
  color: var(--color-charcoal);
}

/* ⚠️ The header's two actions were a 40px ghost button beside a 40px filled
   one, at the app's inline label size. In a dense working UI that is correct;
   in a marketing bar it made the primary action — the only thing on this site
   with a job — the same weight as a nav link, and the ghost "Sign in" beside
   it had no border, no fill and charcoal type, so it read as a fourth nav item
   that had drifted right.

   They are `--topbar-cta` tall now, the primary carries the brand fill and a
   lift on hover, and Sign in takes a hairline border so it reads as a control
   without competing. Still two tiers and still pill-shaped: the app's language,
   one size up, which is the same concession .btn--lg already makes. */
.topbar-actions { display: flex; align-items: center; gap: var(--space-xs); }
.topbar-actions .btn {
  height: 44px;
  padding: 0 var(--space-lg);
  font-size: var(--type-body-lg-size);
}
.topbar-actions .btn--ghost {
  border: 1px solid var(--color-hairline);
  color: var(--color-ink-deep);
}
.topbar-actions .btn--ghost:hover {
  background: var(--color-primary-soft);
  border-color: var(--color-primary-hairline);
  color: var(--color-primary);
}
.topbar-actions .btn--cobalt { box-shadow: var(--shadow-subtle); }
.topbar-actions .btn--cobalt:hover { box-shadow: var(--shadow-panel); }
.topbar-actions .btn--icon-lead svg { width: 18px; height: 18px; }

.nav-toggle {
  display: none;
  width: 40px;
  height: 40px;
  align-items: center;
  justify-content: center;
  background: transparent;
  border: 0;
  border-radius: var(--radius-circle);
  cursor: pointer;
}
.nav-toggle:hover { background: var(--color-surface-alt); }
.nav-toggle:focus-visible { outline: 2px solid var(--color-primary); outline-offset: 2px; }
.nav-toggle-bars,
.nav-toggle-bars::before,
.nav-toggle-bars::after {
  display: block;
  width: 18px;
  height: 2px;
  border-radius: var(--radius-full);
  background: var(--color-ink);
  transition: transform var(--motion-medium), opacity var(--motion-fast);
}
.nav-toggle-bars::before, .nav-toggle-bars::after { content: ''; position: relative; }
.nav-toggle-bars::before { top: -6px; }
.nav-toggle-bars::after  { top: 4px; }
.nav-toggle[aria-expanded='true'] .nav-toggle-bars { background: transparent; }
.nav-toggle[aria-expanded='true'] .nav-toggle-bars::before { top: -2px; transform: rotate(45deg); }
.nav-toggle[aria-expanded='true'] .nav-toggle-bars::after  { top: -4px; transform: rotate(-45deg); }

/* ── Hero ──────────────────────────────────────────────────────────────── */
.hero {
  padding: clamp(32px, 4.5vw, 72px) 0 var(--section-y);
  /* A very soft wash behind the whole band, fading out before the section
     below starts. It gives the scene something to sit ON — a white panel with
     a shadow on a white page has to work its edges hard to read as a surface —
     without introducing a colour: this is the same Blue 50 already used for
     active nav, at the top of a gradient into the page's own white. */
  background: linear-gradient(180deg, var(--pastel-blue) 0%, rgba(232, 240, 254, 0) 62%);
}
.hero-grid {
  display: grid;
  /* The copy column is the wider one. It was 1.02fr against 1fr — effectively
     equal — which gave a sentence-length headline the same room as a picture
     and made it wrap a word at a time. */
  grid-template-columns: 1.08fr 1fr;
  gap: clamp(32px, 4vw, 64px);
  align-items: center;
}
/* A measure limit on the headline itself, not on the column: a hero line is
   the one piece of type on the page where a stray one-word last line is
   visible from across the room. */
.hero h1 { margin-bottom: var(--space-md); max-width: 18ch; }
.hero .lede { margin-bottom: var(--space-xl); max-width: 48ch; }
.hero-actions { display: flex; flex-wrap: wrap; gap: var(--space-sm); }
.hero-reassure {
  margin-top: var(--space-lg);
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-md) var(--space-lg);
  color: var(--color-ink);
  font-size: var(--type-body-size);
  line-height: var(--type-body-lh);
}
.hero-reassure span { display: inline-flex; align-items: center; gap: var(--space-xxs); }
.hero-reassure svg { width: 16px; height: 16px; color: var(--color-success); flex: 0 0 auto; }

/* ── The homepage hero: centred, with one card per widget ─────────────────
   🔑 Replaces the browser-frame scene, which drew the customer's page as grey
   bars — and a flat grey block is what a loading skeleton looks like, so the
   picture read as a page that had failed to load. The phone that overhung its
   corner also covered the second review at most window widths.

   What is here now makes one claim: a review and a feed, both of them yours.
   The two cards are the SAME WIDTH, on the SAME BASELINE, tilted equal and
   opposite. That symmetry is the whole composition — an earlier version had
   four pieces at two sizes and four angles, and it read as scattered. */
.hero--center { text-align: center; }
.hero-center { max-width: 800px; margin: 0 auto; position: relative; z-index: 2; }
.hero--center .hero-actions { justify-content: center; }
.hero--center .lede { margin-left: auto; margin-right: auto; max-width: 52ch; }

.hero-badge {
  display: inline-flex;
  align-items: center;
  gap: var(--space-xs);
  margin: 0 0 var(--space-lg);
  padding: 8px 17px;
  border: 1px solid var(--color-hairline-soft);
  border-radius: var(--radius-full);
  background: var(--color-canvas);
  box-shadow: var(--shadow-subtle);
  font-size: var(--type-body-size);
  font-weight: 500;
  color: var(--color-ink);
}
.hero-badge svg { width: 14px; height: 14px; color: #8134AF; flex: 0 0 auto; }

/* The gradient runs across the payoff phrase only.

   ⚠️ `background-clip: text` needs `color: transparent`, so any engine that
   cannot paint it renders the phrase INVISIBLE rather than merely uncoloured.
   The @supports guard is what keeps the sentence readable in that case. */
.c-gradient { color: var(--color-primary); }
@supports (-webkit-background-clip: text) or (background-clip: text) {
  .c-gradient {
    background-image: linear-gradient(94deg, #1A73E8 0%, #6B3FD4 55%, #A32C86 100%);
    -webkit-background-clip: text;
    background-clip: text;
    color: transparent;
  }
}

/* ⚠️ The headline measure is REMOVED on this hero. `.hero h1` caps at 18ch,
   which is right for a four-word line beside a picture and far too tight for
   an eleven-word sentence in a centred column — it would stack roughly one
   word per line. `balance` evens the lines instead, and needs room to do it. */
.hero--center h1 { max-width: none; text-wrap: balance; }

/* ── the two floating cards ──────────────────────────────────────────────
   Anchored to the COPY COLUMN, not to the section. Anchored to the section
   they drift toward the window edges as the screen widens, so on a large
   monitor they end up a long way from the words with no relationship to them.
   `right: calc(100% + N)` holds a card exactly N px outside the text at every
   width, whatever that card's own width turns out to be. */
.hero-piece {
  position: absolute;
  top: 76px;
  width: 252px;
  padding: var(--space-md);
  text-align: start;
  background: var(--color-canvas);
  border: 1px solid var(--color-hairline-soft);
  border-radius: var(--radius-xl);
  box-shadow: var(--shadow-panel);
  animation: hero-bob 8s cubic-bezier(0.4, 0, 0.2, 1) infinite;
}
.hero-piece--review { right: calc(100% + 40px); --tilt: -3deg; }
.hero-piece--feed { left: calc(100% + 40px); --tilt: 3deg; animation-delay: -4s; }

/* 🔑 The tilt lives INSIDE the keyframes. `transform` is a single property,
   so a separate `rotate()` declaration is overwritten the moment the
   animation runs, and the angle silently disappears. */
@keyframes hero-bob {
  0%, 100% { transform: rotate(var(--tilt, 0deg)) translateY(0); }
  50%      { transform: rotate(var(--tilt, 0deg)) translateY(-8px); }
}

.hpr-top, .hpf-top { display: flex; align-items: center; gap: var(--space-sm); }
.hpf-top { margin-bottom: var(--space-sm); }
.hpr-avatar {
  width: 34px; height: 34px; flex: 0 0 auto;
  border-radius: var(--radius-circle);
  /* The 700-series step, not Green 500: white initials on the brand green
     measure 2.9:1. The same rule the business-list avatars follow. */
  background: var(--color-success);
  color: var(--g-white);
  display: grid; place-items: center;
  font-size: var(--type-caption-size); font-weight: 500;
}
.hpr-name { display: block; font-size: var(--type-subtitle-size); font-weight: 500; color: var(--color-ink-deep); }
.hpr-when { display: block; font-size: var(--type-caption-size); color: var(--color-steel); }
.hpr-stars { display: flex; gap: 2px; margin: var(--space-sm) 0 var(--space-xs); }
.hpr-stars .star { width: 14px; height: 14px; }
.hpr-quote {
  margin: 0;
  font-size: var(--type-body-size);
  line-height: var(--type-body-lh);
  color: var(--color-ink);
}
.hpr-source {
  display: flex; align-items: center; gap: var(--space-xs);
  margin: var(--space-sm) 0 0;
  padding-top: var(--space-sm);
  border-top: 1px solid var(--color-hairline-soft);
  font-size: 11px;
  color: var(--color-steel);
}
/* ⚠️ 13px was too small for this mark and it read as a coloured smudge.
   The glyph is drawn on a 24-unit grid with 1.7-unit strokes, so at 13px the
   white camera outline lands on 0.9 of a device pixel and the browser has to
   blur it across two. 16px puts those strokes back on a full pixel. */
.hpr-source .nav-mark { width: 16px; height: 16px; flex: 0 0 auto; }

/* Instagram draws a ring around an avatar, so the mark sits in one. */
.hpf-ring {
  width: 34px; height: 34px; flex: 0 0 auto; padding: 2px;
  border-radius: var(--radius-circle);
  background: conic-gradient(from 200deg, #F9CE34, #EE2A7B, #6228D7, #F9CE34);
}
.hpf-ring i {
  display: grid;
  place-items: center;
  width: 100%;
  height: 100%;
  border-radius: var(--radius-circle);
  background: var(--color-ink-deep);
  border: 2px solid var(--color-canvas);
  color: var(--g-white);
  font-style: normal;
  font-size: 12px;
  font-weight: 500;
}
.hpf-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 6px; }
.hpf-tile {
  aspect-ratio: 1; border-radius: var(--radius-sm);
  display: grid; place-items: center;
}
/* Pastel gradients, never flat grey — the rule DESIGN.md §13 records about
   the builder's own layout picker. A flat grey block is a loading skeleton. */
/* 🔑 Real photographs, not gradients. The card is the Instagram half of the
   homepage's promise, and a feed drawn as six coloured squares is the loading
   skeleton DESIGN.md §13 warns about — on the one element whose whole subject
   is pictures.

   ⚠️ Six files, shared with the widget cards further down the page, so the
   second section costs nothing: the browser already holds them. They are the
   same photographs the Instagram product page uses, which is deliberate — one
   fictional hotel across the site rather than a different invented business
   on every page. */
.hpf-tile { background-size: cover; background-position: center; background-repeat: no-repeat; }
.hpf-tile--1 { background-image: url(/assets/photo-feed-1.0cda497e.webp); }
.hpf-tile--2 { background-image: url(/assets/photo-feed-2.9cf9f6e6.webp); }
.hpf-tile--3 { background-image: url(/assets/photo-feed-3.ef840e71.webp); }
.hpf-tile--4 { background-image: url(/assets/photo-feed-4.8a6a01e2.webp); }
.hpf-tile--5 { background-image: url(/assets/photo-feed-5.8cb592b1.webp); }
.hpf-tile--6 { background-image: url(/assets/photo-feed-6.4c18061b.webp); }
.hpf-tile svg { width: 20px; height: 20px; filter: drop-shadow(0 1px 3px rgba(0, 0, 0, 0.3)); }

/* ── the platform line ───────────────────────────────────────────────────
   The brand-logo marquee still sits at the end of the install steps, where
   it answers "will that work on my site?" at the moment the question arises.
   This is the same fact in words, for the reader who never scrolls that far. */
.hero-works {
  max-width: 940px;
  margin: var(--space-xxl) auto 0;
  padding-top: var(--space-lg);
  border-top: 1px solid var(--color-hairline-soft);
  text-align: center;
}
.hero-works-title {
  margin: 0 0 var(--space-sm);
  font-size: var(--type-caption-size);
  font-weight: 500;
  color: var(--color-steel);
  /* ⚠️ Tracking is 0 everywhere else on this site. A 12px all-caps label is
     the exception every type system makes, because caps at that size close up
     without it. DESIGN.md §3 sets the rule for running text, which this is not. */
  letter-spacing: 0.05em;
  text-transform: uppercase;
}
.hero-works-list {
  display: flex; flex-wrap: wrap; justify-content: center; align-items: center;
  gap: 10px 20px; margin: 0;
  font-size: var(--type-body-size);
}
.hero-works-list b { font-weight: 500; color: var(--color-ink-deep); }
.hero-works-list i {
  width: 3px; height: 3px; border-radius: var(--radius-circle);
  background: var(--color-stone); display: block;
}
.hero-works-note {
  margin: var(--space-sm) 0 0;
  font-size: var(--type-body-size);
  color: var(--color-charcoal);
}

/* ── The hero widget mock ─────────────────────────────────────────────────
   A CSS mock-up, not a live widget: the marketing page must never fetch a
   customer's reviews to decorate itself. Same reasoning as the builder's
   template thumbnails.

   It sits inside a browser frame, which is the point of the whole illustration.
   A review card floating on its own says "here is a component"; the same card
   inside a window chrome labelled yourwebsite.com says "this is what your site
   looks like afterwards", which is the actual promise. */
.browser {
  border-radius: var(--radius-xxxl);
  background: var(--color-canvas);
  border: 1px solid var(--color-hairline-soft);
  box-shadow: var(--shadow-panel);
  overflow: hidden;
}
.browser-bar {
  display: flex;
  align-items: center;
  gap: var(--space-sm);
  padding: var(--space-sm) var(--space-md);
  background: var(--color-surface-soft);
  border-bottom: 1px solid var(--color-hairline-soft);
}
.browser-dots { display: flex; gap: 6px; flex: 0 0 auto; }
.browser-dots i { width: 10px; height: 10px; border-radius: 50%; background: var(--color-hairline); }
.browser-url {
  flex: 1;
  background: var(--color-canvas);
  border: 1px solid var(--color-hairline-soft);
  border-radius: var(--radius-full);
  height: 26px;
  display: flex;
  align-items: center;
  gap: var(--space-xxs);
  padding: 0 var(--space-sm);
  font-size: var(--type-caption-size);
  color: var(--color-steel);
}
.browser-url svg { width: 12px; height: 12px; flex: 0 0 auto; }
.browser-body { padding: clamp(14px, 1.8vw, 22px); background: var(--color-canvas); }

.mock { display: grid; gap: var(--space-md); }

/* Header: the score, the stars, the count — and the action a real Google
   widget carries, so the mock does not look like a stripped-back version. */
.mock-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-md);
  flex-wrap: wrap;
}
.mock-score { display: flex; align-items: center; gap: var(--space-sm); }
.mock-score b {
  font-size: clamp(30px, 3vw, 38px);
  font-weight: 400;
  line-height: 1;
  color: var(--color-ink-deep);
}
.mock-score-meta { display: grid; gap: 2px; }
.mock-count { font-size: var(--type-caption-size); color: var(--color-charcoal); }
.mock-cta {
  display: inline-flex;
  align-items: center;
  gap: var(--space-xxs);
  height: 32px;
  padding: 0 var(--space-md);
  border-radius: var(--radius-full);
  border: 1px solid var(--color-hairline);
  color: var(--color-primary);
  font-size: var(--type-body-size);
  font-weight: 500;
  white-space: nowrap;
}

.mock-reviews { display: grid; gap: var(--space-sm); }
.mock-review {
  border: 1px solid var(--color-hairline-soft);
  border-radius: var(--radius-xl);
  padding: var(--space-md);
}
.mock-review-top { display: flex; align-items: center; gap: var(--space-sm); margin-bottom: var(--space-xs); }
.mock-avatar {
  width: 36px; height: 36px;
  border-radius: var(--radius-circle);
  display: grid; place-items: center;
  color: var(--color-on-primary);
  font-size: var(--type-body-size);
  font-weight: 500;
  flex: 0 0 auto;
}
.mock-avatar--blue  { background: var(--color-primary); }
.mock-avatar--green { background: var(--color-success); }
.mock-avatar--red   { background: var(--color-critical); }
.mock-who { flex: 1; min-width: 0; display: grid; gap: 2px; }
.mock-name { font-size: var(--type-body-size); font-weight: 500; color: var(--color-ink-deep); }
.mock-meta { display: flex; align-items: center; gap: var(--space-xs); }
.mock-date { font-size: var(--type-caption-size); color: var(--color-steel); }
/* The four-colour square on each review, the way a real Google widget credits
   the source of the review it is showing. */
.mock-review .brand-mark { width: 14px; height: 14px; }
.mock-body { font-size: var(--type-body-size); line-height: var(--type-body-lh); color: var(--color-ink); }
.stars { color: var(--g-yellow-500); letter-spacing: 1px; font-size: var(--type-body-size); }
.stars--sm { font-size: var(--type-caption-size); }
.mock-foot {
  padding-top: var(--space-sm);
  border-top: 1px solid var(--color-hairline-soft);
  display: flex; align-items: center; justify-content: center; gap: var(--space-xxs);
}

/* ── Grids ─────────────────────────────────────────────────────────────── */
.grid-2 { display: grid; grid-template-columns: repeat(2, 1fr); gap: var(--space-lg); }
.grid-3 { display: grid; grid-template-columns: repeat(3, 1fr); gap: var(--space-lg); }
.grid-4 { display: grid; grid-template-columns: repeat(4, 1fr); gap: var(--space-md); }

/* ── Icon cards ────────────────────────────────────────────────────────────
   Bordered, centred, one coloured icon each — the pattern Google uses for
   principles and feature summaries. Flat: the border does the work. */
.icon-card {
  background: var(--color-canvas);
  border: 1px solid var(--color-hairline-soft);
  border-radius: var(--radius-xxl);
  padding: clamp(24px, 2.5vw, 36px) var(--space-lg);
  display: flex;
  flex-direction: column;
  gap: var(--space-sm);
  transition: border-color var(--motion-medium), box-shadow var(--motion-medium);
}
.icon-card:hover { border-color: var(--color-hairline); box-shadow: var(--shadow-subtle); }
.icon-card--center { text-align: center; align-items: center; }

/* The whole card is the link on the integrations hub. The heading keeps ink
   colour rather than turning blue: eight blue headings in a grid reads as a
   list of links rather than a set of cards, and the arrow already carries the
   affordance. The arrow travels on hover of the CARD, not just itself. */
.icon-card--link { text-decoration: none; }
.icon-card--link:hover { text-decoration: none; border-color: var(--color-primary-hairline); }
.icon-card--link h3 { color: var(--color-ink-deep); }
.icon-card--link .link-arrow { margin-top: auto; padding-top: var(--space-xs); }
.icon-card--link:hover .link-arrow svg { transform: translateX(4px); }
.icon-card--link:focus-visible { outline: 2px solid var(--color-primary); outline-offset: 2px; }
.icon-card p { color: var(--color-ink); font-size: var(--type-body-size); line-height: var(--type-body-lh); }

/* The icon sits in a pastel disc. Colour is decorative and rotates through the
   Google palette so a grid of cards reads as one family rather than six
   unrelated boxes. */
.icon-disc {
  width: 56px; height: 56px;
  border-radius: var(--radius-circle);
  display: grid; place-items: center;
  margin-bottom: var(--space-xs);
  flex: 0 0 auto;
}
.icon-disc svg { width: 26px; height: 26px; }
.icon-disc--blue   { background: var(--pastel-blue);   color: var(--color-primary); }
.icon-disc--green  { background: var(--pastel-green);  color: var(--color-success); }
.icon-disc--yellow { background: var(--pastel-yellow); color: var(--color-attention); }
.icon-disc--red    { background: var(--pastel-red);    color: var(--color-critical); }
.icon-disc--purple { background: var(--pastel-purple); color: #7627BB; }

/* ── Pastel feature cards ─────────────────────────────────────────────────
   Google's careers-card pattern: a visual on top, a pastel body below, and the
   action pinned bottom-right in the matching accent. Nothing here is a
   photograph — each visual is a CSS mock-up of the layout it names, which is
   both honest and more useful than stock imagery of people at laptops. */
/* Three at a time, scrolled. `grid-auto-flow: column` with a fixed track width
   rather than four equal columns: the section now holds seven cards, and the
   width has to stay put as they scroll past rather than divide by however many
   there are. The subtraction is the two gaps between the three visible cards.

   The TRACK is the scroll container, so the section works with no JavaScript —
   trackpad, touch and keyboard all scroll it, and the arrows below are an
   addition rather than the mechanism. */
.pastel-grid {
  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: calc((100% - var(--space-md) * 2) / 3);
  gap: var(--space-md);
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  scroll-behavior: smooth;
  /* Room for the hover lift, which would otherwise be clipped by overflow. */
  padding: var(--space-xs) var(--space-xxs) var(--space-md);
  margin: calc(var(--space-xs) * -1) calc(var(--space-xxs) * -1) 0;
  scrollbar-width: none;
  -ms-overflow-style: none;
}
.pastel-grid::-webkit-scrollbar { display: none; }
.pastel-grid > .pastel-card { scroll-snap-align: start; }
.pastel-grid:focus-visible { outline: 2px solid var(--color-primary); outline-offset: 4px; }

/* ── The layouts slider ───────────────────────────────────────────────────
   🔑 The arrows are a BAR UNDER THE TRACK. This is the third position they
   have been in, and the first two both "worked" in the sense that clicking
   them scrolled the row:

     1. Outside the track (`left: -8px`), overlapping its edges. There is only
        a gutter outside the container above about 1420px, so below that they
        had to be pulled back in over the cards — which meant the pair sat at
        two different distances from the cards depending on the window width,
        the left one half off the first card and the right one over the third.
        That asymmetry is what "the navigation looks wrong" was describing.
     2. Over the cards at every width. Symmetrical, and now permanently on top
        of the drawing each card exists to show.
     3. A bar under the track: it cannot be clipped by an overflow rule, it
        cannot cover a card, its position does not depend on the window width,
        and it can be reached on a phone — where the overlapping version had to
        be hidden outright because a 44px button over an 85%-wide card covered
        the mock-up.

   ⚠️ The widget's own carousel learned this first and it is written down in
   DESIGN.md §13. Two controls in one product solving the same problem two
   different ways is how one of them ends up wrong. */
.layout-slider { position: relative; }

.layout-nav-bar {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: var(--space-xs);
  margin-top: var(--space-md);
}
/* Hidden as a PAIR when everything already fits — two dead buttons under a
   static row are worse than no bar. layouts.js decides, because whether the
   cards fit is a measured fact, not a breakpoint. */
.layout-nav-bar[hidden] { display: none; }

.layout-nav {
  width: 44px;
  height: 44px;
  border-radius: var(--radius-circle);
  border: 1px solid var(--color-hairline);
  background: var(--color-canvas);
  color: var(--color-charcoal);
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  transition: background var(--motion-fast), color var(--motion-fast),
              border-color var(--motion-fast), opacity var(--motion-fast);
}
.layout-nav svg { width: 20px; height: 20px; }
.layout-nav:hover {
  background: var(--color-primary-soft);
  border-color: var(--color-primary-hairline);
  color: var(--color-primary);
}
.layout-nav:focus-visible { outline: 2px solid var(--color-primary); outline-offset: 2px; }
/* Disabled rather than removed at the ends: a control that vanishes lets the
   other one slide across, which reads as the page having moved on its own. */
.layout-nav[disabled] { opacity: .4; cursor: default; }
.layout-nav[disabled]:hover {
  background: var(--color-canvas);
  border-color: var(--color-hairline);
  color: var(--color-charcoal);
}


.pastel-card {
  display: flex;
  flex-direction: column;
  border-radius: var(--radius-xxxl);
  overflow: hidden;
  background: var(--pastel-blue);
  min-height: 340px;
  text-decoration: none;
  transition: transform var(--motion-medium), box-shadow var(--motion-medium);
}
.pastel-card:hover { text-decoration: none; transform: translateY(-4px); box-shadow: var(--shadow-panel); }
.pastel-card--blue   { background: var(--pastel-blue); }
.pastel-card--green  { background: var(--pastel-green); }
.pastel-card--yellow { background: var(--pastel-yellow); }
.pastel-card--red    { background: var(--pastel-red); }

/* 🔑 The drawing sits on the CARD'S OWN TINT, not on a white panel.

   The band was white with a hairline under it, which was right when the
   drawing was a set of white review cards with grey outlines: those needed a
   white ground to read as cards ON a page. The feed diagrams are pale
   photographs, and pale photographs on white have nothing behind them — they
   floated, the band read as an empty area somebody had not finished, and the
   card came apart into a blank half and a coloured half.

   One tint the whole way down, and the drawing is a drawing on a coloured
   section. Which is what a widget on a customer's page actually is. */
.pastel-visual {
  /* ⚠️ 180, not 152. The band now carries the feed diagrams as well as the
     review ones, and the tallest of those is Reels — four 9:16 tiles, which at
     this card width need about 144px of inner height. At 152 the band clipped
     the bottom of every upright tile, which on a layout whose whole point is
     that its tiles are upright is the diagram contradicting its own caption. */
  height: 180px;
  padding: var(--space-lg) var(--space-lg) var(--space-md);
  overflow: hidden;
}
/* ⚠️ The review mock-ups go WHITE on the tint.
   They were `--color-surface-soft`, a grey a shade off white, which on a white
   band was the difference between a card and its ground. On a green one it is
   no difference at all — grey cards on a tint read as holes in it. White is
   what a review card actually is. */
.pastel-visual .mini-card { background: var(--color-canvas); border-color: rgba(255, 255, 255, .7); }
.pastel-body {
  padding: var(--space-lg);
  display: flex;
  flex-direction: column;
  flex: 1;
  gap: var(--space-xs);
}
.pastel-body h3 {
  font-size: var(--type-card-size);
  font-weight: 400;
  line-height: var(--type-card-lh);
  color: var(--color-ink-deep);
}
.pastel-body p { font-size: var(--type-body-size); line-height: var(--type-body-lh); color: var(--color-ink); }

.pastel-action {
  margin-top: auto;
  padding-top: var(--space-md);
  display: flex;
  justify-content: flex-end;
}
.pastel-pill {
  display: inline-flex;
  align-items: center;
  gap: var(--space-xs);
  height: 40px;
  padding: 0 var(--space-md) 0 var(--space-lg);
  border-radius: var(--radius-full);
  font-size: var(--type-label-size);
  font-weight: var(--type-label-weight);
  color: var(--color-on-primary);
  background: var(--color-primary);
}
.pastel-pill svg { width: 16px; height: 16px; }

/* 🔑 The pill is ALWAYS the primary blue, whatever the card is tinted.
   It used to take the card's accent, so the seven layouts offered the same
   action in four colours — and two of them were amber and red, which on this
   site mean "waiting on you" and "destructive" (DESIGN.md §2). A red
   "Start free" is the strongest visual warning the palette has, attached to
   the one thing on the page we want pressed. The tint stays: it is what
   separates one card from the next, and it is decoration, which is exactly
   what a button is not. */

/* ── Layout mini-mockups ──────────────────────────────────────────────────
   Tiny CSS renderings of the four widget layouts. Sizes are deliberately small
   — a grid track cannot shrink below its content's intrinsic minimum, so
   oversized internals silently push the columns past the card edge. The
   builder's template thumbnails learned this the hard way. */
.mini { display: grid; gap: 6px; height: 100%; }
.mini-card {
  background: var(--color-surface-soft);
  border: 1px solid var(--color-hairline-soft);
  border-radius: 8px;
  padding: 7px;
  display: grid;
  gap: 4px;
  align-content: start;
  overflow: hidden;
}
.mini-row { display: flex; align-items: center; gap: 4px; }
.mini-dot { width: 12px; height: 12px; border-radius: 50%; flex: 0 0 auto; }
.mini-dot--blue { background: var(--pastel-blue-2); }
.mini-dot--green { background: var(--pastel-green-2); }
.mini-dot--red { background: var(--pastel-red-2); }
.mini-dot--yellow { background: var(--pastel-yellow-2); }
.mini-stars { font-size: 7px; color: var(--g-yellow-500); letter-spacing: .5px; }
.mini-line { height: 4px; border-radius: 2px; background: var(--color-hairline-soft); }
.mini-line--short { width: 55%; }
.mini-line--mid { width: 75%; }

.mini--grid { grid-template-columns: 1fr 1fr; grid-template-rows: 1fr 1fr; }
.mini--list { grid-template-rows: repeat(3, 1fr); }

/* A list row is avatar + body, and the body is its own stack.
   It used to lean on `> div:last-child` while inheriting `align-content: start`
   from .mini-card, so the two children sat on different baselines and the text
   bar drifted out of line with the stars above it. Explicit flex, an explicit
   class, and the bars sized to the row rather than to a percentage of whatever
   space happened to be left. */
.mini--list .mini-card {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 6px 8px;
}
.mini-body { display: grid; gap: 4px; flex: 1; min-width: 0; }
.mini-body .mini-line { width: 100%; }
.mini-body .mini-line--short { width: 55%; }
.mini--carousel { grid-template-columns: 1fr; }
.mini--carousel .mini-card { height: 100%; }
.mini-dots { display: flex; gap: 4px; justify-content: center; margin-top: 2px; }
.mini-dots i { width: 5px; height: 5px; border-radius: 50%; background: var(--color-hairline); }
.mini-dots i:first-child { background: var(--color-primary); }
.mini--marquee { grid-auto-flow: column; grid-auto-columns: 62%; overflow: hidden; }
/* The three layouts that are not a grid of review cards. Each mock-up shows
   the part that distinguishes it — the arrows on a slider, the absence of any
   card at all on a badge, the docked panel and its floating pill on a popup —
   because at this size the shape is the only thing that reads. */
.mini-summary {
  display: flex;
  align-items: center;
  gap: 4px;
  padding-bottom: 5px;
  border-bottom: 1px solid var(--color-hairline-soft);
}
.mini-line--tiny { width: 26px; height: 4px; flex: 0 0 auto; }

.mini--slider { grid-template-rows: auto 1fr auto; }
.mini-arrows { display: flex; gap: 5px; justify-content: flex-end; }
.mini-arrows i {
  width: 9px;
  height: 9px;
  border-radius: 50%;
  border: 1px solid var(--color-hairline);
}

/* Centred, and nothing else in the box: a badge has no review cards. */
.mini--badge { place-content: center; }
.mini-badge {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 7px 11px;
  border-radius: var(--radius-full);
  background: var(--color-surface-soft);
  border: 1px solid var(--color-hairline-soft);
}

/* The panel is docked to the right edge and deliberately overruns the bottom
   of the box — it is full height on a real page, and a panel that stopped short
   would read as a card. */
.mini--popup { position: relative; grid-template-columns: 1fr; }
.mini-panel {
  position: absolute;
  top: -12px;
  right: -12px;
  bottom: -12px;
  width: 58%;
  padding: 8px;
  display: grid;
  gap: 5px;
  align-content: start;
  background: var(--color-canvas);
  border-left: 1px solid var(--color-hairline);
  box-shadow: -4px 0 12px rgba(60, 64, 67, .12);
}
.mini-fab {
  position: absolute;
  left: 0;
  bottom: 0;
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 5px 9px;
  border-radius: var(--radius-full);
  background: var(--color-canvas);
  border: 1px solid var(--color-hairline);
  box-shadow: var(--shadow-subtle);
}

.mini--dark .mini-card { background: var(--color-ink-deep); border-color: var(--color-charcoal); }
.mini--dark .mini-line { background: var(--color-charcoal); }

/* ── Steps ────────────────────────────────────────────────────────────────
   Three cards, each showing what the step actually looks like rather than
   describing it in a paragraph. The earlier version was a 104px number over
   two lines of text — the number was the loudest thing in the section and the
   only thing in it, which made a three-step install read as an infographic
   about nothing.

   The number is now a small chip that labels the card instead of dominating
   it, and a dotted rule runs between the cards on desktop so the three read as
   one sequence rather than three unrelated boxes. */
.steps {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: clamp(20px, 2.4vw, 32px);
  position: relative;
}

.step {
  position: relative;
  /* ⚠️ A grid track cannot shrink below its content's intrinsic minimum, and
     the snippet in step 3 is a single nowrap line about 300px wide. Three
     tracks that each refuse to go under 300px push the PAGE sideways at 768 —
     the card was clipping its own code correctly and the damage was outside
     it.  is what lets the track win the argument. */
  min-width: 0;
  background: var(--color-canvas);
  border: 1px solid var(--color-hairline-soft);
  border-radius: var(--radius-xxl);
  padding: clamp(20px, 2.2vw, 28px);
  display: flex;
  flex-direction: column;
  gap: var(--space-md);
  transition: border-color var(--motion-medium), box-shadow var(--motion-medium);
}
.step:hover { border-color: var(--color-hairline); box-shadow: var(--shadow-subtle); }

.step-head { display: flex; align-items: center; gap: var(--space-sm); }
/* What the step actually costs the reader, at the top of the card where the
   question is asked. "About five minutes" was stated once for the whole
   install, which is the claim a reader discounts; broken across the three it
   is checkable, and the honest one is step 2 — nobody spends a fixed number of
   minutes choosing colours. */
.step-time {
  margin-left: auto;
  font-size: var(--type-caption-size);
  line-height: var(--type-caption-lh);
  color: var(--color-steel);
  white-space: nowrap;
}
.step-num {
  width: 30px;
  height: 30px;
  flex: 0 0 auto;
  display: grid;
  place-items: center;
  border-radius: var(--radius-circle);
  font-size: var(--type-body-size);
  font-weight: 500;
  line-height: 1;
}
/* Tinted, not filled: at 30px a solid brand fill next to a title reads as a
   notification badge. The ink tokens keep the numeral around 7:1. */
.step:nth-child(1) .step-num { background: var(--pastel-green);  color: var(--color-success-ink); }
.step:nth-child(2) .step-num { background: var(--pastel-blue);   color: var(--color-primary-ink); }
.step:nth-child(3) .step-num { background: var(--pastel-yellow); color: var(--color-attention-ink); }

.step-head h3 {
  font-size: var(--type-card-size);
  font-weight: 500;
  line-height: var(--type-card-lh);
  color: var(--color-ink-deep);
}
.step p { color: var(--color-ink); font-size: var(--type-body-size); line-height: var(--type-body-lh); }

/* The illustration: a small, real-looking piece of the product for each step. */
/* ⚠️ A fixed HEIGHT, not a minimum. Three drawings with different numbers of
   parts settle at different depths, and three cards whose pictures start
   together and end apart read as ragged even though every other edge in the
   row lines up. The contents centre inside whatever is left over. */
.step-visual {
  border-radius: var(--radius-xl);
  padding: var(--space-md);
  height: 132px;
  display: grid;
  align-content: center;
  gap: var(--space-sm);
}
.step:nth-child(1) .step-visual { background: var(--pastel-green); }
.step:nth-child(2) .step-visual { background: var(--pastel-blue); }
.step:nth-child(3) .step-visual { background: var(--pastel-yellow); }

/* Step 1 — a search field, the result you want, and a second one under it.
   The second result is what makes it read as a SEARCH rather than as a label:
   one result in a box is a statement, two is a choice being made. */
.step-search {
  background: var(--color-canvas);
  border-radius: var(--radius-full);
  height: 34px;
  display: flex;
  align-items: center;
  gap: var(--space-xs);
  padding: 0 var(--space-md);
  color: var(--color-steel);
  font-size: var(--type-caption-size);
}
.step-search svg { width: 14px; height: 14px; flex: 0 0 auto; }
.step-result {
  background: var(--color-canvas);
  border-radius: var(--radius-md);
  padding: var(--space-xs) var(--space-sm);
  display: flex;
  align-items: center;
  gap: var(--space-xs);
  font-size: var(--type-caption-size);
  color: var(--color-ink-deep);
}
.step-result .brand-mark { width: 14px; height: 14px; flex: 0 0 auto; }
.step-result-name { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.step-result-score { font-weight: 500; }
.step-result .stars { font-size: 10px; }
.step-result--muted { color: var(--color-steel); opacity: .7; }

/* Step 2 — the layout picker with one tile chosen, then the design controls
   under it. Three grey swatches stood for "design" before, which is the shape
   of the idea rather than the screen: a customer picks a LAYOUT first and a
   colour second, and the picture now says so in that order. */
.step-layouts { display: grid; grid-template-columns: repeat(3, 1fr); gap: var(--space-xs); }
.step-layout {
  background: var(--color-canvas);
  border: 2px solid transparent;
  border-radius: var(--radius-md);
  height: 46px;
  padding: 5px;
  display: grid;
  gap: 3px;
}
.step-layout i { display: block; border-radius: 2px; background: var(--color-hairline-soft); }
/* A grid, a list and a row — the three shapes, drawn as shapes. */
.step-layout--on { grid-template-columns: 1fr 1fr; border-color: var(--color-primary); }
.step-layout--list { grid-template-rows: repeat(3, 1fr); }
.step-layout--row { grid-auto-flow: column; }

.step-swatches { display: flex; align-items: center; gap: var(--space-xs); }
.step-swatch {
  width: 18px;
  height: 18px;
  border-radius: var(--radius-circle);
  background: var(--color-primary);
  box-shadow: 0 0 0 2px var(--color-canvas);
}
.step-swatch--on { box-shadow: 0 0 0 2px var(--color-canvas), 0 0 0 4px var(--color-primary); }
.step-swatch--g { background: var(--color-success); }
.step-swatch--y { background: var(--g-yellow-500); }
.step-swatch--r { background: var(--color-critical); }
/* A slider, because half the design panel is sliders and a row of colour dots
   alone reads as a palette rather than as a set of controls. */
.step-slider {
  position: relative;
  flex: 1;
  height: 4px;
  border-radius: var(--radius-full);
  background: var(--color-canvas);
  margin-left: var(--space-xs);
}
.step-slider i {
  position: absolute;
  top: 50%;
  left: 62%;
  width: 12px;
  height: 12px;
  margin-top: -6px;
  border-radius: var(--radius-circle);
  background: var(--color-primary);
}

/* Step 3 — the snippet, abbreviated, and the confirmation that follows it.
   The step is "paste", and the moment a reader is being promised is the one
   where it is already on the clipboard. */
.step-code {
  min-width: 0;
  background: var(--color-ink-deep);
  border-radius: var(--radius-md);
  padding: var(--space-sm);
  font-family: var(--font-mono);
  font-size: 12px;
  line-height: 1.6;
  color: var(--color-on-ink);
  overflow: hidden;
  white-space: nowrap;
}
.step-code .tok-tag { color: var(--pastel-blue-2); }
.step-code .tok-str { color: var(--pastel-yellow-2); }
.step-copied {
  display: flex;
  align-items: center;
  gap: var(--space-xxs);
  font-size: var(--type-caption-size);
  color: var(--color-success-ink);
}
.step-copied svg { width: 14px; height: 14px; color: var(--color-success); flex: 0 0 auto; }

/* The dotted connector, drawn between cards rather than through them. Hidden
   below the point where the cards stop sitting in a row. */
@media (min-width: 1024px) {
  .step + .step::before {
    content: '';
    position: absolute;
    top: 46px;
    right: 100%;
    width: clamp(20px, 2.4vw, 32px);
    border-top: 2px dotted var(--color-hairline);
  }
}

/* ── Showcase: a visual beside a list of expanding features ───────────────
   Google's product-page pattern. The left border is the state indicator: the
   active item goes blue and reveals its paragraph, the rest stay as headings.
   Rows are buttons, so the whole thing works from a keyboard. */
.showcase { display: grid; grid-template-columns: 1.05fr 0.95fr; gap: clamp(32px, 5vw, 80px); align-items: center; }

.showcase-visual {
  background: var(--color-surface-soft);
  border: 1px solid var(--color-hairline-soft);
  border-radius: var(--radius-xxxl);
  padding: clamp(20px, 3vw, 40px);
  display: grid;
  place-items: center;
  min-height: 340px;
}

.showcase-list { display: flex; flex-direction: column; }
.showcase-item {
  /* border: 0 first — a <button> carries a default border on every side, and
     without this the "left rail" reads as four boxed rows. */
  border: 0;
  border-left: 3px solid var(--color-hairline-soft);
  padding: var(--space-md) 0 var(--space-md) var(--space-lg);
  text-align: left;
  background: none;
  font-family: inherit;
  cursor: pointer;
  width: 100%;
  transition: border-color var(--motion-medium);
}
.showcase-item:focus-visible { outline: 2px solid var(--color-primary); outline-offset: 2px; }
.showcase-item h3 {
  font-size: var(--type-card-size);
  font-weight: 400;
  line-height: var(--type-card-lh);
  color: var(--color-charcoal);
  transition: color var(--motion-fast);
}
.showcase-item p {
  color: var(--color-ink);
  font-size: var(--type-body-size);
  line-height: var(--type-body-lh);
  margin-top: var(--space-xs);
  display: none;
}
.showcase-item[aria-expanded='true'] { border-left-color: var(--color-primary); }
.showcase-item[aria-expanded='true'] h3 { color: var(--color-ink-deep); font-weight: 500; }
.showcase-item[aria-expanded='true'] p { display: block; }
.showcase-item:hover h3 { color: var(--color-ink-deep); }

/* Builder mock-up shown beside it. */
.builder-mock { width: 100%; max-width: 420px; display: grid; gap: var(--space-sm); }
.builder-bar {
  background: var(--color-canvas);
  border: 1px solid var(--color-hairline-soft);
  border-radius: var(--radius-full);
  height: 40px;
  display: flex;
  align-items: center;
  gap: var(--space-xs);
  padding: 0 var(--space-md);
}
.builder-bar span { height: 8px; border-radius: 4px; background: var(--color-hairline-soft); flex: 1; }
.builder-bar i { width: 12px; height: 12px; border-radius: 50%; flex: 0 0 auto; }
.builder-body {
  background: var(--color-canvas);
  border: 1px solid var(--color-hairline-soft);
  border-radius: var(--radius-xxl);
  padding: var(--space-md);
  display: grid;
  grid-template-columns: 64px 1fr;
  gap: var(--space-md);
  min-height: 220px;
}
.builder-rail { display: grid; gap: var(--space-xs); align-content: start; }
.builder-rail i {
  height: 36px;
  border-radius: var(--radius-full);
  background: var(--color-surface-alt);
}
.builder-rail i:first-child { background: var(--pastel-blue); }
.builder-canvas { display: grid; gap: var(--space-xs); align-content: start; }

/* ── Platform strip ────────────────────────────────────────────────────── */
/* ⚠️ Eight chips, laid out in FOURS rather than left to wrap.

   Centred flex-wrap fitted seven on the first line and dropped "Plain HTML"
   alone onto the second, which reads as one having fallen off rather than as
   a set — and where it broke depended on the window width and on how long the
   platform names happened to be, so it was a different shape on every screen.
   A four-column track is the same shape everywhere and divides eight exactly.

   `auto` columns, not `1fr`: the chips keep their own widths. Equal columns
   would stretch "Wix" to the width of "WooCommerce" and turn a row of labels
   into a row of buttons. */
.platforms {
  display: grid;
  grid-template-columns: repeat(4, auto);
  justify-content: center;
  justify-items: center;
  gap: var(--space-sm);
}

/* A chip that is a link rather than a label. The outline chip already reads as
   a discrete object; this only adds the affordance — hover tints it the way the
   nav does, and the underline is suppressed because a bordered pill with an
   underline inside it reads as two controls. */
.chip--link {
  color: var(--color-ink);
  transition: background var(--motion-fast), border-color var(--motion-fast);
}
.chip--link:hover {
  background: var(--color-primary-soft);
  border-color: var(--color-primary-hairline);
  color: var(--color-primary-ink);
  text-decoration: none;
}
.chip--link:focus-visible { outline: 2px solid var(--color-primary); outline-offset: 2px; }

/* ── The install guide: a vertical numbered sequence ────────────────────────
   Distinct from `.steps` (§DESIGN.md), which is a three-across marketing
   summary with a pastel visual per card and colours keyed to nth-child. This is
   documentation: five or six steps that are read in order and followed with a
   browser open beside them, so it runs down the page with the number in the
   margin and a rule connecting one to the next.

   The connector is a border on the number's column rather than a pseudo-element
   between cards, so a step of any height stays joined to the one below it. */
.guide { display: grid; gap: 0; counter-reset: guide; }

.guide-step {
  position: relative;
  display: grid;
  grid-template-columns: 40px 1fr;
  gap: var(--space-lg);
  padding-bottom: var(--space-xl);
}
/* The last step ends the line — a rule running past the final number implies a
   step that is not there. */
.guide-step:last-child { padding-bottom: 0; }
.guide-step:not(:last-child)::before {
  content: '';
  position: absolute;
  left: 19px;
  top: 40px;
  bottom: 0;
  border-left: 2px solid var(--color-hairline-soft);
}

.guide-num {
  counter-increment: guide;
  width: 40px;
  height: 40px;
  border-radius: var(--radius-full);
  background: var(--color-primary-soft);
  color: var(--color-primary-ink);
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: var(--type-label-size);
  font-weight: var(--type-label-weight);
  position: relative;
  z-index: 1;
}
.guide-num::before { content: counter(guide); }

.guide-body { padding-top: 6px; min-width: 0; }
.guide-body h3 {
  font-size: var(--type-card-size);
  font-weight: 500;
  color: var(--color-ink-deep);
  margin-bottom: var(--space-xs);
}
.guide-body p { color: var(--color-ink); }
.guide-body p + p { margin-top: var(--space-sm); }
.guide-body .code-well { margin-top: var(--space-md); }
.guide-body .alert { margin-top: var(--space-md); }

/* Menu paths — "Online Store → Themes". Set apart from the sentence so the
   thing to click is findable without reading the whole line again. */
.ui-path {
  font-family: var(--font-mono);
  font-size: 0.92em;
  background: var(--color-surface-alt);
  border-radius: var(--radius-xs);
  padding: 2px 6px;
  color: var(--color-ink-deep);
  white-space: nowrap;
}

/* ── Cross-links between the platform guides ───────────────────────────── */
.guide-nav { display: flex; flex-wrap: wrap; gap: var(--space-sm); }

/* ── Code well ─────────────────────────────────────────────────────────── */
.code-well {
  background: var(--color-ink-deep);
  border-radius: var(--radius-xxl);
  padding: clamp(16px, 2vw, 24px);
  font-family: var(--font-mono);
  font-size: clamp(12px, 1.05vw, 13px);
  line-height: 1.7;
  color: var(--color-on-ink);
  overflow-x: auto;
  white-space: pre;
}
.code-well .tok-tag  { color: var(--pastel-blue-2); }
.code-well .tok-attr { color: var(--pastel-green-2); }
.code-well .tok-str  { color: var(--pastel-yellow-2); }

.code-frame { max-width: 860px; margin: clamp(32px, 4vw, 56px) auto 0; }
.code-caption { text-align: center; margin-top: var(--space-md); }

/* ── Pricing ───────────────────────────────────────────────────────────── */
.billing-toggle {
  display: inline-flex;
  border: 1px solid var(--color-hairline);
  border-radius: var(--radius-full);
  overflow: hidden;
  background: var(--color-canvas);
}
.seg-btn {
  appearance: none;
  background: transparent;
  border: 0;
  font-family: inherit;
  font-size: var(--type-label-size);
  font-weight: var(--type-label-weight);
  color: var(--color-charcoal);
  height: 40px;
  padding: 0 var(--space-lg);
  cursor: pointer;
}
.seg-btn + .seg-btn { border-left: 1px solid var(--color-hairline); }
.seg-btn:hover { background: var(--color-surface-soft); }
.seg-btn[aria-pressed='true'] { background: var(--color-primary-soft); color: var(--color-primary); }
.seg-btn:focus-visible { outline: 2px solid var(--color-primary); outline-offset: -2px; }

.plan-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: var(--space-lg); align-items: stretch; }

.plan {
  background: var(--color-canvas);
  border: 1px solid var(--color-hairline-soft);
  border-radius: var(--radius-xxl);
  padding: clamp(24px, 2.4vw, 32px);
  display: flex;
  flex-direction: column;
  gap: var(--space-md);
  height: 100%;
}
/* The recommended plan gets a blue border and tint — Google's selected-card
   pattern, the same one the builder uses for the active template. No dark
   fill, no scale transform. */
.plan--featured { border-color: var(--color-primary-hairline); background: var(--pastel-blue); }

.plan-head { display: flex; align-items: center; justify-content: space-between; gap: var(--space-xs); }
.plan-head h3 { font-size: var(--type-card-size); font-weight: 400; line-height: var(--type-card-lh); color: var(--color-ink-deep); }
.plan-price { display: flex; align-items: baseline; gap: var(--space-xxs); }
.plan-price b { font-size: clamp(32px, 3.2vw, 40px); font-weight: 400; line-height: 1.2; color: var(--color-ink-deep); }
.plan-price span { color: var(--color-charcoal); font-size: var(--type-body-size); }
.plan-tagline { color: var(--color-ink); font-size: var(--type-body-size); line-height: var(--type-body-lh); min-height: 40px; }

.plan-features { list-style: none; display: grid; gap: var(--space-xs); padding-top: var(--space-xs); border-top: 1px solid var(--color-hairline-soft); }
.plan-features li {
  display: grid;
  grid-template-columns: 20px 1fr;
  gap: var(--space-xs);
  align-items: start;
  font-size: var(--type-body-size);
  line-height: var(--type-body-lh);
  color: var(--color-ink);
}
.plan-features svg { width: 16px; height: 16px; margin-top: 2px; color: var(--color-success); }
.plan-features li.is-absent { color: var(--color-steel); }
.plan-features li.is-absent svg { color: var(--color-stone); }

.plan .btn { width: 100%; }
.plan-loading, .plan-error { padding: var(--space-xl); text-align: center; }

/* ── Alerts — pale tint, coloured icon, near-black text ─────────────────── */
.alert {
  display: flex;
  gap: var(--space-sm);
  padding: var(--space-md) var(--space-lg);
  border-radius: var(--radius-xxl);
  font-size: var(--type-body-size);
  line-height: var(--type-body-lh);
  align-items: flex-start;
}
.alert svg { width: 20px; height: 20px; flex: 0 0 auto; margin-top: 1px; }
.alert--warning { background: var(--pastel-yellow); color: var(--color-attention-ink); }
.alert--warning svg { color: var(--color-attention); }
.alert--info    { background: var(--pastel-blue); color: var(--color-primary-ink); }
.alert--info svg { color: var(--color-primary); }
.alert--success { background: var(--pastel-green); color: var(--color-success-ink); }
.alert--success svg { color: var(--color-success); }

/* ── Comparison table ──────────────────────────────────────────────────────
   After Google's Workspace comparison. Four things carry it: grouped sections
   instead of one long list, body-size rather than caption-size text, the plan
   price repeated in the header so a column still identifies itself after a
   scroll, and a dash rather than a cross for "not included" — a grid of
   crosses reads as a page full of failures. */
.table-wrap {
  overflow-x: auto;
  border: 1px solid var(--color-hairline-soft);
  border-radius: var(--radius-xxl);
  background: var(--color-canvas);
}
table.compare {
  width: 100%;
  border-collapse: collapse;
  min-width: 680px;
  font-size: var(--type-body-lg-size);
  line-height: var(--type-body-lg-lh);
}
table.compare th, table.compare td {
  padding: var(--space-md) var(--space-lg);
  text-align: left;
  vertical-align: middle;
}

/* Header. NOT sticky: .table-wrap sets overflow-x, which makes the wrap itself
   the scroll container, so a sticky thead sticks to the top of the wrap rather
   than to the viewport — it sat on top of the first rows and hid them. The
   plan name is repeated as a price under each column heading instead, which is
   what a reader actually needs after scrolling. */
table.compare thead th {
  background: var(--color-surface-soft);
  border-bottom: 1px solid var(--color-hairline);
  text-align: center;
  padding: var(--space-md);
}
/* Same type as the plan names beside it — only the alignment differs, because
   it heads the label column rather than a value column. It was set at label
   size in charcoal, which made the one cell that explains the table the
   quietest thing in its own header row. */
table.compare thead th:first-child { text-align: left; }
table.compare thead th:first-child,
.compare-plan {
  font-size: var(--type-card-size);
  font-weight: 500;
  line-height: var(--type-card-lh);
  color: var(--color-ink-deep);
}
/* Only the span. `display: block` on the <th> takes the cell out of the table
   layout entirely — it stops stretching to the row height and leaves a white
   strip under it where the header background should be. */
.compare-plan { display: block; }
.compare-price { display: block; font-size: var(--type-body-size); color: var(--color-charcoal); margin-top: 2px; }

/* Section headings inside the table. */
tr.compare-group th {
  background: var(--color-canvas);
  border-top: 1px solid var(--color-hairline-soft);
  border-bottom: 1px solid var(--color-hairline-soft);
  font-size: var(--type-label-size);
  font-weight: var(--type-label-weight);
  letter-spacing: var(--type-tracking-wide);
  color: var(--color-primary);
  text-transform: uppercase;
  padding-top: var(--space-lg);
  padding-bottom: var(--space-xs);
  border-top-width: 0;
}
tbody tr.compare-group:not(:first-child) th { border-top-width: 1px; }

/* Row heading: the label, and an optional line of context under it. */
table.compare tbody th {
  font-weight: 400;
  color: var(--color-ink);
  border-top: 1px solid var(--color-hairline-soft);
}
tr.compare-group + tr th, tr.compare-group + tr td { border-top: 0; }
.compare-label { display: block; }
.compare-note {
  display: block;
  font-size: var(--type-caption-size);
  line-height: var(--type-caption-lh);
  color: var(--color-steel);
  margin-top: 2px;
}

table.compare tbody td {
  text-align: center;
  border-top: 1px solid var(--color-hairline-soft);
}
.compare-value { font-weight: 500; color: var(--color-ink-deep); }
.compare-dash { color: var(--color-stone); }
table.compare svg { width: 20px; height: 20px; vertical-align: middle; }
table.compare .is-yes { color: var(--color-success); }

/* The recommended plan's column, tinted the whole way down. */
table.compare .is-featured { background: var(--pastel-blue); }
table.compare thead th.is-featured { background: var(--pastel-blue-2); }

/* ── Disclosure / FAQ ──────────────────────────────────────────────────── */
.faq-list { display: grid; gap: var(--space-sm); }

.disclosure {
  border: 1px solid var(--color-hairline);
  border-radius: var(--radius-xxl);
  overflow: hidden;
  background: var(--color-canvas);
}
.disclosure > summary {
  list-style: none;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-md);
  padding: var(--space-md) var(--space-lg);
  font-size: var(--type-body-lg-size);
  font-weight: var(--type-label-weight);
  line-height: var(--type-body-lg-lh);
  color: var(--color-ink-deep);
}
.disclosure > summary::-webkit-details-marker { display: none; }
.disclosure > summary:hover { background: var(--color-surface-soft); }
.disclosure > summary:focus-visible { outline: 2px solid var(--color-primary); outline-offset: -2px; }
.disclosure[open] > summary { background: var(--pastel-blue); color: var(--color-primary-ink); }
.disclosure-chevron { flex: 0 0 auto; width: 20px; height: 20px; transition: transform var(--motion-medium); }
.disclosure[open] .disclosure-chevron { transform: rotate(180deg); }
.disclosure-body {
  padding: var(--space-md) var(--space-lg) var(--space-lg);
  color: var(--color-ink);
  font-size: var(--type-body-lg-size);
  line-height: 1.65;
}
.disclosure-body > * + * { margin-top: var(--space-sm); }

/* FAQ category heading with its own coloured icon. */
.faq-group + .faq-group { margin-top: clamp(40px, 5vw, 64px); }
.faq-group-head { display: flex; align-items: center; gap: var(--space-md); margin-bottom: var(--space-lg); }

/* ── Contact ───────────────────────────────────────────────────────────── */

.field { display: block; }
.field > span {
  display: block;
  font-size: var(--type-label-size);
  font-weight: var(--type-label-weight);
  color: var(--color-ink-deep);
  margin-bottom: var(--space-xxs);
}
.field input, .field textarea, .field select {
  width: 100%;
  font-family: inherit;
  font-size: var(--type-body-lg-size);
  line-height: var(--type-body-lg-lh);
  color: var(--color-ink);
  background: var(--color-canvas);
  border: 1px solid var(--color-hairline);
  border-radius: var(--radius-md);
  padding: 0 var(--space-md);
  height: 48px;
  transition: border-color var(--motion-fast), box-shadow var(--motion-fast);
}
.field textarea { height: auto; min-height: 128px; padding: var(--space-sm) var(--space-md); resize: vertical; }
.field input:hover, .field textarea:hover, .field select:hover { border-color: var(--color-ink); }
.field input:focus, .field textarea:focus, .field select:focus {
  outline: none;
  border-color: var(--color-primary);
  box-shadow: inset 0 0 0 1px var(--color-primary);
}

.contact-card {
  border-radius: var(--radius-xxl);
  padding: clamp(24px, 2.4vw, 32px);
  display: grid;
  gap: var(--space-sm);
}
.contact-card--tint { background: var(--pastel-green); }
.contact-card--plain { background: var(--color-canvas); border: 1px solid var(--color-hairline-soft); }
/* Icon beside the text rather than above it — in a single column the stacked
   version pushes the address a long way down its own card. */
.contact-card--row { grid-template-columns: auto 1fr; align-items: start; gap: var(--space-lg); }
.contact-card--row h2 { margin-bottom: var(--space-xxs); }
.contact-card--row .contact-email { margin-bottom: var(--space-xs); }
.contact-email {
  font-size: var(--type-card-size);
  line-height: var(--type-card-lh);
  word-break: break-word;
}

/* ── Legal / long-form prose ───────────────────────────────────────────── */
.prose { max-width: 720px; }
.prose h2 {
  font-size: clamp(20px, 2vw, 24px);
  font-weight: 400;
  line-height: 1.3;
  color: var(--color-ink-deep);
  margin: var(--space-xl) 0 var(--space-sm);
}
.prose h3 {
  font-size: var(--type-title-size);
  font-weight: var(--type-title-weight);
  line-height: var(--type-title-lh);
  color: var(--color-ink-deep);
  margin: var(--space-lg) 0 var(--space-xs);
}
.prose p, .prose li { color: var(--color-ink); font-size: var(--type-body-lg-size); line-height: 1.7; }
.prose p + p { margin-top: var(--space-sm); }
.prose ul, .prose ol { margin: var(--space-sm) 0 var(--space-sm) var(--space-lg); display: grid; gap: var(--space-xxs); }
.prose table { width: 100%; border-collapse: collapse; margin: var(--space-md) 0; font-size: var(--type-body-size); }
.prose th, .prose td { border: 1px solid var(--color-hairline-soft); padding: var(--space-xs) var(--space-sm); text-align: left; vertical-align: top; }
.prose th { font-weight: var(--type-label-weight); color: var(--color-ink-deep); background: var(--color-surface-soft); }
.prose code {
  font-family: var(--font-mono);
  font-size: 0.9em;
  background: var(--color-surface-alt);
  padding: 1px 6px;
  border-radius: var(--radius-xs);
}

/* Placeholders the owner still has to fill in. Loud on purpose: a legal page
   that quietly ships "[REGISTERED ADDRESS]" is worse than one that is
   obviously unfinished. */
.todo {
  background: var(--pastel-yellow-2);
  color: var(--color-attention-ink);
  border-radius: var(--radius-xs);
  padding: 0 var(--space-xxs);
  font-weight: 500;
}

/* ── Closing call to action ────────────────────────────────────────────── */
.cta-panel {
  background: var(--pastel-blue);
  border-radius: var(--radius-xxxl);
  padding: clamp(32px, 5vw, 72px) var(--space-lg);
  text-align: center;
}
.cta-panel p {
  margin: var(--space-md) auto var(--space-xl);
  max-width: 52ch;
  color: var(--color-ink);
  font-size: var(--type-lead-size);
  line-height: var(--type-lead-lh);
}

/* ── Footer ────────────────────────────────────────────────────────────────
   Four lists of links beside a logo is what a footer looks like when nobody
   decided anything about it. Three things were wrong with the one this
   replaces, and they are all the same fault — everything in it was the same
   size, so nothing in it was more important than anything else:

     - the column titles were set at the SAME size and weight as the links
       under them, so four headed lists read as one list of twenty-two links;
     - the brand column was a logo and a sentence, taking a fifth of the width
       to say what the page above had already said, and asking for nothing;
     - the copyright and the trademark disclaimer shared a row, so the longest
       and least important line on the site set the height of the last thing a
       reader sees.

   The titles are now small, tracked and quiet — a label rather than a link —
   which is what lets the ink-coloured links under them read as the content of
   the column. The brand block carries the offer. The disclaimer sits under the
   rule on its own line. */
.site-footer {
  border-top: 1px solid var(--color-hairline-soft);
  background: var(--color-surface-soft);
  padding: clamp(48px, 6vw, 80px) 0 var(--space-lg);
}
.footer-inner {
  max-width: var(--container-max);
  margin: 0 auto;
  padding: 0 var(--space-lg);
  display: grid;
  /* The brand column is wider than a link column but narrower than it was: it
     holds a button, and a button needs about 240px, not 380px. */
  grid-template-columns: minmax(240px, 1fr) 2.4fr;
  gap: clamp(32px, 5vw, 72px);
}
.footer-brand { display: grid; gap: var(--space-md); justify-items: start; align-content: start; }
.footer-logo { height: 32px; width: 151px; display: block; }
/* Body size, not .hint. This is the one line that says what the product is —
   it was set at caption size in the palest grey on the page, which is the
   treatment for fine print, not for the only description in the footer. */
.footer-tagline {
  font-size: var(--type-body-lg-size);
  line-height: var(--type-body-lg-lh);
  color: var(--color-ink);
  max-width: 26ch;
}
.footer-cta { display: grid; gap: var(--space-xs); justify-items: start; }

.footer-cols { display: grid; grid-template-columns: repeat(4, 1fr); gap: var(--space-lg); }
/* Small, steel, still sentence case. A column title is a LABEL for the list
   under it; at the links' own size and colour it was simply the first line in
   each column that happened not to be a link.

   ⚠️ Deliberately NOT uppercase with wide tracking, which is the usual move
   here. Tracking on this site is 0 — the one exception Google itself makes is
   0.25px on a button label — and setting four headings in small caps would put
   a type treatment in the footer that appears nowhere else in the product.
   Size and colour separate a label from a link perfectly well. */
.footer-col-title {
  font-size: var(--type-caption-size);
  font-weight: var(--type-label-weight);
  color: var(--color-steel);
  margin-bottom: var(--space-md);
}
/* Except where the title IS a link — the integrations hub left the top nav and
   this heading is how a reader reaches it. It keeps the label treatment and
   takes the link colour on hover, so it reads as a heading first. */
/* ⚠️ Two class names, because `.footer-col a` below is the same specificity
   and is declared later — so a single-class override loses on source order and
   the hub's heading kept the links' ink and the links' size, which is the one
   thing that made it not read as a heading. */
.footer-col .footer-col-title a { color: inherit; font: inherit; }
.footer-col .footer-col-title a:hover { color: var(--color-primary); }
.footer-col ul { list-style: none; display: grid; gap: var(--space-sm); }
.footer-col a { color: var(--color-ink); font-size: var(--type-body-size); line-height: var(--type-body-lh); }
.footer-col a:hover { color: var(--color-primary); text-decoration: none; }

.footer-base {
  max-width: var(--container-max);
  margin: clamp(32px, 4vw, 56px) auto 0;
  padding: var(--space-lg) var(--space-lg) 0;
  border-top: 1px solid var(--color-hairline-soft);
  display: grid;
  gap: var(--space-sm);
  color: var(--color-steel);
}
/* The quietest treatment on the site, for the longest and least important line
   on it. A measure limit, because at 1280px it would otherwise run edge to
   edge as one 190-character line. */
.footer-disclaimer { max-width: 92ch; color: var(--color-stone); }

/* ── Responsive ────────────────────────────────────────────────────────────
   Three real breakpoints. Everything between them is handled by the fluid
   clamp() type and spacing above, which is why there are so few. */

@media (max-width: 1023px) {
  .hero-grid { grid-template-columns: 1fr; gap: var(--space-xxl); }
  .hero .lede { max-width: none; }
  .browser { max-width: 560px; }
  .pastel-grid { grid-auto-columns: calc((100% - var(--space-md)) / 2); }
  .plan-grid, .grid-3 { grid-template-columns: 1fr 1fr; }
  .grid-4 { grid-template-columns: 1fr 1fr; }
  /* Eight into three leaves a last row of two, which is the raggedness the
     four-column rule exists to avoid. Two columns divides it exactly. */
  .platforms { grid-template-columns: repeat(2, auto); }
  .showcase { grid-template-columns: 1fr; gap: var(--space-xl); }
  .showcase-visual { order: 2; }
  .footer-inner { grid-template-columns: 1fr; }
  /* Four columns of links under a full-width brand block leaves each one about
     150px wide, which wraps "Squarespace" onto two lines. Two-up reads. */
  .footer-cols { grid-template-columns: repeat(2, 1fr); }
  .section-head--center { max-width: 620px; }
}

/* ── The bar collapses at 1023px, not 767px ──────────────────────────────
   ⚠️ It used to hold its desktop shape down to 768, and at exactly 768 it
   overflowed the viewport: five nav items at their proper size plus a 44px
   Sign in and a 44px Get started free need about 920px, and 768 leaves 720
   inside the gutters. The bar did not wrap — it pushed the page sideways, on
   every page of the site, which is the one thing the width pass exists to
   catch. Making the header legible is what cost the 80px, so the header is
   what gives way.

   Everything else still changes at 767: this is a bar that ran out of room,
   not a layout that stopped working. */
@media (max-width: 1023px) {
  /* The nav collapses into a panel under the bar. Hidden rather than removed,
     so the links stay in the DOM for assistive tech and for search. */
  .nav-toggle { display: inline-flex; }
  .topbar-inner { flex-wrap: wrap; }
  .topbar-right {
    display: none;
    width: 100%;
    flex-direction: column;
    align-items: stretch;
    gap: var(--space-md);
    padding: var(--space-md) 0 var(--space-sm);
    border-top: 1px solid var(--color-hairline-soft);
  }
  .topbar-right.is-open { display: flex; }
  .nav { flex-direction: column; align-items: stretch; gap: var(--space-xxs); }
  .nav-link { height: auto; padding: var(--space-sm) var(--space-md); }

  /* The bar is already a panel behind the hamburger here, so the group stays
     expanded: a second disclosure inside a menu that is itself disclosed is
     one more tap for nothing. The button becomes a heading — it has no job
     left, so it stops looking like a control and stops taking focus. */
  .nav-group { position: static; }
  .nav-link--group { display: none; }
  .nav-js .nav-menu,
  .nav-js .nav-group.is-open .nav-menu {
    position: static;
    display: block;
    min-width: 0;
    padding: 0;
    border: 0;
    border-radius: 0;
    box-shadow: none;
  }
  .nav-menu-item { padding: var(--space-sm) var(--space-md); }
  .topbar-actions { flex-direction: column-reverse; align-items: stretch; }
  .topbar-actions .btn { width: 100%; justify-content: center; }
}

@media (max-width: 767px) {

  .section-head, .section-head--center { max-width: none; }
  .plan-grid, .grid-2, .grid-3, .grid-4, .steps { grid-template-columns: 1fr; }
  /* One card at a time. The arrows STAY: they are a bar under the track now,
     so they no longer cover the mock-up they exist to show, and a phone is
     where a one-card-wide row most needs a way forward that is not a guess
     about whether this thing swipes. */
  .pastel-grid { grid-auto-columns: 85%; }
  .layout-nav-bar { justify-content: center; }
  .plan-tagline { min-height: 0; }
  .hero-actions { width: 100%; }
  .hero-actions .btn { width: 100%; }
  .cta-panel { border-radius: var(--radius-xxl); }
  .cta-panel .btn { width: 100%; }
  .cta-panel .row { flex-direction: column; align-items: stretch; }

  .footer-cols { grid-template-columns: 1fr; gap: var(--space-lg); }
  .footer-base { flex-direction: column; gap: var(--space-xs); }

  .disclosure > summary { padding: var(--space-md); font-size: var(--type-body-size); }
  .disclosure-body { padding: var(--space-sm) var(--space-md) var(--space-md); font-size: var(--type-body-size); }

  .contact-card--row { grid-template-columns: 1fr; gap: var(--space-sm); }

  .browser { border-radius: var(--radius-xxl); }
  .showcase-visual { min-height: 0; padding: var(--space-md); border-radius: var(--radius-xxl); }
  .pastel-card { min-height: 0; }
}

/* ── /instagram-demo ──────────────────────────────────────────────────────
   The sample feed. Every "photograph" here is a CSS gradient: the site ships
   no photography, has no licensed images, and the CSP blocks every external
   host — so a stock photo is not available even if it were wanted. The tiles
   are deliberately abstract rather than pretending to be pictures of things.  */

/* Square by ratio rather than by a fixed height, so the tiles stay square at
   every column width without a media query per breakpoint. */

/* Two lines, clamped. A caption is arbitrary length and often absent, and a
   grid whose cells grow to fit the longest one stops being a grid. */

.checklist { list-style: none; display: grid; gap: var(--space-sm); max-width: 720px; }
.checklist li {
  position: relative;
  padding-left: 26px;
  color: var(--color-ink);
}
.checklist li::before {
  content: "";
  position: absolute;
  left: 0;
  top: 7px;
  width: 8px;
  height: 8px;
  border-radius: var(--radius-circle);
  background: var(--color-success);
}
.checklist strong { font-weight: 500; color: var(--color-ink-deep); }

.hint--center { text-align: center; }

@media (max-width: 1023px) {
}
@media (max-width: 560px) {
  /* One column, not two: at phone width a two-up grid puts each caption on
     four lines and the tiles stop being the thing you look at. */
}

/* ── The homepage's two widget cards ──────────────────────────────────────
   The page's centre of gravity: one card per widget.

   🔑 A card is a small product page, not a paragraph with a link under it.
   What was here before was a mark, a heading, three lines of prose and a
   "see the widget" arrow — true, and interchangeable with the same card on any
   competitor's site, because nothing in it was a fact a reader could not have
   guessed. The pair now carries what the prose was standing in for: a drawing
   of what the widget draws, the two counts that answer "how much choice do I
   get", three capabilities stated concretely, and a way in.

   ⚠️ The card is no longer a link, it CONTAINS two. A card that is one big <a>
   cannot hold a second — "Start free" goes to the app and "see the widget"
   goes to the product page, and nesting them is invalid HTML that browsers
   resolve by dropping one. The hover lift stays on the card so it still reads
   as one object. */
.widget-cards {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: var(--space-lg);
}

.widget-card {
  display: flex;
  flex-direction: column;
  background: var(--color-canvas);
  border: 1px solid var(--color-hairline-soft);
  border-radius: var(--radius-xxl);
  overflow: hidden;
  color: var(--color-ink);
  transition: border-color var(--motion-medium), box-shadow var(--motion-medium),
              transform var(--motion-medium);
}
.widget-card:hover {
  border-color: var(--color-hairline);
  box-shadow: var(--shadow-panel);
  transform: translateY(-2px);
}

/* The drawing. A fixed height across both cards, because two cards whose
   pictures are different depths never line up again lower down however equal
   the copy is — and the two drawings are naturally different shapes. */
.widget-card-visual {
  height: 196px;
  padding: var(--space-md);
  display: flex;
  flex-direction: column;
  gap: var(--space-sm);
  border-bottom: 1px solid var(--color-hairline-soft);
  overflow: hidden;
}
.widget-card--google .widget-card-visual { background: var(--pastel-blue); }
.widget-card--instagram .widget-card-visual { background: var(--pastel-purple); }
/* ⚠️ The drawings here are NOT the layout thumbnails from the slider.
   Those are 152px-wide diagrams, and stretched across a 570px card their
   12px dots and 4px bars became a wall of pale grey rectangles — which is
   exactly what this site's own loading skeleton looks like, and it is the
   failure DESIGN.md records about the builder's picker. At this size a
   drawing has to carry real type, so these carry it: a name, a date, a star
   row and a sentence somebody could actually have written. */
.wcard-score {
  display: flex;
  align-items: center;
  gap: var(--space-xs);
  background: var(--color-canvas);
  border-radius: var(--radius-md);
  padding: var(--space-xs) var(--space-sm);
}
.wcard-score b { font-size: 20px; font-weight: 500; color: var(--color-ink-deep); line-height: 1.2; }
.wcard-count { font-size: var(--type-caption-size); color: var(--color-charcoal); margin-left: auto; }

.wcard-reviews { display: grid; grid-template-columns: 1fr 1fr; gap: var(--space-sm); flex: 1; min-height: 0; }
.wcard-review {
  background: var(--color-canvas);
  border-radius: var(--radius-md);
  padding: var(--space-sm);
  display: flex;
  flex-direction: column;
  gap: var(--space-xs);
  overflow: hidden;
}
.wcard-review-top { display: flex; align-items: center; gap: var(--space-xs); }
.wcard-review .mock-avatar { width: 22px; height: 22px; font-size: 11px; }
.wcard-who { display: grid; gap: 1px; min-width: 0; }
.wcard-name { font-size: var(--type-caption-size); font-weight: 500; color: var(--color-ink-deep); }
.wcard-when {
  display: flex;
  align-items: center;
  gap: var(--space-xxs);
  font-size: 10px;
  color: var(--color-steel);
}
.wcard-when .stars { font-size: 9px; }
/* ⚠️ The sentences are SHORT ENOUGH TO FIT, rather than clamped to fit.
    was the obvious answer and it silently does nothing
   here: the paragraph is a flex item, and a flex item's  is
   blockified —  computes to , the clamp is dropped,
   and the fixed-height band slices the last line through the middle instead.
   The band is a drawing whose copy we write, so the copy is written to the
   space. */
.wcard-review p {
  font-size: var(--type-caption-size);
  line-height: 1.45;
  color: var(--color-charcoal);
  flex: 0 0 auto;
}

/* Instagram: the account line, then the tiles.
   ⚠️ The tiles take their height from the BAND, not from a 1:1 ratio. Four
   square tiles across a 570px card are 135px deep, which overflows a fixed
   188px visual and gets clipped along the bottom — a row of photographs with
   their feet cut off reads as a rendering fault, not as a crop. */
.wcard-account {
  display: flex;
  align-items: center;
  gap: var(--space-xs);
  background: var(--color-canvas);
  border-radius: var(--radius-md);
  padding: var(--space-xs) var(--space-sm);
}
.wcard-account .igmock-avatar { width: 24px; height: 24px; }
.wcard-handle {
  font-size: var(--type-body-size);
  font-weight: 500;
  color: var(--color-ink-deep);
  margin-right: auto;
}
.wcard-account .igmock-follow { height: 22px; padding: 0 var(--space-sm); font-size: var(--type-caption-size); }
.wcard-tiles {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: var(--space-sm);
  flex: 1;
  min-height: 0;
}
.wcard-tiles .igmock-tile { aspect-ratio: auto; height: 100%; border-radius: var(--radius-md); }

.widget-card-body {
  display: flex;
  flex-direction: column;
  gap: var(--space-md);
  flex: 1;
  padding: var(--space-lg);
}
.widget-card-head { display: flex; align-items: center; gap: var(--space-sm); }
/* The heading stays ink. Two blue headings side by side read as a list of
   links rather than a pair of cards. */
.widget-card h3 { color: var(--color-ink-deep); }
.widget-card p { margin: 0; color: var(--color-ink); }

/* The disc is ours; the artwork inside it is not, and is never tinted. */
.widget-card-mark {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 40px;
  height: 40px;
  border-radius: var(--radius-circle);
  background: var(--color-surface-alt);
  color: var(--color-charcoal);
}
.widget-card-mark .nav-mark { width: 22px; height: 22px; }
.widget-card--google .widget-card-mark { background: var(--pastel-blue); }
/* The app icon is its own disc — see the note in the nav dropdown. */
.widget-card--instagram .widget-card-mark { background: none; }
.widget-card--instagram .widget-card-mark .nav-mark { width: 40px; height: 40px; }

/* The two counts.

   ⚠️ They were a bare row between two full-width hairlines, with the number
   and its word on one baseline — which drew a line across the card, put a 26px
   figure and a 14px label on the same line so neither was the thing you read
   first, and left the right two thirds of the strip empty. It read as a rule
   somebody had forgotten to finish.

   They are two blocks in one tinted well now: figure over word, a hairline
   only BETWEEN them, and the well ends where the numbers do rather than
   spanning a card whose text is much wider. Both are counts of something that
   exists in the product — never a round number chosen for a card. */
.widget-stats {
  display: flex;
  align-self: flex-start;
  background: var(--color-surface-soft);
  border: 1px solid var(--color-hairline-soft);
  border-radius: var(--radius-xl);
  overflow: hidden;
}
.widget-stat {
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding: var(--space-sm) var(--space-lg);
  font-size: var(--type-caption-size);
  line-height: var(--type-caption-lh);
  color: var(--color-charcoal);
}
.widget-stat + .widget-stat { border-left: 1px solid var(--color-hairline-soft); }
.widget-stat b {
  font-size: 24px;
  font-weight: 400;
  line-height: 1.1;
  color: var(--color-ink-deep);
}

.widget-points { list-style: none; display: grid; gap: var(--space-xs); }
.widget-points li {
  display: flex;
  gap: var(--space-xs);
  font-size: var(--type-body-size);
  line-height: var(--type-body-lh);
  color: var(--color-ink);
}
.widget-points svg { width: 16px; height: 16px; flex: 0 0 auto; margin-top: 3px; color: var(--color-success); }

/* Pinned to the foot so two cards with unequal copy share one baseline. */
.widget-card-actions {
  margin-top: auto;
  padding-top: var(--space-xs);
  display: flex;
  align-items: center;
  gap: var(--space-md);
  flex-wrap: wrap;
}

/* ── The Instagram feed mock-up ───────────────────────────────────────────
   🔑 Drawn to match what the widget ACTUALLY renders. The previous mock-up
   showed a caption under every post and a like count beside it; the widget
   renders neither — captions left the feed entirely (DESIGN.md §13) and there
   has never been a like count in the payload. An illustration promising two
   features the product does not have is worse than no illustration: the
   reader finds out after they have connected an account.

   What it draws instead is the real thing: a square tile with the attribution
   strip across the foot of it, a scrim so a pale tile still reads as a
   surface, and a play glyph on the video. */
.igmock { display: grid; gap: var(--space-md); }
.igmock--tight { margin-top: var(--space-md); }

.igmock-head { display: flex; align-items: center; gap: var(--space-sm); }
.igmock-avatar {
  width: 34px;
  height: 34px;
  border-radius: var(--radius-circle);
  /* The story ring, which is the one thing that makes a round avatar read as
     Instagram rather than as any other profile picture. */
  background: linear-gradient(45deg, var(--pastel-yellow-2), var(--pastel-red-2), var(--pastel-purple-2));
  border: 2px solid var(--color-canvas);
  box-shadow: 0 0 0 2px var(--pastel-red-2);
  flex: 0 0 auto;
}
.igmock-ident { display: grid; gap: 1px; margin-right: auto; min-width: 0; }
.igmock-handle {
  font-size: var(--type-label-size);
  font-weight: var(--type-label-weight);
  color: var(--color-ink-deep);
}
.igmock-counts { font-size: var(--type-caption-size); color: var(--color-charcoal); }
/* Instagram violet, not our blue: the one saturated pill on a wall of photos
   is the element a reader most expects to recognise. Google blue is right on
   a wall of reviews and reads as a mistake here. Same reasoning, and the same
   colour, as the widget's own Follow button — DESIGN.md §13. */
.igmock-follow {
  flex: 0 0 auto;
  padding: 5px 12px;
  border-radius: var(--radius-full);
  background: #6A3AB2;
  color: var(--g-white);
  font-size: var(--type-caption-size);
  font-weight: 500;
}

.igmock-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 6px; }
.igmock-grid--row { grid-template-columns: repeat(4, 1fr); }

.igmock-tile {
  position: relative;
  display: block;
  aspect-ratio: 1 / 1;
  border-radius: var(--radius-md);
  overflow: hidden;
}
/* The same six photographs as the hero above — already downloaded by the
   time a reader reaches this section, so the drawing gets real and the page
   gains no weight at all. */
.igmock-tile[class*='igmock-tile--'] {
  background-size: cover;
  background-position: center;
  background-repeat: no-repeat;
}
.igmock-tile--1 { background-image: url(/assets/photo-feed-1.0cda497e.webp); }
.igmock-tile--2 { background-image: url(/assets/photo-feed-2.9cf9f6e6.webp); }
.igmock-tile--3 { background-image: url(/assets/photo-feed-3.ef840e71.webp); }
.igmock-tile--4 { background-image: url(/assets/photo-feed-4.8a6a01e2.webp); }
.igmock-tile--5 { background-image: url(/assets/photo-feed-5.8cb592b1.webp); }
.igmock-tile--6 { background-image: url(/assets/photo-feed-6.4c18061b.webp); }

/* The attribution strip. In the real widget it carries the date and the
   Instagram glyph; the date is the only thing in it that differs per tile and
   is written nowhere else. The scrim is the load-bearing half — a glyph in a
   corner does not rescue a pale tile, because the tile has to read as a
   SURFACE before anything on it reads as a mark. */
.igmock-strip {
  position: absolute;
  inset: auto 0 0 0;
  padding: 10px 8px 5px;
  background: linear-gradient(to top, rgba(0, 0, 0, 0.45), rgba(0, 0, 0, 0));
  color: var(--g-white);
  font-size: 10px;
  line-height: 1.2;
}

/* A reel whose first frame is near-white draws as an empty rectangle without
   this, which reads as an image that failed to load. */
.igmock-play {
  position: absolute;
  inset: 0;
  margin: auto;
  width: 0;
  height: 0;
  border-style: solid;
  border-width: 7px 0 7px 12px;
  border-color: transparent transparent transparent rgba(255, 255, 255, 0.9);
  filter: drop-shadow(0 1px 2px rgba(0, 0, 0, 0.35));
}

/* ── The layout diagrams on the Instagram product page ────────────────────
   🔑 Diagrams, never filled blocks. Flat grey rectangles are exactly what a
   loading skeleton looks like, so a grid of them reads as content that failed
   to arrive and the natural response is to wait rather than to read. What
   separates a diagram from a skeleton is edges and content — an outline and a
   faint wash. The rule generalises: anywhere a placeholder is permanent, draw
   it as a drawing. (DESIGN.md §13.) */
.iglay-shape {
  display: grid;
  gap: 4px;
  width: 100%;
  /* ONE box height for all nine, with the drawing centred inside it. Left to
     size themselves the diagrams ranged from one row to three, so a row of
     three cards had three different picture heights and the text under them
     never lined up — the section read as ragged rather than as one set of
     choices. The tiles keep their own ratios; only the frame is shared. */
  height: 168px;
  align-content: center;
  justify-content: stretch;
  padding: 10px;
  margin-bottom: var(--space-md);
  background: var(--color-surface-soft);
  border: 1px solid var(--color-hairline-soft);
  border-radius: var(--radius-md);
  overflow: hidden;
}
/* Every tile carries its own ratio and the BOX follows, rather than the box
   being a fixed height that squashes the tiles inside it. A 96px box made a
   three-across grid draw wide letterbox bars — which is not what any of these
   layouts renders, and the copy beside them says "square tiles". A diagram
   that contradicts its own caption is worse than no diagram. */
.iglay-shape i {
  display: block;
  min-width: 0;
  min-height: 0;
  aspect-ratio: 1 / 1;
  background: var(--pastel-blue-2);
  border: 1px solid var(--pastel-blue);
  border-radius: 3px;
}

.iglay-shape--grid { grid-template-columns: repeat(3, 1fr); }
.iglay-shape--minimal { grid-template-columns: repeat(3, 1fr); gap: 10px; }
.iglay-shape--minimal i { border-radius: 8px; }
.iglay-shape--wall { grid-template-columns: repeat(3, 1fr); gap: 0; }
.iglay-shape--wall i { border-radius: 0; }
/* Columns is a band of 2:3 portraits, gapless and sharp. */
.iglay-shape--columns { grid-template-columns: repeat(3, 1fr); gap: 0; }
.iglay-shape--columns i { aspect-ratio: 2 / 3; border-radius: 0; }
/* Cards put the account and the date UNDER the picture, so the diagram has to
   show the band below the tile or it is only the grid again. */
.iglay-shape--cards { grid-template-columns: repeat(3, 1fr); }
.iglay-shape--cards i { border-bottom: 9px solid var(--color-hairline-soft); }
/* The footer bar under the row — the third position the arrows were tried in
   and the only one that worked, so the diagram shows it rather than arrows
   floating over the pictures. */
.iglay-shape--carousel { grid-template-columns: repeat(3, 1fr); }
.iglay-shape--carousel span {
  grid-column: 1 / -1;
  height: 6px;
  margin-top: 2px;
  border-radius: var(--radius-full);
  background: var(--color-hairline);
}
/* 🔑 On Reels the corner IS the layout: the reference turns its corners about
   a third of the way down the side, so the tile reads as a lozenge rather
   than as a photo with the edges taken off. */
.iglay-shape--reels { grid-template-columns: repeat(4, 1fr); }
.iglay-shape--reels i { aspect-ratio: 9 / 16; border-radius: 10px; }
.iglay-shape--circles { grid-template-columns: repeat(4, 1fr); }
.iglay-shape--circles i { border-radius: var(--radius-circle); }
/* The circle grid: the same round tile, WRAPPED.
   Three across rather than the row's four, and six tiles rather than four, so
   it draws two rows — the wrap is the only thing separating this layout from
   Circles, so a diagram that showed one row would be the card above it in a
   different colour. */
/* ⚠️ FIXED-WIDTH COLUMNS, not `1fr`, and this is the whole bug fix.

   At `repeat(3, 1fr)` each circle took a third of the card — about 113px —
   so two rows plus the gap came to 236px inside a band that is 168px tall.
   The rows had nowhere to go and drew over one another, which is what "the
   circles overlap" was describing. Sized to fit the band instead: two rows of
   66px plus a 10px gap is 142px, inside the 148px the padding leaves. */
.iglay-shape--circlegrid {
  grid-template-columns: repeat(3, 66px);
  gap: 10px;
  justify-content: center;
}
.iglay-shape--circlegrid i { border-radius: var(--radius-circle); }
/* 🔑 A DRAWING, not a rule: six slots at set sizes and set positions, and only
   the photographs change. The sizes and the offsets ARE the design, so the box
   carries the fixed ratio and the slots span cells within it — a diagram that
   rearranged itself would be advertising a layout the widget does not draw. */
/* ⚠️ Redrawn 2026-09-22 to the widget's collage: one 2x2 tile left, four
   small right. The six-slot drawing it replaced advertised an arrangement the
   widget had stopped drawing. */
.iglay-shape--cluster {
  grid-template-columns: repeat(4, 1fr);
  grid-template-rows: repeat(2, 1fr);
  height: 168px;
  align-content: stretch;
}
.iglay-shape--cluster i { aspect-ratio: auto; }
.iglay-shape--cluster i:nth-child(1) { grid-column: span 2; grid-row: span 2; }


/* ── The feed diagrams inside a layout card ─────────────────────────
   The diagrams were drawn for a white `.icon-card`, where each carried its own
   grey frame — a bordered, tinted box around the drawing. Inside a pastel
   card the visual band IS that frame, so the diagram brings its own second
   one: a box inside a box, with two different greys and two radii.
   The drawing keeps its geometry and gives up its frame. */
.pastel-visual .iglay-shape {
  height: 100%;
  margin: 0;
  padding: 0;
  background: none;
  border: 0;
  border-radius: 0;
}
/* 🔑 The tiles are the same pastel gradients the hero and the homepage cards
   draw a photograph as — not flat blue blocks.

   At thumbnail size a pale filled rectangle passes for a diagram. At the size
   a layout card gives it, nine of them read as a grid of loading skeletons,
   which is the exact failure DESIGN.md §13 records about the builder's own
   picker. Six gradients, cycling, so no two neighbours match and the drawing
   reads as pictures at a glance — and it is the same vocabulary the rest of
   the site uses for an Instagram post, rather than a second one invented here.

   The border goes with the fill: an outline was what stopped a flat block
   reading as a skeleton, and a gradient does that job on its own. */
.pastel-visual .iglay-shape i {
  border-color: transparent;
  /* 🔑 REAL PHOTOGRAPHS, cycling in sixes.

     They were pastel gradients, which was already a step up from the flat
     blocks before them — but this page is selling a widget whose whole subject
     is photographs, and a set of ten diagrams drawn as coloured rectangles
     shows the shape of each layout while saying nothing about what it looks
     like full of pictures. The shapes are unchanged; only the fill is real.

     ⚠️ These are the SAME six files the hero above already loads, so the
     section costs nothing extra to render — the browser has them cached by
     the time the reader scrolls this far. Using a seventh photograph here
     would add weight for no gain. */
  background-size: cover;
  background-position: center;
  background-repeat: no-repeat;
}
.pastel-visual .iglay-shape i:nth-child(6n + 1) { background-image: url(/assets/photo-feed-1.0cda497e.webp); }
.pastel-visual .iglay-shape i:nth-child(6n + 2) { background-image: url(/assets/photo-feed-2.9cf9f6e6.webp); }
.pastel-visual .iglay-shape i:nth-child(6n + 3) { background-image: url(/assets/photo-feed-3.ef840e71.webp); }
.pastel-visual .iglay-shape i:nth-child(6n + 4) { background-image: url(/assets/photo-feed-4.8a6a01e2.webp); }
.pastel-visual .iglay-shape i:nth-child(6n + 5) { background-image: url(/assets/photo-feed-5.8cb592b1.webp); }
.pastel-visual .iglay-shape i:nth-child(6n + 6) { background-image: url(/assets/photo-feed-6.4c18061b.webp); }

/* The cluster pins its own height, because it is a fixed composition rather
   than a rule — see DESIGN.md §13. In a shared band it has to follow the band
   like the other eight, or the one card in nine that is a scale model is also
   the one card in nine that is a different height. */
.pastel-visual .iglay-shape--cluster { height: 100%; }

/* ── Responsive for the components added above ────────────────────────────
   ⚠️ These media queries have to sit AFTER the base rules they override, not
   up with the rest of the breakpoints earlier in this file. The new component
   rules were appended at the end, so an earlier @media block would lose to
   them on source order at every width — the classic "my media query does
   nothing" bug, and it is silent. */
@media (max-width: 1023px) {
  /* One card at a time, a breakpoint earlier than it used to be. The card is
     no longer a paragraph — it carries a drawing with real type in it, two
     figures and three lines of capability — and at 330px the drawing's own
     review cards clipped their first sentence, which is the one thing in the
     picture a reader was meant to read. */
  .widget-cards { grid-template-columns: 1fr; gap: var(--space-md); }
}

@media (max-width: 767px) {
  .widget-card-body { padding: var(--space-md); }
  /* Two review cards side by side are 150px wide on a phone, which is not a
     card, it is a column of broken words. One, at a legible width. */
  .wcard-reviews { grid-template-columns: 1fr; }
  .wcard-review:last-child { display: none; }
  .widget-card-actions { flex-direction: column; align-items: stretch; }
  .widget-card-actions .btn { width: 100%; justify-content: center; }

  /* Four across is a row of 70px thumbnails on a phone, which is under the
     size at which the attribution strip is legible at all. */
  .igmock-grid--row { grid-template-columns: repeat(3, 1fr); }

  /* The cluster gives its composition up below 600px in the real widget, for
     the same reason: its smallest slot is under a fifth of the width, and a
     drawing rendered illegibly is worse than no drawing. The diagram follows
     the widget rather than shrinking into a smudge. */
  .iglay-shape--cluster {
    grid-template-columns: repeat(2, 1fr);
    grid-template-rows: none;
    aspect-ratio: auto;
  }
  /* On a phone the widget stacks the big tile over a 2x2; so does this. */
  .iglay-shape--cluster i:nth-child(n) { grid-area: auto; aspect-ratio: 1 / 1; }
  .iglay-shape--cluster i:nth-child(1) { grid-column: span 2; }
}

/* ── The homepage hero, narrow ───────────────────────────────── */
@media (max-width: 1240px) {
  /* ⚠️ The cards are REMOVED, never faded behind the words. Ghosting them at
     low opacity put fragments of the review directly behind the headline and
     the lede, so the decoration was making the copy harder to read — the one
     thing decoration must never do. A clean centred hero beats a decorated
     unreadable one, and these two cards were always a luxury the layout had
     room for rather than content the page depends on. */
  .hero-piece { display: none; }
}

@media (max-width: 1023px) {
  .hero h1 { max-width: none; }
}

@media (max-width: 767px) {
  /* One statement per line. Three ticks wrapped mid-phrase across two lines
     read as five items, two of which are nonsense. */
  .hero-reassure { flex-direction: column; gap: var(--space-xs); }

  /* The platform names lose their separator dots: seven names plus six dots
     wrapping over three lines on a phone is a lot of punctuation doing very
     little, and the wrap itself already separates them. */
  .hero-works-list i { display: none; }
  .hero-works-list { gap: 8px 14px; }
  .hero-badge { font-size: var(--type-caption-size); padding: 7px 14px; }

  .step-time { display: none; }
}

/* ══════════════════════════════════════════════════════════════════════════
   THE TWO PRODUCT-PAGE HERO SCENES

   One system, used twice, so the reviews page and the feed page read as the
   same product photographed twice rather than as two sites. Everything below
   is shared; only the glow's second colour and the contents of the frame
   differ between them.

   🔑 BOTH SCENES ARE FICTIONAL AND BOTH SAY SO IN THE PICTURE. The browser bar
   carries an "Example" pill and the frame's own footer carries the
   not-affiliated line, which used to sit alone under the hero where a reader
   meets it only after the image has already been believed.

   ⚠️ Every scene element is aria-hidden and none is focusable. They are
   illustrations: a screen reader walking into a fake Instagram account would
   be told about posts that do not exist, and a keyboard user would tab through
   a dozen decorative tiles to reach the next real control.
   ══════════════════════════════════════════════════════════════════════════ */

/* The hero column becomes the positioning context for everything that
   overlaps the frame. Without this the floats resolve against the page and
   drift away from the frame as the column changes width. */
/* 🔑 The SCENE is the positioning context, not the hero row. Anchored to
   .hero-grid a card offset "just left of the frame" resolves against the whole
   two-column row and lands on the headline — which is exactly what the rating
   badge did. The scene is the right-hand column, so an offset from it means
   what it says. */
.hero-scene {
  position: relative;
  /* ⚠️ z-index 1, and it is not optional. The glow is .hero-grid::after —
     a positioned element at z-index 0 that comes LAST in the box tree, so
     with the scene at `z-index: auto` the glow painted ON TOP of the frame
     and laid a pale wash over the photographs it was meant to sit behind.
     Everything in the scene looked slightly faded and nobody could say why. */
  z-index: 1;
}
.hero-grid { position: relative; }

/* The glow. Behind the frame, never over it, and it is what stops a white
   panel with a shadow having to work its edges hard to read as a surface on
   a white page. */
.hero-grid::after {
  content: '';
  position: absolute;
  z-index: 0;
  inset: -8% -6% auto auto;
  width: min(760px, 62%);
  aspect-ratio: 1;
  border-radius: 50%;
  pointer-events: none;
  background: radial-gradient(circle at 42% 42%,
    rgba(26, 115, 232, 0.16), rgba(26, 115, 232, 0.07) 44%, transparent 68%);
}
/* The feed page warms one edge — blue into a soft pink. It is deliberately
   SOFTER than the Instagram ramp used on the headline and the phone: the glow
   sits behind everything, and the full-strength gradient at that size would
   tint the photographs it is meant to sit behind. */
.hero--feed.hero .hero-grid::after {
  background: radial-gradient(circle at 42% 42%,
    rgba(26, 115, 232, 0.15), rgba(221, 42, 123, 0.10) 46%, transparent 70%);
}

.browser--tilt {
  transform: rotate(-2.2deg);
  box-shadow: 0 2px 6px rgba(32, 33, 36, 0.06),
              0 18px 40px rgba(32, 33, 36, 0.10),
              0 48px 90px rgba(32, 33, 36, 0.10);
}
/* 🔑 The tilt is removed for anyone who asked for less motion as well as for
   anyone on a phone. A static rotation is not motion, but it is the same
   visual noise the preference is asking us to turn down, and the frame reads
   perfectly well square. */
@media (prefers-reduced-motion: reduce) {
  .browser--tilt { transform: none; }
}

.browser-pill {
  margin-left: auto;
  flex: 0 0 auto;
  padding: 3px 10px;
  border-radius: var(--radius-full);
  background: var(--color-attention-soft);
  color: var(--color-attention);
  font-size: 11px;
  font-weight: 500;
}

/* ⚠️ Room reserved on the right for the phone, which overlaps this corner.
   Without it the last words of the disclaimer sit underneath the device —
   and this is the one line in the scene that must stay readable. */
.hero-scene .browser-foot { padding-right: 150px; }
.browser-foot {
  margin: 0;
  padding: var(--space-sm) var(--space-md);
  border-top: 1px solid var(--color-hairline-soft);
  background: var(--color-surface-soft);
  font-size: 11px;
  line-height: 1.5;
  color: var(--color-steel);
}

/* The customer's own site name above the widget, so the frame reads as a page
   rather than as a component on a white card. */
.mock-site {
  margin: 0 0 var(--space-md);
  font-size: var(--type-subtitle-size);
  font-weight: 500;
  color: var(--color-ink-deep);
}

/* Four reviews need two columns; three fitted in one. */
.mock-reviews--two {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--space-sm);
}
/* The fifth star of a four-star rating: present, so the row keeps its width
   and the eye can see it is four OF five rather than a shorter row. */
.stars i { font-style: normal; color: var(--color-hairline); }

.mock-avatar--amber { background: var(--color-attention); }

/* ── the floating cards ───────────────────────────────────────────────────
   Anchored to the FRAME, not to the section, for the reason the homepage
   records: anchored to the section they drift toward the window's edges as
   the screen widens and stop having any relationship to the thing they
   overlap. */
.hero-float {
  position: absolute;
  z-index: 3;
  display: flex;
  align-items: center;
  gap: var(--space-sm);
  padding: 10px 16px 10px 10px;
  background: var(--color-canvas);
  border: 1px solid var(--color-hairline-soft);
  border-radius: var(--radius-full);
  box-shadow: 0 2px 6px rgba(32, 33, 36, 0.07), 0 14px 34px rgba(32, 33, 36, 0.12);
}
.hero-float b {
  display: block;
  font-size: var(--type-caption-size);
  font-weight: 500;
  color: var(--color-ink-deep);
}
.hero-float i {
  display: block;
  font-style: normal;
  font-size: 11px;
  color: var(--color-steel);
  margin-top: 1px;
}
.hero-float .mock-avatar { width: 26px; height: 26px; font-size: 11px; }

.hero-float--badge { left: -20px; bottom: 76px; }
/* Above the frame's top edge rather than inside it: at `top: 64px` this card
   sat squarely on the widget's own "Write a review" button, which reads as a
   collision rather than as a layer. */
.hero-float--new { right: -10px; top: -22px; }

/* 🔑 The slide-in runs ONCE, not on a loop. A "New review · just now" card
   that arrives every few seconds is a claim that reviews land every few
   seconds, and a repeating motion in the corner of a page is the thing people
   scroll past to escape. `forwards` holds the end state. */
.hero-float--new {
  animation: hero-float-in 640ms cubic-bezier(0.4, 0, 0.2, 1) 900ms both;
}
@keyframes hero-float-in {
  from { opacity: 0; transform: translate(14px, -10px) scale(0.96); }
  to   { opacity: 1; transform: none; }
}
@media (prefers-reduced-motion: reduce) {
  .hero-float--new { animation: none; }
}

/* The feed page's post preview: a photograph with a caption, which is what
   "a post opens on your page" looks like. */
.hero-float--post {
  /* ⚠️ Kept SMALL and low. At 220px wide and 104px up it reached into the
     account header and hid the follower count behind it — a card demonstrating
     "a post opens on your page" was covering the page it opens on. */
  left: -26px;
  bottom: 58px;
  flex-direction: column;
  align-items: stretch;
  gap: 0;
  width: 178px;
  padding: 0;
  overflow: hidden;
  border-radius: var(--radius-xl);
}
.hero-float--post img { display: block; width: 100%; height: auto; }
.hero-float-body { padding: var(--space-sm) 12px 12px; }
.hero-float--post b { font-weight: 400; line-height: 1.45; color: var(--color-ink); }
.hero-float--post i { margin-top: 6px; }

/* ── the phone ────────────────────────────────────────────────────────────
   The one shape the browser frame cannot show: a narrow column. On the feed
   page it carries the Reels row, on the reviews page the Slider. */
.hero-phone {
  position: absolute;
  z-index: 4;
  right: -24px;
  bottom: -38px;
  width: 148px;
  padding: 7px;
  background: var(--color-ink-deep);
  border-radius: 22px;
  box-shadow: 0 3px 8px rgba(32, 33, 36, 0.14), 0 20px 46px rgba(32, 33, 36, 0.24);
}
.hero-phone-screen {
  background: var(--color-canvas);
  border-radius: 16px;
  padding: 10px;
}
.hero-phone-label {
  margin: 0 0 7px;
  font-size: 10px;
  font-weight: 500;
  color: var(--color-steel);
}
.hero-phone-reels { display: flex; gap: 5px; overflow: hidden; }
.hero-phone-reels img {
  display: block;
  width: 50%;
  height: auto;
  border-radius: 9px;
}
.hero-phone-rating {
  display: flex;
  align-items: baseline;
  gap: 6px;
  padding-bottom: 7px;
  border-bottom: 1px solid var(--color-hairline-soft);
}
.hero-phone-rating b { font-size: 20px; font-weight: 400; color: var(--color-ink-deep); }
.hero-phone-review {
  margin: 8px 0 0;
  font-size: 11px;
  line-height: 1.45;
  color: var(--color-ink);
}
.hero-phone-review i {
  display: block;
  font-style: normal;
  margin-top: 5px;
  font-size: 10px;
  color: var(--color-steel);
}

/* ── the feed grid's photographs ─────────────────────────────────────────
   The tiles were gradient panels. They are photographs now, which is the
   whole point of this widget — and the reason DESIGN.md §13's rule about
   never drawing a flat block applies here more than anywhere: a page selling
   a photo widget illustrated with grey squares shows the product failing. */
.igmock-tile img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  border-radius: inherit;
}
.igmock-avatar--mono {
  display: grid;
  place-items: center;
  background: var(--color-ink-deep);
  color: var(--g-white);
  font-size: var(--type-caption-size);
  font-weight: 500;
}
.igmock-duration {
  position: absolute;
  right: 5px;
  top: 5px;
  padding: 1px 5px;
  border-radius: var(--radius-sm);
  background: rgba(23, 24, 26, 0.62);
  color: var(--g-white);
  font-size: 9px;
  font-weight: 500;
}

/* ── narrow ──────────────────────────────────────────────────────────────
   🔑 THE FRAME STAYS AND THE PROPS GO — the reverse of the first attempt.

   That version hid the frame and kept only the phone, reasoning that a
   desktop page drawn at a third of its size is illegible. It is the wrong
   trade: the phone is a PROP, and what a visitor on a phone most needs to see
   is the WIDGET. Six square photographs at a third of 390px read perfectly
   well, and the grid is the layout most customers actually ship.

   So the frame loses its tilt and most of its shadow, every overlapping
   element goes — none has a gutter to sit in at this width, and each would
   land on the thing it points at — and the widget fills the column. */
@media (max-width: 767px) {
  .hero-scene > .browser {
    transform: none;
    box-shadow: 0 1px 2px rgba(32, 33, 36, 0.06), 0 6px 18px rgba(32, 33, 36, 0.08);
  }
  .hero-scene .browser-foot { padding-right: var(--space-md); }

  .hero-float,
  .hero-phone { display: none; }

  /* Two columns of review cards inside a 390px frame gives a 150px card and
     four words to a line. One column, and the reader sees two whole reviews. */
  .mock-reviews--two { grid-template-columns: 1fr; }

  .hero-grid::after { inset: -10% -20% auto auto; width: 90%; }
}


/* ── Instagram's own ramp, used on the feed page ──────────────────────────
   🔑 One definition, two uses: the h1's highlight and the phone's border.
   They MUST be the same ramp — two hand-written gradients drift, and the
   whole point is that the device and the headline read as one decision.

   ⚠️ This overrules the earlier note beside the glow, which said the ramp
   was deliberately not used as decoration. Owner's call on 2026-09-21: the
   feed page leans on Instagram's colours on purpose, so the page looks like
   what it is about. Nominative use still applies — the site claims no
   partnership, and the footer carries the trademark notice on every page. */
:root {
  --instagram-ramp: linear-gradient(45deg,
    #F9CE34 0%, #EE2A7B 48%, #6228D7 100%);
}

/* `background-clip: text` needs `color: transparent`, so an engine that
   cannot paint it renders the words INVISIBLE rather than uncoloured. The
   fallback is the page's existing accent. */
.c-instagram { color: var(--color-critical); }
@supports (-webkit-background-clip: text) or (background-clip: text) {
  .c-instagram {
    background-image: var(--instagram-ramp);
    -webkit-background-clip: text;
    background-clip: text;
    color: transparent;
  }
}

/* The phone's bezel is the gradient. The 7px padding already in .hero-phone
   IS the border — there is no border property to colour, which is why this
   sets the background rather than a border-image. */
.hero--feed .hero-phone { background: var(--instagram-ramp); }

/* ── Google's own colours, on the reviews page ────────────────────────────
   The mirror of --instagram-ramp: one definition feeding the h1's highlight
   and the phone's bezel, so the device and the headline are one decision.

   🔑 TWO RAMPS, NOT ONE, and the difference is accessibility.

   The TEXT ramp uses the 700-series steps. Google's brand 500s are what the
   logo is drawn in, and three of the four fail contrast on white — Yellow 500
   is far below even the 3:1 large-text floor, so a headline running through it
   would have a word in it nobody can read. DESIGN.md §2 already makes this
   swap for buttons and §11 makes it for coloured words in a headline.

   The BEZEL ramp keeps the brand 500s, because no text sits on it and those
   are the colours people recognise.

   ⚠️ Nominative use, and the line is thinner here than on the feed page:
   DESIGN.md §9 forbids imitating the Google LOGOTYPE. Four of their colours
   running through a sentence is not their wordmark and claims nothing — but
   do not let this drift toward setting "Google" itself in their letterforms. */
:root {
  /* 🔑 TIGHT STOPS, NOT A SMOOTH BLEND, and that is the whole trick.

     A four-colour gradient with evenly spaced stops spends most of its width
     in the blends rather than on the colours — and the yellow-to-green blend
     lands squarely on OLIVE, which is what made "Widget" look dull and
     off-brand. Google's own mark has no blends at all: each letter is one
     flat colour. Holding each colour for a band and crossing between them
     quickly is as close as a gradient gets to that, and it keeps all four
     recognisable.

     The blend zones are FOUR PERCENT wide. At sixteen they were wide enough
     for a whole letter to sit inside one, so "Widget" had a letter of olive
     in the middle of it — the yellow-to-green crossing, which is the worst
     pair of the three.

     ⚠️ The only substitution is the yellow: Yellow 500 (#FBBC04) measures
     about 1.8:1 on white and fails even the 3:1 large-text floor, so a word
     crossing it would be genuinely hard to read. Yellow 700 is the same
     substitution DESIGN.md §11 already makes for `.c-yellow`. Red, green and
     blue are Google's published 500s, untouched. */
  --google-ramp-text: linear-gradient(95deg,
    #4285F4 0%, #4285F4 28%,
    #EA4335 32%, #EA4335 56%,
    #F9AB00 60%, #F9AB00 70%,
    #34A853 74%, #34A853 100%);
  /* No text sits on the bezel, so all four are the published brand values. */
  --google-ramp-edge: linear-gradient(135deg,
    #4285F4 0%, #4285F4 24%,
    #EA4335 28%, #EA4335 50%,
    #FBBC04 54%, #FBBC04 70%,
    #34A853 74%, #34A853 100%);
}

.c-google { color: var(--color-primary); }
@supports (-webkit-background-clip: text) or (background-clip: text) {
  .c-google {
    background-image: var(--google-ramp-text);
    -webkit-background-clip: text;
    background-clip: text;
    color: transparent;
  }
}

.hero--reviews .hero-phone { background: var(--google-ramp-edge); }
