/* =====================================================
   GO-TWO, Loop bundle page styling (Sleep Support)

   INSTALL LOCATION (confirmed 2026-07-08): Loop dashboard,
   this bundle's Custom CSS field. Served live from
   branding.loopwork.co/...<bundleId>.custom.css, NOT a theme
   asset, so it cannot be pushed by CLI/API. Update = re-paste
   this whole file into that field. It is PER-BUNDLE, so Debloat
   and Duo each get their own paste.

   2026-07-31 HOMEPAGE PASS. Rebuilt against DESIGN.md and the
   live bespoke homepage (sections/gotwo-home.liquid) so this
   page reads as the same design system. What changed and why:

     - Canvas. .loop-background was measured live at #F4EDE7,
       Horizon scheme-1's plum-era cream, while the homepage
       canvas is paper #FCFBF6. That one value was the largest
       single reason this page did not look like the homepage.
     - Neutrals. WM's grey/opacity neutrals replaced by the
       brand's paper / ink / ink-soft / sand roles.
     - Periwinkle tokens moved to DESIGN.md's canonical Sleep
       values. The old ones (#8990c4 / #454b80 / #eeeff7) were
       eyeballed off packaging in a pre-DESIGN.md session.
     - Type. Homepage scale, weights, tracking and measure caps.
       Long body lines at the 800px wrap were running past 100ch
       against the homepage's 56ch.
     - Ornament removed. The centered 3px underline bar under
       every heading is a generic supplement-page tell; the
       homepage separates with whitespace and hairlines, never
       with bars or boxes.
     - Loop chrome. The native CTA, quantity stepper, progress
       bar, discount badge and bottom sheet were pure black
       (#000), Arial, and carried grey drop shadows. All three
       violate DESIGN.md guardrails. Primary action is now the
       brand yellow fill with ink text, per the homepage button.

   BRAND: periwinkle is the Sleep product accent. Yellow #F8E678
   is a FILL ONLY and always carries ink #1A1A1A, never white,
   and is never a text, link, icon or hairline color.
   ===================================================== */

@import url('https://fonts.googleapis.com/css2?family=Cormorant+Garamond:ital,wght@0,500;0,600;1,500;1,600&family=Montserrat:wght@300;400;500;600;700&display=swap');

:root {
  /* Neutral roles, straight from DESIGN.md */
  --gt-paper: #FCFBF6;       /* canvas. never pure white at canvas level */
  --gt-white: #FFFFFF;       /* raised surfaces only */
  --gt-ink: #1A1A1A;         /* every word. never #000 */
  --gt-ink-soft: #4A4A4A;    /* secondary copy, captions, list body */
  --gt-hairline: #ECE7DC;    /* 1px rules only. never text, never fill */

  /* Action. FILL ONLY, always with ink text */
  --gt-yellow: #F8E678;
  --gt-yellow-deep: #EBD24E;

  /* Sleep product accent.
     2026-07-31: these are now the LIVE HOMEPAGE's declared values, read
     out of sections/gotwo-home.liquid, not DESIGN.md's. The two disagree
     and the homepage has to win here, because matching the homepage is
     the whole point of this page. DESIGN.md says #5B67B0 / #414C90; the
     homepage declares --peri:#7C8AD1 and --peri-ink:#4A5596. Only the
     tint agrees. Worth reconciling in DESIGN.md; until then the shipped
     site is the source of truth for what "on brand" looks like. */
  --gt-peri: #7C8AD1;
  --gt-peri-ink: #4A5596;
  --gt-peri-tint: #E9EBF8;
  --gt-peri-bright: #A9B6F2;
  --gt-peri-soft: #C3CCF4;

  /* Brand fonts. Montserrat is the structural workhorse;
     Cormorant Garamond carries mantras only, one per section. */
  --gt-sans: 'Montserrat', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
  --gt-serif: 'Cormorant Garamond', Georgia, 'Times New Roman', serif;

  /* 8px spacing scale. No one-off values. */
  --gt-sp-1: 4px;  --gt-sp-2: 8px;  --gt-sp-3: 12px; --gt-sp-4: 16px;
  --gt-sp-5: 24px; --gt-sp-6: 32px; --gt-sp-7: 48px; --gt-sp-8: 64px;
  --gt-sp-9: 96px;

  /* Section rhythm. Verbatim from the homepage's .section
     padding-block. The previous stepped-down curve was the reason this
     page felt tighter and busier than the homepage at every width;
     the homepage does NOT reduce this inside a narrower shell, it is
     clamp-driven and changes at no breakpoint. */
  --gt-section: clamp(64px, 8vw, 96px);

  /* Radii. Homepage: pill 999, card 18, image 14, input 12.
     Never mix radii inside one component. */
  --gt-r-pill: 999px;
  --gt-r-card: 18px;
  --gt-r-img: 14px;

  /* Media measure. Sits near the 56ch body measure so a photo's
     left AND right edges agree with the type block beside it
     inside the 800px column. Deliberately a px value, not ch,
     because ch on an image resolves against a font-size the
     image never uses. */
  --gt-media: 640px;

  --gt-ease: cubic-bezier(0.22, 1, 0.36, 1);
}

/* =====================================================
   CANVAS
   =====================================================
   Loop paints .loop-background over the full page height
   (measured 4693px live at 430px wide). It inherited Horizon's
   scheme-1 cream #F4EDE7, which is the plum-era surface, not
   the homepage's paper. body is already #FCFBF6; this makes the
   painted layer agree with it. */
.loop-background {
  background: var(--gt-paper) !important;
}

/* =====================================================
   BRAND FONTS
   =====================================================
   The page shipped with no font-family set, so it rendered in
   Loop's default face, which was the single biggest thing making
   it read as a generic supplement page. Montserrat structural,
   Cormorant editorial. Loaded via the @import above; if Loop's
   Custom CSS field ever strips the import, the stacks degrade to
   system sans/serif rather than breaking. */
.gt-bundle-wrap,
.gt-bundle-wrap *,
.loop-bundle-header-wrapper,
.loop-bundle-header-wrapper *,
.loop-bundle-new-description,
.loop-bundle-new-description *,
.loop-bundle-select-product-title-new-container,
.loop-bundle-select-product-title-new-container * {
  font-family: var(--gt-sans);
}

.gt-bundle-wrap {
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
  color: var(--gt-ink);
}

/* Eyebrow "Sleep Support": the homepage eyebrow recipe. */
.loop-bundle-title {
  font-family: var(--gt-sans) !important;
  font-size: 0.75rem !important;
  font-weight: 600 !important;
  letter-spacing: 0.2em !important;
  text-transform: uppercase !important;
  color: var(--gt-peri-ink) !important;
  opacity: 1;
}

/* The homepage eyebrow's leading rule. Delivered as an
   inline-block ::before rather than by making the element an
   inline-flex row, because .loop-bundle-title has to stay
   display:block width:100% to beat Loop's flex left-pin (see the
   layout note further down). An inline-block pseudo sits on the
   text baseline and centers with it, so both survive. */
.loop-bundle-title::before {
  content: "";
  display: inline-block;
  width: 28px;
  height: 1px;
  background: currentColor;
  opacity: 0.6;
  vertical-align: middle;
  margin-right: var(--gt-sp-3);
}

/* Hero headline. Cormorant, the first-impression serif. */
.loop-bundle-new-description h2 {
  font-family: var(--gt-serif) !important;
  font-weight: 500 !important;
  font-size: clamp(2.4rem, 9vw, 3rem) !important;
  line-height: 1.06 !important;
  color: var(--gt-ink);
}

/* Hero subtext. Quiet Montserrat at the homepage lede measure. */
.loop-bundle-new-description p {
  font-family: var(--gt-sans) !important;
  font-size: 1.0625rem !important;
  font-weight: 400 !important;
  line-height: 1.65 !important;
  color: var(--gt-ink-soft);
  max-width: 36ch;
  margin-left: auto !important;
  margin-right: auto !important;
}

