/* merchOne homepage — static port of the Figma Make demo (see README.md).
   Type sizes and spacing are the demo's values verbatim. Colours were too, until
   design's review rounds; where one has been changed since, the rule carries a
   comment saying who chose the new value and when. The demo
   switched layout with a JS `useIsMobile()` hook at 768px; here the same
   breakpoint is a media query, so layout no longer depends on script. */

@font-face {
  font-family: 'Inter';
  src: url('assets/fonts/inter-latin.woff2') format('woff2');
  font-weight: 100 900;
  font-style: normal;
  font-display: swap;
}

:root {
  --white: #fff;
  --navy: #0b1332;
  --navy2: #0d1640;
  --gray1: #3a4468;
  --gray2: #68708b;
  --border: #dddfec;
  --primary: #6f52f5;
  --primary-light: #f1eefe;
  --bg: #f7f9fc;
  --tile: #141d2e;
  --coral: #ff5165;
  --green: #2e7d52;
  --grad: linear-gradient(135deg, #4a49ff 0%, #ba3fff 55%, #ff5165 100%);
  --shell: 1200px;
}

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

html { -webkit-text-size-adjust: 100%; }

body {
  margin: 0;
  font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Arial, sans-serif;
  color: var(--navy);
  background: var(--white);
  line-height: 1.5;
}

img, svg, video { display: block; max-width: 100%; }
/* -0.5px, not an em fraction, requested by design 2026-09-03: the headings were
   at -0.03em to -0.04em, which is -1.4px at the 36px hero size, and design read
   that as cramped. A flat px value keeps every heading at the same tracking
   whatever its size, so the per-element letter-spacing overrides below are gone
   rather than re-tightening it. text-wrap: balance is the no-orphan-word ask
   from the same round: it evens the lines instead of leaving one word alone. */
h1, h2, h3 { margin: 0; letter-spacing: -0.5px; text-wrap: balance; }
p { margin: 0; }
a { color: inherit; }

.wrap { max-width: 1100px; margin: 0 auto; }
.centered { text-align: center; }
.muted { color: var(--gray2); font-size: 14px; line-height: 1.7; }
.grad-text {
  background-image: var(--grad);
  -webkit-background-clip: text;
  background-clip: text;
  -webkit-text-fill-color: transparent;
  color: transparent;
}

.eyebrow {
  margin: 0 0 12px;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--primary);
}
.eyebrow--muted { color: var(--gray2); margin-bottom: 14px; }

/* Buttons */
.btn {
  display: inline-block;
  padding: 14px 32px;
  border-radius: 10px;
  font-size: 15px;
  font-weight: 700;
  text-decoration: none;
  text-align: center;
  border: none;
  cursor: pointer;
}
.btn--sm { padding: 8px 20px; font-size: 13px; border-radius: 8px; }
.btn--primary { background: var(--primary); color: var(--white); }
.btn--coral { background: var(--coral); color: var(--white); }
.btn--white { background: var(--white); color: var(--primary); font-weight: 800; }
/* The ring is an inset shadow rather than a border, because a border is part of
   the box and a filled button has none. In a flex row that stretches both to the
   same height, the bordered one's content starts 1.5px lower and its text
   baseline sits 1px below its neighbour's, which Design reported on 2026-09-21
   for the catalog and integrations rows. An inset shadow paints the same ring
   and occupies no space, so both buttons lay their text out identically. */
.btn--outline { border: none; box-shadow: inset 0 0 0 1.5px var(--border); background: var(--white); color: var(--navy); }
.btn--ghost { border: none; box-shadow: inset 0 0 0 1.5px rgba(255, 255, 255, 0.4); color: var(--white); background: transparent; }
/* forced-colors forces box-shadow to none, so the ring above would simply vanish
   and leave these two variants with no visible edge at all, where the border it
   replaced would have been kept and recoloured by the browser. Hand the border
   back in that mode, and give it to EVERY .btn rather than only these two, so the
   baselines this file just aligned stay aligned there as well. */
@media (forced-colors: active) {
  .btn { border: 1.5px solid; }
  .btn--outline, .btn--ghost { border: 1.5px solid; box-shadow: none; }
}

/* Announcement bar */
.announce {
  position: relative;
  background: #7b5ee8;
  padding: 10px 16px;
  display: flex;
  align-items: center;
  justify-content: center;
  text-align: center;
}
.announce__head { font-size: 12px; font-weight: 800; color: var(--white); letter-spacing: -0.01em; }
.announce__sub { display: none; font-size: 12px; color: rgba(255, 255, 255, 0.8); margin-top: 2px; }
.announce__flags { display: none; border-radius: 8px; overflow: hidden; }

/* Nav */
.nav { position: sticky; top: 0; z-index: 100; background: var(--white); border-bottom: 1px solid var(--border); }
.nav__inner { max-width: var(--shell); margin: 0 auto; padding: 0 16px; height: 64px; display: flex; align-items: center; }
.nav__logo { margin-right: auto; flex-shrink: 0; }
.nav__logo img { height: 26px; width: auto; }
.nav__links { display: none; gap: 24px; flex: 1; }
.nav__links a, .drawer a { font-size: 13px; font-weight: 500; color: var(--gray1); text-decoration: none; white-space: nowrap; }
.nav__links a:hover { color: var(--primary); }
.nav__links a.is-current { color: var(--primary); }
.nav__actions { display: flex; align-items: center; gap: 10px; flex-shrink: 0; }
.nav__login { display: none; font-size: 13px; font-weight: 500; color: var(--gray1); text-decoration: none; }
.nav__burger { display: flex; background: none; border: none; padding: 4px; margin-left: 10px; color: var(--navy); cursor: pointer; }
/* Without scripting the burger cannot open anything, so it is not shown: the
   drawer stays open below the header instead and the nav is reachable as a
   list. The .js class comes from the inline head script in index.html. */
html:not(.js) .nav__burger { display: none; }
/* max-height and its own scrolling are load-bearing: the drawer lives inside the
   sticky header, so once its content is taller than the viewport the overflow sits
   below the fold and page scrolling cannot bring it back. With all three groups
   open the drawer is 558px and the header 647px, which still fits a 640px-tall
   phone, but it does not fit a shorter one and nothing stops a fourth group being
   added later. */
.drawer {
  background: var(--white); border-top: 1px solid var(--border); padding: 16px 20px;
  display: flex; flex-direction: column;
  max-height: calc(100vh - 64px);
  max-height: calc(100svh - 64px);
  overflow-y: auto;
}
/* `display: flex` above outranks the UA sheet's `[hidden] { display: none }`,
   so the closed state needs saying explicitly or the drawer is always open. */
.drawer[hidden] { display: none; }
.drawer a { font-size: 15px; font-weight: 500; color: var(--navy); padding: 12px 0; border-bottom: 1px solid var(--border); }
.drawer a.is-current { color: var(--primary); font-weight: 600; }
.drawer__login { color: var(--gray1); border-bottom: none; }

