/* polish(fovea): the cards section and the menu, item 2 -- Ittai's own
   screenshot: the header block (kicker/statement/paragraph) sat oddly
   against the grid below it. One alignment now: left, flush with the
   wrap's own left edge (.cards-head already had no competing margin/
   text-align of its own, so this is mostly about the spacing around it,
   not fixing an actual centred-text bug), the paragraph capped at 60ch
   so it doesn't run the full 660px header width. 96px above the header
   and 64px above the cards grid, both AT 1512 specifically (the brief's
   own scope) -- clamp() lower bounds keep the existing responsive taper
   for narrower widths rather than replacing it with a flat number that
   would read as too much air on a phone. */
/* Job 29d, item 1 -- the real cause of the width collapse: #cards is a
   flex ITEM of .hero (site.css .hero{display:flex;flex-direction:column;
   align-items:center}), not a normal block-flow section. align-items:
   center means the cross axis (horizontal, since the main axis is
   column) does NOT stretch children to .hero's full width by default --
   #cards shrink-wraps to its own content instead, and .cards-inner's own
   width:min(100% - 48px,1400px) resolves that 100% against a circular,
   content-derived box. align-self:stretch overrides align-items:center
   for this one item only, so #cards gets .hero's real full width and
   .cards-inner's percentage means something. container-type:inline-size
   was never the bug (that hypothesis is retired) -- it's restored below,
   scoped correctly to .pitch-card only, now that the ancestor it sizes
   against is actually stable. */
#cards{align-self:stretch;width:100%;padding:clamp(64px,6.35vw,96px) 0 clamp(96px,11vw,168px)}
#cards .cards-inner{width:min(100% - 48px,1400px);margin-inline:auto}
#cards .cards-head{max-width:660px;margin-bottom:clamp(40px,4.24vw,64px);text-align:left}
#cards .cards-kicker{margin:0 0 22px;color:var(--accent);font:400 12px/1.4 var(--mono);letter-spacing:.06em}
#cards .cards-head h2{margin:0;color:var(--ink);font:500 clamp(30px,2.45vw,36px)/1.13 var(--sans);letter-spacing:-.5px}
#cards .cards-head>p:last-child{margin:22px 0 0;max-width:60ch;color:var(--ink-2);font:400 16px/1.55 var(--sans);letter-spacing:.02em}
/* S9 re-scope: matching flora.ai's own Ideate/Iterate/Scale row -- card 01
   double width, 02 and 03 single, one row, at the same height (the shared
   clamp(690px,55vw,720px) below is viewport-relative, not column-relative,
   so it doesn't move when a column's own width does). */
#cards .cards-grid{display:grid;grid-template-columns:2fr 1fr 1fr;gap:24px}
/* REFINE-V4.md 1.4: interactive on hover (desktop) and tap (touch, via
   cards.js toggling the same .is-active class pointer devices can't
   :hover into) -- the hovered/active card grows 3%, its two neighbours
   give way (a matching 3% shrink), so the row's own total width stays
   put and nothing reflows the grid itself. transform-origin:center so
   growth reads as "coming toward you," not sliding off its own baseline. */
/* polish(fovea): the cards section and the menu, item 3 -- the title was
   still clipping at his width ("Carry it everywhe"), even after the
   earlier vw-based clamp + wrap fix: a vw unit tracks the VIEWPORT, not
   this card's own column, and the two diverge (3 columns, a max-width
   wrap, a gap) enough that no single vw breakpoint ever matched every
   real column width. container-type:inline-size on the card itself is
   the actual fix -- cqw then tracks the CARD's width, not the viewport.

   Job 29d, item 1 -- this had looked like a severe container-type bug
   (setting it anywhere in this subtree appeared to collapse the whole
   #cards section to 660px). Re-diagnosed from the DOM parent chain: the
   real cause was #cards itself being an unstretched flex item of .hero
   (see the #cards{align-self:stretch} rule above) -- container-type was
   never at fault. Restored here, scoped to .pitch-card only, now that
   its ancestor has a stable, non-circular width. */