/* =====================================================
   DELIBERATE LINE WRAPPING
   =====================================================
   Fixes awkward wraps (a lonely "off...", "made for women who
   need / real rest") without brittle hand-placed <br>s that only
   break correctly at one width. balance evens short heading
   lines; pretty prevents a single-word last line on running
   text. Pure CSS, so it also reaches the Loop-native hero and
   subtext, which we cannot inject markup into, and it stays
   responsive at every viewport. Progressive: unsupported
   browsers simply wrap normally. For a break that MUST hold,
   glue words with &nbsp; or mark a point with <wbr>. */
   2026-08-01 CORRECTION. balance was over-applied to RUNNING TEXT,
   not just headings, and on a phone that is what made the hero read
   as though it sat in a narrower container than the rest of the
   page. balance equalises line lengths instead of filling the
   measure, so "The nighttime stick made for / women who need real
   rest." broke after "for" while ~140px of the line was still free.
   Meanwhile the footer h2 ("Cool, crisp Watermelon Lime over ice.")
   filled the full 387px precisely because it was never in this
   list. Same container, two different wrap behaviours, which reads
   as a width bug and is not one. Measured at 430px: every one of
   these boxes is 21.5 -> 408.5.

   RULE: balance is for SHORT DISPLAY HEADINGS only, two or three
   lines. Running text takes pretty, which fills each line and only
   guards the last one against an orphan. */
.loop-bundle-title,
.loop-bundle-new-description h2,
.gt-hero-title,
.gt-compare-good,
.gt-mantra,
.gt-bundle-note,
.gt-final-line,
.gt-section-title {
  text-wrap: balance;
}

.loop-bundle-new-description p,
.loop-bundle-select-product-title-new,
.gt-subtext,
.gt-compare-intro,
.gt-compare-sub,
.gt-body-text,
.gt-list li,
.gt-disclaimer {
  text-wrap: pretty;
}

/* The product-card title was the one centred string on a page that
   reads left everywhere else. It is not the title's own alignment:
   its wrapper .loop-product-title sits in a `flex flex-col
   items-center`, which shrink-wraps it to its text and centres the
   BOX (measured 288px at x=70.9 inside a 398px card). text-align
   alone therefore does nothing. align-self overrides the parent's
   items-center for this one child, so the stepper below it keeps
   its own alignment. Measured after: 38 -> 392, left. */
html body .loop-product-title {
  align-self: stretch !important;
  width: 100% !important;
  text-align: left !important;
}

html body .loop-bundle-product-card-title {
  text-align: left !important;
}

/* =====================================================
   SHOPIFY PREVIEW-BAR NEUTRALIZER (CRITICAL)
   =====================================================
   While the store is password-protected or previewed by staff,
   Shopify injects its preview bar (#PBarNextFrameWrapper >
   iframe#PBarNextFrame) into this app-proxy page. The wrapper is
   a correct 68px bottom strip, but the IFRAME balloons to the
   full viewport (e.g. 412x915) with pointer-events:auto, a
   transparent sheet over the whole page. WebKit clips it to 68px
   so it is invisible on iPhone; Blink lets it cover everything,
   so it eats every scroll gesture and tap, including the sticky
   View cart CTA. Confirmed live 2026-07-09 at 412x915 in Blink:
   elementFromPoint(CTA) returned the iframe, not the button.
   Making the bar click-through frees scroll and tap. The bar's
   own controls are staff-only and unused from this page, so
   losing them is fine. Not customer-facing. Carries to
   Debloat/Duo unchanged. */
#PBarNextFrame,
#PBarNextFrameWrapper {
  pointer-events: none !important;
}

/* =====================================================
   LOOP CHROME, BRANDED
   =====================================================
   Everything below was measured on the live page 2026-07-31.
   Loop's defaults were pure #000 fills, Arial, and grey drop
   shadows, all three of which DESIGN.md forbids outright. */

/* Primary action. The homepage button: yellow fill, ink label,
   pill, Montserrat 600 at 0.9375rem / 0.04em, no shadow.
   Measured before: #000 fill, #fff label, Arial 16px/500,
   48px radius, box-shadow grey 0 4px 5px. White on yellow is
   1.3:1 and fails, which is why the label is ink. */
.loop-btn-add-to-cart,
.loop-btn-add-to-cart.loop-btn {
  background: var(--gt-yellow) !important;
  background-color: var(--gt-yellow) !important;
  color: var(--gt-ink) !important;
  border: 0 !important;
  border-radius: var(--gt-r-pill) !important;
  box-shadow: none !important;
  font-family: var(--gt-sans) !important;
  font-size: 0.9375rem !important;
  font-weight: 600 !important;
  letter-spacing: 0.04em !important;
  padding: 0.95em 1.7em !important;
  transition: background 280ms var(--gt-ease), transform 280ms var(--gt-ease) !important;
}

.loop-btn-add-to-cart:hover,
.loop-btn-add-to-cart.loop-btn:hover {
  background: var(--gt-yellow-deep) !important;
  background-color: var(--gt-yellow-deep) !important;
  color: var(--gt-ink) !important;
  transform: translateY(-1px);
}

/* Every descendant of the CTA inherits the ink label. Loop nests
   the price and the compare-at price in their own spans and sets
   colors on them individually. */
.loop-btn-add-to-cart *,
.loop-bundle-cart-btn-text-container,
.loop-bundle-cart-btn-text-container *,
.loop-bundle-add-to-cart-text,
.loop-bundle-view-cart-btn,
.loop-bundle-view-cart-btn * {
  color: var(--gt-ink) !important;
  font-family: var(--gt-sans) !important;
}

/* Struck compare-at prices. Class names read off the live DOM
   2026-07-31, not guessed. Loop renders three of them and they
   disagreed with each other: the one in the cart button, one on
   the selected-variant row, and one in the subscribe option,
   which sat at a Tailwind grey (oklch 0.707 0.022 261.325) that
   is in no brand palette. All three go to ink-soft so the live
   price stays the loudest number on the row. */
.loop-bundle-sheet-total-text.strike-through,
.loop-selected-variant-price-actual.strike-through,
.loop-bundle-widget-selling-plan-actual-price,
.loop-display-saved-total,
.loop-bundle-saved-label {
  color: var(--gt-ink-soft) !important;
  opacity: 0.85;
}

/* The live subscribe price rendered pure #000, which DESIGN.md
   rules out. */
.loop-bundle-widget-selling-plan-discount-price,
.loop-selected-variant-price-discount {
  color: var(--gt-ink) !important;
}

/* Sticky bottom sheet. Measured with a 0 -4px 12px black shadow;
   the homepage has zero box-shadow anywhere and gets depth from
   a hairline plus contrast instead. */
.loop-bottom-sheet {
  background: var(--gt-paper) !important;
  border-top: 1px solid var(--gt-hairline) !important;
  box-shadow: none !important;
  border-radius: var(--gt-r-card) var(--gt-r-card) 0 0 !important;
}

.loop-bottom-sheet-body-info-text,
.loop-selected-bundle-title,
.loop-bundle-total-label {
  font-family: var(--gt-sans) !important;
  color: var(--gt-ink) !important;
}

/* Quantity stepper. Was #000 on Arial with mismatched corners
   (0% 20px 20px 0%). Ink fill, one radius per side, brand face. */
.loop-btn-input-decrease,
.loop-btn-input-increase,
.loop-btn-input-decrease-small,
.loop-btn-input-increase-small {
  background: var(--gt-ink) !important;
  background-color: var(--gt-ink) !important;
  color: var(--gt-paper) !important;
  border-color: var(--gt-ink) !important;
  font-family: var(--gt-sans) !important;
  font-weight: 600 !important;
  box-shadow: none !important;
  transition: background 280ms var(--gt-ease) !important;
}

.loop-btn-input-decrease:hover,
.loop-btn-input-increase:hover,
.loop-btn-input-decrease-small:hover,
.loop-btn-input-increase-small:hover {
  background: var(--gt-peri-ink) !important;
  background-color: var(--gt-peri-ink) !important;
}

.loop-btn-input-decrease,
.loop-btn-input-decrease-small {
  border-radius: var(--gt-r-pill) 0 0 var(--gt-r-pill) !important;
}

.loop-btn-input-increase,
.loop-btn-input-increase-small {
  border-radius: 0 var(--gt-r-pill) var(--gt-r-pill) 0 !important;
}

.loop-form-input,
.loop-form-input-small,
.loop-selected-variant-actions-input {
  font-family: var(--gt-sans) !important;
  color: var(--gt-ink) !important;
  background: var(--gt-white) !important;
  border-color: var(--gt-hairline) !important;
}

/* Progress bar. Was black on #d9d9d9. Accent on sand. */
.loop-progress-bar {
  background: var(--gt-peri-ink) !important;
  background-color: var(--gt-peri-ink) !important;
}

.loop-new-progress-bar-container,
.loop-bundle-progress-bar-new-container .loop-new-progress-bar-container {
  background: var(--gt-hairline) !important;
  background-color: var(--gt-hairline) !important;
}

