/* Cushion Designer v1.9.62 — styles scoped under .cd-app
 * v1.9.62 (2026-09-01 — same, measured rows): the breakdown holds FOUR
 *   rows (not eight), so 19 -> 20 bought only 7px; rows 20 -> 22 + disclaimer
 *   20 -> 24 close the remaining 12px. Visual disclaimer-to-button gap ~36px.
 * v1.9.61 (2026-09-01 — Step4 gap closed properly): the cta-row
 *   margin-top:auto re-absorbs whatever padding is trimmed (55px visual
 *   stayed constant), so the QUOTE BOX itself must grow: rows 19 -> 20px
 *   (+16px across 8 rows) brings the disclaimer-to-button visual gap to
 *   ~39px. Last change of the uniform-frame round.
 * v1.9.60 (2026-09-01 — one-line trim): cta-row padding-top 22 -> 6 — the
 *   slice itself is 33px; the stacked padding made the visual gap 55px.
 * v1.9.59 (2026-09-01 — Step4 last slice): quote rows 17 -> 19px and the
 *   disclaimer 16 -> 20px — the breakdown box grows ~36px so the
 *   disclaimer-to-CTA visual gap (slice + the row own padding-top)
 *   lands <=40px (was 51 + 22). Nothing else.
 * v1.9.58 (2026-09-01 — measured 1px/25px trims): Step2 slice 2 loses the
 *   unit-toggle residual margin (41 -> ~29px); Step4 quote rows 15 -> 17px
 *   so the disclaimer-to-CTA slice lands ~33px (was 65). Nothing else.
 * v1.9.57 (2026-09-01 — final slice balancing, live-measured): the Step2
 *   form column reaches ~397px of content (input 14px padding, label 8px,
 *   field gap 32, tip 18px, density cards 13px, toggle 10px) and the
 *   meas-tip/dens-pick margin-top hand their space BACK to the even
 *   distribution, so all five slices land ~29px (was 42-51 with margins
 *   stacked on top of the share). No Step1/3/4 change.
 * v1.9.56 (2026-09-01 — third pass, live-measured slices): Step2 form column
 *   gains ~33px of content (input 11px padding, label 5px, field gap 28,
 *   tip 14px, density cards 11px, toggle 8px) so its five space-evenly
 *   slices drop from 42-51px to <=40px; Step4 s4wrap columns stretch
 *   (right column stacks, the render card grows with its canvas centred
 *   in the studio backdrop, params park at the bottom) and quote rows
 *   13 -> 15px so the CTA gap lands <=40px.
 * v1.9.55 (2026-09-01 — second pass of the uniform-frame round, live-measured):
 *   - CORRUPTION FIX: a long-lost opening "/*" above the 961px pv-3d
 *     @media made CSS error recovery DROP the whole rule from the CSSOM —
 * --cd-pv-h never reached .pv-3d (computed 378px with the var holding
 *     529px, bandRulesInCSSOM = 0). Comment restored, the Size preview
 *     now truly fills its column;
 *   - .s2wrap/.s4wrap flex 0 1 auto -> 1 1 auto: the wraps STRETCH to the
 *     step-body (measured dead bands 131px/52px under them);
 *   - form column + quote rows breathe wider so every even slice stays
 *     <=40px; the Order render canvas fills the card width (svg sibling
 *     language).
 * v1.9.54 (2026-09-01 — UNIFORM FIXED FRAME + controlled fill):
 *   - the v1.9.53 per-step :has() height collapse is RETIRED — every step
 *     rides fitScreen's ONE inline height (FRAME_CAP 750 -> 700, designer.js
 *     v1.9.54), so switching steps no longer jumps the frame
 *     (measured 546/515/750/524 -> one uniform 700px box);
 *   - the 165-235px per-step deficit is spent CONTROLLED, not pooled at the
 *     bottom: Step1 card thumbs 115 -> 158 + grid space-evenly; Step2 the
 *     pv-3d preview grows to fill its column (--cd-pv-h = h-171, published
 *     by fitScreen) and the form column redistributes across its four
 *     blocks (space-evenly) with the v1.9.53 over-compaction on inputs/
 *     toggles/cards relaxed back to standard box heights; Step4 the spec
 *     render ceiling rises (240/34vh/380 -> 300/42vh/400) and the quote
 *     rows/spec grid breathe wider. NO margin:auto vertical centring —
 *     the v1.9.34 void root cause stays dead;
 *   - Step3 fabric keeps its internal scroller (the 700 frame shows ~2.9
 *     swatch rows instead of 3 — the one deliberate trade);
 *   - phones (<=760px) zero-touch as ever.
 * v1.9.53 (2026-08-31 — VERTICAL RHYTHM COMPACTION for the in-page embed;
 *
 * v1.9.53 (2026-08-31 — VERTICAL RHYTHM COMPACTION for the in-page embed;
 *   goal: the whole tool (progress top -> bar bottom) lands inside ONE
 *   viewport, .cd-app rect <= 780px at a 900px viewport. Measured root
 *   causes of the two giant voids (2x screenshot / 2 = logical px):
 *   (1) subtitle -> UNITS row ~55-60px: the v1.9.34 d2 `.s2wrap
 *       { margin: auto 0 }` vertical centring splits the body's surplus
 *       ABOVE and BELOW the wrap; (2) density cards -> bar ~50px: the same
 *       margin's lower half. Fix: the centring is retired on >=761px
 *       (margin: 0 — content rides the top; the v1.9.18 space-evenly on
 *       the Size left column is retired too, flex-start), the surplus is
 *       spent nowhere — the content itself is compacted instead:
 *   - wiz margin 14 -> 8 (cd-app rect = wiz + margins must clear 780);
 *   - CSS-side frame backstop: >=761px .wiz max-height 780px (fitScreen's
 *     750px inline height wins when JS runs; if it ever fails, the frame
 *     is capped instead of growing past the fold);
 *   - step padding-top 14 -> 9 (progress -> title ~15-17 -> ~8-10px),
 *     .step-sub margin 10 -> 7;
 *   - Size preview: .pv-3d clamp 340/44vh/440 -> 280/36vh/380 (the JS
 *     --cd-pv-h budget drops in lockstep, designer.js v1.9.53),
 *     .pv-empty 34 -> 18px padding;
 *   - measurement inputs padding 7 -> 4 (height ~28 -> ~24), .fields row
 *     gap 10 -> 8 (desktop clamp 10/1.2vh/16 -> 8/1vh/12);
 *   - density cards padding 8/7 -> 5/5 + 17px -> 16px type (card ~52 ->
 *     ~42px), .dens-pick margins 10/4 + pad 10 -> 8/2 + 8;
 *   - nav-bar padding 10/12 -> 7/8, primary CTA 10/20 -> 8/18, ghost
 *     9/16 -> 7/14, mini 5/11 -> 4/10;
 *   - step 1: shapes row gap clamp 14/2.2vh/26 -> 10/1.6vh/18, fallback
 *     margin-bottom 20 -> 10;
 *   - step 3: fab-tabs margin 12 -> 8, fab-grid pad 14 -> 8 / margin 10 ->
 *     8, sewn-note 6/6 -> 4/4;
 *   - step 4: spec-render canvas ceiling clamp 280/40vh/420 -> 240/34vh/380
 *     and the short-window tier's max-height 860 -> 960px (a 900px
 *     viewport is inside the short band now), spec-params margin 10 -> 8,
 *     cta-row 10 -> 8.
 *   No functional rule moved: prices, validation, 3D parameters, the phone
 *   (<=760px) single-scroll contract and the fabric scroller floor are all
 *   untouched.
 *
 * v1.9.34 (2026-08-28 — user five-round feedback 空隙大/密度低/高度异常/
 *   不在一屏 + 原始设计意图澄清 "固定高度是为了照顾 fabric——布卡占太高
 *   才设固定高+滚动；高度按 fabric 来定；然后再设置其他几个 tab"):
 *   the fixed uniform frame STAYS (the v1.9.32 formula
 *   h = max(640, min(750, vh-top)) is untouched — fabric is the height
 *   benchmark, all four steps share one height, fabric scrolls inside);
 *   the Shape/Size/Order fix is DENSITY + BALANCE, not auto-height:
 *   d1 step-1 row-gap clamp rolled back 3vh/36 -> 2.2vh/26 and the shape
 *      thumb 130 -> 115 (cards ~188 -> ~175) — the v1.9.24-compact and
 *      v1.9.32-sparse midpoint, compact偏紧: the row block reads DENSE
 *      again instead of stretched-thin;
 *   d2 the Size (s2wrap) and Order (s4wrap) blocks ride at their NATURAL
 *      height and VERTICALLY CENTRE in the frame body (margin:auto 0 —
 *      collapses to 0 on overflow, so a cramped window still scrolls from
 *      the top and nothing is ever clipped): no more content pinned to the
 *      top with a one-sided waste band below (measured 166px on Order);
 *   d3 fabric step zero-touch; phones (<=760px single-scroll) zero-touch
 *      (d2 is caged in a >=761px media block).
 *
 * v1.9.31 (2026-08-28 — PHONE-ONLY four-task round; every rule below lives
 *   inside a <=760px media block, the desktop contract is untouched):
 *   t1 the phone tab-row scrollbar is UN-hidden and STYLIZED (4px, Navy
 *      45% thumb on Navy 8% track — the v1.9.30 full hiding left the user
 *      with no visible indicator: "为什么我还是没有看到左右滚动条");
 *   t2 the fabric step's second explainer sentence (.fi-long span) drops
 *      off phones ("Six families... 这句是否删除或简短，手机占空间");
 *   t3 the Size preview narrows to ~70% and the 44px bottom dead zone
 *      under the canvas is gone (root cause: the .pv-meta "Top area" line
 *      + its unreset p margin-bottom + the 8px preview padding);
 *   t4 phone measurement inputs 36px tall, the ? circles (.mq) 24px.
 *
 * v1.9.30 (2026-08-28 — task 2 only; task 1 lives in designer.js): the
 *   phone tab-row scrollbar hiding is MULTIPLIED (::-webkit-scrollbar /
 *   -track / -thumb / -button each display:none + 0-sized + transparent at
 *   !important, over the existing scrollbar-width:none !important) — the
 *   user's real-device Chrome/WebView screenshot still showed a track
 *   though desktop Chromium computed none. Ancestor audit at <=760px: the
 *   tab row is the ONLY horizontal overflow scroller (step-body/wiz are
 *   overflow-visible/hidden on phones); the v1.9.29 right-edge mask fade
 *   is untouched. scrollbar-gutter:stable evaluated and REJECTED (it would
 *   reserve the slot we are removing). The frozen height formula, the
 *   phone single-scroll contract and the bar layout are untouched.
 *
 * v1.9.16 (2026-08-27 — STAGING LAYOUT TRIAGE on /1111-2/, live-measured
 * via CDP at vh 560..4320; two rules moved, nothing else):
 *   - F2: the steps-1/2 .step-body overflow:visible carve-out is RETIRED
 *     (was the "Shape & Size never scroll" promise). 1366x768 measured
 *     106px of Step-1 content that could not scroll and PAINTED PAST the
 *     wizard frame instead (shapes-grid bottom 823 vs frame 768).
 *     Every .step-body now scrolls internally — no step ever bleeds.
 *   - F3: .wiz is overflow:hidden (containment backstop; the .cd-app root
 *     stays visible). Toast / modal keep their viewport containing blocks
 *     and are NOT clipped — verified by CDP injection.
 *   - P0: the price/CTA bar moves from viewport-fixed to sticky INSIDE the
 *     frame (.nav-bar position:sticky bottom:0; static in flow ≤760px).
 *     Kills both measured artifacts: the blank band under a short frame,
 *     and the bar hovering over the SEO copy after the tool.
 *
 * v1.9.15 (2026-08-27 — USER VERDICT: 3D dimension callouts restored, in
 * lockstep with the prototype at 页面原型/custom-cushions/index.html):
 *   - DELETED .pv-dimchart (Size strip) and .spec-render svg.dim-chart
 *     (Order sheet): with paintDims() re-enabled in designer-3d.js the 3D
 *     carries its own live callouts, so those sheets would double-annotate.
 *     drawDimChart() now only serves the no-3D fallback (.pv-svg / the
 *     Order render-failure path), which reuses the generic rules above.
 *   - NO other layout rule moved: .wiz heights, fitScreen, bottom-bar
 *     tiers, canvas max-heights and the phone blocks are exactly v1.9.14.
 *
 * v1.9.14 (2026-08-27 — SPLIT ARCHITECTURE, in lockstep with the prototype
 * at 页面原型/custom-cushions/index.html): [v1.9.15 update: its .pv-dimchart
 * and .svg.dim-chart rules were removed again]
 *   - NEW .pv-dimchart: the Size step's dimension strip UNDER the pure-model
 *     3D (the 3D callout layer is disabled in designer-3d.js v1.9.14) —
 *     drawDimChart()'s 2D SVG engineering sheet, ~120-150px tall, Navy
 *     lines + sand-gold value pills.
 *   - NEW .spec-render svg.dim-chart: the same sheet inside the Order card,
 *     under the pure-model canvas — white plate + hairline top border so
 *     the sheet reads as its own drawing panel under the render.
 *   - the old .pv-svg fallback sizing stays (it now hosts the same sheet,
 *     drawn large when 3D is unavailable).
 *
 * v1.9.10 (2026-08-27 user screenshots 77/78 — "浏览器窗口缩小后底部区域
 * v1.9.10 (2026-08-27 user screenshots 77/78 — "浏览器窗口缩小后底部区域
 *占屏比例越来越大"): the bottom bar's share of the viewport is now FLAT
 * across the shrink ladder. Root cause: the 760px breakpoint flipped the
 * bar from its desktop single row (67px, ~10% of viewport) into the
 * wrapped two-row phone layout (~110px bar + 134px reserved padding =
 * 16%+ bottom zone) — a JUMP, not a growth. Fixes (in lockstep with the
 * prototype at 页面原型/custom-cushions/index.html):
 *   - the bar keeps the single row (price left, buttons right) down to
 *     520px; the auxiliary .nb-note hides on phones (it was the wrap
 *     seed); bar paddings compact 10/14 -> 8/10;
 *   - only below 520px does it wrap, to TWO controlled rows (price,
 *     then Back + Next side by side) at ≈89px;
 *   - --cd-bar-h tiers: 70px desktop kept through the 760px band (no
 *     jump), 98px under 520px; the 116px phone and 152px sub-350 tiers
 *     are retired; --cd-nav-h (no consumer) deleted.
 * Prototype-measured bottom-zone share, before -> after:
 *   1440x900 7.9->7.9 | 1024x768 9.2->9.2 | 900x700 10.1->10.1 |
 *   760x700 16.1->9.6 | 600x720 15.7->9.3 | 375x667 16.9->13.3
 * (320x568 first-gen-SE class: 15.7 — kept, compacting further would
 * eat the 44px tap target)
 *
 * v1.9.8 (2026-08-27 user round 15, screenshots 75/76 — "整体层级太多，可以
 *减少一个层级" + nine fixes; DOM SIMPLIFICATION is the thread):
 *   - #3 DOM: the Order render's .prev-svg frame is GONE — the canvas
 *     renders straight into .spec-card.spec-render (was card > frame >
 *     canvas, now card > canvas); Size's .pv-box pass-through wrapper is
 *     GONE (pv-3d / pv-svg / pv-meta hang directly off .preview);
 *     .fab-tabs-wrap and .cta-col (one-child wrappers) deleted. Four
 *     nesting levels fewer in the rendered tree.
 *   - #1 Size alignment (screenshot 75): the preview column's drawing
 *     rides the TOP of its card — justify-content:center -> flex-start,
 *     the card's top padding 3px -> 0. The v1.9.6 vertical centring sat
 *     the frame ~132px below the left column's Units row on desktop.
 *   - #2 Order render BIGGER: right column 1.1fr/.9fr -> 1.05fr/.95fr,
 *     the 230px max-height cap deleted — the canvas fills the card width.
 *   - #4 the two Order cards gap 10px (was 0 — they touched).
 *   - #5 "Your configuration" is ONE column everywhere (was 2-col desktop
 *     / 1-col phone — "有的两列有的一列").
 *   - #6 phone step tabs scroll WITH the page again (the v1.9.5 fixed pin
 *     under --site-header-h is deleted; --cd-bar-h back to the measured
 *     action-bar stack; --site-header-h stays reserved for launch).
 *   - #8 the render's top band: card padding 0 + overflow:hidden, the
 *     cropTop breathing row 2px -> 1px — no white line above the image.
 *   - #9 phone Order step opens on the IMAGE (render column order:-1,
 *     same image-first language as the Size step).
 *   (pill sizing floor 8px lives in designer-3d.js v1.9.8; asset version
 *   aligned with the JS side; kept in sync with the prototype at
 *   页面原型/custom-cushions/index.html)
 * v1.9.7 (2026-08-26 user round 14 — three "claimed fixed but
 * user-verified broken" root cures; the v1.9.6 E2E had asserted the
 * COLUMN CONTAINERS, not the pixels the user's eye measures):
 *   - #1 Order render card: the "Your cushion" title becomes an overlay
 *     badge (top-right), the card's top padding drops to 2px, the frame's
 *     to 0 — with renderThumb's cropTop (designer-3d.js) the image starts
 *     ~3px under the card's top border, which already sits level with the
 *     left heading. Was: 86px of padding+title+backdrop above the model.
 *   - #3 phone (<=760px): the Size step's DOM controls shrink with the
 *     viewport — density pills 17->12px (54px button -> ~40px), inputs
 *     15->13px, unit toggle ->11px. v1.9.5-6 had scaled ONLY the pills
 *     painted inside the 3D canvas, so on a phone every DOM control read
 *     "not one of them changed".
   (asset version aligned with the JS side; kept in sync with the
   prototype at 页面原型/custom-cushions/index.html)
   v1.9.6 (2026-08-26 user round 12, screenshot 72 — LAYOUT RE-ARCHITECTURE,
   prototype + plugin; the v1.9.4-5 lifts were patches on the wrong model):
   - Order step: the step header + sub MOVE INTO the left column — the
     step is a plain two-column grid, LEFT = header + Price breakdown +
     actions, RIGHT = Your cushion render + Your configuration, TOPS
     ALIGNED (align-items:start). The -84px negative margin is DELETED
     (it was the screenshot-72 bug: the render card escaped its
     container, the image frame got clipped). Phones: natural flow
     (header first), the order:-1 flip is retired.
   - Size step: the drawing no longer pins to the column top nor
     overflows — .pv-3d takes a content height (clamp 340-440px) and is
     VERTICALLY CENTRED in the preview column (justify-content:center).
   - (pill sizing lives in designer-3d.js v1.9.6 — display-width clamp.)
   v1.9.4 (2026-08-26 user round 11 — eight fixes, prototype + plugin):
   - #3 Shape cards (screenshot 61): padding compressed, the 3D thumb
     fills the card width (180px cap gone), canvas 280x210.
   - #4 phone step tabs PINNED to the bottom (screenshot 62): .progress
     position:fixed directly above the action bar; --cd-nav-h/--cd-prog-h
     feed --cd-bar-h (step padding + toast reserve the full stack);
     --site-header-h:80px reserved for the launch WP header.
   - #5 phone cm unit never wraps (screenshot 63): .inwrap .unit nowrap.
   - #6 fabric group tabs are SINGLE-SELECT now: the .cur highlight (the
     picked fabric's family) is retired as a visual SELECTED state — the
     tab row renders exactly ONE highlighted tab (the active grid filter);
     the "✓ SKU" line inside the tab carries the picked-family signal.
   - #7 the Foam-density summary row spans the spec card (.sg-wide); the
     whole .spec-grid drops to one column on phones.
   - #8 "Your cushion" lifts level with the "Review & order — {shape}"
     header (right column's first card, negative top margin; phones keep
     the natural flow).
   v1.9.3 (2026-08-26 user round 10 — five fixes, prototype + plugin):
   - Size step: the right preview column's container padding is
     compressed (3px top) so the 3D box tops out level with the input
     column's Units row.
   - Fabric group tabs: the hover peek panel (.fab-panel + its timer
     wiring) is DELETED — the ⓘ group modal is the ONE fabric family
     explainer, and it is text-only now (the swatch strip and its
     "tap a swatch" note are gone).
   v1.9.1 (2026-08-26 user round 9 — five detail fixes, prototype + plugin
   in sync):
   - Size-step redundancy (see designer.js v1.9.1 #1): .pv-dims and the
     perimeter echo are gone from .pv-meta (one small "Top area" line
     stays); the "3D view — drag to rotate" caption is an absolutely
     positioned corner note inside .pv-3d (.pv-3d-hint — no content row).
   - the static .field .hint rows are GONE (the v1.7 "?" modals own every
     explanation); a .hint now exists ONLY as a validation-error line,
     created/removed by setFieldHint().
   - .shape-thumb renders 240x180 (aspect-ratio 4/3 — was 150x112) and
     the shape-modal canvas is 480x360 with max-width:100%.
   - .spec-grid .sg-item strong WRAPS (was nowrap+ellipsis — the 5-dim
     Size string overflowed, screenshot 54) and the Size row spans both
     columns via .sg-wide.
   v1.9.0 (2026-08-26 supply-chain veto — prototype + plugin in sync):
   - the Style & Add-ons step is GONE (the supplier cannot sew stitch
     styles / anti-slip bottoms / ties): every .finishing / .style-cards /
     .addons / .ao-head / .addon-six / .pickcards / .pc / .pc-zoom /
     .artzoom / .ties-len / .pillrow / .pill / .ties-pr rule was deleted,
     the fabric modals' .grp-addons chip row too, and the wizard is four
     steps again (Step 4 = Review & Order).
   v1.8.2 (2026-08-26 user round 8 on the live v1.8.1 build, 7 micro-
   fixes — prototype + plugin in sync):
   - #1 the per-SKU ⓘ is BACK on every fabric library card (.fab-info,
     top-right circle pill — the v1.6.4 pattern the v1.6.5 round retired):
     opens the per-SKU macro modal directly; the six GROUP-tab ⓘ stay, so
     both levels coexist (group = family explainer, card = SKU detail).
   - #2 Step-4 .addon-six band loosened + enlarged: column gap 10 -> 22px
     (~2x — the anti-slip / ties halves breathe apart), cards 130 -> 168px,
     photos 112 -> 152px. Still ONE row of six, no inner scroll.
   - #3 Review right column: the "Your cushion" 3D frame is capped at
     230px with object-position top (and the card title tightened), so the
     render sits higher and the "Your configuration" parameter card below
     shows more rows before any scrolling.
   - #4/#5 click-to-zoom overlay on ALL Step-4 card art (.artzoom-modal):
     the photo / SVG fallback / corner pill opens a full-screen .cd-modal
     with the art <=640px + one craft line (.az-note). The corner
     .pc-zoom pill carries the same circle-pill language as the ⓘ family
     (diagonal-arrows SVG glyph, no font dependency) — the affordance is
     visible, not guessed.
   - #6 close buttons audited: every .cd-modal (shape / meas / dens /
     group / per-SKU / art-zoom) ships an .cd-modal-x plus backdrop click
     plus Esc — three working channels on each.
   - #7 phone audit at 375px: modal padding tightened (18 -> 10px so the
     600px cards use the small width), the per-SKU modal's 220px shots
     drop to 150px, the group swatch strip min card 86 -> 72px; the five
     step grids / stacking / bar-wrap rules re-verified by the E2E
     walkthrough at 375x667 (tests/e2e-v182.py).
   v1.8.1 (2026-08-26 user screenshot 51 — toast hidden under the fixed
   bottom bar; kept in sync with the prototype at 页面原型/custom-cushions/index.html):
   - .toast was bottom:22px / z-index:50 — INSIDE the fixed .nav-bar's
     zone (bar = bottom:0 z-index:100, 64px tall desktop / 112px phone),
     so every checkout/toast feedback (empty-fabric notice, add-to-bag
     error, "View bag →" link) rendered UNDER the bar. Fix: bottom lifts
     to calc(var(--cd-bar-h) + 16px) and z-index rises to 200.
   - --cd-bar-h moved from .wiz up to .cd-app so the toast (a direct
     .cd-app child, OUTSIDE .wiz) can read it; mobile media override
     follows the same move. Step padding inherits as before. Values
     re-measured against the real five bar markups: 64 -> 70 desktop,
     112 -> 116 phone, NEW sub-350px tier 152 (a 320px phone wraps the
     Step-3/4 long notes to a third row -> 146px bar).
   - .cd-modal z-index 60 -> 120: an open info modal's backdrop now
     covers the fixed bar too (the bar used to sit ON the backdrop and
     stayed clickable through it).
   v1.7 (2026-08-26 视觉规范 §四.5 + §五 — measurement ? popups; kept in
   sync with the prototype at 页面原型/custom-cushions/index.html):
   - .frow wraps each Size field's input/select + a round .mq "?" button;
     click opens .meas-modal (line-art SVG top/side view, sand-gold arrow,
     "measure here" pill, letter bubble, seam-to-seam rules rows)
   - .dens-modal: the density ⓘ now shows the four-cell cross-section
     compare (35D sparse -> 65D tight cells, per-grade use labels); card
     width matches the shape modal (470px)
   - user round 6 folded in: #3 .ftab padding-right 10px -> 30px (the ⓘ
     sat ON the tab's text — screenshot 44); #5 .pickcards switch to the
     same repeat(3, minmax(0,160px)) cap as .style-cards (one card width
     across the three Step-4 blocks); #6 the Review & Order columns
     re-laid in the markup (left = breakdown + actions, right = render +
     parameter card) — .cta-col/.cta-row are position-agnostic so they
     work in the left column as-is.
   v1.6.5 (2026-08-26 user round 5, kept in sync with the prototype at
   页面原型/custom-cushions/index.html):
   - #3 fabric ⓘ on the six GROUP TABS (.ftab-info -> .grp-modal: what/why/
     best-for/CARE rows + the family swatch strip; a swatch click opens the
     per-SKU modal). The per-card ⓘ is GONE (was 119 pills of noise); the
     per-SKU .fab-modal is ENLARGED — 600px card, 220px shots.
   - #4 Step-4 pick-card art fits the card: .pc-img capped at 120px inside a
     fixed frame (the 16:10 width-fill cropped the square source photos);
     .style-cards compressed to <=160px cards so four styles + photos read in
     one eye-level band.
   - #5 Review & Order: the parameter card is BACK above the price breakdown
     (.spec-card.spec-params > .spec-grid, two-column .sg-item rows); right
     column stays render + order actions only.
   v1.6.4 (2026-08-26 macro round + user round 4, kept in sync with the
   prototype at 页面原型/custom-cushions/index.html):
   - fabric detail modal (.fab-modal): swatch + macro weave view 2-up, card
     capped at 90vh with its own scroll; ⓘ trigger on the library cards
   - #1 thickness is a fixed-tier <select> (.tsel) — same box language as
     the typed inputs
   - #4 .pc-img frames derive height from card width (16:10 aspect) — the
     66px cover strip amputated the square source photos
   - #6 Review & Order de-duplicated: edit-chips + spec-grid + copy-spec
     rules removed to the root; right column = 3D render + order actions
   v1.6.3 (2026-08-26 user round 3 — five-step refactor + 6 fixes, kept in
   sync with the prototype at 页面原型/custom-cushions/index.html):
   - five steps: Shape → Size → Fabric → Style & Add-ons → Review & Order.
     Progress bar fits five labels; Step 3 is FABRIC ONLY (8-column grid,
     larger swatches, the fixed two-row max-height box is GONE — the grid
     lives in the step-body's natural scroll); Step 4 gives the finishing
     blocks full width and height (add-on exposure = basket size); Step 5
     is the old quote step renamed
   - #2 measurement inputs are pure numbers with the unit OUTSIDE the box
     (.inwrap > input + .unit); #3 inch/cm toggle above the fields
     (.unit-toggle); both states stay inch internally
   - #4 density capsule ~30% tighter; the ⓘ moved from bottom-right to
     the card's top-right corner
   - #5 3D host: floating presentation (pedestal disc gone from the scene;
     the light radial gradient stays), camera fit is frustum-exact (see
     designer-3d.js)
   v1.6.2 (2026-08-26 user round 2, 8 fixes — kept in sync with the
   prototype at 页面原型/custom-cushions/index.html):
   - #1 bottom action bar pinned to the VIEWPORT bottom edge
     (position:fixed; bottom:0; left:0; right:0; z-index:100) — the
     v1.6.0 "last flex row of the step" scheme is retired; every step
     reserves --cd-bar-h at the bottom so content never hides behind
     the bar
   - #2 wizard height = 100vh flat (the calc(100vh - offset) and the
     --cd-vh-offset machinery are GONE — the tool page is designed so
     the wizard owns a full viewport; site-header scenarios are handled
     at site integration, not hardcoded here). Shape & Size step-bodies
     never scroll (overflow:visible — they fit, user-confirmed); the
     fabric grid keeps its own inner scroll
   - #3 Size tab: the preview column flex-stretches with the viewport
     (no fixed 470px cap; the 3D module's ResizeObserver re-fits the
     canvas)
   - #4 density cards on the Navy system (the v1.6.1 rebrand had left
     this block on the green palette): Navy border + selected state,
     gold fill allowed with Navy text (contrast >=4.9:1)
   - #5 density descriptor moved from the top-right corner tag to a
     small centered line UNDER the grade number
   - palette migrated to the brand spec (02-网站规划/品牌视觉规范.md §2):
     Navy #1b3b5a primary, gold #d2a272 accent/hover, tint #eef2f6
   v1.6.0 (2026-08-25 user-testing round, 12 fixes):
   - B1 full-screen frame: .wiz is a flex column capped at one viewport
     (height calc(100vh - offset)); each active step is a flex column whose
     .step-body scrolls (overflow-y:auto) between the fixed step header and
     the fixed bottom action bar — no page scroll while configuring
   - B2 the bottom bar is the LAST FLEX ROW of the step (no longer
     position:sticky; the v1.5.0 fixStickyContext boot audit is retired)
   - B3 shape grid 5 columns (10 cards = 5+5) with bigger 3D thumbnails
   - B4 fabric cards slimmed to swatch + SKU + color (material/usage tags
     gone); swatch images capped at 84px so two full rows stay visible
   - B5 quote step: spec params as a 2-column grid, breakdown grouped with
     section labels, 4-density compare strip removed (one price per pick)
   - C2 density cards: big centered grade number, small descriptor top-right
   - C3 sewing-style / add-on pick cards show competitor photo placeholders
     (assets/addon-img/, see _replace_before_launch.txt)
   v1.5.0 (2026-08-25 UI round): shape cards show static 3D isometric
   thumbnails (canvas swapped in by designer-3d renderThumb; SVG line art
   = fallback); shape grid fixed at 6 columns (6+5); sticky bar hardened
   against overflow ancestors (see designer.js fixStickyContext); vertical
   rhythm relaxed back to comfortable values while keeping one screen.
   v1.4.0 (rofielty-benchmark round): sticky bottom action bar = live price
   (From → Total) + Back + primary CTA in one row (replaced the nav-row and
   the in-preview from-bar); anti-slip / ties / sewing-style are image pick
   cards with on-card prices (checkboxes and selects gone; tie length is a
   15/25/35 pill row); 3D host got a studio radial-gradient backdrop.
   v1.3.0 (second user-testing round): Popular badge as an absolute pill on
   the shape card + per-card ⓘ shape modal; fabric hover panel is text-only;
   fabric grid scrolls in a fixed ~300px box; add-on diagrams 1 -> 3 SVGs;
   Step-4 duplicate title removed; Step-2 preview upgraded to 3D (SVG kept
   as fallback). English-only UI (Chinese names live in code comments).
   v1.2.0 (one-screen compactness round): tighter paddings/gaps everywhere,
   nav row sticky at the wizard bottom, Step-2 fields in a 2-col grid,
   preview SVG capped at ~40vh, collapsible step-1 fallback note, fabric
   tabs drive the library grid (click = filter to that group).
   v1.1.0: 1280px desktop width, fabric tab bar with hover panel, density
   moved to Step 2 (with ⓘ info modal), add-on diagrams, checkout CTA.
   Palette: #1b3b5a primary, #eef2f6 tint, #d2a272 hover. */