/* N10 item 3 (POLISH-NOTES 37): the old hover was a 3% grow on the
   hovered card plus a 3% shrink on its two neighbours -- its own
   distinct law, not the one every other surface now shares. Replaced
   with the single consistent one: translateY(-2px), the glass edge
   (a ring this card never had at rest, since it had no border at all)
   brightening 8%, 220ms ease-out. The neighbour-shrink is gone
   entirely -- nothing else moves when a card is hovered now, not even
   its own siblings. */
/* Job P1 item 6: measured live -- all three cards share this one height
   already (so "equal across the three" already held), but at 790px
   (55vw's own ceiling, reached at 1512) the shortest mock (the read
   card's own understand-surface, a genuinely smaller mock than the
   other two, not a bug in it) left 181px of empty room between its
   real content and .card-claim; even the tallest-mock cards (lenses,
   carry) still had 93-114px to spare. Retuned to the tallest card's own
   real requirement (carry: its mock bottoms at 605px, its own two-line
   claim needs 63px, plus the existing 28px bottom inset and a 20px
   breathing gap -- 716px, rounded to 720 for a hair of margin) instead
   of an unrelated 790px ceiling: every card now fits its content, the
   tallest (carry) setting the row height the other two inherit from
   this same shared rule, exactly as it did before, just no longer
   oversized for any of them. */
/* S9 re-scope, item 1: a permanent 1px glass edge at rest (flora.ai's own
   cards read as bordered panels even untouched, not just on hover) -- the
   hover ring above it was the only border this card ever had; now it's a
   brightening of an edge that already exists, not the edge's own entrance.
   40px is the card's own padding unit now (was 28px): h3's margin, the
   crop's left inset and the claim's own left/right/bottom all move to it
   together, so the whole card reads on one consistent inset. The crop and
   the surface keep bleeding to the right and bottom (Fable's Sep 22 note,
   below) -- only the top-left origin moves. */
/* S9 re-scope: the crop's own top grew by 114px (26 to 140, to clear the
   new 84px numeral) -- the card's own height grows by the same 114px so
   the mock keeps exactly the room it had (552px of crop, unchanged),
   rather than the new heading eating into it. */
#cards .pitch-card{position:relative;min-width:0;height:clamp(804px,calc(55vw + 114px),834px);padding:40px 0 0;overflow:hidden;background:var(--glass-bg);
  box-shadow:inset 0 0 0 1px var(--glass-line);transform:translateY(0);transition:transform .2s var(--ease),box-shadow .2s var(--ease);container-type:inline-size}
#cards .cards-grid .pitch-card:hover,#cards .cards-grid .pitch-card.is-active{transform:translateY(-2px);box-shadow:inset 0 0 0 1px rgba(244,244,242,.2);z-index:2}
#cards .pitch-card::before{content:"";position:absolute;inset:0;pointer-events:none;background-image:radial-gradient(circle,var(--canvas-dot) 1px,transparent 1.6px);background-size:var(--canvas-pitch) var(--canvas-pitch);opacity:.7}
/* S9 re-scope, item 2: Flora's own two-weight heading -- a big numeral at
   the --t-8 step (84px at 1512) sat dim (35% of --ink), a smaller word at
   the --t-7 step (56, the site's own h2 size) sat bright. Fixed steps read
   right on the wide 01 card and the viewport-driven breakpoints already
   built into --t-7/--t-8 (v3.css) cover 1100px and 640px -- but 02 and 03
   are single columns half that width, so a cqw-clamped floor is added on
   top (min() against the fixed step) rather than replacing the scale, to
   keep both numbers from ever outgrowing their own narrow column. */
#cards .pitch-card h3{position:relative;z-index:2;display:flex;gap:10px;align-items:baseline;margin:0 40px;white-space:nowrap}
#cards .pc-num{color:var(--ink);opacity:.35;font:500 min(var(--t-8),18cqw)/1 var(--sans);letter-spacing:-.03em}
#cards .pc-word{color:var(--ink);font:500 min(var(--t-7),12cqw)/1.05 var(--sans);letter-spacing:-.03em}
/* fix(fovea): the cards fit every width -- retired by S9's re-scope. The
   old title ("Carry it everywhere") could clip at ~1000px because a
   vw-based clamp tracked the viewport, not the card's own column; the
   heading is now just a numeral and one short word ("03 Connect"), and
   the min(step, cqw) floor on .pc-num/.pc-word above already keeps both
   inside the narrowest single-width column at every size -- no title left
   long enough to need its own wrap fallback. */