.loop-progress-bar-label {
  font-family: var(--gt-sans) !important;
  color: var(--gt-ink-soft) !important;
}

/* Discount badge. A saving is good news, so it takes the brand
   yellow fill with ink text, which is the one legal way to use
   it. Was a black pill with white text. */
.loop-bundle-widget-discount-badge-container {
  background: var(--gt-yellow) !important;
  background-color: var(--gt-yellow) !important;
  color: var(--gt-ink) !important;
  border: 0 !important;
  border-radius: var(--gt-r-pill) !important;
  font-family: var(--gt-sans) !important;
  font-size: 0.72rem !important;
  font-weight: 600 !important;
  letter-spacing: 0.14em !important;
  text-transform: uppercase !important;
  padding: 0.5em 0.9em !important;
}

.loop-bundle-widget-discount-badge-container * {
  color: var(--gt-ink) !important;
}

/* Delivery-frequency button. Was black, Arial, 8px radius. */
.loop-bundle-widget-frequency-interval-button {
  background: var(--gt-ink) !important;
  background-color: var(--gt-ink) !important;
  color: var(--gt-paper) !important;
  border: 0 !important;
  border-radius: var(--gt-r-pill) !important;
  font-family: var(--gt-sans) !important;
  font-weight: 600 !important;
  letter-spacing: 0.04em !important;
  box-shadow: none !important;
}

/* Purchase-option rows (one time vs subscribe). Raised white on
   the paper canvas, hairline border, card radius, no shadow. */
.loop-bundle-widget-selling-plan-container {
  background: var(--gt-white) !important;
  border: 1px solid var(--gt-hairline) !important;
  border-radius: var(--gt-r-card) !important;
  box-shadow: none !important;
}

.loop-bundle-widget-selling-plan-text,
.loop-bundle-widget-selling-plan-price,
.loop-bundle-widget-selling-plan-description,
.loop-bundle-widget-frequency-button-label-fixed,
.loop-bundle-product-card-title,
.loop-bundle-product-card-description,
.loop-selected-variant-title {
  font-family: var(--gt-sans) !important;
  color: var(--gt-ink) !important;
}

.loop-bundle-widget-selling-plan-description,
.loop-bundle-product-card-description {
  color: var(--gt-ink-soft) !important;
}

/* Selected radio takes the accent, not black. */
.loop-bundle-widget-radio-btn {
  border-color: var(--gt-hairline) !important;
}

.loop-bundle-widget-selling-plan-subscription .loop-bundle-widget-radio-btn,
.loop-btn-selected .loop-bundle-widget-radio-btn {
  background: var(--gt-peri-ink) !important;
  border-color: var(--gt-peri-ink) !important;
}

/* Loop's generic buttons must not fall back to Arial. */
.loop-btn,
.loop-h1, .loop-h2,
.loop-text-p1, .loop-text-p2, .loop-text-p3,
.loop-text-p4, .loop-text-p5, .loop-text-p6 {
  font-family: var(--gt-sans) !important;
}

/* =====================================================
   LOOP STRUCTURE FIXES
   ===================================================== */

/* Product-card image: sized and rounded to match the injected
   photos. The rendered <img> already computes the radius live,
   but Loop paints a sharp-cornered white box
   (.loop-bundle-product-image-container, radius 0) directly
   behind it at the exact same size, so the corners still read
   square. Fix = round and clip that container too. Verified live
   2026-07-08 at 402px: img br=20, container br=0. Radius moved
   to the homepage image value (14px) 2026-07-31. */
.loop-image-product-lg,
.loop-image-product-lg img,
.loop-bundle-product-image-container,
.loop-bundle-product-image-container img,
.loop-img-fit {
  border-radius: var(--gt-r-img) !important;
}

/* border-radius computes correctly on the <img> but does NOT
   visually clip the (JPG, baked-white) packshot. clip-path
   force-clips replaced content to the rounded rect regardless of
   that quirk. Belt and suspenders so the corners actually round.
   Verified live 2026-07-08: the whole image stack has a
   transparent background, so the white is the JPG itself. */
.loop-img-fit,
.loop-image-product-lg,
.loop-image-product-lg img,
.loop-bundle-product-image-container,
.loop-bundle-product-card img {
  clip-path: inset(0 round 14px) !important;
}

.loop-image-product-lg,
.loop-image-product-lg img {
  height: 300px !important;
}

.loop-image-product-lg,
.loop-bundle-product-image-container {
  overflow: hidden !important;
}

/* Hero: force-center the whole block (Loop hard-sets the
   description to text-align:start, so it ignored the wrapper)
   and stop the description rendering full-viewport-width, which
   spilled 16px past the edge and caused horizontal scroll. The
   wrapper is full-viewport width with no horizontal padding,
   which is why children with their own left offsets could shove
   content off the right edge. */
.loop-bundle-header-wrapper,
.loop-bundle-header-wrapper-new {
  text-align: center;
  margin-bottom: var(--gt-sp-2);
  padding-left: var(--gt-sp-4) !important;
  padding-right: var(--gt-sp-4) !important;
  overflow-x: hidden;
  box-sizing: border-box;
}

/* Loop renders the eyebrow (h1 .loop-bundle-title) as a
   shrink-to-fit box pinned LEFT (width ~202px, margin-left
   computes to 0, not the auto we asked for), and the description
   (p .loop-bundle-new-description) at full-viewport width WITH a
   16px left margin, so its centered text landed 16px past the
   right edge and got clipped by the wrapper's overflow-x:hidden
   ("...women who need" cut off). Verified live 2026-07-08 at
   402px: description right edge 418 vs 402 viewport. Fix = force
   both to full width with zero side margins, so text-align:
   center (which IS honored) actually centers. */
.loop-bundle-title,
.loop-bundle-new-description,
.loop-bundle-new-description h2,
.loop-bundle-new-description p {
  text-align: center !important;
  display: block !important;
  width: 100% !important;
  max-width: 100% !important;
  margin-left: 0 !important;
  margin-right: 0 !important;
  box-sizing: border-box !important;
  overflow-wrap: break-word;
}

.loop-bundle-title {
  margin-top: var(--gt-sp-2) !important;
  margin-bottom: var(--gt-sp-1) !important;
}

/* THE reason "Sleep Support" read left, finally: the hero is a
   flex COLUMN (.loop-bundle-image-container-wrapper) with
   align-items:flex-start ("items-start"), which left-pins every
   shrink-wrapped child, the eyebrow's title wrapper included. No
   width or text-align on the h1 or its <strong> could escape
   that (the h1 got width:100% but its wrapper was still
   shrink-wrapped to 202px). Center the flex cross-axis, and
   full-width the title wrapper as a backstop. Confirmed from
   live DOM 2026-07-08 (tree: image-container-wrapper >
   title-wrapper-new > strong > h1.loop-bundle-title). */
.loop-bundle-image-container-wrapper {
  align-items: center !important;
}

.loop-bundle-title-wrapper-new {
  width: 100% !important;
  text-align: center !important;
}

.loop-bundle-new-description p {
  margin-top: var(--gt-sp-3) !important;
}

.loop-bundle-select-product-title-new {
  font-weight: 600 !important;
  width: 100% !important;
  max-width: 100% !important;
  margin-left: 0 !important;
  margin-right: 0 !important;
  box-sizing: border-box !important;
}

/* "Add all 3 (10 sticks each)..." title. Same full-viewport-width
   issue, so it gets side gutters too. */
.loop-bundle-select-product-title-new-container {
  text-align: center;
  margin: var(--gt-sp-5) 0 var(--gt-sp-3) !important;
  padding-left: var(--gt-sp-4) !important;
  padding-right: var(--gt-sp-4) !important;
  box-sizing: border-box !important;
}

/* The injected footer HTML lives inside this Loop container. It
   was width:90% with no auto margins, so Loop pinned it LEFT and
   left a ~10% dead strip on the RIGHT, which is the "text under
   Directions hangs left, bigger gap on the right" Dallin flagged
   on Debloat 2026-07-14. Widen to 96% AND center it so the small
   gutter is symmetric. Shared, so Sleep/Duo inherit the fix. */
.loop-bundle-footer-description {
  width: 96%;
  max-width: 100%;
  margin-left: auto !important;
  margin-right: auto !important;
  box-sizing: border-box;
}

