/* Theme case study, the store sections: 03 Design (a Shopify-like theme editor beside the storefront, the wireframe-to-render scroll story),
   04 Optimise (the product page decision by decision: hotspots on the page, the decisions beside it) and the phone (a real HTML mobile
   product page in a device). Markup: src/_includes/e2/theme/{design,optimise,phone}.njk; behaviour: e2-theme-store.js. Everything is
   data-driven: nothing here knows which theme it is; the theme's own fonts and colours reach the phone as custom properties on .th-ph, and its
   kind of options, image and related row as modifier classes. Class prefix th-. The shared browser frame, viewport and artboard (.th-browser,
   .th-viewport, .th-artboard, .th-sr) belong to e2-theme.css.
   Storefront geometry is in 1440px artboard px: an artboard is laid out at 1440px and scaled by --k (the viewport width / 1440). */

.th-ds, .th-op, .th-ph-wrap {
  --th-eb: #005bd3;              /* the editor's blue (Shopify's) */
  --th-pin: #1f5fd9;             /* hotspot / decision number blue */
  --th-spring: cubic-bezier(.34, 1.56, .64, 1);
}

/* ---------- The frames: e2-theme.css's .th-browser, a flex column where it has to fill a height, lifted off the black page ---------- */
.th-ds__frame .th-browser { display: flex; flex-direction: column; }
.th-ds__frame .th-browser, .th-op__browser.th-browser { box-shadow: 0 0 0 1px rgba(255,255,255,.1), 0 40px 80px -28px rgba(0,0,0,.75); }

/* ---------- The artboard's sections (the scroll story and the product page frame) ---------- */
.th-ds__sec { position: absolute; left: 0; top: calc(var(--y) * 1px); width: 1440px; height: calc(var(--h) * 1px); overflow: hidden; }
.th-ds__sec img { position: absolute; left: 0; top: 0; width: 1440px; height: 100%; max-width: none; display: block; user-select: none; -webkit-user-drag: none; }
.th-ds__img[data-src] { visibility: hidden; }