/* Fable's own note, Sep 22 (unit now 40px, S9 re-scope's own card padding,
   was 28px): anchored to the card's left padding rather than centred --
   the centred version (`left:50%;transform:translateX(-50%)`) clipped an
   equal amount off BOTH sides of the surface inside its own column, which
   cut words at their edge, not a boundary. Cropping now only ever happens
   on the right and the bottom, the way flora.ai's own cards do -- the
   left edge of the surface and the left edge of the crop window are the
   same 40px, so nothing at that edge is ever clipped. */
/* POLISH-NOTES item 4: the crop window sits right under the title (26px
   below its own bottom edge) the way the card's own bottom edge
   (.card-claim) already sits right against the mock, not floating in its
   own empty band. */
/* S9 re-scope: the crop's own top was 26px, calibrated for the old single-
   line title (its tallest possible box under 48px). The new heading's own
   numeral reaches 84px at line-height:1 (card 01) -- top:26px let it print
   straight through the mock below it (position:absolute's own offset is
   measured from the card's padding box, i.e. from y:0, not from wherever
   the padding pushed the heading's own text down to). 140px clears the
   84px numeral (ending around y:124, padding-top 40 + line-height 84) with
   room to spare, used uniformly across all three cards so their mocks
   still start at the same shared height even though 02/03's own numeral
   is smaller. */
#cards .card-crop{position:absolute;z-index:2;left:40px;right:0;top:140px;bottom:142px;overflow:hidden}
/* Job 29e: the surface was a hardcoded 520px against a crop window that's
   only ever the card's own width minus the 28px left inset (431px at
   1512, 281px at 1000, per Job 29d's own confirmed card widths) --
   overflow silently sliced off the surface's own right edge every time,
   worse the narrower the card got. width:100% here (a positioned
   .card-surface's own percentage resolves against .card-crop, its
   nearest positioned ancestor -- exactly "100 percent of the card's
   inner width") is the DEFAULT for every surface; styles-surface and
   exports-surface below have no fixed-px internal layout wide enough to
   overflow that (checked directly: style-rows/style-chips are already
   flex/wrap, no explicit widths beyond decorative 18px swatch chips),
   so 100% alone reflows them cleanly. understand-surface overrides this
   back below -- it's the one surface genuinely built at a natural,
   comfortable 520px (three real reference photos plus captions plus a
   measured-palette row), and reflowing THAT into an arbitrary column
   width would cram it illegibly rather than just resize it. */
/* POLISH-NOTES item 4, continued: pulling the crop up (above) freed
   dead space above the mock but left it below instead, since the mock's
   own height was fixed and didn't grow into the reclaimed room. Taller
   fills more of the card without touching .card-crop's own bottom inset
   (142px, still clearing .card-claim). */
#cards .card-surface{position:absolute;top:34px;left:0;width:100%;height:520px;overflow:hidden;background:var(--glass-bg);
  transition:transform .22s var(--ease)}
/* N10 item 3: the mock drifting 4px on hover, the same law's own second
   half -- on top of whatever the card's own translateY(-2px) already
   does, since this is a nested transform, not a replacement of it.
   .understand-surface (the read card's own mock) already carries its
   own transform, scale(var(--fit)) -- a single transform property
   can't hold both, so its own hover rule combines them explicitly
   instead of letting the drift silently replace the scale and pop the
   read card's mock back to its own unfit natural size. */
#cards .cards-grid .pitch-card:hover .card-surface:not(.understand-surface),
#cards .cards-grid .pitch-card.is-active .card-surface:not(.understand-surface){transform:translateY(-4px)}
#cards .cards-grid .pitch-card:hover .understand-surface,
#cards .cards-grid .pitch-card.is-active .understand-surface{transform:translateY(-4px) scale(var(--fit))}
#cards .glass-bar{font-size:10px;letter-spacing:.06em}
/* B2 item 11 (docs/RUN-2026-09-25-BAR.md, row 6 of the gap audit): the
   Read card's own content stopped 133px short of the crop's bottom edge
   (measured) -- Flora's own cards run content to the card's own edge,
   caption flush under it. understand-body fills the surface's real
   height now (520px, unaffected by --fit's own scale transform, which
   changes render size, not layout height) and space-between distributes
   its three blocks across that height instead of clustering them at the
   top with the rest left empty below. */