/* Hide the per-unit price on the product card. Customers only
   care about the bundle total (shown in the sticky cart bar),
   not that one unit is $14.99. Mirrors WM, which shows no
   per-unit price. Verified 2026-07-08 that
   .loop-display-product-price renders ONLY inside the product
   card, never in the total or add-to-cart bar, so this is safe. */
.loop-bundle-product-card .loop-display-product-price {
  display: none !important;
}

/* Collapse the big empty gap after the quantity selector. Loop
   reserves padding-bottom:160px under the selector (space for
   the sticky cart bar); with an injected footer right after, it
   read as dead space. Verified live at 402px: card to footer gap
   214px became 66px. */
.loop-bundle-products-wrapper {
  padding-bottom: 6px !important;
}

/* =====================================================
   LOOP-NATIVE SECTIONS, IMAGE STRATEGY
   =====================================================
   REVISED 2026-07-14 (Dallin): SHOW the native "Bundle image"
   above the fold. This reversed the 2026-07-08 text-forward
   call. Loop offers two image slots:
     - Bundle image (.loop-bundle-image-container) renders ABOVE
       the headline. NOW SHOWN, one lifestyle shot per bundle,
       set in Loop > Configuration > Bundle image.
     - Per-variant packshot
       (.loop-bundle-product-image-container) renders in the
       card, per flavor. STILL HIDDEN, not wanted.
   Ritual and ingredient shots still live in the injected footer.
   For Sleep/Debloat/Duo this shared treatment carries over. */

/* Native bundle image, shown above the headline as a rounded
   hero card. The container carries the radius plus overflow:
   hidden as a clip backstop; 16px side gutters and auto margins
   match the padded hero and center it. Homepage image frames
   also carry a hairline, so it gets one. */
.loop-bundle-image-container {
  display: block !important;
  width: calc(100% - 32px) !important;
  max-width: 520px !important;
  margin: var(--gt-sp-1) auto var(--gt-sp-4) !important;
  border: 1px solid var(--gt-hairline) !important;
  border-radius: var(--gt-r-img) !important;
  overflow: hidden !important;
  box-sizing: border-box !important;
}

/* The bundle image itself. border-radius does NOT visually clip
   Loop's replaced <img> (same quirk proven on the product-card
   packshot 2026-07-08), so clip-path force-clips the rounded
   rect regardless. object-fit:cover plus a fixed height give a
   consistent hero band whatever the source aspect. */
.loop-bundle-image-container img,
.loop-bundle-image-container .loop-img-fit,
.loop-bundle-image-container .loop-image-product-lg,
.loop-bundle-image-container .loop-image-product-lg img {
  display: block !important;
  width: 100% !important;
  height: 300px !important;
  object-fit: cover !important;
  border-radius: var(--gt-r-img) !important;
  clip-path: inset(0 round 14px) !important;
}

/* per-variant flavor packshot, hidden (per flavor, not wanted) */
.loop-bundle-product-image-container {
  display: none !important;
}

/* bundle-size chooser, redundant, size is fixed at 3 */
.loop-bundle-size-selector-new-wrapper {
  display: none;
}

/* =====================================================
   GLOBAL IMAGE STYLING
   =====================================================
   2026-07-31, REQUIRED by the real-photography swap, do not
   drop. Every AI render this page used to carry was square
   (2048x2048, 1697x1696), so plain width:100% produced a square
   band and no ratio rule was needed. The 7/27 shoot is 1800x2400
   (3:4 portrait). Left unconstrained inside the 800px wrap, each
   photo renders 800x1067, a full screen of image per slot, and
   the page scrolls past three of them before reaching the
   ingredient list. aspect-ratio 1/1 plus object-fit:cover
   restores the square rhythm and center-crops the portraits.
   Both Sleep frames survive a center square crop with the stick
   intact, verified against the source files. Debloat inherits
   this correctly (DSC03833 is the same 3:4 portrait). DUO IS THE
   EXCEPTION: its hero art is landscape and a square crop guts
   it, so put .gt-img-wide on that wrapper. */
/* 2026-07-31 CORRECTION to the note above. 1/1 was a reasonable
   fix for the height problem but it is not a ratio the homepage
   uses anywhere. The homepage frames a portrait subject at 4/5
   (.ritual__media, .band__media) and a wide one at 16/10 or
   16/11 (.bundle__media, .macros__item). 4/5 also crops the 3:4
   source far less than a square does. The height problem the
   original note describes is solved by the max-width cap on the
   wrapper, not by the ratio. */
.gt-image img,
.gt-compare-img img {
  width: 100%;
  height: 100%;
  display: block;
  border-radius: var(--gt-r-img);
  aspect-ratio: 4 / 5;
  object-fit: cover;
  object-position: center;
}

/* The flat-lay is a still life, not a portrait, so it takes the
   homepage's wide-media ratio instead. */
.gt-image-full img {
  width: 100%;
  height: 100%;
  display: block;
  border-radius: var(--gt-r-img);
  aspect-ratio: 16 / 11;
  object-fit: cover;
  object-position: center;
}

/* Homepage image frames carry a hairline, which is what keeps a
   light photo from bleeding into the paper canvas. position is
   relative so .gt-ritual-badge can sit on the frame. */
.gt-image,
.gt-image-full,
.gt-compare-img,
.gt-facts-panel img {
  position: relative;
  border: 1px solid var(--gt-hairline);
  border-radius: var(--gt-r-img);
  overflow: hidden;
  line-height: 0;
}

/* Opt-out for genuinely wide art (Duo hero, any future
   landscape frame). */
.gt-image.gt-img-wide img,
.gt-image-full.gt-img-wide img,
.gt-compare-img.gt-img-wide img {
  aspect-ratio: auto;
  object-fit: fill;
}

/* =====================================================
   MAIN BUNDLE LAYOUT
   ===================================================== */

.gt-bundle-wrap {
  max-width: 800px;
  margin: 0 auto;
  padding: 0;
}

/* Homepage measure caps. This was the loudest typographic
   difference: at the 800px wrap, unconstrained body copy ran
   past 100 characters per line against the homepage's 56. */
.gt-measure {
  max-width: 56ch;
  margin-left: auto;
  margin-right: auto;
}

.gt-hero-title {
  font-family: var(--gt-serif);
  font-size: clamp(2.4rem, 9vw, 3rem);
  line-height: 1.06;
  font-weight: 500;
  margin-bottom: var(--gt-sp-3);
}

.gt-subtext {
  font-size: 1.0625rem;
  line-height: 1.65;
  color: var(--gt-ink-soft);
  margin-bottom: var(--gt-sp-4);
}

.gt-image-full {
  margin: var(--gt-sp-5) auto var(--gt-sp-6);
  max-width: 560px;
}

/* =====================================================
   SOCIAL PROOF CARD
   (dropped at launch until Judge.me has real reviews, kept
   on-brand here for when it returns)
   ===================================================== */

.gt-social-proof {
  background: var(--gt-white);
  border: 1px solid var(--gt-hairline);
  padding: var(--gt-sp-5);
  border-radius: var(--gt-r-card);
  margin: var(--gt-sp-4) auto var(--gt-sp-5);
  max-width: 46ch;
  text-align: center;
}

.gt-rating-line {
  display: flex;
  justify-content: center;
  align-items: center;
  gap: var(--gt-sp-3);
  margin-bottom: var(--gt-sp-1);
}

.gt-stars {
  color: var(--gt-peri-ink);
  font-size: 1.25rem;
  letter-spacing: 2px;
}

.gt-rating-text {
  color: var(--gt-ink-soft);
  font-size: 0.95rem;
}

.gt-proof-headline {
  font-size: 1.15rem;
  font-weight: 600;
}

/* =====================================================
   RITUAL / COMPARE BLOCK
   ===================================================== */

.gt-compare-block {
  text-align: center;
  margin: var(--gt-sp-4) 0 var(--gt-section);
}

/* Structural heading, so Montserrat at the homepage h2 scale
   with its negative tracking. Cormorant was carrying four
   separate moments on this page against the guide's "sparing,
   one per section"; it now carries the ritual line, the mantra
   band and the closer, one each. */
/* The page's lead headline, built on .whytwo__h: Montserrat 600
   at 1.12 line-height carrying a Cormorant italic tail. The
   homepage runs clamp(2.1rem, 4.3vw, 3.4rem) in a 1240px shell;
   this is the same curve stepped down for the 800px column. */