/* Submenus. No script: hover opens them for a pointer, :focus-within opens them
   for the keyboard, so tabbing through the nav reaches every child link. */
.nav__item { position: relative; display: flex; align-items: center; }
.nav__item > a { display: inline-flex; align-items: center; gap: 5px; }
.nav__chev { flex-shrink: 0; opacity: 0.6; }
.nav__menu {
  /* top: 100% and not a pixel lower. A 6px offset reads as a tidier gap and
     breaks the menu: the strip between link and panel would belong to neither,
     so a pointer travelling down leaves .nav__item and :hover closes the panel
     before it is reached. */
  display: none; position: absolute; top: 100%; left: -14px; min-width: 210px;
  background: var(--white); border: 1px solid var(--border); border-radius: 12px;
  box-shadow: 0 8px 28px rgba(11, 19, 50, 0.12); padding: 8px; z-index: 101;
}
.nav__item:hover .nav__menu, .nav__item:focus-within .nav__menu { display: block; }
/* .nav__links a sets nowrap for the top-level row; the longest child link
   ("Best Print-on-Demand Custom Gifts Fulfiller") has to wrap instead. */
.nav__menu a { display: block; padding: 9px 10px; border-radius: 8px; white-space: normal; line-height: 1.35; }
.nav__menu a:hover { background: var(--primary-light); color: var(--primary); }

/* Search. The live header's field is a JS product search backed by WordPress,
   which a static page cannot call, so this is WordPress site search. The label
   stays in the accessibility tree but off screen; clip-path plus 1px is the form
   that survives a screen reader, unlike display:none. */
.nav__search { display: none; align-items: center; }
/* Collapsed to an icon like the live header, and expanded with no script: the
   magnifier is a <label for>, so clicking it focuses the input, which triggers
   :focus-within on the form. Tabbing to the input does the same, so the keyboard
   path does not depend on the click. */
.nav__search-btn {
  display: flex; align-items: center; padding: 6px; border-radius: 8px;
  color: var(--gray2); cursor: pointer;
}
.nav__search-btn:hover { color: var(--primary); }
.nav__search-label {
  position: absolute; width: 1px; height: 1px; overflow: hidden;
  clip-path: inset(50%); white-space: nowrap;
}
.nav__search input {
  width: 0; min-width: 0; padding: 7px 0; font: inherit; font-size: 13px;
  color: var(--navy); background: none;
  border: 1.5px solid transparent; border-radius: 8px;
  transition: width 0.18s ease, padding 0.18s ease;
}
.nav__search:focus-within input {
  width: 148px; padding: 7px 12px; background: var(--bg); border-color: var(--border);
}
.nav__search input:focus { outline: 2px solid var(--primary); outline-offset: 1px; }

/* Drawer submenus use <details>, so they open with no script either. */
.drawer__group { border-bottom: 1px solid var(--border); }
.drawer__group summary {
  padding: 12px 0; font-size: 15px; font-weight: 500; color: var(--navy);
  cursor: pointer; list-style: none; display: flex; justify-content: space-between;
}
.drawer__group summary::-webkit-details-marker { display: none; }
.drawer__group summary::after { content: '+'; font-size: 18px; line-height: 1; color: var(--gray2); }
.drawer__group[open] summary::after { content: '–'; }
/* display:block and white-space:normal are both load-bearing. The drawer is a
   column flex container, which blockified its direct children, but these links
   are inside <details> and so are plain inline elements: without display:block
   they flow side by side and wrap mid-row. And .drawer a sets nowrap for the
   top-level items, which clipped "Best Print-on-Demand Custom Gifts Fulfiller"
   at the drawer edge. */
.drawer__group a {
  display: block; white-space: normal; line-height: 1.35;
  padding: 10px 0 10px 16px; font-size: 14px; border-bottom: none;
}
.drawer__group a:last-child { padding-bottom: 14px; }

/* Hero */
.hero {
  position: relative;
  min-height: calc(100svh - 64px);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 40px 20px 32px;
  overflow: hidden;
  text-align: center;
}
.hero__video { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; z-index: 0; }
.hero__scrim { position: absolute; inset: 0; background: rgba(11, 19, 50, 0.66); z-index: 1; }
.hero__body { position: relative; z-index: 2; display: flex; flex-direction: column; align-items: center; }
.pill {
  display: inline-flex;
  border: 1px solid rgba(255, 255, 255, 0.25);
  border-radius: 999px;
  padding: 6px 18px;
  margin-bottom: 20px;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.8);
}
.hero h1 { margin: 0 0 16px; font-size: 36px; font-weight: 900; color: var(--white); line-height: 1.05; max-width: 340px; }
.hero__lead { margin: 0 0 32px; font-size: 14px; color: rgba(255, 255, 255, 0.7); line-height: 1.7; max-width: 320px; }
.hero__lead strong { color: var(--white); }
/* Keep the closing clause whole so it cannot leave "the US." alone on a line of
   its own, which is what the 560px paragraph did at every width from 834 up.
   inline-block makes it one unwrappable unit, so it moves to the next line
   entire rather than splitting; below 834 the paragraph is narrower and the
   clause still fits one line, so no breakpoint is needed. Design asked for this
   on 2026-09-21. The <meta name="description"> carries the same sentence and
   deliberately does NOT carry the span. */
.hero__lead-tail { display: inline-block; }
.hero__ctas { display: flex; flex-direction: column; gap: 12px; width: 100%; max-width: 320px; }

/* Trust stats */
.trust { background: var(--white); padding: 40px 20px; }
.trust__grid { max-width: 1100px; margin: 0 auto; display: grid; grid-template-columns: 1fr 1fr; gap: 32px 16px; }
.trust__item { display: flex; flex-direction: column; align-items: center; text-align: center; }
.dot {
  width: 36px; height: 36px; border-radius: 999px; background: var(--primary-light);
  margin-bottom: 16px; position: relative; flex-shrink: 0;
}
.dot::after {
  content: ''; position: absolute; inset: 9px; border-radius: 999px; background: var(--primary);
  animation: pulse 2.4s ease-in-out infinite; animation-delay: var(--pulse-delay, 0s);
}
@keyframes pulse { 0%, 100% { transform: scale(1); opacity: 1; } 50% { transform: scale(1.25); opacity: 0.75; } }
@media (prefers-reduced-motion: reduce) { .dot::after { animation: none; } }
.trust__num { font-size: 30px; font-weight: 900; letter-spacing: -0.5px; color: var(--primary); line-height: 1; margin-bottom: 2px; }
.trust__unit { font-size: 14px; font-weight: 700; color: var(--navy); margin-bottom: 3px; }
/* Reserve two lines for the unit below 1024, so a longer one does not push its
   own label a line lower than the other three. Marketing's approved
   "customized products/day" wraps at those widths while "years" and "owned" do
   not. Above 1024 every unit fits one line, so the reservation is cleared there
   rather than adding empty space under all four. lh is a progressive
   enhancement: without it the row renders exactly as it did before. */