#cards .understand-body{height:100%;box-sizing:border-box;padding:18px;display:flex;flex-direction:column;justify-content:space-between}
#cards .folder-line{display:flex;justify-content:space-between;margin:0;color:var(--ink-3);font:400 10px/1.3 var(--mono);letter-spacing:.04em;text-transform:uppercase}#cards .folder-line strong{color:var(--accent-hi);font-weight:400}
#cards .reference-strip{display:grid;grid-template-columns:repeat(3,1fr);gap:8px}#cards .reference-strip figure{position:relative;height:154px;margin:0;overflow:hidden;border:1px solid var(--glass-line);border-radius:8px}#cards .reference-strip img{width:100%;height:100%;object-fit:cover}#cards .reference-strip figcaption{position:absolute;left:8px;right:8px;bottom:7px;color:var(--ink);font:400 8px/1.2 var(--mono);text-transform:uppercase}
/* Job 29e: the session surface, scaled as a whole rather than reflowed.
   Kept at its own natural 520px design width (min-width holds it there
   against the shared width:100% above), then scaled down by --fit, a
   pure-CSS ratio: the card's own available width (100cqi, .pitch-card's
   own container-size, minus the crop's 28px left inset) divided by the
   surface's natural 520px, capped at 1 so a card wider than 520px never
   scales the surface UP past its real size. transform-origin:top left
   matches the crop's own left/top anchor (Fable's Sep 22 note, above),
   so scaling only ever loses width on the right and height at the
   bottom -- never re-introduces a left-edge or mid-word crop. */
#cards .understand-surface{width:520px;min-width:520px;--fit:min(1,calc((100cqi - 40px)/520px));transform:scale(var(--fit));transform-origin:top left}
#cards .measured-palette{display:flex;height:42px;margin:14px 0;overflow:hidden;border-radius:7px}#cards .measured-palette i{flex:1;background:var(--swatch);transform-origin:left}
/* S9 part 2: the READING dot, autonomous once the card's sequence has
   played (cards.js adds .is-reading) -- gated by the sitewide motion
   policy the same way every other autonomous loop on this site is
   (html.motion-calm forces animation-duration to ~0 and iteration-count
   to 1, site.css), so this needs no reduced-motion query of its own. */
#cards .understand-surface.is-reading .glass-bar .live i{animation:cards-reading 1.6s ease-in-out infinite}
@keyframes cards-reading{0%,100%{opacity:.45;transform:scale(1)}50%{opacity:1;transform:scale(1.35)}}
/* S9 re-scope, item 3: the simulated cursor acting inside each mock --
   positioned at 0,0 and moved entirely by cards.js via transform (x/y),
   never by top/left, so the same live-measured coordinates the connectors
   use also drive this. Reduced motion never adds .on (applyInstant skips
   the cursor build entirely), so it stays display:none for that case
   without a separate query. */
#cards .pc-cursor{position:absolute;top:0;left:0;z-index:6;width:14px;height:14px;margin:-7px 0 0 -7px;border-radius:50%;
  background:var(--accent-hi);box-shadow:0 0 0 4px rgba(232,98,47,.16),0 2px 10px rgba(5,5,5,.5);opacity:0;pointer-events:none}
#cards .pc-cursor.on{opacity:1}
/* Margo's audit delta (5): the Warmth/Negative space/Grain micro-labels
   rendered at 9px, under the 10px floor she measured against directly.
   Bumped to 10px; the row's own 116px label column is unchanged and
   still fits the longest label ("Negative space", 14 characters) at the
   new size, verified by measurement, not assumed from the extra 1px alone. */