.gt-compare-intro {
  font-family: var(--gt-sans);
  font-size: clamp(1.9rem, 5.5vw, 2.6rem);
  font-weight: 600;
  line-height: 1.12;
  letter-spacing: -0.01em;
  color: var(--gt-ink);
  max-width: 24ch;
  margin: var(--gt-sp-4) auto var(--gt-sp-6);
}

.gt-compare-sub {
  font-size: 1.0625rem;
  font-weight: 400;
  line-height: 1.65;
  color: var(--gt-ink-soft);
  max-width: 36ch;
  margin: 0 auto var(--gt-sp-5);
}

.gt-compare-img {
  margin: 0 auto var(--gt-sp-5);
  max-width: 560px;
}

/* Ritual caption under the photo. This is the homepage's
   .band__micro scale (1.4rem Cormorant italic in the accent),
   not a second display-size serif line. The loud Cormorant
   moment in this section is now the headline tail, and the guide
   allows one per section. */
.gt-compare-good {
  font-family: var(--gt-serif);
  font-style: italic;
  font-size: 1.4rem;
  font-weight: 500;
  letter-spacing: 0.01em;
  margin: 0;
  color: var(--gt-peri-ink);
}

/* =====================================================
   BENEFIT ROW
   =====================================================
   The homepage separates a benefit row with hairline rules, not
   with boxes or icon chips. This is that pattern: a grid whose
   cells are divided by 1px sand rules, collapsing to a single
   stacked column with bottom rules under 560px. */

/* 2026-08-01. The track count was hardcoded repeat(4, 1fr), which
   is right only for Sleep. Debloat and Duo carry THREE chips, so the
   fourth track stayed empty. Measured live at 1280 on Debloat: the
   row's hairlines ran 272 -> 1008 (736px) while the chips stopped at
   824, leaving a dead 184px quarter on the right, which is what read
   as a misaligned, orphaned last chip.

   grid-auto-flow: column with grid-auto-columns: 1fr derives the
   count from the content instead: three chips take three 245.3px
   columns, four take four 184px ones, and Sleep's existing 4x184
   layout is unchanged. Chip count and copy length can both move
   without editing this file again, which is the actual failure mode
   here (the copy got longer once today and broke the old rule).

   Do NOT swap this for repeat(auto-fit, minmax(...)): a px floor
   re-introduces a width band (roughly 560-750px) where a four-chip
   row drops one chip to a second line, which is the same defect in
   a different place. The <=560px block below restores row flow. */
.gt-benefits {
  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: 1fr;
  gap: 0;
  border-top: 1px solid var(--gt-hairline);
  border-bottom: 1px solid var(--gt-hairline);
  margin: var(--gt-section) 0;
  padding: 0;
  list-style: none;
}

.gt-benefits span {
  display: block;
  padding: var(--gt-sp-5) var(--gt-sp-4);
  border-right: 1px solid var(--gt-hairline);
  text-align: center;
  font-size: 0.9375rem;
  font-weight: 500;
  line-height: 1.35;
  color: var(--gt-ink);
}

.gt-benefits span:last-child {
  border-right: 0;
}

.gt-benefits strong {
  font-weight: 500;
}

@media (max-width: 560px) {
  .gt-benefits {
    /* grid-auto-flow: column above pins everything to one row, which
       would defeat the two-up stack. Restore row flow before setting
       the explicit tracks. Measured clean at 430px: two 193.5px
       columns, unchanged from before the desktop fix. */
    grid-auto-flow: row;
    grid-template-columns: repeat(2, 1fr);
  }
  .gt-benefits span {
    padding: var(--gt-sp-5) var(--gt-sp-3);
  }
  .gt-benefits span:nth-child(2n) {
    border-right: 0;
  }
  .gt-benefits span:nth-child(-n + 2) {
    border-bottom: 1px solid var(--gt-hairline);
  }
}

/* =====================================================
   MANTRA BAND
   =====================================================
   Replaces the tinted rounded card. The homepage marks an
   emphasis line by bounding it with hairlines and giving it
   room, never by putting it in a box. Cormorant italic at the
   band measure. */

/* 2026-07-31: matched to the homepage's actual emphasis band,
   .marquee / .marquee__item, which is where the homepage runs a
   large Cormorant italic line bounded top and bottom by a
   hairline. Its type is clamp(1.5rem, 3vw, 2.4rem) in text-soft,
   not the product accent; the accent is reserved for the "·"
   between phrases. The 4.5vw curve here was steeper than the
   homepage's 3vw, which made this band overshoot the section
   headings on a phone. */
.gt-mantra,
.gt-highlight {
  font-family: var(--gt-serif);
  font-style: italic;
  font-size: clamp(1.5rem, 3vw, 2.4rem);
  font-weight: 500;
  line-height: 1.25;
  padding-block: var(--gt-sp-6);
  padding-inline: var(--gt-sp-4);
  margin: 0 0 var(--gt-sp-5);
  text-align: center;
  color: var(--gt-ink-soft);
  background: none;
  border: 0;
  border-block: 1px solid var(--gt-hairline);
  border-radius: 0;
  max-width: none;
}

/* font-family and font-style are re-inherited on purpose. The
   blanket `.gt-bundle-wrap *` reset near the top of this file
   sets Montserrat on every descendant at the same specificity as
   this rule, so an inner <span> silently lost the Cormorant its
   parent had and rendered as faux-italic Montserrat. Anything
   nested inside a serif block needs this. */
.gt-mantra span,
.gt-highlight span {
  display: block;
  max-width: 24ch;
  margin: 0 auto;
  font-family: inherit;
  font-style: inherit;
}

/* The benefit grid and the mantra band each carry their own
   hairline, so stacked they drew two rules with a dead gap
   between them, which reads as a mistake rather than as rhythm.
   When they are adjacent the band drops its top rule and closes
   the gap, leaving one hairline doing the dividing. */
.gt-benefits + .gt-mantra {
  border-top: 0;
  margin-top: 0;
}

.gt-benefits:has(+ .gt-mantra) {
  margin-bottom: 0;
}

.gt-bundle-note {
  font-size: 0.95rem;
  font-weight: 500;
  color: var(--gt-ink-soft);
  max-width: 46ch;
  margin: 0 auto var(--gt-section);
  text-align: center;
}

/* =====================================================
   FOOTER SECTIONS
   ===================================================== */

.gt-footer-wrap {
  padding: 0 0 130px 0;
  text-align: left;
}

.gt-section {
  margin-bottom: var(--gt-section);
}

/* Section head: eyebrow, then heading, then body. This is the
   homepage's section pattern, ported rather than approximated.
   Every section on the homepage opens with it, and its absence
   was the main reason this page still read as a different
   system even after the palette and type matched. */
.gt-section-head {
  text-align: center;
  margin-bottom: var(--gt-sp-5);
}

/* Verbatim from .gt-home .eyebrow, including the 28x1px leading
   rule at 0.6 opacity and the 12px gap. Only the selector name
   changed. Centered sections add justify-content:center exactly
   as .duo__head .eyebrow does; the rule stays leading-only, which
   is how the homepage centers it too. */
.gt-eyebrow {
  font-family: var(--gt-sans);
  font-size: 0.75rem;
  font-weight: 600;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--gt-peri-ink);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--gt-sp-3);
}

.gt-eyebrow::before {
  content: "";
  width: 28px;
  height: 1px;
  background: currentColor;
  opacity: 0.6;
}

/* Section heading. The old rule hung a centered 44x3 periwinkle
   bar under every title; the homepage has no such device
   anywhere and reads as a supplement template with it.

   2026-07-31: the size was wrong. It was running --fs-h3,
   clamp(1.35rem, 2.2vw, 1.7rem), while the homepage's section
   heading is --fs-h2, clamp(1.9rem, 3.5vw, 2.9rem) at 1.12 with
   -0.01em. A full step too small is why these headings read as
   captions beside the homepage's. Homepage h2 values verbatim,
   plus .ritual__copy h2's 16/24 margin block. */
.gt-section-title {
  font-family: var(--gt-sans);
  font-size: clamp(1.9rem, 3.5vw, 2.9rem);
  font-weight: 600;
  line-height: 1.12;
  letter-spacing: -0.01em;
  color: var(--gt-ink);
  max-width: 20ch;
  margin: var(--gt-sp-4) auto var(--gt-sp-5);
  padding: 0;
  text-align: center;
}

.gt-section-title::after {
  content: none;
}

/* Mixed-face headline. The homepage sets the tail of a headline
   in Cormorant italic inside an otherwise Montserrat 600 line
   (.whytwo__h .text-soft), which is the "One when you rise. One
   when you rest. That's go-two." treatment. Same recipe here. */