.e-con:has(> .cd-app), .elementor-widget:has(.cd-app), .cd-app { min-height: 0; }
.e-con:has(.cd-app:not(.cd-nopin)) { min-height: calc(100vh + 600px); } /* v1.9.64: pin=off embeds skip the runway */ /* v1.9.49: belt-and-braces — the JS lane (designer.js boot) now sets this inline on the actual host container; this rule stays for non-Elementor hosts */ /* v1.9.47: sticky needs headroom — the container was exactly as tall as the tool, so the sticky range was ZERO and the tool scrolled straight past (live: wizTop -213). One selector gives it 600px of pin runway below the tool. */
.cd-app { position: sticky; top: var(--cd-lane, 0px); z-index: 5; }
.cd-app.cd-nopin { position: static; z-index: auto; } /* v1.9.63: shortcode pin="off" — page-embed mode (tool fully visible, no pin runway) */ /* v1.9.46: NATIVE STICKY — the tool pins itself under the sticky header and stays fully visible no matter how far the page scrolls (user: 下拉滚动 header 后出现已经迟了). --cd-lane is fed by the lane measurer (padding / sticky header height). Phones override below (single-scroll flow, no pinning). */
.cd-app {
	font-family: system-ui, -apple-system, 'Segoe UI', Roboto, Arial, sans-serif;
	color: #222;
	background: #fff;
	line-height: 1.6;
	font-size: 16px;
	max-width: 1280px; /* v1.1.0: widen the configurator on desktop */
	margin-left: auto;
	margin-right: auto;
}
.cd-app *,
.cd-app *::before,
.cd-app *::after { box-sizing: border-box; }
.cd-app p { margin: 0 0 14px; }
.cd-app a { color: #1b3b5a; }

/* ===== wizard frame =====
   v1.6.2 #2: the wizard is its own full viewport — height:100vh, no page
   offset (the calc(100vh - offset) / --cd-vh-offset machinery is retired;
   the tool page is designed so the wizard owns a full screen and header
   scenarios are handled at site integration time). The bottom bar is
   position:fixed to the viewport bottom (#1), so with .wiz at 100vh the
   wizard's bottom edge coincides with the bar on every screen height.
   --cd-bar-h feeds the steps' bottom padding so content clears the bar. */
.cd-app { overflow: visible; }
/* v1.9.16 F3 (staging incident ① — shape cards painted past the wizard
   frame at vh<=864, measured 54.6px spill on 1366x768): the wizard box is
   the containment line. Content that no longer fits now scrolls INSIDE
   its step-body (F2) instead of drawing outside the border. Safe for the
   fixed overlays: .toast / .cd-modal are position:fixed whose containing
   block is the viewport, and fixed elements are not clipped by an
   ancestor's overflow (verified live via CDP style injection). The bar is
   no longer one of them — same round it became sticky INSIDE the frame
   (see P0 above), which F3 also cannot clip: it sits inside the box by
   construction. */
.cd-app .wiz { overflow: hidden; }
/* v1.7.1: --cd-bar-h lives on .cd-app (was .wiz) so elements OUTSIDE .wiz
   but inside .cd-app — the .toast — resolve the same bar height. Values
   re-measured (harness2, 2026-08-26): desktop bar renders 67px tall;
   phone wrap 110px; sub-350px phones wrap to 146px (long Step-3/4 notes
   take a second line) — each tier = measured max + small margin. */
/* v1.9.18: the --cd-bar-h tiers (and the phone 98px override below) are deleted — nothing consumes them any more. --site-header-h stays RESERVED for the launch WP header. */
.cd-app { --site-header-h: 80px; }
.cd-app .wiz { display: flex; flex-direction: column; border: 1px solid #d1d5db; border-radius: 10px; margin: 8px 0; background: #fcfdfd; height: auto; min-height: 0; } /* v1.9.53: margin 14 -> 8 — cd-app rect = wiz + margins must clear 780 */ /* v1.9.10d: 100vh in WP overflows past the fold (theme header above pushes it) — content-height + fixed bar instead */
@media (min-width: 761px) { .cd-app .wiz { max-height: 780px; } } /* v1.9.54: frame BACKSTOP unchanged — fitScreen's inline height (<=700) wins when JS runs; if it fails, the frame is capped at the one-screen budget instead of growing past the fold (step-bodies already scroll internally) */
/* v1.9.54 UNIFORM FRAME: the v1.9.53 :has() per-step height collapse is
   RETIRED (it traded the v1.9.34 voids for a new bug — the frame jumped
   546/515/750/524 across steps and the dead space re-pooled between the
   content bottom and the bar). EVERY step now rides fitScreen's ONE
   inline height; the per-step deficit is spent by the controlled-fill
   rules below (grow thumbs/preview/quote rows + space-evenly spreads),
   never by margin:auto centring and never as one bottom band. Phones
   (<=760px) never had a locked frame — zero-touch. */
.cd-app .progress { display: flex; background: #f3f4f6; border-radius: 9px 9px 0 0; flex: 0 0 auto; }
/* v1.6.3: five labels instead of four — slightly smaller so "4. Style &
   Add-ons" fits one row on 1280px */
.cd-app .progress button { flex: 1; background: none; border: none; border-right: 1px solid #e5e7eb; padding: 5px 2px; font-size: .72rem; font-weight: 600; color: #6b7280; cursor: pointer; font-family: inherit; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } /* v1.9.53: 7 -> 5 — progress strip slimmer */
.cd-app .progress button:last-child { border-right: none; }
.cd-app .progress button.on { background: #1b3b5a; color: #fff; }
.cd-app .progress button.done { color: #1b3b5a; }
/* v1.9.16 P0: bottom padding 8px -> 0. The 8px was breathing UNDER the old
   viewport-fixed bar; with the bar sticky INSIDE the frame it is the last
   in-flow child, and that padding rendered as a phantom 9px strip between
   the CTA row and the frame's bottom edge. */
.cd-app .step { display: none; padding: 9px 20px 0; flex: 1 1 auto; min-height: 0; } /* v1.9.53: padding-top 14 -> 9 — progress -> step title gap ~15-17 -> ~8-10px */
/* v1.6.0 B1: active step = flex column [header | scrollable body | bar] */
.cd-app .step.on { display: flex; flex-direction: column; }
/* v1.9.25 f2 (live site: the step3/4 bar floated 147px off the frame bottom on
   phones — the theme's generic flex/overflow rules broke the chain): the whole
   chain restated at !important so no theme rule can detach the bar. */
.cd-app .step.on { display: flex !important; flex-direction: column !important; }
/* the shrunk/fixed links below are PHONE-BAND only (v1.9.25): on desktop the
   fabric grid must keep flex-shrink so the CRAMPED fallback (body scrolls,
   grid >= its 230px floor) keeps working — a global !important froze the
   grid at its content height and broke the desktop cramped contract. */
@media (max-width: 760px) {
	.cd-app { position: static !important; top: auto !important; } /* v1.9.46: phones never pin */
	.cd-app .step.on .step-body { flex: 1 1 auto !important; min-height: 0 !important; }
	.cd-app .step.on .step-h, .cd-app .step.on .step-sub,
	.cd-app .step.on .fab-tabs-wrap, .cd-app .step.on .fab-filters,
	.cd-app .step.on .sewn-note { flex-shrink: 0 !important; }
	.cd-app .step.on .nav-bar { flex: 0 0 auto !important; margin: 0 !important; }
}
.cd-app .step-h { font-size: 1.02rem; font-weight: 700; margin: 0 0 3px; flex: 0 0 auto; }
.cd-app .step-sub { color: #6b7280; font-size: .8rem; margin: 0 0 7px; line-height: 1.45; flex: 0 0 auto; } /* v1.9.53: 10 -> 7 */
/* v1.6.0 B1: the one scroll container inside the full-screen frame.
   v1.6.2 #2: Shape and Size were user-confirmed to fit one viewport, so
   their bodies were set overflow:visible and the page scrolled instead —
   RETIRED in v1.9.16 F2 (staging incidents ① + ②): at real-world heights
   (vh <= 864 with a theme header above) Step 1 measured 106px of hidden
   content on 1366x768 that painted PAST the wizard frame (shapes-grid
   bottom 823 vs frame 768 = 54.6px out) instead of scrolling. Uniform
   rule now: every step-body scrolls internally when its content is taller
   than the viewport, and nothing ever draws outside the frame (F3). */
.cd-app .step-body { flex: 1 1 auto; min-height: 0; overflow-y: auto; overscroll-behavior: auto; padding-right: 3px; } /* v1.9.29: contain -> auto — Chrome swallows the wheel on a contain-scroll-container EVEN with nothing to scroll, so the page would not turn with the cursor inside the tool (user: middle-wheel anywhere must scroll the page); the CRAMPED fallback keeps overflow:auto, and at its edge chaining to the page is the natural behaviour. fab-grid keeps its own contain — that one is intentional. */
/* v1.6.2 #3: Size tab stretches with the viewport — the two columns are
   each a whole (inputs left, drawing right) and their TOPS align.
   v1.9.6 (screenshot 72 "isn't the drawing supposed to centre?"): the
   preview column no longer glues the drawing to its top edge NOR lets
   it overflow — the drawing takes a moderate content height and is
   VERTICALLY CENTRED in whatever height the column has (the v1.9.3-5
   flex:1 stretch + the lifts were the wrong model: never full-height,
   never pinned, just centred). */
.cd-app .step[data-step="2"] .step-body { display: flex; }
.cd-app .step[data-step="2"] .s2wrap { flex: 1 1 auto; min-height: 0; align-items: stretch; }
/* v1.9.8 #1 (screenshot 75): the drawing rides the column TOP — the v1.9.6
   vertical centring (justify-content:center) parked the frame ~132px below
   the left column's Units row on desktop; flex-start + zero top padding
   puts the frame's top edge level with the left heading, same language as
   the v1.9.7 Order fix. (v1.9.8 #3: .pv-box is deleted — the children below
   are direct kids of .preview now.) */
.cd-app .step[data-step="2"] .preview { position: static; display: flex; flex-direction: column; justify-content: flex-start; min-height: 0; }
.cd-app .step[data-step="2"] .preview { padding: 0 6px 6px; } /* v1.9.53: 8 -> 6 bottom padding */
/* v1.9.18 surplus-height redistribution: the drawing GROWS into whatever the
   column has left instead of parking at a fixed strip — the empty lower half
   users measured on desktop ("密度卡以下全空") becomes preview. The floor keeps
   every prior height reachable (identical when there is no surplus), growth is
   free space only, so no viewport can overflow from this rule alone; the 3D
   module's ResizeObserver re-runs size() + paintDims so callouts stay aligned.
   Ceiling matches the frame cap era: never taller than ~460 CSS px. */
.cd-app .pv-3d { flex: 0 0 auto; height: clamp(300px, 42vh, 400px); min-height: 0; } /* v1.9.54: 280/36vh/380 -> 300/42vh/400 — the static fallback band follows the uniform 700px frame; on >=961px fitScreen publishes the exact --cd-pv-h (frame height minus the column chrome) so the preview FILLS its column (the v1.9.34-era void under the drawing is spent on the model, and the 3D module's ResizeObserver re-fits size()+paintDims) */
.cd-app .pv-svg { flex: 0 0 auto; display: flex; align-items: center; justify-content: center; }
.cd-app .pv-svg svg { max-width: 100%; max-height: 100%; width: auto; height: auto; }
/* v1.9.15: the .pv-dimchart strip rules are DELETED — dimension callouts
   are painted on the 3D canvas again, so no sheet ever renders in that
   node (it stays display:none in the markup; drawDimChart now only serves
   the no-3D fallback in .pv-svg above). */

/* ===== step 1: shapes ===== */
/* v1.6.0 B3+D: 5 columns — the notch-left/right cards merged into ONE
   "Notched" card (side picked on Step 2), so 10 shapes read as 5 + 5 and
   each card grows wider with a bigger 3D thumbnail */
.cd-app .shapes-grid { display: grid; grid-template-columns: repeat(5, 1fr); gap: 11px; } /* v1.9.24: 14->12->11 with the global shrink */
@media (max-width: 900px) {
	.cd-app .shapes-grid { grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); }
}
/* v1.9.4 (screenshot 61): card padding compressed (10/10/9 -> 8/9) and the
   3D thumb fills the card width (the 180px cap is GONE) — the art read
   small inside generous padding. The canvas renders 280x210 now (Retina
   headroom at the ~245px card width); the thumb is already the Step-2
   preview's own 3D render in miniature — no art swap needed. */
.cd-app .shape-card { position: relative; border: 2px solid #e5e7eb; border-radius: 8px; padding: 6px 7px 7px; text-align: center; cursor: pointer; background: #fff; transition: border-color .15s; } /* v1.9.24: GLOBAL unconditional shrink (user screenshot 101 clarified: '我让你把shape卡片变小' — smaller cards as THE fix, at EVERY viewport; no media tiers, no state classes) */
.cd-app .shape-card:hover { border-color: #d2a272; }
.cd-app .shape-card.sel { border-color: #1b3b5a; background: #eef2f6; }
.cd-app .shape-card svg { display: block; margin: 0 auto 2px; }
/* v1.5.0 #1: static 3D thumbnail — hidden until renderThumb() paints it and
   adds .on; the SVG line drawing stays as the fallback layer. */
.cd-app .shape-card .shape-thumb { display: none; margin: 0 auto 4px; width: 100%; height: auto; aspect-ratio: 4 / 3; max-height: 158px; border-radius: 6px; background: radial-gradient(120% 95% at 50% 10%, #ffffff 0%, #f1f6f3 48%, #dfe9e4 100%); } /* v1.9.54: 115 -> 158 — the uniform 700px frame gives Step 1's two card rows room again; 158 sits just under the 4:3 column-width ceiling (~163px at the 5-col 1280 layout, canvas keeps its 280x210 render), and narrow phone columns are width-bound so the cap never bites there */
.cd-app .shape-card .shape-thumb.on { display: block; }
.cd-app .shape-card.sel svg path,
.cd-app .shape-card.sel svg circle,
.cd-app .shape-card.sel svg ellipse { stroke: #1b3b5a; }
.cd-app .shape-card h3 { font-size: .85rem; margin: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } /* v1.9.24 global shrink: one line everywhere so the card height is viewport-independent */
.cd-app .shape-card p { font-size: .72rem; color: #6b7280; margin: 0; line-height: 1.35; display: -webkit-box; -webkit-line-clamp: 1; -webkit-box-orient: vertical; overflow: hidden; } /* v1.9.24: one-line desc, global */
/* v1.3.0: Popular = absolute pill in the top-right corner (was an inline
 * <span> above the drawing, eating a line of card space) */
.cd-app .shape-card .pop { position: absolute; top: 6px; right: 6px; display: inline-block; font-size: .6rem; font-weight: 700; color: #92400e; background: #fef3c7; border-radius: 99px; padding: 1px 6px; line-height: 1.4; }
/* v1.3.0: per-card ⓘ opens the shape detail modal (no selection) */
.cd-app .shape-card .shape-info { position: absolute; top: 4px; left: 5px; width: 24px; height: 24px; min-height: 24px; max-height: 24px; min-width: 24px; max-width: 24px; aspect-ratio: 1 / 1; display: inline-flex; align-items: center; justify-content: center; flex-shrink: 0; border: 1.5px solid #d1d5db; border-radius: 50%; background: #fff; color: #6b7280; font-size: .68rem; font-weight: 700; line-height: 1; cursor: pointer; padding: 0; font-family: inherit; z-index: 1; } /* v1.9.26: joined the four-way-lock family at 24px (live theme had it at 20x42) */
.cd-app .shape-card .shape-info:hover { border-color: #1b3b5a; color: #1b3b5a; background: #eef2f6; }
.cd-app .shape-card .shape-info:focus-visible { outline: 2px solid #d2a272; }
/* v1.2.0: fallback note collapsed into a <details> (was a 3-line block) */
.cd-app .fallback { margin-top: 10px; font-size: .8rem; color: #4b5563; background: #fef9ec; border-left: 3px solid #d97706; padding: 6px 10px; border-radius: 0 6px 6px 0; }
.cd-app .fallback summary { cursor: pointer; font-weight: 600; font-size: .8rem; }
.cd-app .fallback[open] summary { margin-bottom: 4px; }
.cd-app .fallback p { margin: 0; }
/* ===== v1.9.21 (REVERT of the v1.9.19 task-2 Shape card compaction — user
   verdict: the compressed cards read cramped/小家子气 at 790px windows
   (thumb 155 -> 82px); BIG CARDS are preferred even when the bar needs a
   page scroll). Both height tiers and the floor-state chrome squeeze are
   DELETED — cards, thumbs, desc, progress, step padding all return to the
   v1.9.18 baseline sizes at EVERY viewport. The CD-FLOOR-DELETED grid stretch-stop
   is deleted too: with the bar sticky on the frame bottom again, the grid
   always stretches to the frame bottom and the bar always hugs it.
   SUPERSEDED IN PART by v1.9.23 below — see the FIRST-SCREEN-COMPLETE
   contract. */

/* ===== step 2: measurements + foam ===== */
.cd-app .s2wrap { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; align-items: start; }
@media (min-width: 1100px) {
	/* wide desktop: give the input column more room, keep the drawing
	 * prominent on the right (v1.1.0 ratio tuning at 1280px; v1.5.0 #6
	 * relaxed to comfortable spacing) */
	.cd-app .s2wrap { grid-template-columns: 1.15fr 1fr; gap: 24px; }
	.cd-app .s4wrap { grid-template-columns: 1.15fr 1fr; gap: 24px; }
	.cd-app .step { padding: 12px 26px 0; } /* v1.9.16 P0: same bottom 8->0 as the base rule */ /* v1.9.53: 20 -> 12 — wide-desktop step padding follows the compaction */
}

/* ===== v1.9.18: surplus-height redistribution on the NON-Fabric steps =====
 * Staging measured all four content steps at scrollable=0 on common desktops,
 * so a full-screen frame was half empty below the fold line of its content
 * (user screenshot: everything under the density cards is blank). These rules
 * spend that SURPLUS — pure CSS, no new JS state, no DOM change:
 *   - Step 1: the card grid becomes a flex item of the body and CENTRES its
 *     rows in the free space; the inter-card gap breathes with viewport height
 *     (vh-clamped, so short/floor-clamped frames keep today's tight layout).
 *   - Step 2: the preview column already grows (rule at .pv-3d above) and the
 *     left form column distributes its four blocks evenly across the column.
 *   - Step 4: the "Your cushion" render gets the same vh-scaled ceiling the
 *     Size drawing has (still smaller than Size, per the v1.9.9 verdict).
 * Overflow safety: every rule spends free space only (flex-grow / auto
 * margins / align-content), so when content alone is taller than the body the
 * step scrolls exactly as before and the >=768px scrollable==0 contract holds.
 * Fabric (step 3) is intentionally untouched — it must stay scrollable. */

/* Step 1 */
.cd-app .step[data-step="1"] .step-body { display: flex; flex-direction: column; }
.cd-app .step[data-step="1"] .shapes-grid { flex: 1 1 auto; min-height: 0; align-content: space-evenly; gap: 10px; } /* v1.9.54: align-content center -> space-evenly + fixed 10px gap — the uniform-frame deficit splits into (rows+1) equal slices instead of pooling above/below the row block; with 158px thumbs the three slices measure ~16-24px each */
@media (max-width: 900px) {
	.cd-app .step[data-step="1"] .shapes-grid { flex: 0 0 auto; gap: 10px; }
}
/* v1.9.32 t3 — step-1 SURPLUS-GAP systemic fix (user: "整个卡片与上下间距
   超级远", harness-measured at 1920x1080: the row block floated with 205px
   net blank above AND below inside the stretched grid; smaller windows the
   same surplus pooled between the grid and the bar — 150-200px, screenshot
   130). Three levers, ALL scoped to step 1 only (steps 2/3/4 zero-touch):
   (a) the step-1-only chrome squeeze below (padding/sub/fallback margins);
   (b) the taller row block (row-gap clamp above + .shape-thumb 110->130,
       ruler at its own rule) so the content itself claims more of the box;
   (c) the desktop HARDENING chain: the same !important restatement the
       v1.9.25 f2 phone band got — a WP theme rule detaching .step-body's
       flex growth lets the surplus fall BELOW the grid on staging (the
       screenshot-130 symptom); the chain re-pins stretch + centring at
       every >=761px viewport so the blank always splits evenly. */
.cd-app .step[data-step="1"] { padding-top: 8px; }
.cd-app .step[data-step="1"] .step-sub { margin-bottom: 10px; } /* v1.9.54: 6 -> 10 — a touch more air under the subtitle inside the uniform frame */
.cd-app .step[data-step="1"] .fallback { margin-top: 12px; margin-bottom: 14px; } /* v1.9.36: 20px breathing room above the bar (user: 紧贴底部太拥挤) */ /* v1.9.54: 6/10 -> 12/14 — the fallback strip keeps real air on both sides inside the taller frame */
@media (min-width: 761px) {
	.cd-app .step[data-step="1"].on .step-body { flex: 1 1 auto !important; min-height: 0 !important; }
	.cd-app .step[data-step="1"].on .shapes-grid { flex: 1 1 auto !important; min-height: 0 !important; align-content: space-evenly !important; } /* v1.9.54: centring -> even distribution (the hardened chain follows the base rule) */
}

/* ===== v1.9.34 d2 — Size & Order blocks at NATURAL height, VERTICALLY
   CENTRED in the frame body (user five-round feedback: the fixed-height
   frame pinned short content to the top and left a one-sided waste band
   below — Order measured 166px). The frame itself STAYS fixed and uniform
   (fabric is the height benchmark; its scroller is untouched): only the
   CONTENT placement inside the body changes. margin-block auto centres in
   free space AND collapses to 0 when the content is taller than the body
   (auto margins lose the free-space claim first), so a cramped window
   still scrolls from the TOP edge — no clipped head content, ever.
   Caged at >=761px: the phone single-scroll flow is zero-touch. ===== */
@media (min-width: 761px) {
	.cd-app .step[data-step="2"].on .step-body { flex-direction: column; } /* body keeps display:flex (its own rule), now a column so the wrap can centre vertically */
	.cd-app .step[data-step="2"].on .s2wrap { flex: 1 1 auto; margin: 0; min-height: 0; align-items: stretch; } /* v1.9.54: 0 1 auto -> 1 1 auto — the wrap STRETCHES to fill the step-body (measured 422 inside a 553px body = a 131px dead band under both columns); the v1.9.34 margin:auto centring stays retired — the columns fill by GROWING (preview) and by space-evenly (form), never by floating mid-frame */
	.cd-app .step[data-step="4"].on .step-body { display: flex; flex-direction: column; }
	.cd-app .step[data-step="4"].on .s4wrap { flex: 1 1 auto; margin: 0; min-height: 0; } /* v1.9.54: same stretch on Order (measured 555 inside 607) — the CTA row's margin-top:auto keeps anchoring the money column */
}

/* Step 2 form column breathing (desktop band only; phones keep flow order) */
@media (min-width: 961px) {
	.cd-app .step[data-step="2"] .s2wrap > div:first-child { display: flex; flex-direction: column; justify-content: space-evenly; } /* v1.9.54: flex-start -> space-evenly — the uniform 700px frame leaves the form column ~200px of slack; four blocks over five even slices (~30-36px each) beats BOTH the v1.9.34 margin:auto centring (two 50-60px voids) and a space-between trio (~50px each). Content grows too (inputs/toggles/cards below), so the slices stay under 40px */
	.cd-app .step[data-step="2"] .fields { gap: 32px 14px; } /* v1.9.57: 28 -> 32; v1.9.56: 24 -> 28; v1.9.54: clamp(8,1vh,12) -> 24 — field rows breathe inside the taller frame and the form column's even slices stay <=40px */
	/* Order: anchor the money row to the column's lower edge, like the bar */
	.cd-app .step[data-step="4"] .s4wrap > div:first-child { display: flex; flex-direction: column; }
	/* v1.9.55: the RIGHT Order column stacks too — the render card grows into
	   the stretch (the canvas centres inside its studio backdrop) and the
	   params card parks at the column bottom, no dead band under either */
	.cd-app .step[data-step="4"] .s4wrap > div:last-child { display: flex; flex-direction: column; }
	.cd-app .step[data-step="4"] .s4wrap > div:last-child .spec-card.spec-render { flex: 1 1 auto; min-height: 0; display: flex; align-items: center; justify-content: center; }
	.cd-app .step[data-step="4"] .cta-row { margin-top: auto; padding-top: 6px; } /* v1.9.60: 22 -> 6 — the row's own padding stacked on the measured 33px slice made the VISUAL button gap 55px; 6 keeps the visual gap <=40 while the disclaimer's own 20px bottom padding still separates the two blocks */
}

/* Step 4 render ceiling is raised IN PLACE at its own rule further down
   (.spec-card.spec-render canvas/svg) — same-specificity duplicates here
   would lose the cascade to that later block. */
/* v1.2.0: size fields in a 2-col grid — halves the vertical run of the
 * measurement form (width/depth/thickness/… + quantity side by side) */
.cd-app .fields { display: grid; grid-template-columns: 1fr 1fr; gap: 8px 14px; } /* v1.9.53: 10/16 -> 8/14 — field row spacing */
/* v1.6.3 #3: unit toggle pinned above the measurement fields — inch default,
   cm entry converts to the inch-valued state at write time */
.cd-app .unit-toggle { display: flex; align-items: center; gap: 6px; grid-column: 1 / -1; margin-bottom: 0; } /* v1.9.58: 12 -> 0 — the residual margin stacked on slice 2 (measured 41px); the even share owns the whole gap now */
.cd-app .unit-toggle .ut-label { font-size: .78rem; font-weight: 700; color: #6b7280; text-transform: uppercase; letter-spacing: .04em; margin-right: 2px; }
.cd-app .unit-toggle .ut-btn { border: 1.5px solid #d1d5db; background: #fff; color: #374151; border-radius: 6px; padding: 10px 14px; font-size: .82rem; font-weight: 700; cursor: pointer; font-family: inherit; min-width: 44px; } /* v1.9.54: 3 -> 6 — toggle buttons regain click-friendly height with the inputs */
.cd-app .unit-toggle .ut-btn:hover { border-color: #d2a272; }
.cd-app .unit-toggle .ut-btn.on { background: #1b3b5a; border-color: #1b3b5a; color: #fff; }
.cd-app .unit-toggle .unit-note { font-size: .72rem; color: #8a8075; margin-left: 4px; }
/* v1.6.0 D: merged Notched card — the side (left/right) is picked here on
   Step 2, right above the notch fields */
.cd-app .side-toggle { display: flex; align-items: center; gap: 8px; margin-bottom: 10px; grid-column: 1 / -1; }
.cd-app .side-toggle .st-label { font-size: .82rem; font-weight: 700; color: #374151; }
.cd-app .side-toggle .st-btn { border: 1.5px solid #d1d5db; background: #fff; color: #374151; border-radius: 6px; padding: 5px 14px; font-size: .82rem; font-weight: 600; cursor: pointer; font-family: inherit; }
.cd-app .side-toggle .st-btn:hover { border-color: #d2a272; }
.cd-app .side-toggle .st-btn.on { background: #1b3b5a; border-color: #1b3b5a; color: #fff; }
.cd-app .side-toggle .st-note { font-size: .72rem; color: #8a8075; }
.cd-app .field label { display: block; font-weight: 600; font-size: .84rem; margin-bottom: 8px; } /* v1.9.57: 5 -> 8 (v1.9.56: 2 -> 5 — label-to-input air follows the box relaxation */
.cd-app .field label .ltr { display: inline-block; background: #e5e7eb; color: #374151; border-radius: 3px; font-size: .7rem; font-weight: 700; padding: 0 5px; margin-left: 5px; vertical-align: 1px; }
.cd-app .field input { width: 100%; padding: 14px 10px; border: 1px solid #d1d5db; border-radius: 6px; font-size: .95rem; font-family: inherit; } /* v1.9.54: 4 -> 9 — the v1.9.53 over-compaction is relaxed: standard ~32px input boxes claim the uniform frame's room (and read tappable again); v1.9.53 had cut 7 -> 4 for the one-screen budget */
/* v1.6.3 #2: pure-number entry — the unit rides OUTSIDE the box in a fixed
   right tag (flips in|cm with the toggle); competitor input density */
.cd-app .field .inwrap { position: relative; display: block; }
.cd-app .field .inwrap input { padding-right: 38px; min-width: 0; }
/* v1.9.4 (screenshot 63): the unit suffix NEVER wraps to its own line —
   nowrap keeps "cm" beside the digits at 375px */
.cd-app .field .inwrap .unit { position: absolute; right: 10px; top: 50%; transform: translateY(-50%); font-size: .78rem; font-weight: 700; color: #8a8075; pointer-events: none; white-space: nowrap; }
.cd-app .field input:focus { outline: 2px solid #d2a272; border-color: #1b3b5a; }
/* v1.6.4 #1: thickness is a fixed-tier <select> — same box language as the
   typed inputs (no .unit tag: the option label already carries the unit) */
.cd-app .field select.tsel { width: 100%; padding: 9px 10px; border: 1px solid #d1d5db; border-radius: 6px; font-size: .95rem; font-family: inherit; background: #fff; appearance: auto; cursor: pointer; } /* v1.9.54: 4 -> 9 — same relaxation as the inputs (v1.9.53 had cut 7 -> 4) */
.cd-app .field select.tsel:focus { outline: 2px solid #d2a272; border-color: #1b3b5a; }
/* v1.9.1 #2: .hint is an ERROR-ONLY line now — the static per-field hint
   rows are gone (the v1.7 "?" measurement modals own the explanations);
   setFieldHint() in designer.js creates this node on validation failure
   and removes it again when the field turns valid. */
.cd-app .field .hint { font-size: .72rem; color: #6b7280; margin-top: 2px; line-height: 1.35; }
/* v1.7: .frow = input row + the round "?" that opens the measurement
   line-art modal (visual spec §四.5). Same circle-pill language as the
   fabric group tabs' .ftab-info. */
.cd-app .field .frow { display: flex; gap: 6px; align-items: center; }
.cd-app .field .frow .inwrap { flex: 1 1 auto; min-width: 0; }
.cd-app .field .mq { flex: 0 0 auto; width: 34px; height: 34px; min-height: 34px; max-height: 34px; min-width: 34px; max-width: 34px; aspect-ratio: 1 / 1; border: 1.5px solid #d1d5db; border-radius: 50%; background: #fff; color: #6b7280; font-family: inherit; font-size: .74rem; font-weight: 800; line-height: 1; cursor: pointer; padding: 0; }
.cd-app .field .mq:hover { border-color: #1b3b5a; color: #1b3b5a; background: #eef2f6; }
.cd-app .field .mq:focus-visible { outline: 2px solid #d2a272; }
.cd-app .field.err input { border-color: #dc2626; background: #fef2f2; }
.cd-app .field.err .hint { color: #dc2626; font-weight: 600; }
.cd-app .qtyrow { display: flex; align-items: center; gap: 8px; }
.cd-app .qtyrow button { width: 34px; height: 34px; padding: 0; font-size: 1.05rem; line-height: 1; }
.cd-app .qtyrow input { width: 64px; text-align: center; padding: 9px 6px; } /* v1.9.54: 4 -> 9 — same input relaxation */
.cd-app .meas-tip { font-size: .78rem; color: #4b5563; background: #f0f7f4; border-left: 3px solid #1b3b5a; padding: 18px 12px; margin-top: 6px; border-radius: 0 6px 6px 0; } /* v1.9.54: padding 5 -> 12, margin 8 -> 14 — the tip strip breathes inside the taller frame */
/* v1.2.0: preview column — sticky with a hard height cap so tall steps keep
 * the drawing inside one screen (SVG scales down, never overflows) */
.cd-app .preview { position: sticky; top: 8px; background: #fff; border: 1px solid #e5e7eb; border-radius: 8px; padding: 10px; text-align: center; }
.cd-app .preview svg { max-width: 100%; height: auto; }
.cd-app .pv-svg { display: flex; align-items: center; justify-content: center; }
/* cap on the SVG itself — a percentage max-height resolves to none while
 * the flex parent's height is still content-driven */
.cd-app .pv-svg svg { max-height: min(360px, 40vh); width: auto; max-width: 100%; }
/* v1.3.0 #8: 3D preview host — the canvas fills the box, pointer drag
 * rotates (touch-action handled in JS).
 * v1.4.0: radial studio gradient behind the alpha canvas — the cushion
 * sits in light instead of floating on the page background
 * v1.6.2 #3: the fixed height cap is GONE — the host is a flex item that
 * fills whatever the viewport gives the preview column (see the
 * data-step="2" rules above); the 3D module's ResizeObserver re-fits the
 * canvas whenever the box changes size. */
.cd-app .pv-3d { position: relative; display: flex; align-items: center; justify-content: center; background: radial-gradient(120% 95% at 50% 10%, #ffffff 0%, #f1f6f3 48%, #dfe9e4 100%); border-radius: 8px; }
.cd-app .pv-3d canvas { border-radius: 8px; }
.cd-app .pv-empty { color: #9ca3af; font-size: .85rem; padding: 18px 10px; } /* v1.9.53: 34 -> 18 — the empty-state's inner breathing halves */
/* v1.9.1 #1: one small line only — "Top area" (pricing transparency). The
   .pv-dims parameter echo and the perimeter figure are gone; qty note rides
   along when > 1. */
.cd-app .pv-meta { font-size: .7rem; color: #8a8075; margin-top: 4px; }
.cd-app .pv-meta strong { color: #1b3b5a; }
/* v1.9.1 #1: the drag hint is a corner caption INSIDE the 3D host — an
   absolutely positioned small grey note that no longer costs a content row
   under the preview (designer.js appends/removes it with the 3D state) */
.cd-app .pv-3d .pv-3d-hint { position: absolute; right: 10px; bottom: 8px; font-size: .64rem; color: #a8b0b8; pointer-events: none; }

/* ===== bottom action bar (v1.4.0 — rofielty pattern: the old
   .nav-row merged with the live price into ONE bar: left price block,
   right Back + primary CTA).
   v1.6.2 #1: the bar is PINNED TO THE VIEWPORT BOTTOM — position:fixed,
   left:0 right:0 bottom:0, z-index:100 (user clarification: the screen
   bottom, not the tool bottom — the v1.6.0 "last flex row" scheme is
   retired). With .wiz at height:100vh the wizard's bottom edge IS the
   screen bottom, so bar and frame close together on every screen; the
   fixed positioning is the guarantee (double insurance). Content stays
   aligned with the 1280px tool column via the padding-inline clamp. */
/* v1.9.18 (USER VERDICT "把底部吸附的功能取消，因为没有这个必要"): the bar is
   NOT pinned at all any more — not to the viewport (v1.9.16 retired fixed)
   and NOT to the frame either (the v1.9.16 sticky half-cancel is withdrawn).
   Structurally each .nav-bar now lives INSIDE its .step-body as the LAST
   child, so it rides the step's content flow: on short steps it reads as the
   form's footer row, and inside the tall Fabric list you reach the CTA by
   scrolling the list — a trade-off the user explicitly accepted. Pure static
   block, no z-index/backdrop tricks left over from pinning; <=760px was
   already in-flow so both ends now share one semantics. */
.cd-app .nav-bar { flex: 0 0 auto; display: flex; align-items: center; gap: 14px; margin: 0; padding: 7px 14px 8px; justify-content: space-between; background: #1b3b5a; border-top: 1px solid #12293e; } /* v1.9.53: padding 10/12 -> 7/8 — bar strip slimmer on every step */
/* v1.9.22 task 2 (user: "价格和按钮一行背景要不要加个颜色，便于区分，一眼能看到"):
   the bar gets the NAVY ground — labels/copy white, the price figure in sand
   gold #d2a272, Back a white-outline ghost, the primary CTA keeps its gold
   fill (contrast checked: white-on-navy 8.6:1, gold-figure-on-navy 5.4:1,
   navy-on-gold CTA 5.9:1 — all AA). The wizard's overflow:hidden rounds the
   bar's bottom corners to the frame automatically. Structure/height from
   v1.9.21 untouched; four steps identical, phone included. */
.cd-app .nb-label { color: rgba(255,255,255,.78); }
.cd-app .nb-amount, .cd-app .nb-amount.nb-empty { color: #d2a272; }
.cd-app .nb-note { color: rgba(255,255,255,.62); }
.cd-app .nav-bar .mini { background: transparent; border: 1.5px solid rgba(255,255,255,.6); color: #fff; }
.cd-app .nav-bar .mini:hover { background: rgba(255,255,255,.12); border-color: #fff; }
.cd-app .nav-bar button.primary { background: #d2a272; color: #1b3b5a; border-color: #d2a272; } /* v1.9.22: the CTA turns GOLD on the navy ground (page-body primaries stay navy); navy-on-gold 5.9:1 AA */
.cd-app .nav-bar button.primary:hover { background: #e0b385; border-color: #e0b385; }
.cd-app .nav-bar .nb-note a { color: #e8d8c3; }
/* v1.9.21 STRUCTURAL CORRECTION (user screenshot 99 disproved the sticky
   approach — sticky only hugs the edge when content OVERFLOWS; with short
   content the bar rides up and leaves a gap below): each .nav-bar moves back
   OUT of .step-body to the END of .step (the v1.9.16 lineage — .step.on is a
   flex column [header | scrollable body | bar]). The bar therefore rests ON
   the frame bottom BY STRUCTURE on every step, phone and desktop alike: no
   position property anywhere (v1.9.18 task-4 "bar inside step-body" is
   recorded in the readme ledger as a structural error — putting the bar into
   the scrolling content flow made it ride up on short content and sink to
   the scroll bottom on phones; the v1.9.20 phone-sticky is RETIRED, replaced
   by this structure). Content under-fill still stretches the body
   (v1.9.18 T2 space-evenly / --cd-pv-h); content overflow scrolls INSIDE the
   body while the bar stays pinned to the frame bottom; scrolling the page
   moves bar + frame together — never a viewport fixture. */
.cd-app .nb-price { display: flex; align-items: baseline; gap: 8px; min-width: 0; flex: 1 1 auto; }
.cd-app .nb-label { font-size: .68rem; font-weight: 700; text-transform: uppercase; letter-spacing: .06em; }
.cd-app .nb-amount { font-size: 1.3rem; font-weight: 800; white-space: nowrap; }
.cd-app .nb-amount.nb-empty { font-weight: 700; opacity: .85; }
.cd-app .nb-note { font-size: .7rem; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.cd-app .nb-actions { display: flex; gap: 10px; flex: 0 0 auto; }

/* ===== step 3: fabric tabs (v1.1.0 — horizontal tab bar + hover panel;
   v1.2.0 — clicking a group tab also filters the library grid below, so the
   grid always shows the selected family like the first version did) ===== */
/* v1.9.8 #3: .fab-tabs-wrap deleted (its only job was position:relative for
   the retired v1.9.3 hover panel) — .fab-tabs is the step-body's direct
   child and carries the spacing itself */
.cd-app .fab-tabs { display: flex !important; flex-wrap: nowrap !important; width: 100% !important; gap: 8px; margin-bottom: 8px; } /* v1.9.53: margin-bottom 12 -> 8 */ /* v1.9.28: width 100% — the live row sat at 965px inside the 1135px body leaving a dead 170px on the right; equal shares get wider too */ /* v1.9.26: user ruling — seven tabs, ONE desktop row (the v1.9.21 wrapping layout is retired); v1.9.25's wrap restored two rows at 1920 */
/* v1.9.24 (user: "上面的tab我之前只是让你手机端优化，你电脑端干嘛要动呢"):
   the v1.9.22 desktop single-row slider is WITHDRAWN above 760px — the
   desktop row returns to its v1.9.21 wrapping layout (no overflow-x, no
   masks). "All fabrics" stays FIRST (kept from v1.9.22 — an explicit earlier
   request). The slider + masks + compact sizing remain in the <=760px band
   below (user-approved phone behaviour, untouched). */
.cd-app .fab-tabs-wrap { position: relative; flex: 0 0 auto; display: flex; flex-wrap: wrap; }
/* v1.9.22 t1c affordance — PHONE BAND ONLY now (masks + sliding row): */
@media (max-width: 760px) {
	.cd-app .fab-tabs { flex-wrap: nowrap !important; overflow-x: auto !important; scrollbar-width: thin !important; scrollbar-color: rgba(27,59,90,.45) rgba(27,59,90,.08); } /* v1.9.31 t1: scrollbar-width none->thin + scrollbar-color — the bar is a VISIBLE indicator now (Firefox path); !important keeps winning the f3 desktop wrap fight */
	.cd-app .ftab { flex: 0 0 auto !important; min-width: max-content !important; }
	.cd-app .fab-tabs { -webkit-mask-image: linear-gradient(to right, #000 0, #000 calc(100% - 34px), transparent 100%); mask-image: linear-gradient(to right, #000 0, #000 calc(100% - 34px), transparent 100%); } /* v1.9.29: the row slides but nothing said so — right-edge fade is the affordance (user: 要一个很明显的左右滚动的提示) */ /* v1.9.28: natural-width tabs on phones so the row overflows and SLIDES (crushed-to-fit tabs killed the scroll) */
	/* v1.9.31 t1 (user: 为什么我还是没有看到左右滚动条 — v1.9.30's multiplied
	   hiding + the v1.9.29 edge fade were still not an EXPLICIT indicator):
	   the phone tab-row scrollbar is UN-hidden and STYLIZED — a 4px-tall
	   Navy 45% thumb riding a Navy 8% track, both fully rounded. The entire
	   v1.9.30 display:none group (scrollbar / track / thumb / button) is
	   DELETED — it painted nothing, by design. The webkit rules sit at
	   (0,2,0) specificity so they also outrank the plugin-wide .cd-app
	   ::-webkit-scrollbar 6px chrome. Desktop keeps NO bar: the row wraps
	   above 760px and never overflows. */
	.cd-app .fab-tabs::-webkit-scrollbar { height: 4px; }
	.cd-app .fab-tabs::-webkit-scrollbar-thumb { background: rgba(27,59,90,.45); border-radius: 99px; }
	.cd-app .fab-tabs::-webkit-scrollbar-track { background: rgba(27,59,90,.08); border-radius: 99px; }
	.cd-app .fab-tabs-wrap { flex-wrap: nowrap !important; }
	.cd-app .fab-tabs-wrap::after,
	.cd-app .fab-tabs-wrap::before { content: ''; position: absolute; top: 0; bottom: 0; width: 42px;
	  pointer-events: none; opacity: 0; transition: opacity .18s; z-index: 2; }
	.cd-app .fab-tabs-wrap::after { right: 0; background: linear-gradient(to right, rgba(252,253,253,0), #fcfdfd 82%); }
	.cd-app .fab-tabs-wrap::before { left: 0; background: linear-gradient(to left, rgba(252,253,253,0), #fcfdfd 82%); }
	.cd-app .fab-tabs-wrap:has(.fab-tabs.can-scroll-right)::after { opacity: 1; }
	.cd-app .fab-tabs-wrap:has(.fab-tabs.can-scroll-left)::before { opacity: 1; }

	/* v1.9.25 f4 (live site: the theme inflated the use/pattern chips to a 42px
	   row): explicit compact bounds on the phone band; the freed height goes to
	   the swatch grid. */
	.cd-app .fab-filters .chip { padding: 3px 10px !important; font-size: .72rem !important;
	                             line-height: 1.2 !important; min-height: 0 !important; height: auto; }
	.cd-app .fab-filters { gap: 4px 12px !important; }
	/* ===== v1.9.25 t5 (user ruling: single-scroll phones) =====
	   1. step-bodies stop scrolling internally — content lays out in full;
	   2. the fabric grid stops side-scrolling on phones — the page carries it;
	   3. the bar becomes viewport-fixed at the bottom (full width, navy,
	      z-index 100 — above content, below toast 200/modals);
	   4. the app reserves the bar's height at its bottom so nothing hides. */
	.cd-app .step.on .step-body { overflow: visible !important; min-height: 0 !important; }
	.cd-app .step[data-step="3"] .step-body { overflow: visible !important; }
	.cd-app .step[data-step="3"] .fab-grid { overflow: visible !important; min-height: 0 !important; flex: 0 0 auto !important; }
	.cd-app .nav-bar { position: fixed !important; bottom: 0 !important; left: 0 !important; right: 0 !important;
	                   margin: 0 !important; z-index: 100 !important; border-radius: 0 !important;
	                   padding-bottom: calc(8px + env(safe-area-inset-bottom, 0px)) !important; }
	.cd-app { padding-bottom: calc(96px + env(safe-area-inset-bottom, 0px)) !important; }}
.cd-app .ftab { display: block !important; position: relative; border: 2px solid #e5e7eb; border-bottom-color: #d1d5db; border-radius: 8px; background: #fff; padding: 7px 26px 6px 6px; cursor: pointer; text-align: center; font-family: inherit; transition: border-color .15s, background .15s; min-width: 0 !important; max-width: none; } /* v1.9.28: the 1 1 0 equal-share moved to the desktop band below — it had leaked into the phone band and crushed the seven tabs to 41px each, which is why the phone slider stopped overflowing (user: the phone row must scroll) */
@media (min-width: 761px) {
	.cd-app .ftab { flex: 1 1 0 !important; }
} /* v1.9.27: display:block !important — the live theme's inline-flex put name/sub/current BESIDE each other (user: tab content is a vertical stack); min-width locked HERE (the root guard was outranked on this rule) and the 1.9.26 white-space:nowrap is GONE — it froze the seven tabs at text width (1464px in an 1135px body) and spilled a horizontal scrollbar */
/* v1.6.5 #3: the group ⓘ — top-right corner of each family tab. One info
   entry per family (the 119 per-card pills are gone); opens .grp-modal.
   v1.8 #2 CENTERED: the ⓘ is a <span>, and an INLINE span ignores
   width/height — the circle collapsed around the glyph's baseline box and
   the ⓘ rode high-left of center (user round-7 screenshot 3).
   display:inline-flex + align/justify-center puts the glyph dead-center
   on both axes; flex-shrink:0 guards the 20px box inside the tab. */
.cd-app .ftab .ftab-info { position: absolute; top: 4px; right: 4px; width: 24px; height: 24px; min-height: 24px; max-height: 24px; min-width: 24px; max-width: 24px; aspect-ratio: 1 / 1; display: inline-flex; align-items: center; justify-content: center; flex-shrink: 0; border: 1.5px solid #d1d5db; border-radius: 50%; background: #fff; color: #6b7280; font-size: .72rem; font-weight: 700; line-height: 1; cursor: pointer; padding: 0 0 1px; font-family: inherit; z-index: 1; } /* v1.9.26: 34 -> 24 — the user's sizing call on the live site */
.cd-app .ftab .ftab-info:hover { border-color: #1b3b5a; color: #1b3b5a; background: #eef2f6; }
.cd-app .ftab .ftab-info:focus-visible { outline: 2px solid #d2a272; }
.cd-app .ftab:hover { border-color: #d2a272; }
.cd-app .ftab .ft-name { display: block; font-size: .82rem; font-weight: 700; color: #222; }
.cd-app .ftab .ft-name, .cd-app .ftab .ft-sub, .cd-app .ftab .ft-cur { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } /* v1.9.27: each line clips itself; the tab stays shrinkable so seven fit one row */
.cd-app .ftab .ft-sub { display: block; font-size: .66rem; color: #8a8075; margin-top: 1px; }
.cd-app .ftab .ft-cur { display: block; font-size: .66rem; font-weight: 700; color: #1b3b5a; margin-top: 2px; min-height: .66rem; }
/* v1.2.0 heritage: "active" = the group currently shown in the library
 * grid below; "cur" = the family of the picked fabric.
 * v1.9.4 (user round 11, "impossible to have 3 tabs selected at once"):
 * tabs are SINGLE-SELECT now — .cur loses its selected-look entirely (no
 * Navy border, no tint) so the row renders exactly ONE highlighted tab.
 * The picked family still signals itself through the tab's "✓ SKU" line
 * (.ft-cur text, Navy) without competing for the selected state. */
.cd-app .ftab.active { border-color: #1b3b5a; }
.cd-app .ftab.all .ft-name { color: #1b3b5a; }
/* v1.9.3 (user round 10): the .fab-panel hover-peek rules are DELETED with
 * the feature itself — the tab's ⓘ group modal is the one explainer. */

.cd-app .fab-filters { display: flex; flex-wrap: wrap; gap: 6px 16px; margin-bottom: 8px; }
.cd-app .chip-row { display: flex; flex-wrap: wrap; align-items: center; gap: 5px; }
.cd-app .chip-label { font-size: .72rem; font-weight: 700; color: #6b7280; text-transform: uppercase; letter-spacing: .05em; }
.cd-app .chip { border: 1.5px solid #d1d5db; background: #fff; color: #374151; border-radius: 99px; padding: 2px 9px; font-size: .74rem; font-weight: 600; cursor: pointer; font-family: inherit; line-height: 1.35; } /* v1.9.26: compact at desktop too — 42px was the theme floor, not our padding */
.cd-app .chip:hover { border-color: #d2a272; }
.cd-app .chip.on { background: #1b3b5a; border-color: #1b3b5a; color: #fff; }

/* v1.6.3: the fabric step is FABRIC ONLY — the library grid goes to a
 * fixed 8 columns with LARGER swatches and the old "reserve two rows"
 * fixed-height scroll box (258px cap, 84px thumbs) is GONE: the grid lives
 * in the step-body's natural scroll, so browsing 119 SKUs scrolls the step
 * itself (the same container every other over-tall step uses).
 * v1.6.0 B4 heritage: cards stay slimmed to swatch + SKU + color name. */
.cd-app .fab-grid { display: grid; grid-template-columns: repeat(8, 1fr); gap: 10px; margin: 2px 0 8px; padding: 0 4px 8px 0; overscroll-behavior: contain; } /* v1.9.53: bottom margin 10 -> 8, bottom padding 14 -> 8 — the fabric strip's chrome slims with the frame */
/* v1.9.18 task 5 (user-proposed, closes the tall-Fabric CTA trade-off
   of the de-pinned bar): on the Fabric step ONLY, the step-body stops
   scrolling as a whole and the 119-swatch GRID becomes the internal
   scroller. Family tabs + usage/pattern chips stay fixed above it and
   the nav-bar sits after the flexed grid — reachable with zero scroll,
   still a plain in-flow static block (same semantics as tasks 1-4).
   HISTORY NOTE: this revives the v1.3.0 "fixed ~300px box" pattern under
   the four-step architecture; the v1.6.3 natural-scroll reversal assumed
   whole-PAGE scrolling, which v1.9.16 F2 + v1.9.18 have retired. */
.cd-app .step[data-step="3"] .step-body { display: flex; flex-direction: column; overflow: auto; } /* v1.9.24 2c: hidden -> auto — with the 230px grid floor, a too-short body now SCROLLS (bar stays frame-bound) instead of clipping the bar */
.cd-app .step[data-step="3"] .fab-tabs-wrap, .cd-app .step[data-step="3"] .fab-filters { flex: 0 0 auto; } /* v1.9.22: the wrap (non-scrolling) is the fixed flex child; .fab-tabs inside owns the slide */
.cd-app .step[data-step="3"] .fab-empty, .cd-app .step[data-step="3"] .sewn-note { flex: 0 0 auto; }
.cd-app .step[data-step="3"] .fab-grid { flex: 1 1 auto; min-height: 230px; overflow-y: auto; align-content: start; }
/* v1.9.24 2c (live-site report: a card's lower half was sliced at the scroll
   edge): the swatch window keeps AT LEAST 1.5 full card rows (~230px) — when
   the body's own budget cannot afford that, the step-body itself scrolls
   (overflow changed hidden -> auto below) instead of showing half rows. */
/* v1.9.24 2a (live-site report: the theme's global scrollbar — thick, arrow
   buttons — painted over the plugin's scroll areas): every scroller INSIDE
   .cd-app gets the plugin's own slim chrome; WoodMart's global rules stop at
   the .cd-app boundary. */
.cd-app ::-webkit-scrollbar { width: 6px; height: 6px; }
.cd-app ::-webkit-scrollbar-track { background: transparent; }
.cd-app ::-webkit-scrollbar-thumb { background: #c3cad3; border-radius: 3px; }
.cd-app ::-webkit-scrollbar-thumb:hover { background: #aeb7c2; }
.cd-app ::-webkit-scrollbar-button { display: none !important; width: 0 !important; height: 0 !important; }
.cd-app { scrollbar-width: thin; scrollbar-color: #c3cad3 transparent; } /* v1.9.19 task 3 (screenshot 95): the scroller's surplus height made the grid's implicit rows STRETCH to fill it — a single-row family group (Tech, 5 SKUs) rendered 461px-tall cards (1:3.4). align-content:start returns rows to content height; the surplus stays owned by the scroll window. */
/* the bar now lives INSIDE the body: the Size body's legacy row-flex would make
   it a THIRD COLUMN - pin the body back to column so the wrap/grid rows stack */
.cd-app .step[data-step="2"] .step-body { flex-direction: column; }
/* v1.9.54 CORRUPTION FIX: the comment block below had LOST its opening
   "/*" (a long-gone edit sheared it off) — the stray text tripped CSS
   error recovery and the @media rule that follows was silently DROPPED
   from the CSSOM, so --cd-pv-h never reached .pv-3d and the clamp
   fallback ruled (live-verified: bandRulesInCSSOM = 0, computed 378px
   with the var holding 529px). Restored:
   Growth needs a DEFINITE length, so fitScreen publishes one: every refit
   writes --cd-pv-h and the drawing takes it verbatim. Exact pixels ->
   zero recursion; identical maths on any host; the module's
   ResizeObserver re-runs size()+paintDims on every change so callouts
   stay glued to the model. Below 961px (or before the first fit lands)
   the static clamp stands in. */
@media (min-width: 961px) {
	.cd-app .step[data-step="2"] .pv-3d { height: var(--cd-pv-h, clamp(300px, 42vh, 400px)); }
} /* v1.9.54: fitScreen now publishes --cd-pv-h = frame height minus the column chrome (h - 171) — the preview takes the preview column's FULL height instead of a fixed 42% strip */

.cd-app .fabitem { border: 2px solid #e5e7eb; border-radius: 8px; padding: 5px; cursor: pointer; background: #fff; text-align: left; transition: border-color .15s; position: relative; } /* v1.8.2 #1: position anchors the returning card ⓘ */
.cd-app .fabitem:hover { border-color: #d2a272; }
.cd-app .fabitem.sel { border-color: #1b3b5a; background: #eef2f6; }
.cd-app .fabitem .fi-img img { width: 100%; height: 104px; object-fit: cover; border-radius: 5px; display: block; background: #f3f4f6; }
.cd-app .fabitem .fi-sku { display: inline-block; width: 52%; font-size: .72rem; font-weight: 700; margin-top: 3px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; vertical-align: bottom; } /* v1.9.28: SKU left + color right on ONE line (user ruling) — the long SKUs are also shortened at the data layer (ZJG557->ZG etc.) */
.cd-app .fabitem .fi-desc { display: inline-block; width: 47%; font-size: .68rem; color: #6b7280; line-height: 1.4; margin-top: 3px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; text-align: right; vertical-align: bottom; }
/* v1.8.2 #1: the per-SKU ⓘ is BACK on the library card (top-right over
   the swatch — the v1.6.4 pattern; the v1.6.5 round had retired it to the
   group tab only). Same circle-pill language as .ftab-info / .shape-info;
   click opens the per-SKU macro modal and never picks the fabric. */
.cd-app .fabitem .fab-info { position: absolute; top: 7px; right: 7px; width: 24px; height: 24px; min-height: 24px; max-height: 24px; min-width: 24px; max-width: 24px; aspect-ratio: 1 / 1; display: inline-flex; align-items: center; justify-content: center; flex-shrink: 0; border: 1.5px solid #d1d5db; border-radius: 50%; background: #fff; color: #6b7280; font-size: .72rem; font-weight: 700; line-height: 1; cursor: pointer; padding: 0 0 1px; font-family: inherit; z-index: 1; }
.cd-app .fabitem .fab-info:hover { border-color: #1b3b5a; color: #1b3b5a; background: #eef2f6; }
.cd-app .fabitem .fab-info:focus-visible { outline: 2px solid #d2a272; }
.cd-app .fab-empty { font-size: .8rem; color: #6b7280; padding: 10px 0; }
@media (max-width: 1100px) and (min-width: 761px) {
	.cd-app .fab-grid { grid-template-columns: repeat(6, 1fr); }
}

/* density + addons */
.cd-app .dens-pick { margin: 6px 0 6px; padding-top: 20px; border-top: 1px solid #e5e7eb; } /* v1.9.54: margins 8/2 -> 16/8, padding-top 8 -> 16 — the density block regains its pre-compaction breathing (v1.9.53 had cut it for the one-screen budget) */
.cd-app .dens-pick h3 { margin: 0 0 5px; font-size: 14px; display: flex; align-items: baseline; gap: 12px; flex-wrap: wrap; } /* v1.9.53: 6 -> 5 */
.cd-app .dens-help { font-size: .72rem; font-weight: 600; color: #1b3b5a; text-decoration: underline; }
.cd-app .dens-help:hover { color: #122b44; }
/* v1.2.0: fixed 4 columns — the four density grades always sit on one row.
   v1.6.2 #4+#5: the grade number stays the hero, the descriptor sits in a
   small centered line UNDER the number. Colors on the Navy system: border +
   selected state = Navy; the selected HIGHLIGHT may use the gold fill with
   Navy text (contrast ≈4.9:1 on #d2a272).
   v1.6.3 #4: capsule ~30% tighter (was padding 12px 6px 26px — the 26px
   floor existed only to reserve room for the bottom-right ⓘ, which now
   lives in the TOP-RIGHT corner) and the grade number one step down
   (19px -> 17px). */
.cd-app .dens-opts { display: grid; grid-template-columns: repeat(4, 1fr); gap: 8px; } /* v1.9.54: 6 -> 8 — card row breathes with the block */
.cd-app .dens { display: block !important; position: relative; border: 1.5px solid #d1d5db; background: #fff; border-radius: 8px; padding: 13px 6px 13px; cursor: pointer; text-align: center; font-size: 16px; font-weight: 800; font-family: inherit; min-width: 0; letter-spacing: .01em; color: #222; line-height: 1.2; } /* v1.9.54: padding 5 -> 9 — density cards relax back toward their pre-v1.9.53 size inside the taller frame */ /* v1.9.26: block !important — the live theme's button inline-flex put the caption BESIDE the number; this rule is the vertical stack coming back */
.cd-app .dens:hover { border-color: #d2a272; }
.cd-app .dens > span { display: block; font-size: 10.5px; font-weight: 400; color: #6b7280; margin-top: 2px; line-height: 1.3; }
.cd-app .dens.on { border-color: #1b3b5a; box-shadow: 0 0 0 1px #1b3b5a; background: #d2a272; color: #1b3b5a; }
.cd-app .dens.on > span { color: #1b3b5a; }
/* v1.6.3 #4: ⓘ in the card's TOP-RIGHT corner (was bottom-right, which
   forced the tall 26px padding floor) */
.cd-app .dens-info { position: absolute; right: 4px; top: 3px; color: #6b7280; cursor: pointer; line-height: 0; width: 22px; height: 22px; min-height: 22px; max-height: 22px; min-width: 22px; max-width: 22px; aspect-ratio: 1 / 1; display: inline-flex; align-items: center; justify-content: center; } /* v1.9.26 ROOT GUARD: the live theme (WoodMart) ships a global button rule —
   display:inline-flex + min-height:42px — which (a) stretched .shape-info into a
   20x42 ellipse (v1.9.25 fixed the three OTHER info classes but missed this one),
   (b) re-flowed the density cards (.dens: number left, caption right) even though
   their own rules are a vertical stack, (c) inflated the use/pattern chips to 42px.
   One guard zeroes the theme floors for EVERY button inside the widget; individual
   widgets re-declare what they actually need (locks, flex centering) below. */
.cd-app button { min-height: 0 !important; min-width: 0 !important; text-transform: none !important; letter-spacing: normal !important; } /* v1.9.28: + text-transform — the live theme upper-cases every button (live probe: .ft-name computed uppercase) */
/* v1.9.25 f1: square + explicit bounds — the theme stretched these circles into 26x42 ellipses on the live site */
.cd-app .dens-info:hover, .cd-app .dens-info:focus-visible { color: #1b3b5a; }
.cd-app .dens-note { font-size: 11.5px; color: #8a8075; margin: 5px 0 0; }

/* density info modal (v1.1.0 #9) — v1.7.1: z-index 60 -> 120 so an open
   modal's backdrop also covers the fixed bottom bar (it used to show
   through and stay clickable) */
.cd-app .cd-modal { position: fixed; inset: 0; z-index: 120; display: none; align-items: center; justify-content: center; padding: 18px; }
.cd-app .cd-modal.open { display: flex; }
.cd-app .cd-modal-backdrop { position: absolute; inset: 0; background: rgba(17, 24, 39, .5); }
.cd-app .cd-modal-card { position: relative; background: #fff; border-radius: 10px; border: 1px solid #e5e7eb; max-width: 430px; width: 100%; padding: 18px 20px 16px; box-shadow: 0 22px 50px rgba(17, 24, 39, .28); }
.cd-app .cd-modal-x { position: absolute; top: 8px; right: 10px; background: none; border: none; font-size: 1.3rem; line-height: 1; color: #6b7280; cursor: pointer; padding: 4px; }
.cd-app .cd-modal-x:hover { color: #222; }
.cd-app .cd-modal-title { margin: 0 26px 8px 0; font-size: 1.02rem; color: #1b3b5a; }
.cd-app .cd-modal-svg { text-align: center; margin-bottom: 10px; }
.cd-app .cd-modal-rows div { display: flex; gap: 10px; padding: 5px 0; border-bottom: 1px dashed #e5e7eb; font-size: .84rem; }
.cd-app .cd-modal-rows div:last-child { border-bottom: none; }
.cd-app .cd-modal-rows em { font-style: normal; font-weight: 700; color: #6b7280; min-width: 64px; text-transform: uppercase; font-size: .66rem; letter-spacing: .04em; padding-top: 2px; }
.cd-app .cd-modal-rows span { color: #374151; }
.cd-app .cd-modal-note { font-size: .84rem; color: #4b5563; margin: 8px 0 10px; }
.cd-app .cd-modal-link { font-size: .8rem; font-weight: 700; }

/* v1.3.0 #2: shape detail modal — wider card, larger drawing, CTA row */
.cd-app .cd-modal.shape-modal .cd-modal-card { max-width: 470px; }
.cd-app .cd-modal.shape-modal .cd-modal-svg { background: #f8fbf9; border: 1px solid #e5e7eb; border-radius: 8px; padding: 8px 0; }
.cd-app .cd-modal.shape-modal .cd-modal-svg svg { max-width: 100%; height: auto; }
/* v1.9.1 #3: the modal's 480x360 3D canvas downsamples to the card width */
.cd-app .cd-modal.shape-modal .cd-modal-svg canvas { display: block; max-width: 100%; height: auto; margin: 0 auto; }
/* v1.7: measurement ? modal + upgraded density modal — same card language
   as the shape modal (470px, tinted SVG panel) */
.cd-app .cd-modal.meas-modal .cd-modal-card, .cd-app .cd-modal.dens-modal .cd-modal-card { max-width: 470px; }
.cd-app .cd-modal.meas-modal .cd-modal-svg, .cd-app .cd-modal.dens-modal .cd-modal-svg { background: #f8fbf9; border: 1px solid #e5e7eb; border-radius: 8px; padding: 8px 0; text-align: center; }
.cd-app .cd-modal.meas-modal .cd-modal-svg svg, .cd-app .cd-modal.dens-modal .cd-modal-svg svg { max-width: 100%; height: auto; }
.cd-app .sm-actions { display: flex; align-items: center; gap: 14px; flex-wrap: wrap; margin-top: 2px; }
.cd-app .sm-cta { background: #1b3b5a; color: #fff; border: 2px solid #1b3b5a; padding: 9px 16px; border-radius: 6px; font-size: .9rem; font-weight: 700; cursor: pointer; font-family: inherit; }
.cd-app .sm-cta:hover { background: #122b44; }

/* v1.6.4: fabric detail modal — swatch + macro weave view side by side.
   Card capped at 90vh with its own scroll so the taller modal cannot
   break the full-screen wizard frame; the macro figure self-hides via
   onerror (JS) when a shot is missing. */
.cd-app .cd-modal.fab-modal .cd-modal-card { max-width: 600px; max-height: 90vh; overflow-y: auto; } /* v1.6.5 #3: 480 -> 600 — swatch + macro read clearly side by side */
.cd-app .fab-modal .fab-media { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; align-items: start; margin: 10px 0 4px; }
.cd-app .fab-modal .fab-media figure { margin: 0; }
.cd-app .fab-modal .fab-media figcaption { font-size: .66rem; font-weight: 700; text-transform: uppercase; letter-spacing: .04em; color: #6b7280; margin-bottom: 4px; }
.cd-app .fab-modal .fab-media img { width: 100%; height: 220px; object-fit: cover; border-radius: 8px; border: 1px solid #e5e7eb; display: block; background: #f3f4f6; }
.cd-app .fab-modal .fab-media .fm-note { display: block; font-size: .72rem; color: #6b7280; line-height: 1.35; margin-top: 5px; }
/* macro shot missing (onerror -> figure hidden + .fm-solo flag from JS)
   -> the swatch spans the full width without a hole */
.cd-app .fab-modal .fab-media.fm-solo .fm-shot { grid-column: 1 / -1; }
/* v1.6.5 #3: the per-card .fab-info ⓘ rules are REMOVED TO THE ROOT — the
   info entry now lives once per family, on the group tab (.ftab-info). */
/* v1.6.5 #3: GROUP modal (tab ⓘ) — what/why/best-for/CARE rows + the
   family swatch strip; each swatch opens the per-SKU macro modal */
.cd-app .cd-modal.grp-modal .cd-modal-card { max-width: 620px; max-height: 90vh; overflow-y: auto; }
/* v1.9.3: the .grp-sw* strip rules are deleted with the strip itself */
@media (max-width: 520px) {
	.cd-app .fab-modal .fab-media img { height: 120px; }
}

/* (v1.9.0: the Step-4 pick-card system — sewing styles, anti-slip and
   ties cards, the .addon-six band, the tie-length pills and the
   art-zoom overlay — was DELETED with the 2026-08-26 supply-chain
   veto; none of these elements exist in the DOM any more) */
.cd-app .sewn-note { font-size: .74rem; color: #6b7280; margin-top: 4px; font-style: italic;  margin-bottom: 4px; } /* v1.9.53: 6/6 -> 4/4 */ /* v1.9.24 2b: air above the navy bar (live-site visual-crush report); the note stays OUTSIDE the fab-grid scroller by structure */

/* ===== step 4: Review & Order (v1.9.0: Step 4 = the quote step) ===== */
/* v1.9.6: the header/sub live INSIDE the left column now — the grid is the
   whole step: left = header + breakdown + actions, right = render +
   configuration. align-items:start = the two columns' TOPS sit on one
   line; each column flows naturally inside itself. NO negative margins. */
.cd-app .s4wrap { display: grid; grid-template-columns: 1.05fr .95fr; gap: 18px; align-items: stretch; } /* v1.9.55: start -> stretch — both columns fill the (now stretched) wrap so the CTA row anchors to the true column bottom and the right column has no dead band under the params card */
.cd-app .s4wrap .step-h, .cd-app .s4wrap .step-sub { margin-left: 2px; margin-right: 2px; }

/* v1.6.4 #6: .edit-chips / .echip REMOVED TO THE ROOT (v1.6.2 deleted
   them, v1.6.3 regressed them; E2E asserts the container stays gone) */

.cd-app .quote-box { border: 1px solid #e5e7eb; border-radius: 8px; background: #fff; }
.cd-app .quote-box h3 { font-size: .98rem; padding: 12px 14px; margin: 0; border-bottom: 1px solid #e5e7eb; background: #eef2f6; } /* v1.9.54: 8 -> 12 — the quote header breathes inside the uniform frame */
.cd-app .qrows { padding: 6px 14px 10px; } /* v1.9.54: 3/7 -> 6/10 — same relaxation */
.cd-app .qrow { display: flex; justify-content: space-between; gap: 12px; padding: 22px 0; border-bottom: 1px dashed #e5e7eb; font-size: .88rem; } /* v1.9.54: 4 -> 13 — the uniform 700px frame's Step-4 slack is spent on readable quote rows (v1.9.53 had cut 6 -> 4) */
.cd-app .qrow:last-child { border-bottom: none; }
.cd-app .qrow .lab { color: #374151; }
.cd-app .qrow .lab em { display: block; font-style: normal; font-size: .72rem; color: #9ca3af; }
.cd-app .qrow .val { font-weight: 600; white-space: nowrap; font-variant-numeric: tabular-nums; }
.cd-app .qrow.total { border-top: 2px solid #1b3b5a; border-bottom: none; margin-top: 4px; padding-top: 7px; font-size: 1rem; }
.cd-app .qrow.total .val { color: #1b3b5a; font-size: 1.22rem; }
/* v1.6.0 B5: breakdown grouped into labelled sections (cushion vs
   finishing) — replaced the removed 4-density compare strip (A3: density
   picked = one price, per the 2026-08-25 decision) */
.cd-app .qgroup { font-size: .66rem; font-weight: 700; text-transform: uppercase; letter-spacing: .06em; color: #8a8075; padding: 12px 0 4px; } /* v1.9.54: 8/2 -> 12/4 — group headers breathe with the rows */
.cd-app .qgroup:first-child { padding-top: 3px; }
.cd-app .disc { font-size: .72rem; color: #9ca3af; padding: 0 14px 24px; margin: 0; } /* v1.9.54: 8 -> 16 — disclaimer strip breathes with the quote box */

/* v1.2.0 CTA stack trimmed to 1 primary + 2 secondary in one row; the
 * mailto fallback lives as a small text link under the disclaimer
 * v1.9.8 #3: .cta-col deleted — it wrapped exactly ONE .cta-row; the row
 * carries the margin itself (one nesting level fewer) */
.cd-app .cta-row { display: flex; gap: 8px; margin-top: 8px; } /* v1.9.53: 10 -> 8 (the desktop anchor override keeps margin-top:auto) */
.cd-app .cta-row .ghost { flex: 1; }
.cd-app .mail-link { font-size: .76rem; color: #6b7280; }
.cd-app .mail-link:hover { color: #1b3b5a; }
.cd-app button.primary { background: #1b3b5a; color: #fff; border: 2px solid #1b3b5a; padding: 8px 18px; border-radius: 6px; font-size: .98rem; font-weight: 700; cursor: pointer; font-family: inherit; } /* v1.9.53: 10/20 -> 8/18 — CTA height follows the bar compaction */
.cd-app button.primary:hover { background: #122b44; }
.cd-app button.primary:disabled { background: #e5e7eb; border-color: #e5e7eb; color: #9ca3af; cursor: not-allowed; }
/* v1.6.2: the quote-step checkout CTA is the ONE gold button (brand spec
   §IV.1 — gold fill, Navy text; matches the prototype's Add to Bag) */
.cd-app button.primary.nb-checkout { background: #d2a272; border-color: #d2a272; color: #1b3b5a; }
.cd-app button.primary.nb-checkout:hover { background: #c08f5f; border-color: #c08f5f; }
.cd-app .ghost { background: #fff; color: #1b3b5a; border: 2px solid #1b3b5a; padding: 7px 14px; border-radius: 6px; font-size: .9rem; font-weight: 600; cursor: pointer; font-family: inherit; text-decoration: none; display: block; text-align: center; } /* v1.9.53: 9/16 -> 7/14 */
.cd-app .ghost:hover { background: #eef2f6; }
.cd-app button.mini { background: #fff; color: #374151; border: 1px solid #d1d5db; padding: 4px 10px; border-radius: 6px; font-size: .78rem; cursor: pointer; font-family: inherit; } /* v1.9.53: 5/11 -> 4/10 */

/* inquiry form */
.cd-app .inq-form { border: 1px solid #1b3b5a; border-radius: 8px; background: #fafcfb; padding: 12px 14px; margin-top: 10px; }
.cd-app .inq-form h3 { margin: 0 0 3px; font-size: .98rem; }
.cd-app .inq-sub { font-size: .8rem; color: #6b7280; margin-bottom: 8px; }
.cd-app .inq-field { margin-bottom: 8px; }
.cd-app .inq-field label { display: block; font-weight: 600; font-size: .84rem; margin-bottom: 2px; }
.cd-app .inq-field label em { color: #dc2626; font-style: normal; }
.cd-app .inq-field input,
.cd-app .inq-field textarea { width: 100%; padding: 7px 10px; border: 1px solid #d1d5db; border-radius: 6px; font-size: .95rem; font-family: inherit; }
.cd-app .inq-field.hp { position: absolute; left: -9999px; top: -9999px; height: 0; overflow: hidden; }
.cd-app .inq-actions { display: flex; gap: 10px; align-items: center; }
.cd-app .inq-ok { color: #1b3b5a; font-weight: 600; margin: 8px 0 0; }

/* spec card — v1.2.0: pre capped + scrollable, copy button is an inline
 * icon next to the heading (was a full-width mini button) */
/* v1.6.5 #5: the PARAMETER CARD is BACK — .spec-card.spec-params sits in the
   LEFT column above the Price breakdown; the grid lists pure configuration
   (Shape / Size / Fabric / Sewing style / Density / Add-ons) in two columns.
   The right-column .spec-card keeps the 3D render + order actions only. */
.cd-app .spec-card { border: 1px solid #e5e7eb; border-radius: 8px; background: #fff; padding: 10px 12px; font-size: .82rem; }
/* v1.9.8 #4 (screenshot 75): the two Order cards gap 10px — the layout
   move that put the render card first (v1.9.6) left the old spec-params
   margin-bottom stranded (it used to sit ABOVE the breakdown in the left
   column), so the cards touched at 0px. */
.cd-app .spec-card.spec-params { margin-top: 12px; } /* v1.9.54: 8 -> 12 — card gap follows the frame */
/* v1.9.8 #5 (user round 15, "有的两列有的一列"): ONE column everywhere —
   the desktop 2-col / phone 1-col split read inconsistent between steps;
   every row is a full-width label+value line now (the .sg-wide span class
   is retired in the same release — nothing spans in a single column). */
.cd-app .spec-grid { display: grid; grid-template-columns: 1fr; gap: 7px; } /* v1.9.54: 4 -> 7 — spec rows breathe inside the taller frame */
.cd-app .spec-grid .sg-item { display: flex; align-items: baseline; gap: 8px; padding: 5px 0; border-bottom: 1px dashed #eef2f6; font-size: .8rem; min-width: 0; } /* v1.9.54: 3 -> 5 — same relaxation */
.cd-app .spec-grid .sg-item:last-child { border-bottom: none; }
.cd-app .spec-grid .sg-item span { color: #8a8075; font-size: .68rem; font-weight: 700; text-transform: uppercase; letter-spacing: .04em; min-width: 74px; flex: 0 0 auto; }
/* v1.9.1 #5 heritage: values WRAP instead of ellipsizing (a 5-dimension
   Size string folds to two clean lines — full-width rows since v1.9.8). */
.cd-app .spec-grid .sg-item strong { color: #1b3b5a; font-weight: 700; font-size: .8rem; min-width: 0; flex: 1 1 auto; white-space: normal; overflow-wrap: break-word; }
.cd-app .spec-card h3 { border: none; padding: 0; margin-bottom: 6px; display: flex; align-items: center; justify-content: space-between; gap: 8px; font-size: .92rem; }
.cd-app .spec-card .sku { font-size: .7rem; color: #9ca3af; word-break: break-all; margin: 0 0 8px; }
/* v1.9.8 #2+#3+#8 — the Order render card, one level shallower:
   the .prev-svg FRAME wrapper is deleted (was card > frame > canvas);
   the canvas/SVG renders straight into .spec-card.spec-render. The frame's
   studio backdrop + border became the CARD's own (padding 0,
   overflow:hidden so the art meets the rounded border), the v1.8.2 230px
   max-height cap is GONE, and this round's first UNCAPPED take hit 399px —
   too tall (the user trimmed it back the same round): the final sizing is
   max-height 300px with width:auto + max-width:100% so the buffer keeps
   its own 480:336 ratio — desktop renders ~300px tall centred on the wide
   card, phones fill the column width (max-width binds first). With the
   renderer's cropTop (1px breathing since v1.9.8, designer-3d.js) the
   model starts ~1px under the card's top border — no white band above the
   image (#8, screenshot 76). */
.cd-app .spec-card.spec-render { position: relative; padding: 0; overflow: hidden; background: radial-gradient(120% 95% at 50% 10%, #ffffff 0%, #f1f6f3 48%, #dfe9e4 100%); }
.cd-app .spec-card.spec-render canvas { display: block; width: 100%; height: auto; max-width: 100%; max-height: clamp(300px, 42vh, 400px); margin: 0 auto; } /* v1.9.54: width auto -> 100% (uniform-proportion stretch fills the card like the svg sibling; 42vh = 378px at the 900px acceptance viewport) + ceiling 240/34vh/380 -> 300/42vh/400 — the render ceiling climbs back INSIDE the uniform 700px frame (Step 4's slack is spent on the product render + wider quote rows; still under the Size preview) */ /* v1.9.9: user wants Order img SMALLER than Size (340-440); v1.9.18 ceiling raised 340 -> 420 to spend the surplus height */
.cd-app .spec-card.spec-render svg { display: block; width: 100%; height: auto; max-height: clamp(300px, 42vh, 400px); margin: 0 auto; } /* v1.9.54 same ceiling as the canvas */
/* v1.9.18 cramped-desktop tiers: the ceilings above spend SURPLUS height, but
   their 280px floor also FORCED 280px onto frames that have no surplus — the
   Order body then inner-scrolled 34px at 1280x680 / 108px at 1024x600,
   breaking the zero-scroll contract. Height-query tiers give the render back
   its flexibility on short windows while every common desktop keeps the
   raised ceiling untouched. */
@media (min-width: 961px) and (max-height: 820px) {
	.cd-app .spec-card.spec-render canvas, .cd-app .spec-card.spec-render svg { max-height: clamp(240px, 32vh, 340px); } /* v1.9.54: band 960 -> 820 + 200/28vh -> 240/32vh — with the uniform 700px frame a 900px viewport has ~150px of true surplus again (900 - 48 lane - 716 tool), so the FULL ceiling above applies there; only genuinely short windows (<=820) step down */
}
@media (min-width: 961px) and (max-height: 700px) {
	.cd-app .spec-card.spec-render canvas, .cd-app .spec-card.spec-render svg { max-height: clamp(150px, 24vh, 320px); } /* 1024x600 -> 144px */
}
/* v1.9.15: the .spec-render svg.dim-chart rule is DELETED — the Order card
   renders the annotated 3D image again, so a sheet only appears there on
   the render-failure fallback (the generic svg cap above then applies). */
/* v1.9.7 heritage: the "Your cushion" title is an overlay badge pinned to
   the card's top-right — it never occupies a text row above the image.
   v1.9.8: selector simplified to .spec-render (the structural
   :first-child chain died with .prev-svg). */
.cd-app .spec-card.spec-render > h3 {
	position: absolute; top: 6px; right: 10px; z-index: 2;
	margin: 0; font-size: .66rem; font-weight: 700; letter-spacing: .05em;
	text-transform: uppercase; color: #6b7280; line-height: 1.4;
	background: rgba(255, 255, 255, .92); border: 1px solid #eef2f6;
	padding: 1px 8px; border-radius: 4px; pointer-events: none;
}
/* v1.9.6 (screenshot 72 — layout re-architecture, not another patch): the
   step header + sub MOVE INTO THE LEFT COLUMN of .s4wrap (see the PHP
   markup), so the Order step is a plain two-column grid — LEFT one whole
   (header, Price breakdown, actions, natural flow) and RIGHT one whole
   (Your cushion render, Your configuration card, natural flow), TOPS
   ALIGNED via align-items:start. The v1.9.4 -84px negative margin that
   "lifted" the render card is DELETED (it was the bug: the card escaped
   its own container and the image frame got clipped/overlaid); there is
   nothing to compensate once the header owns its column. */

/* toast — v1.9.18: with the bar de-pinned there is no fixed bar height to
   clear any more, so the v1.7.1 calc(var(--cd-bar-h)+16px) reservation is
   retired and the toast floats a plain 28px above the viewport bottom
   (z-index stays high — it overlays whatever is under it for 3.2s).
   --cd-bar-h therefore has NO consumers left; its tier rules are deleted. */
.cd-app .toast { position: fixed; bottom: 28px; left: 50%; transform: translateX(-50%); background: #111827; color: #fff; padding: 11px 20px; border-radius: 8px; font-size: .9rem; opacity: 0; pointer-events: none; transition: opacity .25s; z-index: 200; max-width: 90vw; text-align: center; }
.cd-app .toast.show { opacity: 1; }
.cd-app .toast a { color: #d2a272; }

/* ===== responsive (760px breakpoint, as prototype) ===== */
@media (max-width: 760px) {
	/* v1.9.1-3 (screenshots 55/56 — phone round): the wizard frame goes
	   height:auto on phones — the fixed 100vh cap let tall steps overflow
	   the frame onto the page and the last shape row ended UNDER the
	   fixed bar; with auto the page scrolls naturally and every step's
	   --cd-bar-h bottom padding does its job again. The Size preview is
	   CAPPED at 200px (the 380px min-height made the preview column stack
	   over the input fields at 375px — the 3D image and the fields drew
	   on top of each other), and the Size step-body drops its flex
	   stretch (the root of the stacking). Review puts the render column
	   FIRST (visual-first order, matching the Size step's image-first). */
	.cd-app .wiz { height: auto; min-height: 0; }
	/* v1.9.5 (screenshot 70 red box): the 3D image grows into the space
	 * the old chrome wasted — taller host, preview padding halved.
	 * v1.9.8 #1: top padding 6 -> 0 (the frame rides the card top). */
	.cd-app .pv-3d { flex: 0 0 auto; height: 280px; min-height: 0; }
	.cd-app .spec-card.spec-render canvas { max-height: 200px; } /* v1.9.9 phone: smaller than Size 280 */
	.cd-app .preview { padding: 0 3px 8px; }
	.cd-app .pv-svg { flex: 0 0 auto; min-height: 200px; }
	.cd-app .step[data-step="2"] .step-body { display: block; }
	/* v1.9.8 #9 (user round 15, "order还是图片在上面比较好"): the Order
	   step's phone stack opens on the IMAGE — the render column flips
	   first (render -> configuration -> header -> breakdown -> actions),
	   the same image-first language the Size step has kept since v1.9.1
	   (user-approved there). The v1.9.6 "natural flow only" note is
	   retired: with the v1.9.8 scrollable tab bar the header sits one
	   flick away, while the product picture is the anchor. */
	.cd-app .s2wrap,
	.cd-app .s4wrap { grid-template-columns: 1fr; }
	.cd-app .s2wrap > div:last-child,
	.cd-app .s4wrap > div:last-child { order: -1; }
	.cd-app .shapes-grid { grid-template-columns: repeat(2, 1fr); }
	.cd-app .fab-grid { grid-template-columns: repeat(2, 1fr); }
	.cd-app .ftab { max-width: none; }
	.cd-app .dens-opts { grid-template-columns: repeat(2, 1fr); }
	.cd-app .cta-row { flex-direction: column; }
	/* v1.9.10 (user screenshots 77/78 — "窗口缩小后底部占屏越来越大"): the
	   phone bar KEEPS THE DESKTOP SINGLE ROW down to 520px — price left,
	   buttons right, the auxiliary .nb-note hidden (it was the wrap seed).
	   The v1.9.8 wrap-to-two-rows rule made the bar JUMP 67 -> ~110px the
	   moment the window crossed 760px (bottom zone ~10% -> 16% of a 700px
	   viewport, prototype-measured); single-row it holds ~10% at every
	   width down to 520. --cd-bar-h keeps the 70px desktop tier in this
	   band (no jump); --cd-nav-h had no consumer — deleted.
	   v1.9.8 #6 heritage: the tab pin stays deleted, the bottom action bar
	   carries Back/Next on every step; --site-header-h stays reserved on
	   .cd-app for the launch WP header. */
	.cd-app .step { padding-left: 8px; padding-right: 8px; padding-top: 10px; } /* v1.9.21: restores the phone step chrome that v1.9.20 inherited from the (now reverted) 800px height tier */
	.cd-app .progress button { padding: 4px 1px; font-size: .66rem; } /* v1.9.21: same restoration */
	/* v1.9.21: the v1.9.20 phone-sticky is RETIRED — the bar now sits OUTSIDE
	   .step-body as the step's last child, resting on the frame bottom by
	   STRUCTURE (the very thing the phone sticky tried to emulate). Phone
	   sizing/visuals below stay from v1.9.20. */
	.cd-app .nav-bar { gap: 10px; margin: 0; padding: 8px 16px 10px; } /* v1.9.22: background/border inherit the navy bar */
	.cd-app .nb-note { display: none; } /* v1.9.10 heritage + v1.9.20 task 3: keeps the bar one uniform height */
	/* v1.9.20 task 2 (CDP 390x844): the family tabs used to wrap into a
	   260px stack (39% of the tool) leaving the grid 338px. One horizontally
	   scrollable row instead; v1.9.31 t1: the bar is STYLIZED-VISIBLE, not hidden. */
	.cd-app .fab-tabs { display: flex; flex-wrap: nowrap; overflow-x: auto; gap: 6px;
	                    margin-bottom: 8px; padding-bottom: 8px;
	                    scrollbar-width: thin; scrollbar-color: rgba(27,59,90,.45) rgba(27,59,90,.08); } /* v1.9.31 t1: was scrollbar-width:none — the thin VISIBLE bar is the indicator; v1.9.32 t2: padding-bottom 2 -> 8px so the 4px bar breathes BELOW the tab content (user: "滚动条紧紧压着 tab，看着别扭") — last tab bottom now sits 8px off the container bottom, the bar paints in that lane */
	.cd-app .fab-tabs::-webkit-scrollbar { height: 4px; } /* v1.9.31 t1: was display:none */
	.cd-app .fab-tabs::-webkit-scrollbar-thumb { background: rgba(27,59,90,.45); border-radius: 99px; }
	.cd-app .fab-tabs::-webkit-scrollbar-track { background: rgba(27,59,90,.08); border-radius: 99px; }
	/* v1.9.22 task 4 (user follow-up: the v1.9.20 compact tier still read
	   big): tabsH 61 -> ~48-52px. min-width 108 -> 92, one tighter padding
	   step, name one size down, count line inline-micro — tap height stays
	   >= 40px. The height saved goes to the swatch grid. */
	.cd-app .ftab { flex: 0 0 auto; min-width: 92px; max-width: 140px; padding: 3px 22px 3px 7px; min-height: 40px; }
	.cd-app .ftab .ft-name { font-size: .68rem; line-height: 1.2; }
	.cd-app .ftab .ft-sub { font-size: .5rem; line-height: 1.1; white-space: nowrap; overflow: hidden;
	                        text-overflow: ellipsis; margin-top: 0; }   /* name + micro count line */
	.cd-app .ftab .ft-cur { display: none; }            /* reserve line gone on phones */
	.cd-app .ftab .ftab-info { width: 18px; height: 18px; min-height: 18px; max-height: 18px; min-width: 18px; max-width: 18px; aspect-ratio: 1 / 1; font-size: .62rem; top: 4px; right: 4px; } /* v1.9.25 f1: square at the phone tier */
	/* v1.6.3: five step labels on a phone — let them shrink onto their
	   natural width instead of ellipsizing the long ones away */
	.cd-app .progress button { font-size: .58rem; padding: 7px 1px; white-space: normal; line-height: 1.25; }
	/* v1.8.2 #7 (375px phone audit): every modal — shape / meas / dens /
	   group / per-SKU / art-zoom — uses the small width fully (18px outer
	   padding -> 10px; the 600px cards are width:100% so they fit 92vw+),
	   the per-SKU shots drop 220 -> 150px so the 2-up media fits one phone
	   screen, and the group swatch strip min card shrinks 86 -> 72px.
	   Cards already scroll internally (max-height 90vh + overflow-y). */
	.cd-app .cd-modal { padding: 10px; }
	.cd-app .fab-modal .fab-media img { height: 150px; }
	.cd-app .artzoom-modal .az-media img { max-height: 52vh; }
	.cd-app .artzoom-modal .az-media .az-svg svg.pc-svg { max-height: 40vh; }
	/* v1.9.4 phone spec-grid single-column rule DELETED in v1.9.8 — the
	   base .spec-grid is one column everywhere now (#5). */
	/* v1.9.7 (user round 14, "not one of them changed"): v1.9.5-6 scaled
	   only the pills painted INSIDE the 3D canvas — every DOM control of
	   the Size step still rendered at desktop size on a 375px phone (the
	   density pills measured 17px type in a 54px-tall button). The whole
	   control stack shrinks with the viewport now: density pills 17->12px
	   (sub-label 8.5px), unit toggle 13->11px, measurement inputs 15->13px,
	   field labels ->11.5px, the density heading ->12.5px. Tap targets
	   stay >=36px wide; nothing drops under readable. */
	.cd-app .dens { font-size: 12px; padding: 7px 5px 6px; }
	.cd-app .dens > span { font-size: 8.5px; }
	.cd-app .dens-pick h3 { font-size: 12.5px; }
	.cd-app .dens-help { font-size: .62rem; }
	.cd-app .unit-toggle .ut-btn { font-size: 11px; padding: 2px 10px; min-width: 36px; }
	.cd-app .unit-toggle .ut-label { font-size: 10px; }
	.cd-app .unit-toggle .unit-note { font-size: 9.5px; }
	.cd-app .field input { font-size: 13px; padding: 6px 9px; }
	.cd-app .field label { font-size: 11.5px; }
	/* ===== v1.9.31 phone-only four-task round (desktop contract untouched;
	   everything in this block lives at <=760px) ===== */
	/* t2 — the fabric step's SECOND explainer sentence (the .fi-long span in
	   the PHP template) drops off phones (user: "Six families, 119 cuts...
	   这句是否删除或简短，手机占空间"); the lead sentence stays on all
	   widths, desktop reads the full text. */
	.cd-app .fi-long { display: none; }
	/* t3 — the 44px bottom dead zone under the Size canvas is gone. Root
	   cause, CDP-measured: preview 326px tall vs canvas 280px = 1px top
	   gap + 45px bottom gap, of which the .pv-meta "Top area" line (18px
	   + 4px margin-top + its UNRESET p margin-bottom ~11px) plus the
	   preview's 8px bottom padding. Phones hide .pv-meta (desktop keeps
	   it) and the padding shrinks 8 -> 3px: canvas bottom sits 4px off
	   the card.
	   v1.9.32 t1 (user: "你只是改了宽度，高度没改，现在样式太丑——在现有
	   高度情况下增加宽度，基本接近全宽"): the v1.9.31 width:70%/273px
	   narrowing is REVERTED — full width restored; only the dead-zone
	   kill above survives (padding + .pv-meta rules). 390px-band canvas
	   goes 250x280 (ratio 0.89, stretched-tall) -> ~364x280 (ratio 1.30,
	   inside the 1.0-1.4 visual band) with NO height change. */
	.cd-app .step[data-step="2"] .preview { width: 100%; max-width: none; margin: 0; padding: 0 3px 3px; }
	.cd-app .pv-meta { display: none !important; }
	/* t4 — phone measurement inputs 36px tall (the theme's input floor
	   stretched them to 42px live) and the ? circles join the site-wide
	   24px family (was the 34px tier); the root button guard (min-height:0
	   !important) already floors the theme's 42px stretch. */
	.cd-app .field input { height: 36px !important; min-height: 0 !important; padding: 5px 9px !important; }
	.cd-app .field .mq { width: 24px; height: 24px; min-height: 24px; max-height: 24px; min-width: 24px; max-width: 24px; }
}

/* v1.9.10: below 520px the single row genuinely cannot fit (price block
   + Back + the long Next labels ≈ 490px) — the bar wraps to TWO
   CONTROLLED rows: price row, then Back + Next side by side filling the
   width. Compact paddings keep the wrapped bar ≈ 89px = ~13% of a 667px
   phone (measured on the prototype's identical tier). The v1.7.1/v1.9.8
   sub-350px third-row tier (152px) is RETIRED: with .nb-note hidden
   nothing re-wraps — a 320px phone keeps the same two rows. */
@media (max-width: 520px) {
	/* v1.9.18: former .cd-app { --cd-bar-h: 98px; } phone tier removed with the var */
	.cd-app .nav-bar { flex-wrap: wrap; gap: 6px; padding: 8px 12px 9px; }
	.cd-app .nb-price { flex: 1 1 100%; justify-content: center; }
	.cd-app .nb-amount { font-size: 1.05rem; }
	.cd-app .nb-actions { flex: 1 1 100%; }
	/* v1.9.20 task 3: labels never wrap — all four steps render the SAME
	   two-row bar (was 89 vs 108px depending on label length). */
	.cd-app .nb-actions button { flex: 1; padding: 8px 10px; font-size: .85rem; white-space: nowrap; }
}

/* ===== v1.6.0 Inter round (brand visual spec) — DELIBERATELY LAST: these
   must win over the per-widget `font-family: inherit` resets above (e.g.
   .cd-app .field input / .cd-app .dens), which otherwise re-inherit the
   base system stack. Numerals — measurement inputs, prices, density
   grades, spec values — in Inter with tabular figures; headings in
   Manrope. Pure font-stack swap: no webfont fetched here (the theme ships
   the files at launch; missing glyphs fall through to system-ui). ===== */
.cd-app h1, .cd-app h2, .cd-app h3, .cd-app .step-h, .cd-app .dens-pick h3, .cd-app .ao-head strong {
	font-family: 'Manrope', 'Segoe UI', system-ui, sans-serif;
}
.cd-app .field input, .cd-app .field select.tsel, .cd-app .qtyrow input, .cd-app .nb-amount, .cd-app .qrow .val, .cd-app .dens {
	font-family: 'Inter', 'Segoe UI', system-ui, sans-serif !important;
	font-variant-numeric: tabular-nums;
}

/* ===== v1.9.10b WP theme integration (user screenshot 79, real WP env) =====
   The 33% bottom gap lives in the THEME layer (.entry-content.clear),
   not inside .cd-app — harness tests could never see it. Neutralize the
   host container's bottom spacing when it wraps our tool. :has() is
   supported by all evergreen browsers (Chrome 105+/Safari 15.4+/FF 121+). */
.entry-content:has(.cd-app),
.wp-block-group:has(.cd-app),
.entry:has(.cd-app){ padding-bottom: 0 !important; margin-bottom: 0 !important; }
.cd-app{ margin-bottom: 0 !important; }

/* v1.9.18 task 5, phone: the internal Fabric scroller keeps a usable >=40vh
   window so a tiny host cannot squeeze it flat; chips/tabs stay visible. */
@media (max-width: 760px) {
	.cd-app .step[data-step="3"] .fab-grid { min-height: 40vh; }
}