.trust__unit { min-height: 2lh; }
.trust__label { font-size: 12px; color: var(--gray2); }

/* Why merchOne */
.why { background: var(--white); overflow: hidden; }
.why__copy { padding: 48px 20px; background: linear-gradient(to bottom, var(--primary-light) 0%, var(--white) 100%); }
.why h2 { margin: 0 0 12px; font-size: 28px; font-weight: 900; line-height: 1.1; }
.why .muted { margin-bottom: 28px; }
.why__stats { display: grid; grid-template-columns: 1fr 1fr; gap: 20px 16px; }
.why__num { display: block; font-size: 28px; font-weight: 900; line-height: 1; letter-spacing: -0.5px; }
/* -0.5px here too: design's 2026-09-03 screenshots included "factories" and
   "days", which are spans rather than headings, so the h1/h2/h3 rule above
   never reached them. */
.why__unit { font-size: 16px; letter-spacing: -0.5px; }
.why__label { display: block; font-size: 11px; color: var(--gray2); margin-top: 4px; line-height: 1.4; }
.why__map { padding: 0 20px 48px; background: var(--white); }
.why__map img { width: 100%; height: auto; border-radius: 14px; }
/* No pin key. Design's 2026-09-15 export uses one pin colour for every location and
   asked for the office and production split to be dropped, so a key naming two
   types would describe an artwork that no longer has them. --coral stays, it is
   also .btn--coral. */