.gt-heading-soft {
  font-family: var(--gt-serif);
  font-style: italic;
  font-weight: 500;
  color: var(--gt-ink-soft);
}

/* Section body. 2026-07-31: this was set at --fs-body (1.0625rem),
   which on the homepage is the size of running UI text, not of a
   section's lead paragraph. The homepage's section body is .whytwo__p,
   --fs-lede at a 56ch measure. The measure was already right; only
   the size was a step low. */
.gt-body-text {
  font-size: clamp(1.08rem, 1.5vw, 1.28rem);
  line-height: 1.65;
  color: var(--gt-ink-soft);
  max-width: 56ch;
  margin: 0 auto;
}

/* =====================================================
   PILLARS  (replaces the old dot-bullet .gt-list)
   =====================================================
   2026-07-31. The dot-bullet list was the single loudest reason
   this page still read as a supplement template rather than as
   go-two. The homepage never uses a bulleted list. Where it has
   to enumerate, it uses .pillars: a Cormorant italic index in
   the accent, a Montserrat 600 title on its own line, a soft
   description under it, and hairline rules doing the dividing.

   Ported from .gt-home .pillars with two deliberate changes:
     - 2 columns, not 3. The homepage runs 3 across a 1240px
       shell; this column is 800px, and 3 across it forces the
       descriptions to a ~14ch ragged measure.
     - Collapses at 640px rather than the homepage's 880px, for
       the same reason: the collapse point should track the
       column, not the viewport.
   .gt-list is kept as an alias so any older staged block still
   renders sanely instead of losing its list styling outright. */
.gt-pillars,
.gt-list {
  list-style: none !important;
  padding: 0 !important;
  margin: 0;
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 0;
  border-top: 1px solid var(--gt-hairline);
  counter-reset: gt-pillar;
}

/* align-content:start is load-bearing. The outer grid stretches
   every cell to the tallest in its row; because the cell is
   itself a grid, that slack was being distributed across its
   three auto rows, so the Cormorant index box grew from 21px to
   29px in whichever cells happened to be short and the titles
   fell out of alignment across the row by 8px. Packing the rows
   to the top sends the slack to the bottom of the cell, where it
   belongs. */
.gt-pillars li,
.gt-list li {
  display: grid !important;
  align-content: start;
  gap: var(--gt-sp-2);
  padding: var(--gt-sp-6);
  margin: 0 !important;
  border-right: 1px solid var(--gt-hairline);
  border-bottom: 1px solid var(--gt-hairline);
}

/* First cell of each row sheds its left padding so the type
   aligns to the same left edge as the heading above it, which is
   what .pillars li:first-child does on the homepage. */
.gt-pillars li:nth-child(odd) {
  padding-left: 0;
}

.gt-pillars li:nth-child(even) {
  border-right: 0;
}

.gt-pillars__n {
  font-family: var(--gt-serif);
  font-style: italic;
  font-size: 1.3rem;
  line-height: 1;
  color: var(--gt-peri-ink);
}

.gt-pillars__t {
  font-family: var(--gt-sans);
  font-weight: 600;
  font-size: 1.4rem;
  line-height: 1.12;
  color: var(--gt-ink);
}

.gt-pillars__d {
  font-size: 0.92rem;
  line-height: 1.6;
  color: var(--gt-ink-soft);
  max-width: 28ch;
}

@media (max-width: 640px) {
  .gt-pillars,
  .gt-list {
    grid-template-columns: 1fr;
    border-top: 0;
  }

  .gt-pillars li,
  .gt-list li {
    border-right: 0;
    border-bottom: 1px solid var(--gt-hairline);
    padding: var(--gt-sp-5) 0;
  }
}

/* =====================================================
   ROWS
   =====================================================
   The same title/description split as .gt-pillars, in one
   column and without an index. Used for the ingredient list,
   which is eight items long: two columns would make a wall, and
   numbering it would imply a ranking or a dosage order that the
   label does not mean. Same hairline rhythm, so it still reads
   as one family with the pillars above it. */
.gt-rows {
  list-style: none !important;
  padding: 0 !important;
  margin: 0;
  display: grid;
  gap: 0;
  border-top: 1px solid var(--gt-hairline);
}

.gt-rows li {
  display: grid !important;
  align-content: start;
  gap: var(--gt-sp-1);
  padding: var(--gt-sp-5) 0;
  margin: 0 !important;
  border-bottom: 1px solid var(--gt-hairline);
}

.gt-rows__t {
  font-family: var(--gt-sans);
  font-weight: 600;
  font-size: 1.0625rem;
  line-height: 1.3;
  color: var(--gt-ink);
}

.gt-rows__d {
  font-size: 0.98rem;
  line-height: 1.6;
  color: var(--gt-ink-soft);
  max-width: 52ch;
}

/* =====================================================
   RITUAL BADGE
   =====================================================
   The homepage's .ritual__badge: a pill chip sitting on the
   image at top-left, in letterspaced caps. It is the homepage's
   way of labelling a photograph and it is the one component this
   page was missing entirely. color-mix is used exactly as the
   homepage uses it, with a flat paper fallback first for any
   engine that does not support it. */
.gt-ritual-badge {
  position: absolute;
  top: var(--gt-sp-4);
  left: var(--gt-sp-4);
  z-index: 2;
  background: var(--gt-paper);
  background: color-mix(in srgb, var(--gt-paper) 88%, transparent);
  padding: 0.5em 0.9em;
  border-radius: var(--gt-r-pill);
  font-family: var(--gt-sans);
  font-size: 0.72rem;
  font-weight: 600;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--gt-peri-ink);
}

/* Supplement facts panel. Portrait label art, so it is capped
   rather than filled to the column. */
.gt-facts-panel {
  text-align: center;
  margin: 0 auto var(--gt-section);
}

.gt-facts-panel img {
  max-width: 420px;
  width: 100%;
  height: auto;
  display: block;
  margin: 0 auto;
  border-radius: var(--gt-r-img);
}

/* Closing mantra. Cormorant italic, the page's sign-off, in the
   same register as the homepage's closing marquee line
   ("Wake up light · Go to bed quiet"), which is text-soft. */
.gt-final-line {
  font-family: var(--gt-serif);
  font-style: italic;
  font-size: clamp(1.5rem, 3vw, 2.4rem);
  font-weight: 500;
  text-align: center;
  letter-spacing: 0.01em;
  margin: var(--gt-section) 0 var(--gt-sp-6);
  color: var(--gt-ink-soft);
}

/* =====================================================
   TRUST STRIP + DISCLAIMER
   =====================================================
   The homepage reassurance line is a single centered row of
   small letter-spaced soft text above a hairline, with accent
   dots between items. Not a bordered pill, not a badge. */

.gt-trust-badge {
  text-align: center;
  padding: 0 0 var(--gt-sp-6);
  border-bottom: 1px solid var(--gt-hairline);
  margin-bottom: var(--gt-sp-5);
}

/* Graphic seal size cap. Kept so a footer still carrying
   <img class="gt-usa-seal"> renders small, not full width. */
.gt-usa-seal {
  width: 112px;
  height: auto;
  display: inline-block;
  opacity: 0.95;
}

.gt-usa-mark {
  display: inline-block;
  font-family: var(--gt-sans);
  font-size: 0.82rem;
  font-weight: 500;
  letter-spacing: 0.04em;
  text-transform: none;
  color: var(--gt-ink-soft);
  padding: 0;
  border: 0;
  border-radius: 0;
}

.gt-disclaimer {
  font-size: 0.8125rem;
  line-height: 1.55;
  color: var(--gt-ink-soft);
  opacity: 0.9;
  max-width: 62ch;
  margin: 0 auto;
  text-align: center;
}

/* =====================================================
   PROP 65
   =====================================================
   The block ships with inline styles so it survives even if this
   stylesheet is missing. These rules only bring it onto the
   brand's neutrals; nothing here can hide it.

   The !important on the side margins is load-bearing, not habit:
   the block carries an inline `margin:16px 0`, and an inline
   shorthand beats a plain stylesheet margin-left, so without it
   the notice pins to the left of the 800px column instead of
   centering under the content. */
.prop65-warning {
  font-family: var(--gt-sans);
  max-width: 62ch;
  margin-left: auto !important;
  margin-right: auto !important;
}

/* =====================================================
   MOTION
   ===================================================== */