/* ---------- The theme editor (a light panel, like Shopify's, beside the storefront) ---------- */
.th-ed {
  display: flex; flex-direction: column; min-height: 0; min-width: 0; border-radius: 16px; overflow: hidden;
  background: #fff; color: #303030; font-family: var(--font);
  box-shadow: 0 0 0 1px rgba(255,255,255,.08), 0 30px 60px -28px rgba(0,0,0,.75);
}
.th-ed__top { flex: none; display: flex; flex-direction: column; gap: 2px; padding: 14px 16px 12px; border-bottom: 1px solid #e3e3e3; }
.th-ed__page { font-size: 14px; font-weight: 600; line-height: 1.3; letter-spacing: -.01em; }
.th-ed__meta { font-size: 12px; line-height: 1.4; color: #616161; }
/* When a theme has more rows than the panel holds, the list scrolls and follows the active row; its edges fade so a cut row reads as more.
   In the scroll story the wheel and the trackpad belong to the story, so the list only moves with it (and to show a row reached with Tab). */
.th-ed__list { flex: 1 1 auto; min-height: 0; overflow-y: auto; scrollbar-width: thin; scrollbar-color: #d4d4d4 transparent; padding: 6px 8px 8px; }
.th-ds:not([data-tabs]) .th-ed__list { overflow-y: hidden; }
.th-ed__list.is-over { -webkit-mask-image: linear-gradient(transparent, #000 14px, #000 calc(100% - 22px), transparent); mask-image: linear-gradient(transparent, #000 14px, #000 calc(100% - 22px), transparent); }
.th-ed__grp { margin: 0; padding: 10px 8px 4px; font-size: 12px; font-weight: 550; line-height: 1.3; color: #616161; }
.th-ed__items { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 4px; }
.th-ed__item {
  position: relative; display: flex; align-items: center; gap: 10px; width: 100%; min-height: 40px; padding: 0 10px; margin: 0;
  border: 0; border-radius: 8px; background: transparent; color: #303030; font: 500 13px/1.2 var(--font); text-align: left; cursor: pointer;
  transition: background-color .15s ease, box-shadow .15s ease, color .15s ease;
}
.th-ed__item::after { content: ""; position: absolute; inset: -2px 0; }   /* the 44px hit area: the row is 40px, the gap 4px, so each row owns 2px of it each side and no two overlap */
.th-ed__item:hover { background: #f1f1f1; }
/* The active row: a tint and a blue bar on its left edge. The full blue ring is keyboard focus only, so the two never look alike. */
.th-ed__item.is-on { background: #f2f7fe; box-shadow: inset 3px 0 0 var(--th-eb); }
.th-ed__item:focus-visible { outline: 2px solid var(--th-eb); outline-offset: -2px; }
.th-ed__ico { flex: none; color: #616161; transition: color .15s ease; }
.th-ed__item.is-on .th-ed__ico { color: var(--th-eb); }
.th-ed__name { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.th-ed__dot { flex: none; width: 8px; height: 8px; border-radius: 50%; box-shadow: inset 0 0 0 1.5px #c4c7cb; transition: background-color .3s ease, box-shadow .3s ease; }
.th-ed__item.is-done .th-ed__dot { background: var(--th-eb); box-shadow: inset 0 0 0 1.5px var(--th-eb); }
.th-ed__note { flex: none; display: grid; margin: 10px 12px 12px; padding: 11px 13px 12px; border-radius: 10px; background: #f6f6f7; box-shadow: inset 0 0 0 1px #e3e3e3; }
/* The live note and an unseen copy of every note share one grid cell, so the card is as tall as the longest note at this width and font. */
.th-ed__note > * { grid-area: 1 / 1; }
.th-ed__notetxt { margin: 0; font-size: 13px; line-height: 1.45; color: #303030; text-wrap: pretty; transition: opacity .18s ease, transform .18s ease; }
.th-ed__notesz { margin: 0; font-size: 13px; line-height: 1.45; text-wrap: pretty; visibility: hidden; pointer-events: none; }
.th-ed__note.is-swap .th-ed__notetxt { opacity: 0; transform: translateY(4px); }
/* The counter and one tick per section: the ticks share what the counter leaves (7, 9 or 11 of them), up to 12px each. */
.th-ed__prog { flex: none; display: flex; align-items: center; justify-content: space-between; gap: 12px; padding: 11px 16px 13px; border-top: 1px solid #e3e3e3; }
.th-ed__count { flex: none; margin: 0; font-family: var(--mono); font-size: 11px; font-weight: 500; line-height: 1.3; letter-spacing: .08em; text-transform: uppercase; font-variant-numeric: tabular-nums; color: #616161; white-space: nowrap; }
.th-ed__ticks { flex: 0 1 auto; min-width: 0; display: flex; justify-content: flex-end; gap: 3px; }
.th-ed__tick { display: block; flex: 0 1 auto; width: 12px; min-width: 4px; height: 3px; border-radius: 2px; background: #e3e3e3; transition: background-color .3s ease; }
.th-ed__tick.is-done { background: #9ec1f1; }
.th-ed__tick.is-on { background: var(--th-eb); }
/* The whole page is in (the end of the scroll story): every row and tick reads as done; no single row claims to be the one in view. */
.th-ds.is-done-all .th-ed__item.is-on:not(:hover) { background: transparent; box-shadow: none; }
.th-ds.is-done-all .th-ed__item.is-on .th-ed__ico { color: #616161; }
.th-ds.is-done-all .th-ed__tick { background: var(--th-eb); }
/* ...and the note card steps aside (JS says "All N sections" in the counter): its note was about one section, and none is the one in view
   any more. It keeps its box (visibility, not display), so the editor never changes size at the finale. */
.th-ed__note { transition: opacity .25s ease, visibility 0s; }
.th-ds.is-done-all .th-ed__note { opacity: 0; visibility: hidden; transition: opacity .25s ease, visibility 0s .25s; }

/* ---------- 03 Design, the scroll story ---------- */
.th-ds__track { position: relative; height: calc(var(--steps, var(--n)) * 55vh + 100vh); height: calc(var(--steps, var(--n)) * 55svh + 100svh); }   /* --steps: a Header-group section's step is short (design.njk) */
.th-ds__sticky {
  position: sticky; top: calc(var(--header-h) + 16px); min-height: 520px; max-height: 800px;
  height: calc(100vh - var(--header-h) - 32px); height: calc(100svh - var(--header-h) - 32px);
}
.th-ds__stage { display: grid; grid-template-columns: 296px minmax(0, 1fr); gap: 20px; height: 100%; }
.th-ds__frame { min-width: 0; min-height: 0; display: flex; flex-direction: column; }
.th-ds__frame .th-browser { flex: 1; min-height: 0; }
.th-ds__frame .th-viewport { flex: 1; min-height: 0; }
.th-ds__stage .th-artboard { translate: 0 calc(var(--th-ty, 0) * var(--k) * -1px); will-change: translate; }
.th-ds[data-tabs] .th-ds__stage .th-artboard { transition: translate .55s var(--ease-out); }

/* Inking: the wireframe shows until the section is active; then the render wipes in left to right (a clip) with a thin blue scan line
   riding its edge. The scan line is 2 screen px wide whatever the artboard's scale. The wipe waits for the render itself (.is-ready: loaded
   and decoded, set by e2-theme-store.js), so it never sweeps over an empty layer: until then the wireframe stays. */
.th-ds__ink { position: absolute; inset: 0; clip-path: inset(0 100% 0 0); transition: clip-path .65s cubic-bezier(.35, .55, .2, 1); will-change: clip-path; }
.th-ds__sec.is-inked.is-ready .th-ds__ink { clip-path: inset(0 0 0 0); }
.th-ds__scan {
  position: absolute; top: 0; bottom: 0; left: 0; width: calc(2px / var(--k)); opacity: 0; pointer-events: none; z-index: 2;
  background: #2c7be5; box-shadow: 0 0 calc(18px / var(--k)) calc(3px / var(--k)) rgba(44, 123, 229, .5);
}
.th-ds__sec.is-inked.is-ready .th-ds__scan { animation: th-scan .65s cubic-bezier(.35, .55, .2, 1) both; }
@keyframes th-scan {
  0% { transform: translateX(0); opacity: 0; }
  10% { opacity: 1; }
  80% { opacity: 1; }
  100% { transform: translateX(1440px); opacity: 0; }
}
/* The finale: the banner clip plays over the banner photo, cut round the storefront UI drawn on it (the clip-path from themesOver), so the
   header, the headline and the buttons stay the exact render. Hidden until it plays; the theme's image overlay (.th-ds__shade) fades in
   with it. Three kinds of themesOver rect: a plain one is cut out of the clip (above), a "matte" one is light type keyed from the render
   over it (.th-ds__matte), a "layer" one is the copy block as an RGBA picture over it (.th-ds__layer). */
.th-ds__fin {
  position: absolute; z-index: 3; left: calc(var(--bx) * 1px); top: calc(var(--by) * 1px); width: calc(var(--bw) * 1px); height: calc(var(--bh) * 1px);
  opacity: 0; transition: opacity .7s ease; pointer-events: none;
}
.th-ds__video { display: block; width: 100%; height: 100%; object-fit: cover; background: transparent; }
.th-ds__shade { position: absolute; inset: 0; background: #000; opacity: var(--shade); }
/* Registration (th.hero.banner.dy, optional), as the hero's: the clip and its shade drawn that much lower, together. */
.th-ds__video, .th-ds__shade { transform: translateY(calc(var(--dy, 0) * 1px)); }
/* Light type on the photo (a themesOver rect with "matte"): the render's own pixels, keyed by luminance, over the clip. Not .th-banner__matte
   (e2-theme.css keeps that hidden outside the playing hero): this one rides the fin's own fade. */
.th-ds__defs { position: absolute; width: 0; height: 0; overflow: hidden; }
.th-ds__matte { position: absolute; overflow: hidden; left: calc(var(--mx) * 1px); top: calc(var(--my) * 1px); width: calc(var(--mw) * 1px); height: calc(var(--mh) * 1px); }
.th-ds__matte img { position: absolute; left: calc(var(--ix) * -1px); top: calc(var(--iy) * -1px); width: 1440px; max-width: none; height: auto; }
/* A copy block as a picture (a themesOver rect with "layer"): at its rect, in the render's px (no --dy: it is registered to the render, not to the
   clip). No opacity of its own: it rides the fin's fade, so it comes in and goes with the clip. */
.th-ds__layer { position: absolute; max-width: none; pointer-events: none; user-select: none; -webkit-user-select: none; left: calc(var(--mx) * 1px); top: calc(var(--my) * 1px); width: calc(var(--mw) * 1px); height: calc(var(--mh) * 1px); }
.th-ds.is-finale .th-ds__fin.is-ready { opacity: 1; }
/* The cut back to the banner is a cross-dissolve: the page's top, finished, waits in a second artboard under the moving one (it takes the
   same translate, --th-ty = the first section's view, from the script) and shows only while the moving one fades out over it (.is-dip). */
.th-ds__under { visibility: hidden; }
.th-ds__stage .th-ds__under { will-change: auto; }   /* (the moving artboard's own hint is not for the one that only holds still) */
.th-ds.is-dip .th-ds__under { visibility: visible; }
.th-ds__uimg:not([src]) { visibility: hidden; }
.th-ds[data-tabs] .th-ds__under { display: none; }
/* The finale's pause (shown by JS only while the clip is the one playing): a 44px hit area round a 28px disc, at the frame's top-right
   corner, over the browser bar's end (about 10px of it overlaps the page, clear of the storefront header's icons). */
.th-ds__pause {
  position: absolute; z-index: 4; top: 0; right: 4px; width: 44px; height: 44px; display: grid; place-items: center; padding: 0; border: 0;
  background: none; color: #303030; cursor: pointer; -webkit-tap-highlight-color: transparent;
}
.th-ds__pause[hidden], .th-ds[data-tabs] .th-ds__pause { display: none; }
.th-ds__pause::before { content: ""; position: absolute; inset: 8px; border-radius: 999px; background: rgba(255, 255, 255, .9); box-shadow: 0 0 0 1px rgba(0, 0, 0, .12); transition: background-color .2s ease, scale .15s ease; }
.th-ds__pause:hover::before { background: #fff; }
.th-ds__pause:active::before { scale: .94; }
.th-ds__pause:focus-visible { outline: 2px solid var(--th-eb); outline-offset: -4px; border-radius: 999px; }   /* the stage's own focus blue, as the editor's rows */
.th-ds__pico { position: relative; display: block; }
.th-ds__pico--play, .th-ds__pause[aria-pressed="true"] .th-ds__pico--pause { display: none; }
.th-ds__pause[aria-pressed="true"] .th-ds__pico--play { display: block; }

/* ---------- 04 Optimise: the decisions ---------- */
.th-dec { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 8px; min-width: 0; }
.th-dec__item { border-radius: 16px; background: var(--surface-1); box-shadow: inset 0 0 0 1px var(--border); transition: background-color .2s ease, box-shadow .2s ease; }
.th-dec__item.is-on { background: var(--surface-2); box-shadow: inset 0 0 0 1.5px var(--blue); }
.th-dec__head { display: flex; align-items: center; gap: 14px; width: 100%; min-height: 56px; margin: 0; padding: 12px 16px; border: 0; border-radius: 16px; background: none; color: inherit; font: inherit; text-align: left; cursor: pointer; }
.th-dec__head:focus-visible { outline: 2px solid var(--blue-light); outline-offset: -2px; }
.th-dec__num {
  flex: none; display: grid; place-items: center; width: 28px; height: 28px; border-radius: 50%; background: #fff; color: var(--th-pin);
  box-shadow: inset 0 0 0 2px var(--th-pin); font: 600 13px/1 var(--font); font-variant-numeric: tabular-nums; transition: background-color .2s ease, color .2s ease;
}
.th-dec__item.is-on .th-dec__num { background: var(--th-pin); color: #fff; }
.th-dec__title { font-size: 16px; font-weight: 500; line-height: 1.3; letter-spacing: -.01em; color: var(--text-2); transition: color .2s ease; }
.th-dec__item.is-on .th-dec__title { color: #fff; }
.th-dec__body { display: grid; grid-template-rows: 1fr; transition: grid-template-rows .32s var(--ease-out); }
.th-dec__body > p { margin: 0; min-height: 0; overflow: hidden; padding: 0 18px 0 58px; font-size: 15px; line-height: 1.5; color: var(--text-3); text-wrap: pretty; }
.th-dec__body > p::after { content: ""; display: block; height: 16px; }
/* Once the script runs, only the open decision shows its reason (without it, every reason stays open). A closed reason is hidden as well
   as collapsed, so a screen reader never reads a "collapsed" panel: it hides once its collapse has run (an opening one shows at once). */
.th-op[data-ready] .th-dec__item:not(.is-on) .th-dec__body { grid-template-rows: 0fr; visibility: hidden; transition: grid-template-rows .32s var(--ease-out), visibility 0s .32s; }

/* ---------- 04 Optimise: the product page frame and its hotspots ---------- */
.th-op__still { display: grid; grid-template-columns: minmax(0, 1fr) 340px; gap: clamp(32px, 4vw, 56px); align-items: start; content-visibility: auto; contain-intrinsic-size: auto 1100px; }
.th-op__main { display: flex; flex-direction: column; gap: 28px; min-width: 0; }
.th-op__browser .th-viewport { aspect-ratio: 1440 / var(--vis); }
/* The open decision's row on the page: a hairline of the pin's blue round it, a few px clear of the crop (the editor's selection, drawn
   on the storefront). 1.5 screen px whatever the frame's scale. */
.th-op__row {
  position: absolute; left: calc(var(--x) * 1px); top: calc(var(--y) * 1px); width: calc(var(--w) * 1px); height: calc(var(--h) * 1px);
  border-radius: calc(6px / var(--k)); background: rgba(31, 95, 217, .045); box-shadow: 0 0 0 calc(1.5px / var(--k)) var(--th-pin);
  opacity: 0; transition: opacity .18s ease;
}
.th-op__row.is-on { opacity: 1; }
.th-op__hots { position: absolute; inset: 0; z-index: 2; }
/* The button is the 44px hit area; the dot is 26px (22px on a frame narrower than ~850px, where two close rows' dots would touch); the
   ring pulses until the decision is open. */
.th-op__hots { --dot: clamp(22px, 3.06cqw, 26px); }
.th-hot {
  position: absolute; left: var(--x); top: var(--y); width: 44px; height: 44px; margin: -22px 0 0 -22px; padding: 0; border: 0; border-radius: 50%;
  display: grid; place-items: center; background: none; cursor: pointer; -webkit-tap-highlight-color: transparent;
}
/* A row's hotspot sits in the gutter right of the buy box (--gut, artboard px), every one on the same vertical line, its dot's edge just
   clear of the row's outline (the outline is 8 artboard px out; the dot is --dot wide, x1.12 when open, so the left offset in screen px is
   8 * k + .56 * dot + 4). On a small frame the gutter is narrower than that: the dot centres in it instead. The offsets are margins, not a
   transform, so a button makes no stacking context of its own (see the dot below).
   Two short neighbouring rows would put their dots closer than a fingertip: e2-theme-store.js (initOpt) spaces the row dots at least 44px
   apart, centre to centre, as little as it can (--dy, screen px), and draws a hairline from a moved dot back to its row (.th-op__lead).
   The 44px button (and its 2px ring + 2px offset when focused) must lie wholly inside the frame, or its right side is clipped and the tap
   width falls short of 44: where the gutter is narrower than the dot's own offset plus that, --ov moves the button left by the overflow and
   the dot back right by the same, so the dot stays where it was and only the hit area slides in (the button then overlaps the buy box's
   edge, which is not interactive in the replica). */
.th-hot--row {
  --off: min(calc(100cqw * 8 / 1440 + var(--dot) * .56 + 4px), calc(100cqw * var(--gut, 100) / 2880));
  --ov: max(0px, calc(26px - (100cqw * var(--gut, 100) / 1440 - var(--off))));
  margin-left: calc(-22px + var(--off) - var(--ov)); margin-top: calc(-22px + var(--dy, 0px));
}
.th-hot--row .th-hot__dot { translate: var(--ov) 0; }
.th-op__lead { position: absolute; inset: 0; width: 100%; height: 100%; overflow: visible; pointer-events: none; }
.th-op__lead line { stroke: var(--th-pin); stroke-opacity: .55; stroke-width: 1; }
/* Every dot paints and hit-tests above its neighbours' transparent 44px halos, so a press on a dot is always that dot's. */
.th-hot__dot {
  position: relative; z-index: 1; display: grid; place-items: center; width: var(--dot); height: var(--dot); border-radius: 50%; background: #fff; color: var(--th-pin);
  box-shadow: 0 0 0 2px var(--th-pin), 0 6px 14px rgba(0, 0, 0, .2); font: 600 calc(var(--dot) * .46)/1 var(--font); font-variant-numeric: tabular-nums;
  transition: transform .2s var(--th-spring), background-color .2s ease, color .2s ease;
}
/* The ring pulses four times once the frame is in view (.is-live), and stops for good at the first hotspot or decision opened (.is-touched):
   a hint, not motion that runs beside the reading (WCAG 2.2.2). Longhands, so the staggered delays below still apply. */
.th-hot__dot::before { content: ""; position: absolute; inset: 0; border-radius: 50%; pointer-events: none; box-shadow: 0 0 0 2px rgba(31, 95, 217, .6); opacity: 0; }
.th-op.is-live:not(.is-touched) .th-hot:not(.is-on) .th-hot__dot::before { animation-name: th-pulse; animation-duration: 2.4s; animation-timing-function: ease-out; animation-iteration-count: 4; }
.th-hot:nth-child(2) .th-hot__dot::before { animation-delay: -.5s; }
.th-hot:nth-child(3) .th-hot__dot::before { animation-delay: -1s; }
.th-hot:nth-child(4) .th-hot__dot::before { animation-delay: -1.5s; }
.th-hot:nth-child(5) .th-hot__dot::before { animation-delay: -2s; }
@keyframes th-pulse { 0% { transform: scale(1); opacity: .9; } 70%, 100% { transform: scale(2); opacity: 0; } }
.th-hot:hover .th-hot__dot { transform: scale(1.1); }
.th-hot.is-on .th-hot__dot { background: var(--th-pin); color: #fff; transform: scale(1.12); }
.th-hot.is-on .th-hot__dot::before { animation: none; opacity: 0; }
/* On the white storefront the page's light blue is too faint for a focus ring: a solid pin-blue ring, set off from the dot. */
.th-hot:focus-visible { outline: 2px solid var(--th-pin); outline-offset: 2px; }

/* ---------- The buy box on phones: every row crop at its own y inside a box as wide as the real one, so stacked they rebuild it ---------- */
.th-bx { position: absolute; left: 0; top: 0; width: calc(var(--w) * 1px); height: calc(var(--h) * 1px); }
.th-bx__row { position: absolute; left: 0; top: calc(var(--y) * 1px); width: 100%; height: calc(var(--h) * 1px); }
.th-bx__row img { display: block; width: 100%; height: 100%; max-width: none; -webkit-user-drag: none; }
.th-bx__pin {
  position: absolute; left: 100%; top: 50%; display: grid; place-items: center; border-radius: 50%; background: #fff; color: var(--th-pin);
  --pin: 22px; width: calc(var(--pin) * var(--inv, 1)); height: calc(var(--pin) * var(--inv, 1)); margin: calc(var(--pin) * var(--inv, 1) / -2) 0 0 calc(var(--pin) * var(--inv, 1) / -2);
  box-shadow: 0 0 0 calc(2px * var(--inv, 1)) var(--th-pin), 0 calc(6px * var(--inv, 1)) calc(14px * var(--inv, 1)) rgba(0, 0, 0, .18);
  font: 600 calc(12px * var(--inv, 1)) / 1 var(--font); font-variant-numeric: tabular-nums;
}
.th-op__mob { display: none; }

/* ---------- The phone ---------- */
.th-ph-wrap { display: flex; flex-direction: column; align-items: center; gap: 14px; width: 100%; }
.th-ph-fit { --w: var(--ph-w, min(100%, 320px)); position: relative; width: var(--w); aspect-ratio: 414 / 868; container-type: inline-size; }
.th-ph {
  position: absolute; left: 0; top: 0; width: 414px; height: 868px; padding: 12px; border-radius: 60px; background: #0a0a0c; transform-origin: 0 0;
  transform: scale(var(--pk, tan(atan2(100cqw, 414px)))); box-shadow: 0 0 0 1.5px #3a3a40, 0 0 0 3px #17171a, 0 50px 90px -24px rgba(0, 0, 0, .8); user-select: none; -webkit-user-select: none;
}
.th-ph__screen { position: relative; width: 390px; height: 844px; border-radius: 48px; overflow: hidden; isolation: isolate; color: var(--ink); font-family: var(--f-ui); font-weight: var(--w-ui); }
/* The display's rim: a bezel-coloured ring over the screen's outermost 1.5px (and 1px of the bezel), as a real phone's black border round
   the display. The composited scroll layer's clip isn't always drawn like the rest of the screen's edge (at some scales it snaps to whole
   device px, tile by tile); under the rim no edge pixel shows, only the rim's own even inner edge. */
.th-ph::after { content: ''; position: absolute; z-index: 2; inset: 11px; border-radius: 49px; box-shadow: inset 0 0 0 2.5px #0a0a0c; pointer-events: none; }
.th-ph__island { position: absolute; z-index: 8; top: 11px; left: 50%; width: 122px; height: 35px; margin-left: -61px; border-radius: 20px; background: #000; }
.th-ph__home { position: absolute; z-index: 8; bottom: 8px; left: 50%; width: 136px; height: 5px; margin-left: -68px; border-radius: 3px; background: var(--ink); opacity: .92; }
.th-ph__status { position: absolute; z-index: 7; top: -2px; left: -2px; right: -2px; height: 49px; padding: 2px 32px 0 40px; display: flex; align-items: center; justify-content: space-between; background: #fff; color: var(--ink); }
.th-ph__time { font: 600 16px/1 var(--f-ui); letter-spacing: -.01em; }
.th-ph__sys { display: flex; align-items: center; gap: 6px; }
/* The screen's edges. Only the screen's rounded clip draws an edge: the layers under it (the status bar, the page, the sticky bar) run 2px
   past it on every side that meets it (padding puts their content back), so no layer's own edge lands on the clip's anti-aliased one,
   where the two would blend twice and let whatever is under them through. The screen paints no background (the page does): the clip's
   edge blends the page with the bezel, never white beside the dark footer. The page lives in a viewport that starts 3px above the status
   bar's bottom, under it, so nothing scrolled ever reaches the top edge (the CTA's label used to show there as a dotted line above the
   island). While the sticky bar is up, .is-under ends the viewport just inside the bar's top edge, so nothing sits under the bar at the
   bottom edge either (--vb: set by initPhone from the bar's height). */
.th-ph__vp { position: absolute; z-index: 1; left: -2px; right: -2px; top: 44px; bottom: -2px; overflow: hidden; }
.th-ph__vp.is-under { bottom: var(--vb, -2px); }   /* a plain box clip (not clip-path): the compositor applies it as is, no repaint */
.th-ph__scroll {
  position: absolute; left: 0; top: -44px; width: 100%; min-height: calc(100% + 44px); padding: 47px 2px 0; display: flex; flex-direction: column; background: #fff;
  transform: translateY(calc(var(--sy, 0) * -1px)); will-change: transform;
}
.th-m-ico { display: grid; place-items: center; width: 40px; height: 40px; }
.th-m-ico--acc { color: var(--acc); width: auto; height: auto; flex: none; }
.th-m-bar { display: flex; align-items: center; justify-content: space-between; height: 52px; padding: 0 14px; }
.th-m-brand { font-family: var(--f-brand); font-size: 26px; font-weight: var(--w-head); line-height: 1; letter-spacing: -.01em; white-space: nowrap; }
/* A theme with its own wordmark artwork (th.wordmark): the brand is drawn from it as a mask, in the text's colour, not set in a font. */
.th-wm { display: block; width: auto; background: currentColor; -webkit-mask: var(--wm) center / contain no-repeat; mask: var(--wm) center / contain no-repeat; }
.th-m-brand.th-wm { height: 24px; max-width: 150px; }   /* a wide, tracked mark (Aurelle) is capped by width; the mask keeps its shape */
.th-m-foot__brand.th-wm { height: 28px; max-width: 200px; align-self: flex-start; }
@media (forced-colors: active) { .th-wm { forced-color-adjust: none; background: CanvasText; } }
/* The product image, full bleed (into the page's 2px overhang). A cut-out stands on the theme's mist, its shadow under it; a photo fills. */
.th-m-img { position: relative; height: 410px; margin-inline: -2px; display: grid; place-items: center; overflow: hidden; background: var(--mist); }
.th-m-img--cut img { max-height: 350px; max-width: 300px; width: auto; height: auto; filter: drop-shadow(0 18px 14px rgba(2, 9, 18, .16)); transition: opacity .2s ease; }
.th-m-img--photo { height: 470px; }
.th-m-img--photo img { position: absolute; inset: 0; width: 100%; height: 100%; max-width: none; object-fit: cover; object-position: 50% 30%; transition: opacity .2s ease; }
.th-m-img img.is-swap { opacity: 0; }
/* A colour chip renames the product as it swaps the photo ("Tan Structured Tote Bag"): the colour word dips out and back with it. */
.th-m-word { transition: opacity .2s ease; }
.th-m-word.is-swap { opacity: 0; }
.th-m-dots { position: absolute; left: 0; right: 0; bottom: 14px; display: flex; justify-content: center; gap: 6px; }
.th-m-dots i { width: 6px; height: 6px; border-radius: 50%; background: var(--ink); opacity: .22; }
.th-m-dots i.is-on { opacity: .85; width: 18px; border-radius: 3px; }
.th-m-img--photo .th-m-dots i { background: #fff; opacity: .55; box-shadow: 0 0 0 .5px rgba(0, 0, 0, .12); }
.th-m-img--photo .th-m-dots i.is-on { opacity: 1; }
.th-m-info { display: flex; flex-direction: column; gap: 14px; padding: 22px 20px 36px; }
.th-m-eyebrow { margin: 0 0 -8px; font: 600 11.5px/1.2 var(--f-ui); letter-spacing: .13em; text-transform: uppercase; color: var(--acc); }   /* sits close over the name (the page's 14px gap less 8) */
.th-m-name { margin: 0; font-family: var(--f-head); font-size: 30px; font-weight: var(--w-head); line-height: 1.12; letter-spacing: -.015em; text-wrap: balance; }
.th-m-rate { margin: 0; display: flex; align-items: center; gap: 8px; font-size: 14px; line-height: 1; }
.th-m-stars { display: inline-flex; gap: 2px; }
.th-m-star {
  width: 15px; height: 15px; background: linear-gradient(90deg, var(--star, #f5b301) var(--fill), var(--star-off, #d6d9e0) var(--fill));   /* --star / --star-off: the theme's own (phone.njk), amber and grey without */
  -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M12 2.2l2.9 6.2 6.8.8-5 4.7 1.3 6.7L12 17.2 6 20.6l1.3-6.7-5-4.7 6.8-.8z'/%3E%3C/svg%3E") center / contain no-repeat;
  mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M12 2.2l2.9 6.2 6.8.8-5 4.7 1.3 6.7L12 17.2 6 20.6l1.3-6.7-5-4.7 6.8-.8z'/%3E%3C/svg%3E") center / contain no-repeat;
}
.th-m-reviews { color: var(--slate); }
.th-m-price { margin: 0; display: flex; align-items: baseline; flex-wrap: wrap; gap: 10px; font-family: var(--f-prod); }
.th-m-now { font-size: 30px; font-weight: var(--w-prod); line-height: 1.1; letter-spacing: -.02em; font-variant-numeric: tabular-nums; }
.th-m-was { font-size: 18px; font-weight: 400; color: var(--slate); text-decoration: line-through; transition: opacity .18s ease; }
.th-m-was.is-off, .th-m-sprice s.is-off { opacity: 0; }
/* The sale badge is the theme's own (red, the product font), as in the product page render and the token strip's .th-badge
   (e2-theme-sig.css): keep the two in step. It steps out (in place, the row never reflows) when another option is picked. */
.th-m-save {
  align-self: center; padding: 4px 10px; border-radius: 999px; background: #fde7e5; color: #d3453d; font: 500 13px/1.2 var(--f-prod); font-variant-numeric: tabular-nums;
  transition: opacity .18s ease, transform .18s ease;
}
.th-m-save.is-off { opacity: 0; transform: scale(.92); }
/* A status beside the price ("Pre order"): the theme's accent on a tint of it, a quiet dot before it, never mistaken for a sale. */
.th-m-badge, .th-m-sbadge {
  align-self: center; display: inline-flex; align-items: center; gap: 6px; padding: 5px 10px 5px 8px; border-radius: 999px;
  background: color-mix(in srgb, var(--acc) 10%, #fff); color: var(--acc); font: 500 13px/1.2 var(--f-prod);
}
.th-m-badge::before, .th-m-sbadge::before { content: ""; width: 6px; height: 6px; border-radius: 50%; background: currentColor; }
/* The size chart (M.sizeChart): the product page's own line above the size boxes, a label and a tape-measure icon. Not an option, so not a
   chip and never in the chip row; pulled 4px closer than the page's rhythm so it reads as the boxes' label. */
.th-m-sizechart { margin: 0 0 -4px; display: flex; align-items: center; gap: 8px; font: 500 14px/1.3 var(--f-ui); color: var(--ink); }
.th-m-sizechart svg { flex: none; width: 18px; height: 18px; }
/* The options. Packs are pills; sizes are square boxes (a hairline, the picked one inked); colours are pills with their swatch. */
.th-m-chips { display: flex; flex-wrap: wrap; gap: 8px; }
.th-m-chip { display: inline-flex; align-items: center; gap: 8px; padding: 11px 16px; border-radius: 999px; background: var(--mist); color: var(--ink); font: 500 14px/1 var(--f-prod); transition: background-color .15s ease, color .15s ease, box-shadow .15s ease; }
.th-m-chip.is-hover { background: color-mix(in srgb, var(--mist) 70%, var(--slate)); }
.th-m-chip.is-sel { background: var(--ink); color: #fff; }
.th-ph--size .th-m-chip { justify-content: center; min-width: 52px; height: 48px; padding: 0 12px; border-radius: var(--btn-r, 2px); background: #fff; color: color-mix(in srgb, var(--ink) 70%, #fff); box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--slate) 30%, #fff); }
.th-ph--size .th-m-chip.is-hover { box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--ink) 50%, #fff); }
.th-ph--size .th-m-chip.is-sel { color: var(--ink); box-shadow: inset 0 0 0 1.5px var(--ink); }
.th-ph--colour .th-m-chip { padding: 8px 15px 8px 9px; background: #fff; box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--slate) 28%, #fff); }
.th-ph--colour .th-m-chip.is-hover { box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--ink) 50%, #fff); }
.th-ph--colour .th-m-chip.is-sel { background: #fff; color: var(--ink); box-shadow: inset 0 0 0 1.5px var(--ink); }
/* The options as the page draws them (M.optionStyle 'outline'): outlined pills side by side, the name left in semibold and the option's price
   right, muted; the picked one in a 1.5px ink line and its price in ink. Two to a row, any more wrap. */
.th-ph--outline .th-m-chip { flex: 1 1 calc(50% - 4px); min-width: 0; justify-content: space-between; height: 48px; padding: 0 18px; background: transparent; color: var(--ink); font: 400 14.5px/1 var(--f-prod); box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--acc) 26%, var(--mist)); }
.th-ph--outline .th-m-chip.is-hover { background: transparent; box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--ink) 50%, #fff); }
.th-ph--outline .th-m-chip.is-sel { background: transparent; color: var(--ink); box-shadow: inset 0 0 0 1.5px var(--ink); }
.th-m-chip__n { font-weight: 600; white-space: nowrap; }
.th-m-chip__p { font-style: normal; font-weight: 400; font-variant-numeric: tabular-nums; color: color-mix(in srgb, var(--ink) 62%, #fff); transition: color .15s ease; }
.th-m-chip.is-sel .th-m-chip__p { color: var(--ink); }
.th-m-chip__sw { flex: none; width: 22px; height: 22px; border-radius: 50%; background: var(--c); box-shadow: inset 0 0 0 1px rgba(0, 0, 0, .12); }
/* "Good for" (M.goodFor): a label and static chips, the page's own, tinted from the accent and the mist (never an option, so not tappable). */
.th-m-good { display: flex; flex-direction: column; gap: 8px; }
.th-m-good__label { margin: 0; font: 600 13px/1.2 var(--f-ui); letter-spacing: .02em; }
.th-m-good__row { list-style: none; margin: 0; padding: 0; display: flex; flex-wrap: wrap; gap: 8px; }
.th-m-good__row li { display: inline-flex; align-items: center; height: 32px; padding: 0 14px; border-radius: 999px; background: color-mix(in srgb, var(--acc) 10%, var(--mist)); color: color-mix(in srgb, var(--ink) 88%, #fff); font: 500 13px/1 var(--f-ui); }
.th-m-subtxt { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.th-m-subtxt .th-m-sublabel { font-weight: 600; }
.th-m-subnote { font-size: 12.5px; line-height: 1.35; color: color-mix(in srgb, var(--ink) 62%, #fff); }
.th-m-sub { display: flex; align-items: center; gap: 12px; padding: 12px 14px; border-radius: 14px; box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--slate) 35%, #fff); font-size: 14px; line-height: 1.3; transition: box-shadow .2s ease, background-color .2s ease; }
.th-m-sub.is-hover { background: color-mix(in srgb, var(--mist) 60%, #fff); }
.th-m-sub.is-on { box-shadow: inset 0 0 0 1.5px var(--acc); }
.th-m-switch { flex: none; position: relative; width: 42px; height: 25px; border-radius: 13px; background: color-mix(in srgb, var(--slate) 45%, #fff); transition: background-color .22s ease; }
.th-m-switch i { position: absolute; top: 2.5px; left: 2.5px; width: 20px; height: 20px; border-radius: 50%; background: #fff; box-shadow: 0 1px 3px rgba(2, 9, 18, .3); transition: transform .25s var(--th-spring); }
.th-m-sub.is-on .th-m-switch { background: var(--acc); }
.th-m-sub.is-on .th-m-switch i { transform: translateX(17px); }
/* The page's button and the bar's: the theme's own shape when it has one (th.tokens.button -> --btn-r / --btn-case / --btn-ls on the root: a
   square, upper-case button reads as capitals with a little tracking); 4px, as written, without. */
.th-m-cta { display: grid; place-items: center; height: 54px; border-radius: var(--btn-r, 4px); background: var(--ink); color: #fff; font: 600 16px/1 var(--f-ui); letter-spacing: var(--btn-ls, .005em); text-transform: var(--btn-case, none); transition: transform .12s ease; }
.th-m-promise { margin: 0; display: flex; align-items: flex-start; gap: 12px; padding: 14px; border-radius: 12px; background: var(--mist); font-size: 14px; line-height: 1.4; }
.th-m-promise .th-m-ico { margin-top: 1px; }
/* One column per item (--cols, set from the data: three at most; two items are two halves, not two thirds of a row). */
.th-m-trust { list-style: none; margin: 0; padding: 0; display: grid; grid-template-columns: repeat(var(--cols, 3), minmax(0, 1fr)); gap: 8px; }
.th-m-trust li { display: flex; flex-direction: column; align-items: center; gap: 6px; text-align: center; font-size: 12px; line-height: 1.3; font-weight: 500; }
.th-m-copy { margin: 0; font-size: 14px; line-height: 1.6; color: color-mix(in srgb, var(--ink) 78%, #fff); }
/* The related row: the store's own grid on a phone, a swipeable row (two cards and the edge of a third), a full section gap below the
   product; name, note and price centred, in the product font. Cut-outs stand on the theme's mist with a soft pool of their own colour under
   them (a bottle in a tall card, a bag in a near-square one); photos (crops of a look) fill a square card. Its height is what keeps the
   footer below the bar where the scroll rests. */
.th-m-rel { padding: 44px 0 64px; border-top: 1px solid color-mix(in srgb, var(--slate) 18%, #fff); }
.th-m-rel__title { margin: 0 20px 18px; font: var(--w-head) 24px/1.15 var(--f-head); letter-spacing: -.01em; }
.th-m-rel__row { list-style: none; margin: 0 -2px 0 0; padding: 0 22px 0 20px; display: flex; gap: 12px; overflow: hidden; }   /* its clip ends in the overhang, past the screen's edge */
.th-m-card { flex: none; width: 164px; display: flex; flex-direction: column; align-items: center; gap: 2px; text-align: center; font-family: var(--f-prod); }
.th-m-card__art { position: relative; display: grid; place-items: end center; width: 100%; aspect-ratio: 2 / 3; margin-bottom: 10px; overflow: hidden; padding-bottom: 20px; background: var(--mist); }
.th-m-rel--cut .th-m-card__art { background: radial-gradient(60% 26% at 50% 91%, color-mix(in srgb, var(--c, var(--slate)) 34%, transparent), transparent 70%), var(--mist); }
.th-m-rel--cut .th-m-card__art img { max-height: 190px; max-width: 138px; width: auto; height: auto; filter: drop-shadow(0 12px 9px rgba(2, 9, 18, .15)); }
.th-m-rel--photo .th-m-card__art { aspect-ratio: 1; padding: 0; }
.th-m-rel--photo .th-m-card__art img { position: absolute; inset: 0; width: 100%; height: 100%; max-width: none; object-fit: cover; }
.th-m-card__name { font-size: 15px; font-weight: 500; line-height: 1.3; }
.th-m-card__note { font-size: 12.5px; line-height: 1.35; color: var(--slate); }
.th-m-card__price { margin-top: 2px; font-size: 15px; font-weight: 600; line-height: 1.3; font-variant-numeric: tabular-nums; }
/* The store's footer ends the page: the brand in its own font, then the menu as a mobile store lists it (one row each, hairlines between),
   with room at the end for the sticky bar. It grows to fill whatever the screen still shows (initPhone makes the page at least that long),
   so the end is never white. */
.th-m-foot { flex: 1 0 auto; margin-inline: -2px; padding: 36px 22px 112px; background: var(--ink); color: #fff; display: flex; flex-direction: column; gap: 22px; }   /* full bleed, as .th-m-img */
.th-m-foot__brand { font: var(--w-head) 28px/1 var(--f-brand); letter-spacing: -.01em; }
.th-m-foot__nav { list-style: none; margin: 0; padding: 0; border-bottom: 1px solid rgba(255, 255, 255, .14); }
.th-m-foot__nav li { padding: 15px 0; border-top: 1px solid rgba(255, 255, 255, .14); }
.th-m-foot__link { font: 400 15px/1.2 var(--f-ui); color: rgba(255, 255, 255, .86); }
.th-m-sticky {
  position: absolute; z-index: 6; left: -2px; right: -2px; bottom: -2px; display: flex; align-items: center; gap: 16px; padding: 12px 20px 32px; background: #fff;
  box-shadow: 0 -10px 28px rgba(2, 9, 18, .12); transform: translateY(calc(100% + 40px)); transition: transform .5s var(--ease-out);   /* down far enough that its shadow goes too */
}
.th-m-sticky.is-on { transform: none; }
.th-m-sprice { margin: 0; display: flex; flex-direction: column; align-items: flex-start; gap: 3px; font-family: var(--f-prod); }
.th-m-sprice b { font-size: 21px; font-weight: var(--w-prod); line-height: 1.15; letter-spacing: -.02em; font-variant-numeric: tabular-nums; }
.th-m-sprice s { font-size: 13px; color: var(--slate); line-height: 1.2; transition: opacity .18s ease; }
.th-m-sbadge { padding: 3px 8px 3px 7px; font-size: 11.5px; gap: 5px; }
.th-m-sbadge::before { width: 5px; height: 5px; }
.th-m-cta--bar { flex: 1; height: 48px; }
.th-m-cta.is-press { transform: scale(.97); }
/* The page's button with the price on it (M.ctaPrice): one grid item, the label, a 4px dot and the live price. */
.th-m-cta__l { display: inline-flex; align-items: center; gap: 12px; }
.th-m-cta__dot { flex: none; width: 4px; height: 4px; border-radius: 50%; background: rgba(255, 255, 255, .5); }
/* The price's digit roll: a changed character is a column of two (old over new) that slides up. */
.th-roll { display: inline-block; height: 1.15em; overflow: hidden; vertical-align: bottom; line-height: 1.15; }
.th-roll > i { display: block; font-style: normal; height: 1.15em; transition: transform .28s var(--ease-out); }
.th-roll.is-go > i { transform: translateY(-1.15em); }
.th-m-was .th-roll, .th-m-sprice s .th-roll { text-decoration: line-through; }   /* a decoration doesn't reach into an inline-block on its own */
.th-m-now.is-rolling, .th-m-sprice b.is-rolling { overflow: hidden; }

/* Pause / Play: one width whichever word it shows. */
.th-ph__pause {
  position: relative; display: inline-flex; align-items: center; justify-content: center; gap: 8px; min-width: 106px; min-height: 44px; padding: 0 18px; border-radius: 999px;
  border: 1px solid #3a3a40; background: #141416; color: #fff; font: 500 14px/1 var(--font); cursor: pointer; transition: border-color .2s ease, background-color .2s ease;
}
.th-ph__pause:hover { border-color: #fff; background: #1c1c20; }
.th-ph__pause:focus-visible { outline: 2px solid var(--blue-light); outline-offset: 3px; }
.th-ph__pico--play { display: none; }
.th-ph__pause.is-paused .th-ph__pico--pause { display: none; }
.th-ph__pause.is-paused .th-ph__pico--play { display: block; }
.th-ph__sr { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; }
/* The phone's options for the keyboard: real radios (and the switch), visually hidden; the ring shows on the chip inside the phone. */
.th-ph-wrap { position: relative; }
.th-ph__keys { position: absolute; left: 0; top: 0; width: 1px; height: 1px; margin: 0; padding: 0; overflow: hidden; clip-path: inset(50%); white-space: nowrap; }
.th-m-chip.is-kbd, .th-m-sub.is-kbd { outline: 3px solid var(--blue-light); outline-offset: 3px; }
.th-ph__copy { display: flex; flex-direction: column; gap: 6px; width: min(100%, 320px); margin-top: 6px; text-align: left; }
.th-ph__ctitle { margin: 0; font-size: 20px; font-weight: 600; line-height: 1.2; letter-spacing: -.03em; }
.th-ph__cbody { margin: 0; font-size: 15px; line-height: 1.5; color: var(--text-3); text-wrap: pretty; }

/* ---------- Tabs: phones, reduced motion and short screens swap the scroll story for chips + one section at a time ---------- */
.th-ds[data-tabs] .th-ds__track { height: auto; }
.th-ds[data-tabs] .th-ds__sticky { position: static; height: auto; max-height: none; min-height: 0; }
/* The editor matches the frame's height (contain: size keeps its content from sizing the row): its list scrolls on a short screen. */
.th-ds[data-tabs] .th-ds__stage { height: auto; align-items: stretch; }
@media (min-width: 700px) { .th-ds[data-tabs] .th-ds__stage > .th-ed { contain: size; } }
.th-ds[data-tabs] .th-ds__frame .th-viewport { flex: none; height: min(640px, 72vh); height: min(640px, 72svh); }
/* a picked section inks in with the same wipe as the story (JS adds .is-inked once its render is decoded) */
.th-ds[data-tabs] .th-ds__fin { display: none; }
.th-ds[data-tabs] .th-ed__ticks, .th-ds[data-tabs] .th-ed__dot { display: none; }
/* Short screens (a landscape phone): the list needs the room more than the editor's title does (the title stays in the group's aria-label). */
@media (max-height: 599px) and (min-width: 700px) {
  .th-ds[data-tabs] .th-ed__top { display: none; }
  .th-ds[data-tabs] .th-ed__note { margin: 8px 10px 10px; }
}

/* ---------- Reduced motion and short screens: the finished states, no pinned stage ---------- */
@media (prefers-reduced-motion: reduce) {
  .th-ds__track { height: auto; }
  .th-ds__sticky { position: static; height: auto; max-height: none; min-height: 0; }
  .th-hot__dot::before { animation: none !important; opacity: 0; }   /* !important: the pulse's own rule above is more specific */
  .th-ph__pause { display: none; }
  /* The still frame holds the page's top with the bar already up: the bar is the frame's one button, so the page's own (above or under the
     bar) is hidden. visibility, not display: the layout, and so initPhone's measure(), stay the same. */
  .th-m-info > .th-m-cta { visibility: hidden; }
  .th-m-sticky { transition: none; }
  .th-ph__scroll { will-change: auto; }   /* a still page needs no layer of its own (one less layer edge under the screen's clip) */
  .th-ds__ink, .th-ed__item, .th-dec__body, .th-dec__item, .th-op__row, .th-m-save, .th-m-was, .th-m-sprice s, .th-m-img img { transition: none !important; }
  .th-ds[data-tabs] .th-ds__ink { clip-path: inset(0 0 0 0); }
  .th-ds[data-tabs] .th-ds__scan { display: none; }
}
@media (max-height: 599px) {
  .th-ds__track { height: auto; }
  .th-ds__sticky { position: static; height: auto; max-height: none; min-height: 0; }
}

/* ---------- Narrower desktops ---------- */
@media (max-width: 1099px) {
  .th-ds__stage { grid-template-columns: 252px minmax(0, 1fr); gap: 16px; }
  .th-dec__title { font-size: 15px; }
}
@media (max-width: 899px) {
  .th-op__still { grid-template-columns: 1fr; }
  .th-op__phonecol { justify-self: center; width: 100%; }
}
/* From 900px: the decisions beside the frame (a hotspot's reason opens next to it, in view, a landscape tablet included), the phone under
   both with its caption beside it. */
@media (min-width: 900px) {
  .th-op__still { grid-template-columns: minmax(0, 1fr); row-gap: clamp(48px, 5vw, 72px); }
  .th-op__main { display: grid; grid-template-columns: minmax(0, 1fr) 300px; gap: 24px; align-items: start; }
  .th-op__main .th-dec { margin-top: 34px; }   /* level with the top of the page in the frame, under its browser bar */
  .th-op__phonecol .th-ph-wrap { display: grid; grid-template-columns: 320px minmax(0, 420px); column-gap: 56px; row-gap: 14px; align-items: center; justify-content: center; }
  .th-op__phonecol .th-ph-fit { grid-column: 1; grid-row: 1; }
  .th-op__phonecol .th-ph__pause { grid-column: 1; grid-row: 2; justify-self: center; }
  .th-op__phonecol .th-ph__copy { grid-column: 2; grid-row: 1 / span 2; width: 100%; margin-top: 0; }
}

/* ---------- Phones: no pinned stage. Chips for the sections, the frame shows one section's render; the buy box shown assembled, with numbered notes ---------- */
@media (max-width: 699px) {
  .th-ds__track { height: auto; }
  .th-ds__sticky { position: static; height: auto; max-height: none; min-height: 0; }
  .th-ds__stage, .th-ds[data-tabs] .th-ds__stage { display: flex; flex-direction: column; align-items: stretch; gap: 14px; height: auto; }
  /* the editor dissolves: its chips come first, its note card after the frame */
  .th-ed { display: contents; }
  .th-ed__top, .th-ed__grp, .th-ed__prog, .th-ed__dot, .th-ed__ico { display: none; }
  /* The list is a scroller (overflow-x auto clips the y overflow too), so the chips' focus ring, which sits 2px outside them, needs room in
     the list's own padding: 5px, pulled back by a -3px margin, which is the 2px the row had. */
  .th-ed__list { order: -1; display: flex; flex-direction: row; gap: 8px; margin: -3px calc(var(--pad) * -1); padding: 5px var(--pad); overflow-x: auto; overflow-y: visible; scrollbar-width: none; scroll-snap-type: x proximity; scroll-padding-inline: var(--pad); }
  .th-ed__list.is-over { -webkit-mask-image: none; mask-image: none; }
  .th-ed__list::-webkit-scrollbar { display: none; }
  .th-ed__items { display: contents; }
  .th-ed__items--fixed { display: none; }   /* the chips are the page's sections; a header drawn inside the first section has no chip of its own */
  .th-ed__list li { flex: none; scroll-snap-align: start; }
  .th-ed__item { width: auto; min-height: 44px; padding: 0 18px; border-radius: 999px; background: var(--surface-2); color: var(--text-2); box-shadow: inset 0 0 0 1px var(--border-strong); font-size: 14px; white-space: nowrap; }
  .th-ed__item::after { inset: 0; }
  .th-ed__item:hover { background: var(--surface-3); }
  .th-ed__item.is-on { background: #fff; color: #000; box-shadow: 0 0 0 2px var(--th-eb); }
  /* On the dark page the white panel's inset ring (blue on #18181B: 2.9:1) gives way to the page's own light-blue ring, outside the chip
     and clear of the active chip's blue halo. */
  .th-ed__item:focus-visible { outline: 2px solid var(--blue-light); outline-offset: 2px; }
  .th-ed__name { overflow: visible; }
  .th-ed__note { order: 3; margin: 0; background: var(--surface-1); box-shadow: inset 0 0 0 1px var(--border); }
  .th-ed__notetxt { color: var(--text-2); }
  .th-ds__frame { order: 2; }
  .th-ds__frame .th-browser { flex: none; border-radius: 14px; }
  /* one frame shape for every chip (the tallest section's), so the note under it never moves */
  .th-ds[data-tabs] .th-ds__frame .th-viewport { flex: none; height: auto; aspect-ratio: 1440 / var(--sec-h, 878); }

  /* Optimise: the buy box, assembled, with its numbered notes; then the phone */
  .th-op__head { order: -2; }
  .th-op__mob { order: -1; display: flex; flex-direction: column; gap: 20px; }
  .th-op__mobbox { padding: 20px 40px 20px 16px; border-radius: 16px; background: #fff; }
  .th-op__mobfit { container-type: inline-size; position: relative; aspect-ratio: var(--w) / var(--h); width: 100%; }
  .th-op__mobfit .th-bx { --inv: calc(1 / tan(atan2(100cqw, calc(var(--w) * 1px)))); transform-origin: 0 0; transform: scale(tan(atan2(100cqw, calc(var(--w) * 1px)))); }
  .th-op__mobfit .th-bx__pin { left: calc(100% + 18px * var(--inv)); z-index: 1; }   /* clear of the Add to Cart bar's end */
  /* A decision that is a point on the page (a gallery, say), not a buy-box row: a window onto the render round it, its pin on the point. */
  .th-op__mobshot { padding: 0; overflow: hidden; }
  .th-bx--crop { overflow: hidden; }
  .th-op__mobfit .th-bx__pin--xy { left: calc(var(--px) * 1%); top: calc(var(--py) * 1%); }
  .th-op__notes { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 14px; }
  .th-op__note { display: flex; gap: 14px; align-items: flex-start; }
  .th-op__note .th-dec__num { margin-top: 1px; }
  .th-op__notetxt { display: flex; flex-direction: column; gap: 3px; font-size: 15px; line-height: 1.45; color: var(--text-3); text-wrap: pretty; }
  .th-op__notetxt b { font-weight: 500; font-size: 16px; color: #fff; letter-spacing: -.01em; }
  .th-op__main { display: none; }
  .th-op__still { display: block; }
  .th-ph-wrap { --ph-w: min(300px, 76vw); }
  .th-ph__copy { width: 100%; max-width: 360px; align-self: center; text-align: center; align-items: center; }
}