#cards .measure-list{display:grid;gap:9px}#cards .measure-list p{display:grid;grid-template-columns:116px 1fr 28px;align-items:center;gap:12px;margin:0;color:var(--ink-2);font:400 10px/1 var(--mono);text-transform:uppercase}#cards .measure-list p::after{content:"";grid-column:2;grid-row:1;height:2px;background:var(--line)}#cards .measure-list i{z-index:1;grid-column:2;grid-row:1;width:calc(var(--value)*100%);height:2px;background:var(--accent);transform:scaleX(.14);transform-origin:left}#cards .mm-num{grid-column:3;text-align:right;color:var(--ink-3);font:400 10px/1 var(--mono)}
/* S9 part 3: a single-row grid (name / number+swatches / bar) replaces the
   old stacked layout (name+number, then swatches, then a full-width bar
   below) -- the name keeps one line at 44% of the row, the number and the
   three mini-swatches share one fixed column (68px, just enough for
   "100" plus the swatch strip stacked under it), the bar fills whatever's
   left. display:contents on nothing is needed here since the markup itself
   now emits the three grid children directly (.style-name, .style-meta,
   em) -- no wrapper divs standing between the row and its own columns. */
#cards .style-rows{display:flex;flex-direction:column;gap:12px;padding:24px}
#cards .style-row{display:grid;grid-template-columns:44% 68px 1fr;align-items:center;column-gap:12px}
#cards .style-name{min-width:0;overflow:hidden;white-space:nowrap;text-overflow:ellipsis;color:var(--ink);font:400 12px/1.3 var(--mono);letter-spacing:.02em}
#cards .style-meta{display:flex;flex-direction:column;align-items:flex-end;gap:5px}
#cards .style-meta span{color:var(--ink-3);font:400 12px/1.3 var(--mono);letter-spacing:.02em}
#cards .mini-swatches{display:flex;gap:4px}#cards .mini-swatches i{width:18px;height:12px;border-radius:2px;background:var(--swatch)}
#cards .style-row em{display:block;width:100%;height:2px;background:linear-gradient(90deg,var(--accent) calc(var(--value)*100%),var(--line) 0);transform:scaleX(.14);transform-origin:left}
#cards .style-chips{display:flex;flex-wrap:wrap;gap:7px;padding:0 24px 24px}#cards .style-chips span{padding:6px 8px;border:1px solid var(--glass-line);border-radius:100px;color:var(--ink-2);font:400 12px/1 var(--mono)}
/* S9 part 3: the old .export-lens/.export-list were absolutely positioned
   as percentages of a fictional 520x430 reference box baked into the SVG
   viewBox -- a fragile coupling, since the real content (rows' worth of
   fixed-px text and padding) never scaled with that box the way the
   percentages assumed. Replaced with a real two-column grid (the lens
   left, the list right, both in normal flow) -- "the lens node centred on
   that column" is then just align-items:center, free, and a fifth row
   just grows the list, no percentage to retune. The connectors are the
   one thing that still needs live coordinates, so cards.js measures both
   boxes with getBoundingClientRect() and draws the paths itself, the same
   live-measured approach the stage's own Connect panel and the lit list's
   scenes already use -- no reference box to keep in sync by hand. */
#cards .exports-surface{isolation:isolate}
/* S9 re-scope: the 2-1-1 layout gives this card a narrow ~338px column at
   EVERY desktop width, not just a phone's -- "orthogonal connectors, side
   by side with the lens" (the spacing rules the re-scope kept) has to fit
   in less room than the old equal 3-column grid ever gave it. The lens
   column shrinks to just enough for its own icon-sized box, the list gets
   the rest, and the gap and the item padding both tighten -- see the
   font-size and padding retune on .export-list span below, the other half
   of the same fit. */
#cards .exports-flow{position:relative;height:calc(100% - 38px);padding:14px;display:grid;grid-template-columns:minmax(72px,.35fr) minmax(0,1.65fr);column-gap:10px;align-items:center}
#cards .export-lens{position:relative;z-index:2;height:112px;border:1px solid var(--glass-line);background:var(--bg-3)}
/* the node-bar/node-title/node-source primitives (v3.css) are sized for a
   wide card; at this card's own 72px lens column "LENS 01" has ~44px of
   real room, which their default --t-1 (12px) plus .12em tracking breaks
   mid-word. A scoped, smaller size (still above the 10px floor) and
   tighter tracking keep the label reading as words, not single letters. */