@media (prefers-reduced-motion: reduce) {
  .loop-btn-add-to-cart,
  .loop-btn-input-increase,
  .loop-btn-input-decrease {
    transition: none !important;
  }
  .loop-btn-add-to-cart:hover {
    transform: none;
  }
}

/* =====================================================
   ALIGNMENT, MATCHING THE HOMEPAGE DEFAULT
   =====================================================
   The homepage's section head is LEFT aligned by default. Only
   two blocks override to centre (.duo__head and .inside__head,
   each with justify-content:center on the eyebrow). This page
   was centring everything, which is the single remaining reason
   it still read as a different system once the palette, type and
   eyebrow pattern matched.

   What stays centred is what the homepage also centres: the
   emphasis band (its .proof blockquote), the closer, the trust
   line, the legal text, and standalone media. To flip the whole
   page back to centred, delete this block. */

.gt-compare-block,
.gt-section-head,
.gt-section-title,
.gt-body-text,
.gt-pillars,
.gt-rows,
.gt-list {
  text-align: left;
}

.gt-eyebrow {
  justify-content: flex-start;
}

.gt-compare-intro,
.gt-section-title,
.gt-body-text,
.gt-pillars,
.gt-rows,
.gt-list {
  margin-left: 0;
  margin-right: 0;
}

/* 2026-07-31. Media used to be centred under a left-aligned
   column, which is the one thing that read as broken on desktop:
   every heading and paragraph started at the column's left edge
   while every photo floated in the middle of it, so nothing in
   the page shared a vertical line.

   The homepage never does this. A photo there either fills its
   column edge to edge (.macros__item img is width:100% of its
   cell) or sits beside the copy in a two-column grid
   (.ritual__grid, .band__grid, which collapse at 880px). Loop's
   footer is a single 800px column and cannot take a two-column
   split without crushing the measure, so this is the first
   pattern: the media shares the type's left edge.

   The cap moves 560px to --gt-media, which is set near the 56ch
   body measure so the right edges land close too. It is NOT set
   in ch: ch resolves against each element's own font-size, and
   an image has no text, so a ch value there would silently mean
   something different from the same number on a paragraph.

   Only genuinely full-width bands stay centred, which is what
   the homepage centres too: the mantra band, the bundle note,
   the closer, the trust line and the legal text. */
.gt-compare-img,
.gt-image-full,
.gt-facts-panel {
  margin-left: 0;
  margin-right: 0;
  max-width: var(--gt-media);
}

/* The ritual caption is a caption for the photo above it, so it
   hangs off the same left edge rather than centring under it. */
.gt-compare-good {
  text-align: left;
  display: block;
  margin-top: var(--gt-sp-5);
}

/* Below the media cap there is no centring to undo, and the
   column is narrow enough that a left-hung label art panel reads
   as an orphan. Let it fill. */
@media (max-width: 680px) {
  .gt-compare-img,
  .gt-image-full,
  .gt-facts-panel {
    max-width: 100%;
  }
}

/* =====================================================
   PAGE GUTTER + HERO ALIGNMENT
   =====================================================
   2026-07-31, second live pass. Two defects Dallin caught on a
   real phone that no desktop check would have surfaced.

   1. NO GUTTER ON MOBILE. Measured live at 430px: the injected
      block's left edge sat at 9px. That 9px was not ours, it was
      Loop's own 96%-wide centred .loop-bundle-footer-description
      column (2% a side). We had never set a gutter at all, so
      body copy ran essentially flush to the phone bezel. The
      homepage gutter is --margin, clamp(20px, 5vw, 80px). Loop's
      2% column is neutralised and we own the gutter outright,
      rather than stacking padding on a percentage that drifts
      with viewport.

   2. HERO CENTRED, EVERYTHING BELOW LEFT. The Loop-native hero
      (eyebrow, headline, subtext, "Add all 3" selector title) all
      computed text-align:center while the injected footer had
      gone left in the homepage pass. The seam was obvious on a
      phone. The centring was OUR doing, from the 2026-07-08 pass
      that fixed a clipped hero; that block is left in place above
      because its width and overflow fixes are still load-bearing,
      and only the alignment is overridden here.

   THREE THINGS THIS BLOCK GOT WRONG BEFORE IT GOT THEM RIGHT, all
   found by injecting into the live page and measuring, never by
   eye. Keep them in mind when porting to Debloat and Duo:

   a) Plain declarations lose here. Loop re-injects its own
      stylesheet at runtime, so source order is not reliable and
      an un-!important rule silently did nothing even though it
      was present in the sheet. Everything below is !important on
      purpose, not as a shortcut.
   b) The gutter belongs on ONE element per column. Putting it on
      both .loop-bundle-image-container-wrapper and its nested
      .loop-bundle-header-wrapper doubled it to 43px.
   c) The eyebrow does NOT live in .loop-bundle-header-wrapper. It
      is image-container-wrapper > title-wrapper-new > strong >
      h1.loop-bundle-title, so gutter applied to the header
      wrapper alone leaves the eyebrow hanging 16px right of
      everything else.

   VERIFIED LIVE at 430px, every element flush at left 22 /
   right 409, document scrollWidth 430 (no overflow): bundle
   image, eyebrow, hero h2, hero subtext, selector title, footer
   eyebrow, section title, body, media and pillar grid. */

:root {
  --gt-gutter: clamp(20px, 5vw, 32px);
}

/* Loop's 96% column is neutralised; we own the gutter. */
.loop-bundle-footer-description {
  width: 100% !important;
  margin-left: 0 !important;
  margin-right: 0 !important;
}

.loop-bundle-footer-description .gt-bundle-wrap,
.gt-bundle-wrap {
  padding-left: var(--gt-gutter) !important;
  padding-right: var(--gt-gutter) !important;
  box-sizing: border-box !important;
}

/* The two outer hero columns. Same cap, centring and gutter as
   the footer, so all three share left AND right edges. */
.loop-bundle-image-container-wrapper,
.loop-bundle-select-product-title-new-container {
  max-width: 800px !important;
  margin-left: auto !important;
  margin-right: auto !important;
  padding-left: var(--gt-gutter) !important;
  padding-right: var(--gt-gutter) !important;
  box-sizing: border-box !important;
  text-align: left !important;
}

/* Cross-axis back to the start edge now the page reads left. */
.loop-bundle-image-container-wrapper {
  align-items: flex-start !important;
}

/* Nested inside the wrapper above, so it must NOT carry the
   gutter again. See note (b). */
.loop-bundle-header-wrapper,
.loop-bundle-header-wrapper-new {
  max-width: none !important;
  padding-left: 0 !important;
  padding-right: 0 !important;
  margin-left: 0 !important;
  margin-right: 0 !important;
  text-align: left !important;
}

.loop-bundle-image-container,
.loop-bundle-title,
.loop-bundle-title-wrapper-new {
  margin-left: 0 !important;
  margin-right: 0 !important;
}

.loop-bundle-title.loop-h1,
.loop-bundle-title,
.loop-bundle-title-wrapper-new,
.loop-bundle-new-description,
.loop-bundle-new-description h2,
.loop-bundle-new-description p,
.loop-bundle-select-product-title-new {
  text-align: left !important;
}