/* POD split */
.pod { background: var(--bg); padding: 56px 20px; }
.pod__head { margin: 0 0 40px; font-size: 28px; font-weight: 400; letter-spacing: -0.5px; text-wrap: balance; line-height: 1.1; max-width: 760px; }
.pod__head strong { font-weight: 900; color: #4a49ff; }
.pod__head a { font-weight: 900; color: #4a49ff; text-decoration: underline; text-underline-offset: 4px; }
.pod__cols { display: grid; gap: 16px; }
.checklist { background: var(--white); border: 1px solid var(--border); border-radius: 16px; overflow: hidden; }
.checklist__head { padding: 16px 20px; border-bottom: 1px solid var(--border); font-size: 14px; font-weight: 700; }
.checklist ul { margin: 0; padding: 0; list-style: none; }
.checklist li {
  display: flex; align-items: center; gap: 10px; padding: 10px 20px;
  font-size: 14px; color: var(--gray1); border-bottom: 1px solid var(--border);
}
.checklist li:last-child { border-bottom: none; }
.checklist li::before {
  content: ''; flex-shrink: 0; width: 16px; height: 16px;
  background: no-repeat center/contain url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='none'%3E%3Ccircle cx='8' cy='8' r='7.5' stroke='%236F52F5'/%3E%3Cpath d='M5 8l2 2 4-4' stroke='%236F52F5' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
}
/* Darker at the 60% and 100% stops than the round 1 gradient (#7b6bf8, #6d96f1),
   which could not carry 11-13px copy anywhere: solid white measured 2.88 against
   that gradient's lightest stop and 4.28 at the darkest point text actually sat
   on, both under the 4.5 that size needs. Design picked these two values on
   2026-09-10, after asking first whether full opacity alone would do it: it does
   not, because at 100% opacity on the old gradient the labels still measured
   4.23 and the field text 3.24. NGE-11640. */
.podcard {
  background: linear-gradient(135deg, var(--primary) 0%, #7162e4 60%, #5575bc 100%);
  border-radius: 16px; padding: 28px; display: flex; flex-direction: column; min-height: 300px;
}
.podcard__title { margin: 0 0 20px; font-size: 20px; font-weight: 800; letter-spacing: -0.02em; color: var(--white); line-height: 1.3; }
.podcard__form { display: flex; flex-direction: column; flex: 1; }
.podcard label { font-size: 12px; font-weight: 600; color: var(--white); margin-bottom: 5px; letter-spacing: 0.02em; }
/* :not([type="checkbox"]) so the consent box below is not dressed as a text
   field: this rule's padding, border, background and full width would render a
   16px checkbox as a 36px translucent input. Excluded here rather than undone
   in .podcard__consent, so the exception cannot be defeated by rule order. */
/* The fill DARKENS the card instead of tinting it white. A white tint is what
   makes a field the lightest place on the card, so 13px white text inside one
   measured 3.24 while the label right above it measured 4.23 on the same
   gradient: the field, not the gradient, was the worst case. */
.podcard input:not([type="checkbox"]), .podcard select {
  width: 100%; padding: 10px 14px; margin-bottom: 12px; border-radius: 9px;
  border: 1.5px solid rgba(255, 255, 255, 0.45); background: rgba(0, 0, 0, 0.14);
  color: var(--white); font-size: 13px; font-family: inherit; outline: none;
}
/* 0.85, not full white: the placeholder has to stay distinguishable from a typed
   value, and 0.85 over the darkened fill measures 4.80. */
.podcard input::placeholder { color: rgba(255, 255, 255, 0.85); }
.podcard input:focus, .podcard select:focus { border-color: rgba(255, 255, 255, 0.7); }
/* The border alone is not a conforming focus indicator: raising a translucent
   white from 0.45 to 0.70 over the darkened fill lands near 3:1 against the
   gradient and only about 1.5:1 against its own unfocused state, and the rule
   above sets outline: none, so a select has no caret to fall back on either. A
   solid white ring is 4.51 at the card's lightest stop, well over the 3:1 that
   WCAG 1.4.11 asks of a focus indicator. :focus-visible so a mouse click does
   not paint a ring the pointer user did not ask for. */
.podcard input:not([type="checkbox"]):focus-visible, .podcard select:focus-visible {
  outline: 2px solid var(--white); outline-offset: 2px;
}
/* appearance: none removes the platform dropdown arrow, and until 2026-09-10
   nothing replaced it, so the select read as a text input. It was two selects
   until the topic field was removed on 2026-09-11. The chevron is a
   background-image rather than a pseudo-element, because a select cannot carry
   one. background-image only, so the fill from the rule above still applies, and
   padding-right keeps a long option clear of the glyph. */
.podcard select {
  -webkit-appearance: none; appearance: none; cursor: pointer; padding-right: 38px;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' fill='none'%3E%3Cpath d='M1 1.5L6 6.5L11 1.5' stroke='%23ffffff' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
  background-repeat: no-repeat; background-position: right 14px center;
}
/* In a forced-colors palette the field background is remapped while a URL
   background image is not repainted, so a hard-coded white chevron can end up
   invisible on a light high-contrast background, or dropped altogether. Hand the
   arrow back to the platform there, which is the one version that adapts. */
@media (forced-colors: active) {
  .podcard select { -webkit-appearance: auto; appearance: auto; background-image: none; padding-right: 14px; }
}
.podcard select option { color: var(--navy); background: var(--white); }

/* The custom dropdown, and why the native select above still exists.
   Design asked on 2026-09-15 for the open list to match the nav's Integrations
   panel: white, rounded, light purple on hover. None of that is reachable on a
   native select, whose popup the operating system draws; `option` takes a colour
   and a background and nothing else. So JS upgrades the select into the listbox
   below and hides it. Without script the select is what ships and still works,
   which is the same bargain the submit button makes a few rules down.
   .podcard--custom is set by app.js, so every rule here is inert until the
   script has actually built the listbox. */
.podcard--custom .podcard__select-native { display: none; }
.podselect { position: relative; margin-bottom: 12px; }
/* Same box as the inputs above, repeated rather than shared, because the input
   rule keys off element names this button is not one of. Kept in step by hand:
   if the field padding, radius, border or fill change up there, change them
   here too. */
.podselect__btn {
  width: 100%; display: flex; align-items: center; justify-content: space-between;
  gap: 10px; padding: 10px 14px; border-radius: 9px; cursor: pointer;
  border: 1.5px solid rgba(255, 255, 255, 0.45); background: rgba(0, 0, 0, 0.14);
  color: var(--white); font-size: 13px; font-family: inherit; text-align: left;
}
.podselect__btn:focus { outline: none; border-color: rgba(255, 255, 255, 0.7); }
/* The same solid white ring the inputs use, for the same reason: a translucent
   border shift is not a conforming indicator on this gradient. */
.podselect__btn:focus-visible { outline: 2px solid var(--white); outline-offset: 2px; }
/* 0.85 matches .podcard input::placeholder, so an unmade choice reads as a
   placeholder rather than as a value. */
.podselect__btn[data-empty="true"] .podselect__value { color: rgba(255, 255, 255, 0.85); }
.podselect__chev { flex-shrink: 0; transition: transform 0.15s ease; }
.podselect__btn[aria-expanded="true"] .podselect__chev { transform: rotate(180deg); }
/* Design's reference is .nav__menu, so this takes that panel's own values: white
   card, 1px --border, 12px radius, 8px padding, and --primary-light on hover.
   The list sits ON the card, so it needs its own stacking context above the
   fields below it. */
.podselect__list {
  position: absolute; top: calc(100% + 6px); left: 0; right: 0; z-index: 30;
  margin: 0; padding: 8px; list-style: none; max-height: 240px; overflow-y: auto;
  background: var(--white); border: 1px solid var(--border); border-radius: 12px;
  box-shadow: 0 8px 28px rgba(11, 19, 50, 0.18);
}
.podselect__option {
  display: block; padding: 9px 10px; border-radius: 8px; cursor: pointer;
  color: var(--navy); font-size: 13px; line-height: 1.35;
}
/* Hover and keyboard land on the same visual, so the pointer and the arrow keys
   never disagree about which row is current. aria-selected carries the chosen
   one for assistive technology; .is-active is only the transient highlight.
   #6c4ff2 rather than --primary, and it is three per channel darker: --primary
   on --primary-light measures 4.38:1, just under the 4.5 this 13px row needs,
   while #6c4ff2 on the same background measures 4.56. The nav panel Design
   pointed at uses the token pair and so carries the same 4.38 on its own hover;
   that is pre-existing and is not touched here. */
.podselect__option:hover, .podselect__option.is-active { background: var(--primary-light); color: #6c4ff2; }
.podselect__option[aria-selected="true"] { font-weight: 600; }
/* Forced colors repaints the card but not a hand-picked highlight, which can
   leave the active row indistinguishable. A border does get repainted. */
@media (forced-colors: active) {
  .podselect__option.is-active { outline: 2px solid; outline-offset: -2px; }
}
/* Row, not the column the other fields use: the label sits beside the box and
   wraps under it, so the checkbox keeps its own line and the label does not
   stretch to the card width. align-items: flex-start keeps the box level with
   the first line of a wrapped label rather than centred against both lines, and
   is the keyword the rest of this file uses for flex containers. */
.podcard__consent { display: flex; align-items: flex-start; gap: 9px; margin: 4px 0 18px; }
.podcard__consent input { width: 16px; height: 16px; flex-shrink: 0; margin: 2px 0 0; accent-color: var(--white); }
.podcard__consent label { margin: 0; font-size: 12px; font-weight: 400; line-height: 1.5; color: var(--white); letter-spacing: 0; }
/* The nine text elements of the card's resting state now clear the ratio their
   size needs, so the navy chip on the validation messages is no longer the only
   thing on the card that can be read: it stays because a message that blocks
   submission should not depend on the gradient behind it at all (white on
   --navy is 18.21). Those nine are the title, a field label, the input and
   select text, the placeholder, the consent label, the privacy note and its
   link, and the submit button, measured on the rendered pixels rather than on
   the stops, because the field fill and the gradient position both move the
   backdrop: 4.57 at the thinnest. Not covered by that run, and each on a solid
   backing rather than the gradient: the four validation messages, the sending
   and confirmation states, and the open option list. NGE-11640. */
/* The privacy note above the submit button. The negative top margin tightens it
   against the block above, which is the consent row and its 18px bottom
   margin, leaving 12px. Was written for the phone disclaimer, which sat under
   the phone input and was removed on Legal's instruction. */
.podcard__note { margin: -6px 0 12px; font-size: 11px; line-height: 1.5; color: var(--white); }
.podcard__note a { color: var(--white); text-decoration: underline; text-underline-offset: 2px; }
/* Validation messages. Plain <p> with no display declaration, so the UA sheet's
   `[hidden] { display: none }` is enough here and needs no reset. */
.podcard__err {
  margin: -4px 0 12px; padding: 7px 10px; border-radius: 7px;
  background: var(--navy); color: var(--white);
  font-size: 11px; font-weight: 700; line-height: 1.4;
}
.podcard__err a { color: var(--white); text-decoration: underline; text-underline-offset: 2px; }
.podcard__cta { margin-top: auto; padding: 12px 22px; font-size: 14px; font-weight: 700; }
/* .btn sets no font-family, which a <button> does not inherit on its own, so
   the submit would render in the UA default while the anchor beside it uses
   the page font. */
.podcard__submit { font-family: inherit; }
/* Both carry .btn's `display: inline-block`, which outranks the UA sheet's
   `[hidden] { display: none }`, so without this the button app.js ships hidden
   would be visible before its listener exists. Which of the two is shown is
   decided in app.js and not by the `js` class: the class only proves the inline
   head script ran, not that the submit handler was installed. */
.podcard__submit[hidden], .podcard__nojs[hidden] { display: none; }
.podcard__done { display: flex; flex-direction: column; justify-content: center; flex: 1; }
/* app.js focuses this so the confirmation is announced, and it carries
   tabindex="-1" to be focusable at all. It therefore needs a VISIBLE ring:
   suppressing the outline would move focus somewhere a keyboard user cannot
   see. White on the card rather than the UA default, which is a dark ring on
   a purple gradient. */
.podcard__done:focus { outline: 2px solid var(--white); outline-offset: 4px; }
/* Chipped like the validation messages, and for the same reason: this is the
   only message the visitor gets after submitting, and by then the form is gone
   so there is no text hierarchy on the card to preserve. White on --navy is
   18.21 whatever the gradient does behind it. The privacy note stays on the
   gradient rather than getting a chip of its own, because a dark chip would
   make the quietest text the loudest element in the field list. On the darkened
   gradient it passes at 4.57. NGE-11640. */
.podcard__doneline {
  margin: 0; padding: 9px 12px; border-radius: 8px; align-self: flex-start;
  background: var(--navy); color: var(--white);
  font-size: 13px; line-height: 1.6;
}
/* Same trap as .drawer above: both of these carry `display: flex`, which
   outranks the UA sheet's `[hidden] { display: none }`, so the finished state
   would be permanently visible and the form would never disappear behind it. */
.podcard__done[hidden], .podcard__form[hidden] { display: none; }

/* Catalog */
.catalog { background: var(--bg); padding: 56px 0; }
.catalog .wrap { padding: 0 20px; }
.catalog h2 { margin: 0 0 12px; font-size: 26px; font-weight: 900; }
.catalog__lead { margin: 0 auto; max-width: 560px; }
.tabs {
  display: flex; gap: 8px; margin: 28px -20px 24px; padding: 0 20px 6px;
  overflow-x: auto; -webkit-overflow-scrolling: touch;
}
.tab {
  flex-shrink: 0; padding: 8px 16px; border-radius: 999px; border: none;
  font-family: inherit; font-size: 12px; font-weight: 600; white-space: nowrap;
  background: var(--white); color: var(--navy); box-shadow: 0 2px 8px rgba(11, 19, 50, 0.08);
  cursor: pointer; transition: background 0.15s, color 0.15s;
}
.tab.is-active { background: var(--primary); color: var(--white); box-shadow: none; }
/* One white card holds the five panels and the CTA pair. The visible panel is
   `display: contents`, so its list and preview become grid items of the card and
   the CTAs can sit under the preview the way the demo has them. */
.catalog__card {
  background: var(--white); border-radius: 20px; padding: 20px;
  display: grid; gap: 20px;
  box-shadow: 0 2px 24px rgba(11, 19, 50, 0.06);
}
.panel:not([hidden]) { display: contents; }
.panel__list { order: 2; }
.previews { order: 1; }
.panel h3 { margin: 0 0 12px; font-size: 16px; font-weight: 800; }
/* height:auto is load-bearing. The img carries width/height attributes, which are
   presentational hints, so a declared height makes the browser ignore
   aspect-ratio: the preview rendered 310x1000 on a phone instead of 4:3. */
.previews img { display: none; width: 100%; height: auto; aspect-ratio: 4 / 3; object-fit: cover; border-radius: 14px; background: var(--border); }
.previews img.is-selected { display: block; }
.products { margin: 0; padding: 0; list-style: none; display: flex; flex-direction: column; gap: 10px; }
.product > a {
  display: flex; align-items: center; gap: 10px; padding: 10px 12px;
  background: var(--bg); border: 1.5px solid var(--border); border-radius: 12px;
  text-decoration: none; transition: background 0.15s, border-color 0.15s;
}
.product > a img { width: 40px; height: 40px; border-radius: 8px; object-fit: cover; flex-shrink: 0; background: var(--border); }
.product__name { display: block; font-size: 13px; font-weight: 700; color: var(--navy); }
.product__price { display: block; font-size: 11px; color: var(--gray2); margin-top: 1px; }
.product > a > span { display: block; }
.product > a > span.product__name + .product__price { margin-top: 1px; }
.product.is-selected > a { background: #ede9fd; border-color: var(--primary); }
.product.is-selected .product__name { color: var(--primary); }
/* One Explore link per product lives in the markup and only the selected one is
   shown. That is why no script ever assigns an href: CodeQL flagged the old
   DOM-to-href copy as js/xss-through-dom, and it also means the CTAs work with
   scripting disabled. */
.catalog__ctas { order: 3; display: flex; flex-direction: column; gap: 10px; }
.cta-explore { display: none; }
.cta-explore.is-selected { display: block; }
.catalog__ctas .btn { padding: 13px 0; font-size: 14px; }

/* Reviews */
.reviews { background: var(--white); padding: 56px 0; overflow: hidden; }
.reviews__head { margin-bottom: 32px; padding: 0 20px; }
.reviews h2 { margin: 0 0 12px; font-size: 26px; font-weight: 900; }
.marquee { overflow: hidden; width: 100%; }
/* The animation always travels exactly one set of cards, so the duration has to scale
   with the card count or the scroll speed changes. 60s was the reviewed speed at 7
   cards, so the calc carries the card count instead of a rounded literal: change the 12
   when a card is added. Computes to 102.857s, checked in Chrome. It holds at every
   breakpoint, because the distance and the card width scale together. */
.marquee__track { display: flex; width: max-content; animation: slide calc(60s * 12 / 7) linear infinite; }
.marquee__track:hover { animation-play-state: paused; }
@keyframes slide { from { transform: translateX(0); } to { transform: translateX(-50%); } }
@media (prefers-reduced-motion: reduce) {
  .marquee { overflow-x: auto; }
  .marquee__track { animation: none; }
  /* The second set exists only to make the -50% loop seamless. With the animation off
     the strip is scrolled by hand, so leaving it in means scrolling the same 12 reviews
     twice; aria-hidden takes it out of the accessibility tree but not off the screen. */
  .review[aria-hidden="true"] { display: none; }
}
/* Column with the attribution pushed to the bottom. The quotes are the live site's
   own text and run from 199 to 451 characters, so the flex row stretches every card
   to the tallest; margin-top: auto keeps the shortest card's name at the bottom
   instead of leaving blank space under it. */
.review {
  display: flex; flex-direction: column;
  flex-shrink: 0; width: 280px; margin: 0 16px 0 0; padding: 24px 20px;
  background: var(--white); border: 1px solid var(--border); border-left: 4px solid var(--primary);
  border-radius: 16px; box-shadow: 0 2px 16px rgba(11, 19, 50, 0.05);
}
/* The source logos are 400x160 and the img carries 240x96, so width/height:auto is
   needed for the max-* pair to size the box instead of the attributes. */
.review__logo { width: auto; height: auto; max-width: 120px; max-height: 40px; object-fit: contain; object-position: left center; margin: 0 0 16px; }
.review blockquote { margin: 0 0 20px; font-size: 13px; color: var(--gray1); line-height: 1.7; }
.review figcaption { margin-top: auto; font-size: 13px; font-weight: 700; color: var(--primary); }

/* Integrations */
.integrations { background: var(--navy2); padding: 56px 0; position: relative; overflow: hidden; }
/* 32px, matched to the .integrations__ctas margin below, because design asked on
   2026-09-04 for the phone logo grid to sit centred between the paragraph above
   it and the button below. This flex container has exactly two children, so the
   gap separates copy from the logo column and nothing else. Measured before:
   40px above against 24px below. Row direction from 768px up, where both values
   are overridden. */
.integrations__inner { max-width: var(--shell); margin: 0 auto; padding: 0 20px; display: flex; flex-direction: column; gap: 32px; }
.integrations h2 { margin: 0 0 16px; font-size: 26px; font-weight: 900; color: var(--white); line-height: 1.15; }
.integrations__lead { margin: 0 0 28px; font-size: 14px; color: rgba(255, 255, 255, 0.55); line-height: 1.7; }
.intlist { margin: 0; display: flex; flex-direction: column; }
/* Grid rather than flex: the marker sits in its own column spanning both rows so
   the name stays above the description. */
.intlist > div {
  display: grid; grid-template-columns: 22px 1fr; gap: 0 14px;
  padding: 14px 0; border-bottom: 1px solid rgba(255, 255, 255, 0.08);
}
.intlist > div:last-child { border-bottom: none; }
.intlist > div::before {
  content: ''; grid-column: 1; grid-row: 1 / span 2; align-self: start;
  width: 22px; height: 22px; margin-top: 1px; border-radius: 999px;
  border: 2px solid var(--primary);
  background: radial-gradient(circle, var(--primary) 0 4px, transparent 4px);
}
.intlist dt, .intlist dd { grid-column: 2; }
.intlist dt { font-size: 14px; font-weight: 700; color: var(--white); }
.intlist dd { margin: 3px 0 0; font-size: 13px; color: rgba(255, 255, 255, 0.5); line-height: 1.6; }
.integrations__right { display: flex; flex-direction: column; align-items: flex-start; flex: 1; }
/* Staggered rather than a straight grid, to match the live merchOne page. Round 1
   read the reference as offset ROWS; design corrected that on 2026-09-03, and the
   live rule is per COLUMN: .different_blocks .logo_wrap:nth-child(2) and (4) take
   margin-top: 20px while :nth-child(5n+6), (5n+8) and (5n+10) take -20px, which
   nets out to odd columns sitting 20px above even ones. Same 20px here, absolute
   like the original rather than a fraction of the tile. The shift is a transform,
   which does not take part in layout, so the container reserves the distance as
   padding-bottom. */
.tiles {
  --tile-size: 88px;
  --tile-gap: 10px;
  --tile-offset: 20px;
  display: grid;
  grid-template-columns: repeat(3, var(--tile-size));
  gap: var(--tile-gap);
  margin: 0 auto;
  padding-bottom: var(--tile-offset);
}
/* Below 375px the 88px tile plus the stagger offset is wider than the viewport
   and .integrations{overflow:hidden} clips the shifted tile: measured 33px lost
   at 320px and 9px at 344px. 72px keeps three across plus the offset inside a
   320px screen (3x72 + 2x10 + 41 = 277 against 280 available). */
@media (max-width: 374px) {
  .tiles { --tile-size: 72px; }
}

/* three across here, so column 2 is items 2, 5, 8, 11 and 14 */
.tile:nth-child(3n + 2) { transform: translateY(var(--tile-offset)); }
.tile {
  /* from --tile-size on .tiles, so the box, the grid columns and the stagger
     offset all move together. --tile is the colour token, unrelated. */
  width: var(--tile-size); height: var(--tile-size); border-radius: 18px; background: var(--tile);
  border: 1px solid rgba(255, 255, 255, 0.07);
  display: flex; align-items: center; justify-content: center; color: var(--white);
}
/* All 15 show at every width. The live merchOne page shows the same 15 in the
   same order, and design asked for the same quantity, so the old rule that hid
   the last 6 below 1100px is gone. Three across here, five rows. */
/* All 15 logos are now the live site's own assets, so the per-brand type and
   colour rules that styled the hand-drawn versions are gone. The live sheet caps
   its logos at 45px tall inside a ~145px tile; these caps are that proportion at
   our 88px tile. */
.tile img { width: auto; height: auto; max-width: 60px; max-height: 38px; object-fit: contain; }
/* webflow and walmart: the live asset draws its own rounded colour plate, which
   is where the blue tile comes from, so it fills the tile and the tile's own
   background and border would otherwise show as a ring around it. */
.tile--plate { background: none; border-color: transparent; }
.tile--plate img { max-width: none; max-height: none; width: 100%; height: 100%; }
.integrations__ctas { display: flex; gap: 12px; width: 100%; margin-top: 32px; }
.integrations__ctas .btn { flex: 1; padding: 13px 0; font-size: 14px; }

/* Sustainability */
.sustain { background: var(--white); padding: 56px 20px; }
.sustain__inner { max-width: 1100px; margin: 0 auto; display: flex; flex-direction: column; gap: 40px; align-items: center; }
.sustain h2 { margin: 0 0 16px; font-size: 28px; font-weight: 900; line-height: 1.1; }
.sustain .muted { margin-bottom: 28px; }
.sustain__list { margin: 0; padding: 0; list-style: none; display: flex; flex-direction: column; gap: 14px; }
.sustain__list li { display: flex; gap: 12px; align-items: flex-start; }
.sustain__list p { font-size: 14px; color: var(--gray1); line-height: 1.6; }
.tick {
  flex-shrink: 0; width: 20px; height: 20px; margin-top: 1px; border-radius: 999px; background: var(--green);
  color: var(--white); font-size: 11px; display: flex; align-items: center; justify-content: center;
}
.sustain__cert { width: 160px; }
.sustain__cert img { width: 100%; height: auto; }

/* CTA */
.cta { background-image: var(--grad); padding: 48px 20px; text-align: center; }
/* Positive tracking, overriding the global -0.5px above: at weight 900 the two f
   crossbars in "Off" fuse into one bar. Flat px to match the heading-tracking
   decision at the top of this file. NGE-11640. */
.cta h2 { margin: 0 0 12px; font-size: 26px; font-weight: 900; color: var(--white); line-height: 1.15; letter-spacing: 1px; }
.cta p { margin: 0 0 28px; font-size: 14px; color: rgba(255, 255, 255, 0.8); }
.cta__buttons { display: flex; flex-direction: column; gap: 12px; align-items: center; }
.cta__buttons .btn { width: 100%; max-width: 320px; padding: 14px 36px; }

/* Footer */
.footer { background: var(--white); border-top: 1px solid var(--border); padding: 40px 20px 0; }
.footer__inner { max-width: var(--shell); margin: 0 auto; display: flex; flex-direction: column; gap: 32px; margin-bottom: 40px; }
.footer__brand img { height: 28px; width: auto; margin-bottom: 16px; }
.footer__social { display: flex; gap: 12px; }
.footer__social a {
  width: 36px; height: 36px; border-radius: 8px; border: 1px solid var(--border);
  display: flex; align-items: center; justify-content: center; color: var(--navy);
}
.footer__social a:hover { border-color: var(--primary); color: var(--primary); }
.footer__cols { display: grid; grid-template-columns: 1fr 1fr; gap: 24px 16px; }
.footer__cols h3 { font-size: 13px; font-weight: 700; margin-bottom: 12px; letter-spacing: 0; }
.footer__cols a { display: block; margin-bottom: 10px; font-size: 13px; color: var(--gray1); text-decoration: none; }
.footer__cols a:hover { color: var(--primary); }
.footer__legal { border-top: 1px solid var(--border); padding: 20px 0; font-size: 13px; color: var(--gray2); }
.footer__legal span { display: block; max-width: var(--shell); margin: 0 auto; }

/* ── Desktop (the demo's isMobile === false branch) ───────────────────────── */
@media (min-width: 768px) {
  .announce { padding: 14px 32px; }
  .announce__head { font-size: 15px; }
  .announce__sub { display: block; }
  .announce__flags { display: block; position: absolute; right: 32px; top: 50%; transform: translateY(-50%); }

  .nav__inner { padding: 0 32px; }

  .hero { min-height: 88vh; padding: 80px 24px; }
  .hero h1 { font-size: 68px; max-width: 800px; }
  .hero__lead { font-size: 16px; max-width: 560px; }
  .hero__ctas { flex-direction: row; width: auto; max-width: none; }
  .pill { margin-bottom: 32px; }

  .trust { padding: 48px 64px 40px; }
  .trust__grid { grid-template-columns: repeat(4, 1fr); gap: 0; }
  .trust__num { font-size: 38px; }
  .trust__unit { font-size: 16px; }

  .why { display: flex; align-items: stretch; min-height: 560px; }
  .why__copy {
    flex: 1; padding: 72px 56px; display: flex; flex-direction: column; justify-content: center;
    background: linear-gradient(to right, var(--primary-light) 0%, var(--white) 100%);
  }
  .why h2 { font-size: 36px; margin-bottom: 16px; }
  .why .muted { margin-bottom: 36px; }
  .why__stats { gap: 24px 28px; }
  .why__num { font-size: 38px; }
  .why__unit { font-size: 20px; }
  .why__label { font-size: 12px; margin-top: 5px; text-wrap: balance; }
  /* The column held the map and the pin key; since 2026-09-15 the key is gone and
     the image is the only child, so the flex box now just centres it. Kept rather
     than unwound because the image stays width-driven, which is what holds the
     54% basis, and height-100-with-contain would change what renders here.
     `0 1 54%`, not `0 0 54%`, and that is what stops the map being clipped on the
     right between 768 and 878px. .why__copy is flex: 1 but cannot go below its
     min-content, measured at a constant 404.2px, while a rigid 54% basis gives
     nothing back, so the row sums past the viewport and `overflow: hidden` on
     .why cuts the map off: 51.0px lost at 768, 20.6 at 834, 8.6 at 860. The two
     fit unaided only once 404.2 <= 0.46w, which is 879px. Letting the map shrink
     changes nothing at or above that, because there is no shortage to shrink
     for. min-width: 0 because a flex item's default min-width: auto would hold
     it at the image's own width and make the shrink factor inert. */
  .why__map { flex: 0 1 54%; min-width: 0; padding: 0; display: flex; flex-direction: column; align-items: center; justify-content: center; }
  .why__map img { border-radius: 0; width: 100%; height: auto; }

  .pod { padding: 96px 32px; }
  .pod__head { font-size: 48px; }
  .pod__cols { grid-template-columns: 1fr 1fr 1fr; }

  /* 72 rather than the 96 the other sections use: the one-screen ask on
     2026-09-03 is measured against the viewport a real browser leaves after
     its own chrome, not the full screen height. */
  .catalog { padding: 72px 32px; }
  .catalog .wrap { padding: 0; }
  .catalog h2 { font-size: 40px; }
  .catalog__lead { font-size: 15px; }
  .tabs { margin: 40px 0 24px; padding: 0; overflow: visible; flex-wrap: wrap; justify-content: center; }
  .tab { padding: 9px 22px; font-size: 13px; }
  /* 10px row gap, not 16: design asked on 2026-09-04 for the distance from the
     bottom of the product image to the buttons to equal the distance between two
     product cards, and .products uses gap: 10px. */
  .catalog__card { grid-template-columns: 320px 1fr; grid-template-rows: 1fr auto; gap: 10px 32px; padding: 32px; }
  .panel h3 { font-size: 18px; }
  .panel__list { order: 0; grid-column: 1; grid-row: 1 / span 2; }
  /* Round 1 kept one 4:3 crop on every tier (2026-08-28). Round 2 (2026-09-03)
     asks for the whole section on one 100%-zoom screen with the CTA pair level
     with the bottom of the product boxes, and at this column width 4:3 rendered
     the preview 525px tall, which by itself pushed the card past the fold. The
     cap crops via object-fit: cover rather than distorting, and once the right
     column is no longer the tallest, the grid's second row lands level with the
     list. Mobile and tablet keep the 4:3 crop. */
  /* Round 3 (2026-09-04): design asked for the image to run all the way down to
     the buttons rather than stopping short, so it fills the grid row instead of
     carrying a 260px cap. The row is 1fr and the left column spans both rows, so
     the row height is whatever the product list leaves after the button row and
     the 10px gap: the image bottom therefore lands exactly one card-gap above
     the buttons, and the buttons stay level with the bottom of the list.
     The image is taken out of flow to get there. A plain height: 100% does not
     work: the row is 1fr, so its height is not definite while the row is being
     sized, the percentage falls back to the image's intrinsic ratio, and the right
     column then becomes the tallest and drives the grid instead of the product
     list, which pushes the buttons well below the bottom of the list and loses
     the alignment round 2 asked for. Absolutely
     positioned, the image contributes nothing to row sizing, .previews is
     stretched by the 1fr row, and object-fit: cover crops instead of
     distorting. */
  .previews { grid-column: 2; grid-row: 1; min-height: 0; position: relative; }
  .previews img.is-selected { position: absolute; inset: 0; width: 100%; height: 100%; max-height: none; aspect-ratio: auto; object-fit: cover; }
  .catalog__ctas { grid-column: 2; grid-row: 2; flex-direction: row; gap: 12px; }
  .catalog__ctas .btn { flex: 1; }

  .reviews { padding: 96px 0; }
  .reviews__head { margin-bottom: 56px; }
  .reviews h2 { font-size: 40px; }
  .reviews .muted { font-size: 15px; }
  .review { width: 340px; }

  .integrations { padding: 96px 0; }
  .integrations__inner { flex-direction: row; align-items: stretch; padding: 0 32px; gap: 80px; }
  .integrations__copy { flex: 0 0 440px; }
  .integrations h2 { font-size: 36px; }
  /* padding-bottom, not a smaller height: margin-top:auto anchors this box to the
     column bottom, so the padding lifts the buttons by exactly the 14px of bottom
     padding the last list item carries. That puts the button edge level with the
     last line of TEXT rather than with the item box. Requested by design. */
  .integrations__ctas { margin-top: auto; padding-top: 14px; padding-bottom: 14px; }

  .sustain { padding: 96px 32px; }
  /* space-between rather than the default flex-start: the copy is a fixed 500px
     and the mark 200px inside a 1100px container, so without it the mark sits
     336px short of the section's right edge with dead space beside it. */
  .sustain__inner { flex-direction: row; gap: 64px; justify-content: space-between; }
  /* The copy shrinks and the mark does not, so the mark's stated size is the size
     it renders at. The other way round, the default flex-shrink on .sustain__cert
     took the 200px mark down to 140px at a 768 viewport, narrower than the 160 it
     gets on a phone. */
  .sustain__copy { flex: 0 1 500px; }
  .sustain__cert { flex-shrink: 0; }
  .sustain h2 { font-size: 40px; }
  .sustain__cert { width: 200px; }

  .cta { padding: 72px 32px; }
  .cta h2 { font-size: 40px; }
  .cta p { font-size: 15px; }
  .cta__buttons { flex-direction: row; justify-content: center; }
  .cta__buttons .btn { width: auto; max-width: none; }

  .footer { padding: 56px 32px 0; }
  .footer__brand img { height: 32px; margin-bottom: 20px; }
  .footer__social { gap: 16px; }
  .footer__cols { grid-template-columns: repeat(4, 1fr); gap: 24px; }
  .footer__cols h3 { font-size: 14px; margin-bottom: 20px; }
  .footer__cols a { font-size: 14px; margin-bottom: 14px; }
}

@media (min-width: 1024px) {
  /* Every unit fits one line from here up, so the two-line reservation above
     would only add empty space under all four. */
  .trust__unit { min-height: 0; }

  /* The map's right gutter, matching the copy column's own 56px, so the section
     carries the same inset on both sides instead of the artwork ending at the
     viewport edge. It starts at 1024 rather than at the 768 two-column breakpoint
     for two measured reasons: at 768 the copy column makes the box 757.84px tall,
     so the image is height-constrained and this padding yields a 13.42px gutter
     rather than 56, and the artwork there is already down to 358.72px wide from
     727px at 767. */
  .why__map { padding-right: 56px; }

  /* The footer's brand column beside four link columns needs ~880px; below that
     the brand stays stacked above a four-across link grid. */
  .footer__inner { flex-direction: row; gap: 40px; margin-bottom: 56px; }
  .footer__brand { flex: 0 0 240px; }
  .footer__cols { display: flex; flex: 1; gap: 40px; }
  .footer__cols > div { flex: 1; min-width: 120px; }
}

/* The nav needs more room than the rest of the layout, and it needs it measured
   with the search field OPEN, which is the state the earlier 1024px breakpoint
   missed. The links are nowrap and .nav__links is allowed to shrink, so an
   expanded field does not push the document wider, it overlaps Help and Blog:
   measured 78px of overlap at 1024, 42px at 1060 and 2px at 1100, while document
   width equalled the viewport at all three. A scrollWidth check therefore cannot
   see this. 1120 is the first width where the open field clears the links.
   hover: hover is the second condition and it is not cosmetic: the submenus open
   on :hover, and a large tablet in landscape is wide enough for this breakpoint
   while having no hover at all, which would leave its submenus unreachable. Such
   a device keeps the burger and the drawer's <details>, which open on tap. A
   desktop with a touchscreen still reports hover: hover, so it is unaffected. */
@media (min-width: 1120px) and (hover: hover) {
  .nav__inner { gap: 32px; }
  .nav__links { display: flex; min-width: 0; }
  .nav__login { display: inline; }
  .nav__search { display: flex; }
  .nav__actions { gap: 12px; }
  .nav__burger { display: none; }
  .drawer { display: none; }
}

/* The five-across logo grid is 496px wide and cannot shrink, so beside 440px of
   copy it needs ~1100px. Below that the section keeps the mobile 3x3 grid; the
   full 5x3 arrives at 1100. (Measured: 5 columns at 834px pushed the document to
   912px of scrollWidth.) */
@media (min-width: 768px) and (max-width: 1099px) {
  .integrations__inner { gap: 40px; }
  .integrations__copy { flex: 1 1 340px; }
  .integrations__right { align-items: center; }
}

@media (min-width: 1100px) {
  /* Design asked on 2026-09-04 to scale the logo block up towards the Read API
     Docs button, keeping the tiles square. The columns become 1fr of the full
     right-hand column instead of a fixed 88px, which is the largest uniform
     scale that does not overflow: measured 112px per tile against 88px, so the
     block goes from 496x312 to 616x384. The tiles stay square via aspect-ratio,
     and the logo caps below are the same 60/38 proportion at the new size.
     Note this does NOT close the gap to the buttons completely, because the
     button row is anchored to the bottom of the taller left column: five square
     tiles across would have to be 145px to reach it, which needs 781px of width
     against the 616px the column has. Residual gap is reported in the PR. */
  .tiles { --tile-gap: 14px; grid-template-columns: repeat(5, 1fr); width: 100%; margin: 0; }
  .tile { width: auto; height: auto; aspect-ratio: 1; }
  /* :not(.tile--plate) is load-bearing. A plain .tile img here has the same
     specificity as the .tile--plate img exception above and sits later in the
     sheet, so it re-applied the cap to the two logos that draw their own colour
     plate and must fill the tile: measured 48px tall webflow and walmart plates
     inside 112px tiles. */
  .tile:not(.tile--plate) img { max-width: 76px; max-height: 48px; }
  /* five per row here, so the offset row is items 6 to 10 rather than 4 to 6 and 10 to 12 */
  /* five across, so the even columns are items 2, 4, 7, 9, 12 and 14 */
  .tile:nth-child(3n + 2) { transform: none; }
  .tile:nth-child(5n + 2),
  .tile:nth-child(5n + 4) { transform: translateY(var(--tile-offset)); }
}