#cards .export-lens .node-bar{display:block;padding:8px}
#cards .export-lens .node-title,#cards .export-lens .node-source{font-size:10px;letter-spacing:.03em}
/* overflow-wrap:anywhere used to be a ≤900px-only override (a phone-width
   fix); the 2-1-1 layout gives this card the same narrow lens box at
   every desktop width now too -- "unsentimental" alone (13 characters) is
   wider than the 72px column's own ~56px of content room, so the break
   is needed unconditionally, not just under a phone breakpoint. */
#cards .export-lens .node-source{display:block;margin-top:6px;text-align:left;white-space:normal;overflow-wrap:anywhere}
#cards .export-swatches{position:absolute;left:11px;right:11px;bottom:11px;display:flex;height:18px;overflow:hidden;border-radius:4px}#cards .export-swatches i{flex:1;background:var(--swatch)}
/* Job 29e (still true, unrelated to the layout change above): no explicit
   grid-template-columns on .export-list sizes its one column to the
   widest row's own min-content rather than the column's real share,
   overflowing and getting sliced by .card-surface's own overflow:hidden
   at the narrowest widths. minmax(0,1fr) is the same fix .cards-grid
   already uses; min-width:0 on the row is the flex-item half of it. */
#cards .export-list{position:relative;z-index:2;display:grid;grid-template-columns:minmax(0,1fr);gap:10px}#cards .export-list span{display:flex;justify-content:space-between;align-items:center;gap:4px;min-width:0;padding:10px 6px;border:1px solid var(--glass-line);border-radius:8px;background:var(--bg-3);color:var(--ink-2);font:400 10px/1.1 var(--mono);white-space:nowrap;transition:border-color .3s}#cards .export-list b{color:var(--ink-3);font-weight:400;transition:color .3s}#cards .export-list .is-live.pill-lit{border-color:rgba(232,98,47,.4)}#cards .export-list .is-live.pill-lit b{color:var(--accent-hi)}
#cards .export-connectors{position:absolute;inset:0;z-index:1;width:100%;height:100%;pointer-events:none}
/* Completion Run item 1 (Section C): the Carry panel's own dead blank
   region, named directly -- the space between the LENS 01 node and the
   export list was visually empty at rest (measured: the connector
   paths that fill it were stroke-dashoffset:1, fully undrawn, until a
   mouse hover replayed them; touch and reduced-motion already draw them
   at rest via the two media queries below, so this was specifically a
   "read instantly at first glance" gap for desktop mouse users only).
   Base state now matches those two exceptions instead of contradicting
   them: drawn at rest. is-live vs next status was already encoded
   separately, by stroke colour (v3.css, .connector path.is-live), so
   nothing about the live/next read changes -- only the "half the panel
   looks unfinished until you touch it" problem does. cards.js's own
   hover replay (killTweensOf, reset to 1, animate to 0) is untouched
   and still works exactly as before, just as a redraw flourish on an
   already-visible connection instead of the connection's only
   entrance. */
#cards .export-connectors path{stroke-dasharray:1;stroke-dashoffset:0}
/* Job 29f, item 2 -- measured the real cause before picking a fix: the
   row itself is only ~98px wide at 1000px (35.38% of a surface that's
   itself fluid since Job 29e, not the ~345px a quick estimate suggested)
   -- with 11px padding each side that's 76px of content room, which
   "Midjourney sref" + "Next" (7.7px over the surface's own edge,
   measured) can't fit on one line, but neither can "SKILL.md" + "LIVE"
   or any other row (94px of text against 76px of room) -- this isn't
   one long label overflowing, it's every row too narrow for space-
   between's single-line assumption. flex-wrap:wrap alone was tried
   first and rejected: :space-between still tries to keep both items on
   one line as long as possible, so short labels wrapped inconsistently
   while long ones fought for a second line too, three different row
   heights and a badge left stranded on its own line, unaligned with its
   label. A deliberate column stack -- label above badge, both left-
   aligned, a small fixed gap -- is the same content, uniform across all
   four rows, nothing negotiating for horizontal space it doesn't have. */