/* =====================================================
   BUNDLE IMAGE SLOT
   =====================================================
   2026-07-31. The Sleep hero is a 3:4 portrait (1800x2400).
   Loop was rendering it into a 353x240 slot with
   object-fit:cover, i.e. a 1.47 letterbox, which centre-cropped
   roughly half the frame away and cut the subject out entirely.

   Loop's Design > Styles > "Bundle image height" was raised to
   460px and DID save, but it is honoured on desktop only; on
   mobile Loop hard-caps the slot at 240px, so the setting alone
   does not fix the phone, which is where it matters most. Hence
   this rule.

   Two selector facts, both load-bearing:
     - The wrapper div AND the <img> both carry the class
       .loop-bundle-image-container, so a single-class rule hits
       both and the two need different declarations.
     - The <img> also carries .loop-img-fit, and Loop styles it
       with a two-class rule. A one-class selector loses on
       specificity no matter how many !importants it has, which
       is why the wrapper resized and the image did not. The
       image rule therefore names BOTH classes.

   aspect-ratio rather than a fixed height so the slot scales
   with the column. 4/5 against a 3/4 source is about a 6% crop
   instead of a 47% one.

   2026-08-01. Three defects fixed here, all measured live, none
   visible by eye. Read this before touching either rule.

   1) THE 4px OFFSET. The <img> carries .loop-bundle-image-container
      too (see the selector note above), so the WRAPPER rule up at
      "Native bundle image" also hits the image: it hands the img
      `margin: var(--gt-sp-1) auto var(--gt-sp-4)` and a
      `border: 1px`. The later rule in the GUTTER block zeroes
      margin-left/right on that class but NOT top/bottom, so 4px of
      margin-top survived on the image. That pushed it 4px down
      inside a container whose height is exactly the image height,
      and overflow:hidden then ate 4px off the bottom. Symptom was a
      gap along the top and a clipped bottom edge. The image rule
      below therefore zeroes margin explicitly. Do not delete it
      assuming the wrapper rule is the only consumer of that class.

   2) DOUBLED HAIRLINE. Same cause: the img inherited the wrapper's
      1px border, drawing a second hairline just inside the
      container's. That is the faint left inset. `border: 0` on the
      image. border-radius is deliberately left alone, since
      `border: 0` does not reset it and clip-path is what actually
      rounds Loop's replaced <img> anyway.

   3) max-height DEFEATED aspect-ratio. The container carried BOTH
      max-width:520px and max-height:520px. At 4/5 those cannot both
      hold: 520 wide wants 650 tall, so the height cap won and the
      slot rendered 520x520, a square. A 3:4 source into a 1:1 slot
      is a 25% crop, which is what was gutting the photos on desktop.
      The height cap is the intent worth keeping (a 650px hero is a
      full screen of photo), so it is now expressed as the width that
      YIELDS it: 520 * 0.8 = 416px. Cap the width, never the height,
      whenever aspect-ratio is doing the sizing.

   VERIFIED LIVE on Sleep, 2026-08-01, with real mobile emulation
   (confirm window.innerWidth, the viewport change is what the last
   session got wrong). At 430px: 355x443.75, ratio 0.800, img delta
   top/left 0, overflow bottom/right 0, scrollWidth 430 (no
   overflow). At 1440px: 416x520, ratio 0.800, all four deltas 0.

   NOTE FOR ANYONE DIAGNOSING THIS BY INJECTION: changing the
   emulated viewport RELOADS the page (navType "reload"), which
   silently destroys an injected <style> tag. Set the viewport
   FIRST, inject SECOND, measure THIRD. Injecting then resizing is
   why a correct candidate fix looked like it had zero effect. */
/* 4) THE 32px RIGHT GAP. Found 2026-08-01 from a phone screenshot
      AFTER defects 1-3 were fixed and "verified". The earlier pass
      measured the img inside its FRAME and never the frame inside
      the PAGE, so it reported all-zero deltas while the whole frame
      sat 32px short of the right gutter.

      The base rule still carried `width: calc(100% - 32px)`, a
      self-gutter from before this page had a gutter system, paired
      with `margin: ... auto ...` to centre the slack. The GUTTER
      block later zeroed margin-left/right on that class, which
      killed the auto centring, so 100% of the 32px slack piled up
      on ONE side. Measured at 430px: frame 21.5 -> 376.5 while the
      eyebrow, headline and body all span 21.5 -> 408.5.

      The wrapper already supplies the gutter, so the frame wants a
      plain 100%. Do not reintroduce a self-gutter here.

      LESSON: an element can be perfectly aligned INSIDE its own box
      and still be misaligned ON THE PAGE. Measure the outermost box
      against a sibling known to be flush, not just child vs parent. */
html body div.loop-bundle-image-container {
  height: auto !important;
  width: 100% !important;
  aspect-ratio: 4 / 5 !important;
  max-height: none !important;
  max-width: 416px !important;
}

html body img.loop-bundle-image-container.loop-img-fit {
  height: 100% !important;
  width: 100% !important;
  max-height: none !important;
  max-width: none !important;
  margin: 0 !important;
  border: 0 !important;
  object-fit: cover !important;
  object-position: 50% 45% !important;
}

/* =====================================================
   SELECTED-BUNDLE PANEL ("Your bundle")
   =====================================================
   2026-08-01. This panel is Loop's .loop-actions-container, and it
   is DESKTOP ONLY: Loop ships
   `@media not all and (min-width:64rem){.loop-actions-container{display:none}}`
   so below 1024px it is hidden and the sticky bottom sheet stands
   in for it. At 1024px the column is only 35% wide (~327px), which
   is where all three of these defects show up.

   1) THE RUNAWAY THUMBNAIL, which caused the other two.
      Loop sizes the line-item image with the Tailwind utilities
      `h-[50px] w-[50px]`, and those rules ARE present in Loop's
      stylesheet. They lose anyway: Prestige's base.css sets a
      global img width UNLAYERED, and an unlayered author rule beats
      anything inside `@layer utilities` regardless of specificity.
      So the 50px never applied and the thumbnail rendered 222x296.
      That single width drove the row to 296px tall, pushed the
      title out past the 327px column, and dropped it underneath the
      quantity stepper. Proven by disabling each stylesheet in turn
      on the live page: with base.css off the image snapped to 38x50
      and everything else fell into place. It is NOT our CSS and it
      is NOT a specificity war with Loop.

      Because the cause is a whole missing layer of sizing, restate
      width AND height AND the flex basis. width alone lets flex
      re-stretch it.

   2) The title then had room to wrap in its own 165px column
      instead of overflowing. No text rule was needed; the overflow
      was a symptom, not a cause. Verified scrollWidth == clientWidth.

   3) The panel frame shipped 1.5px #D9D9D9 at 12px radius, which is
      neither our hairline nor our card radius.

   VERIFIED LIVE at 1024px on Sleep: thumbnail 50x50, row height
   296 -> 69, title 756->920 with a 12px gap to the stepper at
   932->991, stepper vertically centred, nothing past the column
   edge at 1008. */
html body .loop-selected-variant-info-container > img {
  width: 50px !important;
  height: 50px !important;
  min-width: 50px !important;
  flex: 0 0 50px !important;
  object-fit: cover !important;
  border-radius: 8px !important;
}

/* Flex children default to min-width:auto, which refuses to shrink
   below content width. Without this the title still forces overflow
   even once the image is sized. */
html body .loop-selected-variant-info-container,
html body .loop-selected-variant-info {
  min-width: 0 !important;
}

/* The stepper is 20px against a 46px title block, so flex-start
   pinned it to the first line of the title and read as a collision
   even when the boxes did not actually overlap. */
html body .loop-selected-variant-container {
  align-items: center !important;
}

html body .loop-selected-bundle {
  border: 1px solid var(--gt-hairline) !important;
  border-radius: var(--gt-r-card) !important;
}

/* The qty input sat white with a near-invisible hairline between
   two ink pill buttons, so the control read as three loose parts.
   Same ink edge top and bottom, none on the sides, makes it one. */
html body .loop-selected-variant-actions-input,
html body .loop-form-input-small {
  border-top: 1px solid var(--gt-ink) !important;
  border-bottom: 1px solid var(--gt-ink) !important;
  border-left: 0 !important;
  border-right: 0 !important;
}

/* =====================================================
   EYEBROW -> MEDIA GAP
   =====================================================
   2026-08-01. An eyebrow followed directly by media had no gap at
   all. Measured live at 1280 on Debloat: .gt-eyebrow "The morning
   ritual" ended at y=1762.0 and .gt-compare-img began at y=1762.0.
   Zero. Same zero at 430px (eyebrow bottom 1114.2, image top 1114.2),
   so this is not a desktop-only defect and the fix is deliberately
   NOT scoped to a media query.

   Nothing was ever setting that gap. .gt-eyebrow carries
   margin-bottom:0 and .gt-compare-img margin-top:0; the other five
   eyebrows on the page only look correct because .gt-section-title
   brings its own 16px margin-top with it. So 16px (--gt-sp-4) is the
   page's real, already-shipped eyebrow rhythm, measured on five of
   six eyebrows on both Debloat and Sleep, and this restates it for
   the media case.

   Deliberately not the 24px (--gt-sp-5) the media carries BELOW
   itself: a label should hug its subject more tightly than the
   subject is separated from whatever follows. 16 above, 24 below.

   Written as a sibling combinator rather than a margin on the
   eyebrow itself so it cannot leak onto the five eyebrows that
   already get their spacing from the heading below them, and so it
   keeps working if a section swaps one media type for another. */
.gt-eyebrow + .gt-compare-img,
.gt-eyebrow + .gt-image-full,
.gt-eyebrow + .gt-facts-panel {
  margin-top: var(--gt-sp-4);
}