@media(max-width:1100px){#cards .export-list span{flex-direction:column;align-items:flex-start;gap:3px}}
/* S9 re-scope, item 2: "01 Read your eye" splits at the heading -- the
   numeral and "Read" stay in h3, "your eye" moves down here as the
   caption's own bold first line (20px), the old single claim line drops
   to a second, grey line (16px) below it. Left-aligned always (never
   centred): the same 40px inset as .card-crop and h3's own margin. */
#cards .card-claim{position:absolute;z-index:3;left:40px;right:40px;bottom:40px;padding-top:20px;border-top:1px solid var(--line);text-align:left}
#cards .card-claim b{display:block;color:var(--ink);font:600 20px/1.25 var(--sans);letter-spacing:-.01em}#cards .card-claim p{margin:6px 0 0;color:var(--ink-2);font:400 16px/1.45 var(--sans)}
/* fix(fovea): the cards fit every width -- the stack breakpoint moves
   from 1050px to 900px per the exact spec given ("below 900px the cards
   stack to one column"). The 3-column band now starts at 901px instead
   of 1051px; the h3 wrap fix above already covers the whole 901-1200px
   range this widens into. */
/* Job 29e: this stack-breakpoint's own width:min(620px,...) is for
   styles-surface/exports-surface (still fluid, still capped so they
   don't run edge to edge on a very wide single stacked column). Without
   a matching exception here, it would also catch understand-surface (it
   shares the .card-surface class) and fight with its own width:520px +
   scale(var(--fit)) above -- restated after, same specificity, later in
   source, so it wins back for this one surface; --fit's own cqi-based
   math already adapts correctly at every width in this range on its
   own, nothing else needs retuning per breakpoint. */
@media(max-width:900px){#cards .cards-grid{grid-template-columns:1fr}#cards .pitch-card{height:834px}#cards .card-surface{width:min(620px,calc(100% - 40px))}#cards .understand-surface{width:520px;min-width:520px}}
/* fix(fovea): the Carry card mock fits at phone width. The node's own
   uppercase source text ("Warm light, unsentimental") is one unbreakable
   CSS "word" after text-transform, and .node's own overflow:hidden
   (v3.css) silently clipped it mid-letter once the node's own width ran
   out of room -- overflow-wrap now fixes it unconditionally (moved to
   the base .export-lens .node-source rule above, S9 re-scope), since the
   2-1-1 layout makes this card's own lens column just as narrow at every
   desktop width, not only a phone's.
   S9 part 3: now that .exports-flow is a real two-column grid (not an
   absolute layer), stacking it at phone width is just one column --
   the connectors are still hidden (their live-measured paths are drawn
   for the side-by-side layout and would need redrawing for a stacked
   one; not worth it at this width, matching every other stacked mock on
   this card group). */
@media(max-width:900px){
  #cards .export-lens .node-source{overflow-wrap:anywhere}
  #cards .export-connectors{display:none}
  #cards .exports-flow{grid-template-columns:1fr;row-gap:16px;padding:16px}
}
@media(max-width:600px){#cards{padding:80px 0}#cards .cards-inner{width:min(100% - 32px,480px)}#cards .cards-head{margin-bottom:36px}#cards .cards-grid{gap:14px}#cards .pitch-card{height:660px}#cards .pitch-card h3{margin-inline:20px}#cards .card-crop{left:20px;top:104px;bottom:138px}#cards .card-surface{top:28px;left:0;width:min(440px,calc(100% - 20px));min-width:0;transform:none}#cards .measure-list i,#cards .style-row em{transform:none}#cards .style-row{opacity:1}#cards .export-connectors path{stroke-dashoffset:0}#cards .card-claim{left:20px;right:20px;bottom:22px}}
/* Job 23 mobile audit / S9 re-scope: the old long title ("03 Carry it
   everywhere") once clipped at 390 even with the desktop nowrap fix --
   moot now (the heading is "03 Connect", a numeral plus one short word,
   and .pc-num/.pc-word's own min(step,cqw) floor already fits every
   column). white-space:normal stays as a defensive fallback only. */
@media(max-width:480px){#cards .pitch-card h3{white-space:normal}}
@media(hover:none),(prefers-reduced-motion:reduce){#cards .measure-list i,#cards .style-row em,#cards .measured-palette i{transform:none}#cards .style-row,#cards .reference-strip figure{opacity:1}#cards .export-connectors path{stroke-dashoffset:0}}
@media(prefers-reduced-motion:reduce){#cards .pitch-card{transform:none!important;opacity:1!important}}
