/** Shopify CDN: Minification failed

Line 2210:3 Unexpected "="

**/
/* ==========================================================================
   RENAISSANCE BLOCK / RB-WEB
   Ported from the static prototype (assets/site.css, "V8").

   The source file appended a "V8 POLISH" block that overrode ~40 earlier
   rules, including a second :root{--spine}. Those overrides are folded into
   the base rules here, so each selector is declared once. Dead selectors from
   the removed HOME/ABOUT pages are preserved at the bottom, commented out.
   ========================================================================== */

:root{
  /* The light palette, and the only place it is written down.

     Everything below is derived from it, including every hairline: --ink-rgb is
     the ink as bare channels so a rule can ask for it at any opacity without a
     literal. Ten different rgba(17,17,15,...) values used to be spelled out
     across the file, and each one was a place dark mode could have been missed. */
  --concrete:#e8e8e5;
  --paper:#f2f1ed;
  --bone:#eee9df;
  --ink:#11110f;
  --ink-rgb:17,17,15;
  /* The serif body ramp. These were five near-identical literals — #302e2a,
     #393631, #3b3833, #3f3b36 and #403c36 — differing by less than 2%, which is
     below what anyone can see and above what anyone can maintain. */
  --ink-2:#3f3b36;
  --ink-3:#56524c;
  /* #77746e until now, which measured 3.79 against the concrete ground — under
     4.5, and carried on 9px labels, which is the wrong side of the line to be on
     with the smallest type on the site. Ten percent darker, the same warm cast,
     and the least move that clears 4.5 on all three grounds (4.52 / 4.87 / 4.59).
     Chosen as the minimum rather than the comfortable margin: the client's note
     on this card was that it did not oversell, and heavier secondary type is how
     that goes. */
  --muted:#6b6863;
  --ghost:#aaa7a0;
  --faint:#b0ada6;
  --hair:rgba(var(--ink-rgb),.22);
  --hair2:rgba(var(--ink-rgb),.11);
  --alert:#7a2f1f;
  --scrim:rgba(0,0,0,.12);
  /* The house mark is a black glyph with no light version. It is inverted, not
     swapped — exact for a one-colour mark, and one file instead of two. On a
     dark ground it needs inverting by default, which is what this carries. */
  --mark-base:none;
  --spine:304px;
  /* The header's own height. Sized against, not guessed at, by anything that
     has to fit inside the viewport the header leaves behind. */
  --nav-h:62px;
  --announce-h:0px;   /* the bar publishes its own height when it renders */
  --tile:4/5;
  --tile-bg:#eeebe5;
  --mono:"SFMono-Regular",Menlo,Monaco,Consolas,"Liberation Mono",monospace;
  --sans:"Helvetica Neue",Helvetica,Arial,sans-serif;
  --serif:Georgia,"Times New Roman",serif;
}

/* ---------------------------------------------------------------- DARK --- */
/* Opt-in, and only opt-in. prefers-color-scheme is deliberately NOT consulted:
   this is a paper-ground identity, and following the operating system would put
   most phone visitors into a mode the house never chose. The toggle in the bar
   is the whole switch, and it is remembered.

   THE PHOTOGRAPHY DOES NOT MOVE. Every frame pads an off-ratio shot with that
   shot's own measured backdrop — it is what stops the never-crop rule reading as
   a gap, and it is a property of the photograph, not of the page. So on a dark
   ground the tiles stay light and the garments sit on them as plates on a table.
   That is the intent, not an oversight: darkening the pad would put a colour
   behind the shot that is not the colour the shot ends in, and the seam is
   exactly what the measurement exists to remove. --tile-bg stays light with
   them, so an unmeasured shot lands on the same ground as a measured one.

   Only the tokens move. Nothing below this block knows which theme it is in. */
:root[data-theme="dark"]{
  --concrete:#161614;
  --paper:#1c1b19;
  --bone:#232019;
  --ink:#edeae4;
  --ink-rgb:237,234,228;
  --ink-2:#c4bfb6;
  --ink-3:#a8a39a;
  --muted:#8e8a83;
  --ghost:#6d6a64;
  --faint:#57544f;
  --alert:#c98b76;
  --scrim:rgba(0,0,0,.46);
  --mark-base:invert(1);
}

*{box-sizing:border-box}
html,body{margin:0;min-height:100%;background:var(--concrete);color:var(--ink);font-family:var(--sans)}
body{font-size:14.5px}
button,input,select{font:inherit;color:inherit}
button{border:0;background:transparent;padding:0;cursor:pointer}
a{color:inherit;text-decoration:none}
img{display:block;max-width:100%}
::selection{background:var(--ink);color:var(--paper)}

/* Everything in the prototype was a <button>. In the theme, navigation is
   real anchors — these keep anchors visually identical to the old buttons. */
/* letter-spacing:normal is deliberate. In the prototype every tree item was a
   <button>, and the UA stylesheet stops form controls inheriting letter-spacing,
   so .tree's .002em never actually reached them. These are anchors now, which
   would inherit it — this keeps the rendered tree identical to the live site. */
.tree a,.tree button,.tree span{display:block;width:100%;text-align:left;font:inherit;line-height:1.44;letter-spacing:normal}
:focus-visible{outline:1.5px solid var(--ink);outline-offset:2px}
/* The browser's default object-fit is `fill`, which STRETCHES. Nothing on
   this site ever wants that, so the default is changed globally. This is a
   backstop, not the mechanism: it means a rule that fails to match — a
   class that goes missing, a snippet that drifts — degrades to a correctly
   proportioned image instead of a distorted one. Frames below still set
   their own fit explicitly. */
img{object-fit:contain}
.visually-hidden{position:absolute;width:1px;height:1px;margin:-1px;padding:0;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap;border:0}
.skip-link{position:absolute;left:-9999px;z-index:200}
.skip-link:focus{left:12px;top:12px;padding:8px 12px;background:var(--ink);color:var(--paper);font:8px var(--mono);letter-spacing:.06em}

/* -------------------------------------------------- ANNOUNCEMENT BAR ---- */
/* Same treatment as the tape on a product card, run the full width of the page
   so the store opens on a market strip. */
/* height from --announce-h, which the section sets, so the number the product
   page subtracts and the number this bar actually occupies are the same number.
   z-index below .nav (60), not above it: the bar is in normal flow above a
   sticky header, so at 70 it scrolled across the FRONT of the bar on the way
   out — invisible while the header is opaque, unmissable once it is not. */
.announce{background:var(--announce-bg,#11110f);color:var(--announce-fg,#f2f1ed);
  overflow:hidden;white-space:nowrap;font:9px var(--mono);letter-spacing:.08em;
  height:var(--announce-h);display:flex;align-items:center;
  position:relative;z-index:50}
.announce[hidden]{display:none}
.announce-close{position:absolute;right:0;top:0;bottom:0;width:38px;display:flex;
  align-items:center;justify-content:center;color:inherit;font-size:15px;line-height:1;
  background:linear-gradient(to right,transparent,var(--announce-bg,#11110f) 42%)}
.announce-run{display:inline-block;white-space:nowrap;
  animation:announce-run var(--announce-speed,80s) linear infinite;will-change:transform}
.announce:hover .announce-run{animation-play-state:paused}
@keyframes announce-run{from{transform:translateX(0)}to{transform:translateX(-50%)}}
.announce a{color:inherit;border-bottom:.6px solid transparent}
.announce a:hover{border-bottom-color:currentColor}
.tick-chg{opacity:.72}
@media (prefers-reduced-motion:reduce){.announce-run{animation:none}}

/* ------------------------------------------------------------- HEADER --- */
/* The tree is the brand, but as a permanent 304px rail it cost a fifth of the
   viewport on every page. Here the top level reads as a conventional bar and
   the tree unfolds beneath it, in the styling it always had. */
/* Four tokens, read with a fallback rather than given a default here.

   Written with the fallback form on purpose: a `--nav-bg:var(--concrete)`
   declaration on .nav would be a value set ON the element, and would beat the
   one the :has() rule below inherits down from the body — the transparent state
   could then never reach the bar. The fallback lives in the var() call, so the
   body's value wins when there is one and the solid bar is what you get when
   there is not. */
/* The rule under the bar is an inset shadow, not a border. A border adds its
   width to the bar's height: .6px rounds to a whole pixel on a standard screen,
   so the bar was 63px against a --nav-h of 62, the hero was pulled up by 62, and
   a one-pixel strip of the light page ground showed across the very top of
   every overlay hero. A shadow takes no room, so the bar is exactly --nav-h
   tall at every pixel density. */
.nav{position:sticky;top:0;z-index:60;
  background:var(--nav-bg,var(--concrete));
  box-shadow:inset 0 -.6px 0 var(--nav-line,var(--hair2));
  color:var(--nav-fg,var(--ink));
  transition:background .28s ease,color .28s ease,box-shadow .28s ease}

/* The header over the hero.

   The picture runs to the very top of the frame and the bar sits on it with no
   ground of its own. Type colour is chosen from what is actually behind the
   bar — the letterbox band for a contained image, the photograph itself for one
   set to fill — so it is legible on a black campaign shot and on a cream one.

   Decided by the stylesheet, not by a script.

   This used to wait for site.js to add body.hero-overlay and .nav--over. Every
   concern in that file is its own bare top-level IIFE, so a throw in any earlier
   one aborts the rest of the file — and the header overlay sat near the end of
   it. When that happened the home page painted with a solid bar and the hero
   pushed a header's height down the page, with nothing in the markup to say why.
   :has() asks the same question the script was asking — does this page OPEN with
   an overlay hero — and answers it in the first painted frame, with no deferred
   script and no layout jump. layout/theme.liquid makes the same argument for the
   product page, which is why that one sets the class server-side.

   .stage is not pulled up; only the first section is, so the offset cannot
   leak onto pages that have no hero.

   A descendant combinator, not a child one: the home page carries the marker on
   .bleed itself, one Shopify section wrapper down, while the product page
   carries it on the single .pdp-shot several levels inside .pdp-media. One
   mechanism covers both. */
.view>:first-child:has([data-hero-overlay]),
.view>[data-hero-overlay]:first-child,
body.hero-overlay .view>:first-child{margin-top:calc(-1 * var(--nav-h))}
body:has(.view>:first-child [data-hero-overlay]),
body.hero-overlay{--nav-bg:transparent;--nav-line:transparent}
/* Cream type over a dark picture. The tone is measured in Liquid from the shot's
   own backdrop, so the first frame is already right; site.js re-decides it for a
   hero set to fill, where what sits behind the bar is the photograph rather than
   the pad. Both write the same two tokens.

   The mark is a black glyph on transparency, so it does not follow currentColor
   the way every other thing in the bar does. Inverting it is exact for a
   one-colour mark and needs no second file to keep in step with the first. */
body:has(.view>:first-child [data-hero-overlay][data-hero-tone="dark"]),
body.hero-overlay:has([data-hero-overlay][data-hero-tone="dark"]),
body.hero-overlay.hero-on-dark{
  --nav-fg:#f4f3ef;--nav-mark:invert(1)}
.nav--on-dark{--nav-fg:#f4f3ef;--nav-mark:invert(1)}
/* The tone pair is about the PHOTOGRAPH under the bar, not about the page: a
   pale shot wants the black mark whichever theme the site is in. --mark-base is
   for the other case — the bar standing on its own ground. */
.nav--on-light{--nav-fg:var(--ink);--nav-mark:none}
/* The bar has left the hero behind. Set on .nav, so it takes the tokens back
   from the body whatever the body said — and last of the three, so it wins over
   a tone class at the same specificity. */
.nav--past{--nav-bg:var(--concrete);--nav-line:var(--hair2);--nav-fg:var(--ink);--nav-mark:var(--mark-base)}
/* The phone sheet has a ground of its own, and a bar with none floating above it
   reads as two unrelated pieces — so while the sheet is up the header takes its
   ground back and the whole thing is one shape again.

   ONLY the sheet. This used to say `.nav:has(.nav-panel:not([hidden]))` as well,
   and that was right when a panel was a dropdown hanging off the bottom edge of
   the bar: the two touched, so they had to share a ground.

   A panel is a rail down the left now, and on a product page it is open from the
   moment the page loads — so that clause was matching on arrival and painting the
   header solid over the photograph on every product page, at every width. It is
   the transparency the client reported missing on a phone, and it arrived with
   the rail. The rail does not touch the bar; it has no claim on its ground. */
.nav[data-open]{--nav-bg:var(--concrete);--nav-line:var(--hair2);--nav-fg:var(--ink);--nav-mark:var(--mark-base)}
@media(prefers-reduced-motion:reduce){.nav{transition:none}}
/* Search and menus left, mark centred, account controls right — the mark holds
   the optical centre of the bar regardless of how wide either side runs. */
.nav-bar{display:grid;grid-template-columns:1fr auto 1fr;align-items:center;gap:24px;height:62px;padding:0 3.4vw}
.nav-brand{justify-self:center;display:flex;align-items:center}
.nav-brand img{width:30px;height:30px;object-fit:contain;opacity:.92;filter:var(--nav-mark,var(--mark-base))}

/* Menus left, mark centred, controls right. */
.nav-left{display:none;align-items:center;gap:14px;justify-self:start}
.nav-menu{display:flex;align-items:center;gap:26px;height:100%;justify-self:start;min-width:0}
.nav-item{height:100%;display:flex;align-items:center}
/* The trigger fills the row so its hover rule can sit at a fixed offset from
   the bottom, but it also has to centre its own text: without this the label
   renders at the top of a 62px box while the controls opposite are centred,
   which measured as a 25.5px baseline split across the bar. Uppercase is
   applied here rather than trusted to the merchant, because these labels come
   from a Shopify menu and arrive in whatever case someone typed. */
.nav-trigger{display:inline-flex;align-items:center;font:9.5px var(--mono);letter-spacing:.1em;
  text-transform:uppercase;height:100%;padding:0;position:relative;white-space:nowrap}
.nav-trigger::after{content:"";position:absolute;left:0;right:0;bottom:18px;border-bottom:.7px solid var(--nav-fg,var(--ink));opacity:0;transition:opacity .15s}
.nav-item:hover .nav-trigger::after,.nav-trigger[aria-expanded="true"]::after{opacity:1}

/* Panel spans the viewport so the tree has room to breathe. */
.nav-panel{position:absolute;left:0;right:0;top:100%;background:var(--concrete);border-bottom:.6px solid var(--hair);box-shadow:0 24px 40px rgba(var(--ink-rgb),.06)}
.nav-panel[hidden]{display:none}
.nav-panel-inner{padding:30px 3.4vw 40px;max-height:min(70vh,620px);overflow:auto}
.nav-panel-all{display:inline-block;font:8px var(--mono);letter-spacing:.12em;color:var(--muted);margin-bottom:20px;border-bottom:.6px solid var(--hair)}
.nav-panel-all:hover{color:var(--ink);border-color:var(--ink)}
/* The tree inside a panel flows in columns rather than one tall rail. Every
   level that can start a column has to declare break-inside:avoid, or a branch
   is sheared in half across the gutter. */
.nav-panel .tree{columns:4;column-gap:5vw}
.nav-panel .branch{break-inside:avoid;margin:0 0 22px;display:inline-block;width:100%}
.nav-panel .sub{break-inside:avoid;margin-bottom:22px;display:inline-block;width:100%}
/* In the phone sheet it is one column, and the rail's own left margin is all
   the indentation a 20px gutter can spare. */
.nav[data-open] .nav-panel .tree{columns:1}
.nav[data-open] .nav-panel .branch{margin-bottom:16px}

/* ------------------------------------------------------- THE NAV RAIL --- */
/* The dropdown IS the rail. PLATFORMS and MODULES already carry a panel holding
   that collection's own tree — products, then the colours and sizes those
   products actually have — so this is where it is drawn, not what it contains.

   It replaced a panel that ran the full width of the bar and stood about 120px
   tall: a tree laid out in four columns across the top of the page, which is the
   shape a tree is worst in. Down the left at 304px it is the shape the rail had
   in the prototype, and the shape a directory wants.

   Desktop only. Below 901px the panels still open inline inside the phone sheet
   and are still display:none when hidden — and a panel left un-hidden by the
   steady-state rule below cannot leak onto a phone either, because .nav-menu is
   display:none on a closed bar. */
@media(min-width:901px){
  .nav-panel{position:fixed;left:0;top:var(--nav-h);bottom:0;right:auto;
    width:var(--spine);overflow:auto;overscroll-behavior:contain;
    border-bottom:0;border-right:.6px solid var(--hair);
    box-shadow:none;
    opacity:1;transform:none;visibility:visible;
    transition:opacity .2s ease,transform .2s ease,visibility .2s}
  /* [hidden] is display:none in the UA stylesheet, and display cannot
     transition — which is why the panel could only ever appear, never arrive.
     visibility:hidden is what replaces it, and it is not a downgrade: it takes
     the panel out of the focus order and out of the accessibility tree exactly
     as hidden does, so nothing becomes reachable that was not reachable before. */
  .nav-panel[hidden]{display:block;opacity:0;visibility:hidden;transform:translateX(-14px)}
  .nav-panel-inner{max-height:none;padding:26px 20px 40px;overflow:visible}
  .nav-panel .tree{columns:1}
  .nav-panel .branch{margin:0 0 14px}
  .nav-panel-all{margin-bottom:16px}

  /* Steady on a collection or a product page, transient on the home page.

     A rail you click may move the page; one that opens on hover may not, or the
     layout shifts out from under the pointer. So the margin is only taken where
     the rail is permanent — and it is a body class written by Liquid from the
     page type, which means the first painted frame is already right and there is
     no stored state and no script in the way. */
  body.rail-fixed .stage{margin-left:var(--spine)}
}

@media(prefers-reduced-motion:reduce){
  .nav-panel,.stage{transition:none}
}

.nav-utils{justify-self:end;display:flex;align-items:center;gap:26px;white-space:nowrap}
/* One type spec across all three controls, so the corner reads as one row — but
   the search now says it is a field. It used to hide that deliberately: the
   underline was transparent until focus, and the placeholder took the link
   colour, so SEARCH read as a third word rather than something you could click.

   That also made it look mis-set. The word is ~45px inside a 78px box, so 33px
   of invisible field sat before the 26px gap, and the space to WATCHLIST looked
   like double the space to BAG. It was never misaligned — the three text centres
   measure identically — the box was just invisible. A resting hairline shows the
   field's extent and both complaints go with it. */
.nav-utils .nav-word,.nav-utils .nav-num,.nav-search input{
  font:9.5px var(--mono);letter-spacing:.09em;color:var(--nav-fg,var(--ink))}
.nav-search{display:flex}
.nav-search input{width:92px;border:0;border-bottom:.7px solid var(--hair);outline:0;
  background:transparent;padding:4px 0;transition:width .2s ease,border-color .15s ease}
.nav-search input::placeholder{color:var(--muted);opacity:1}
.nav-search input:hover{border-bottom-color:var(--nav-fg,var(--ink))}
.nav-search input:focus{width:170px;border-bottom-color:var(--nav-fg,var(--ink))}
.nav-search input:focus::placeholder{color:var(--ghost)}
.nav-icon{display:inline-flex;align-items:center;gap:7px;min-height:44px}
.nav-glyph{width:18px;height:18px;display:none}
.nav-word{font:9.5px var(--mono);letter-spacing:.09em}
.nav-num{font:9.5px var(--mono);letter-spacing:.06em;font-variant-numeric:tabular-nums}
/* The watchlist mark is a market pulse, so it carries one. Slow and shallow —
   it should read as a live readout, not a notification badge demanding a tap. */
@keyframes watch-pulse{0%,100%{opacity:.55}50%{opacity:1}}
.nav-watch .nav-glyph{animation:watch-pulse 2.6s ease-in-out infinite}
@media(prefers-reduced-motion:reduce){.nav-watch .nav-glyph{animation:none;opacity:1}}
/* MENU and the search glyph are phone-only; on desktop the left cluster is the
   live search field and the menus themselves. */
.nav-toggle,.nav-search-toggle{display:none;padding:0}

/* Mobile: the bar opens into a full-screen sheet.

   The sheet is POSITIONED OUT OF THE BAR, and that is the whole fix. .nav-bar is
   a three-column grid holding four children; on desktop .nav-left is hidden and
   on a closed phone bar .nav-menu is hidden, so either way three items land in
   three columns. Merely un-hiding .nav-menu to open the menu made it four — the
   menu took the centre column at 104px wide and .nav-utils wrapped onto a second
   row, which is exactly how it rendered: a narrow strip of links floating in the
   middle of the screen with the icons stranded underneath.

   Taking the sheet out of flow returns the bar to three children, so the bar
   keeps its layout while the sheet covers the page beneath it. */
.nav[data-open]{height:100vh}
.nav[data-open] .nav-menu{position:fixed;left:0;right:0;top:56px;bottom:0;z-index:1;
  display:flex;flex-direction:column;align-items:stretch;gap:0;height:auto;padding:0;
  background:var(--concrete);overflow:auto;overscroll-behavior:contain}
.nav[data-open] .nav-item{display:block;height:auto;border-top:.6px solid var(--hair2)}
.nav[data-open] .nav-trigger{height:auto;min-height:52px;display:flex;align-items:center;font-size:13px;width:100%;text-align:left;padding:0 20px}
.nav[data-open] .nav-trigger::after{display:none}
.nav[data-open] .nav-panel{position:static;box-shadow:none;border:0;background:transparent}
.nav[data-open] .nav-panel-inner{padding:0 20px 22px;max-height:none;overflow:visible}
.nav[data-open] .nav-panel .tree{columns:1}

/* Sheet furniture. Hidden entirely until the sheet exists, so desktop never
   sees a second search field or a duplicate set of account links. */
.nav-sheet-only{display:none}
.nav[data-open] .nav-sheet-only{display:block}
.nav-sheet-search{align-items:center;gap:10px;padding:18px 20px;border-bottom:.6px solid var(--hair2)}
/* Display for both sheet parts is declared ONLY under [data-open]. Declaring
   it on the component instead ties with .nav-sheet-only{display:none} on
   specificity, and source order silently un-hides it on desktop. */
.nav[data-open] .nav-sheet-search{display:flex}
.nav-sheet-search svg{width:18px;height:18px;flex:0 0 auto;color:var(--muted)}
.nav-sheet-search input{width:100%;min-width:0;border:0;outline:0;background:transparent;
  font:13px var(--mono);letter-spacing:.06em;color:var(--ink);padding:6px 0}
.nav-sheet-search input::placeholder{color:var(--ghost);opacity:1}

/* margin-top:auto moved to .nav-sheet-theme, which now sits directly above this
   and does the pinning for both — the foot follows it. */
.nav-sheet-foot{padding:26px 20px calc(26px + env(safe-area-inset-bottom));
  border-top:.6px solid var(--hair);flex-direction:column;gap:15px}
.nav[data-open] .nav-sheet-foot{display:flex}
.nav-sheet-foot a{font:10.5px var(--mono);letter-spacing:.1em;display:flex;gap:8px;min-height:24px;align-items:center}
.nav-sheet-num{font-variant-numeric:tabular-nums;color:var(--muted)}

/* -------------------------------------------------------- LIVE SEARCH --- */
/* Rows under the field, in the storefront's own materials. Sits in the same
   plane as the dropdown panels and one step above them, because it opens while
   a panel may already be open. */
.nav-search{position:relative}
.ps{position:absolute;top:calc(100% + 14px);right:0;z-index:80;width:min(420px,92vw);
  max-height:min(70vh,560px);overflow:auto;overscroll-behavior:contain;
  background:var(--concrete);border:.6px solid var(--hair2);
  box-shadow:0 24px 40px rgba(var(--ink-rgb),.10);padding:14px 0}
.ps[hidden]{display:none}
.ps-group+.ps-group{border-top:.6px solid var(--hair2);margin-top:10px;padding-top:10px}
.ps-label{font:7.5px var(--mono);letter-spacing:.12em;color:var(--muted);padding:4px 16px 8px}
.ps-hit{display:grid;grid-template-columns:38px 1fr auto;gap:12px;align-items:center;
  padding:8px 16px;color:var(--ink)}
.ps-hit--plain{grid-template-columns:1fr;font:10.5px var(--mono);letter-spacing:.01em}
/* Hover and keyboard selection are the same state — the arrow keys move a
   highlight, they do not invent a second vocabulary for the same idea. */
.ps-hit:hover,.ps-hit.is-active{background:rgba(var(--ink-rgb),.05)}
.ps-thumb{display:block;width:38px;aspect-ratio:var(--tile);background:var(--shot-bg,var(--tile-bg))}
.ps-thumb img{width:100%;height:100%;object-fit:contain;display:block}
.ps-body{min-width:0}
.ps-code{display:block;font:7.5px var(--mono);letter-spacing:.09em;color:var(--muted)}
.ps-title{display:block;font:10.5px var(--mono);letter-spacing:.01em;
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.ps-price{font:9px var(--mono);letter-spacing:.04em;font-variant-numeric:tabular-nums;color:var(--muted)}
.ps-empty{padding:10px 16px 4px;font:9px var(--mono);letter-spacing:.08em;color:var(--muted)}
.ps-all{display:block;margin-top:10px;padding:12px 16px 4px;border-top:.6px solid var(--hair2);
  font:8px var(--mono);letter-spacing:.1em;color:var(--muted)}
.ps-all:hover,.ps-all.is-active{color:var(--ink)}

/* In the phone sheet the field is at the top of a full-screen panel, so the
   results belong in the flow directly under it — an absolute layer would cover
   the menu the client asked to keep visible below the search. */
.nav-sheet-search{position:relative;flex-wrap:wrap}
.nav-sheet-search .ps{position:static;width:100%;flex:0 0 100%;margin-top:14px;
  border:0;border-top:.6px solid var(--hair2);box-shadow:none;background:transparent;
  max-height:none;padding:8px 0 0}
.nav-sheet-search .ps-label,.nav-sheet-search .ps-hit,
.nav-sheet-search .ps-empty,.nav-sheet-search .ps-all{padding-left:0;padding-right:0}

/* ------------------------------------------------------- FULL-BLEED HERO - */
/* A feed directly under a bleed hero should not start hard against it. */
.bleed + * .market{--pad-top:52px}
.bleed{--pad-top:0px;--pad-bottom:0px;
  padding-top:var(--space-top,var(--pad-top));padding-bottom:var(--space-bottom,var(--pad-bottom));
  display:grid;grid-template-columns:1fr;position:relative;background:#ebe7df}
.bleed--split{grid-template-columns:1fr 1fr}
/* Fit is the default: the hero takes the image's own proportions, so a
   landscape shot runs edge to edge with no band at all and nothing cropped.
   A portrait taller than the viewport is capped at 100vh and centred rather
   than running off the screen — that leaves a side band, painted with the
   shot's own backdrop like every other frame. The fixed heights below are
   still available and still never crop; they letterbox instead. */
.bleed--fit{height:auto}
.bleed--fit .bleed-frame{aspect-ratio:var(--hero-ratio,1.5);max-height:100svh}
.bleed--fit .bleed-frame>img,.bleed--fit .bleed-img{width:100%;height:100%}
/* The default, and the reason the split exists. A hero cut from the viewport is
   whatever shape the screen is — 3:1 on a wide desktop, which no garment
   photograph fits, and which is why filling one meant uploading a picture that
   was already three pictures side by side. Two 4:5 panels are the site's own
   tile, twice: the same frame the cards, the thumbnails and the gallery use, so
   the same photography drops straight in. One panel on a phone, so it is 4:5
   there too rather than half of one. */
.bleed--ratio{height:auto}
.bleed--ratio .bleed-frame{aspect-ratio:var(--tile)}
.bleed--ratio .bleed-frame>img,.bleed--ratio .bleed-img{width:100%;height:100%}
/* A hero the header sits ON is a whole screen tall. Subtracting the header
   would leave its own height of ground below the fold. */
.bleed--full{height:calc(100vh - var(--nav-h))}
.view>:first-child .bleed--full[data-hero-overlay]{height:100svh}
.bleed--tall{height:min(78vh,860px)}
.bleed--short{height:min(52vh,560px)}
.bleed-frame{display:block;position:relative;overflow:hidden;min-width:0;background:var(--shot-bg,#ebe7df)}
/* Selected structurally as well as by class, for the same reason the card is:
   the class is one Liquid slip away from vanishing, and without a rule the
   image falls back to the width/height attributes and the fill default. */
.bleed-frame>img,.bleed-img{width:100%;height:100%;object-fit:contain;display:block}
/* The deliberate exception to the never-crop rule, and the only one on the
   site: a landscape photograph cannot fill a portrait phone screen any other
   way. Opt-in per hero, in the theme editor. */
.bleed--cover .bleed-frame>img,.bleed--cover .bleed-img{object-fit:cover}
/* Fit and fill are not combinable and do not need to be: a fitted frame already
   takes the image's own proportions, so there is nothing left for cover to crop.
   Overriding the ratio here to force a fill gave the frame height:100% of an
   auto-height parent, which is zero. Fit wins on sizing; cover is a no-op under
   it. */
.bleed-frame--empty{display:flex;align-items:center;justify-content:center;background:#e3dfd7}

/* Only drawn when light copy is present, so an image-only hero stays untouched. */
.bleed--scrim::after{content:"";position:absolute;left:0;right:0;bottom:0;height:52%;
  background:linear-gradient(to top,rgba(var(--ink-rgb),.62),rgba(var(--ink-rgb),.18) 46%,transparent);
  pointer-events:none;z-index:1}
.bleed-copy{position:absolute;left:3.4vw;bottom:38px;max-width:min(46ch,44vw);z-index:2}
.bleed-copy h1{font:400 clamp(34px,4.4vw,72px)/.92 var(--sans);letter-spacing:-.05em;margin:12px 0 0;text-wrap:balance}
.bleed-copy--light,.bleed-copy--light .micro{color:#f4f3ef}
.bleed-copy--light h1{color:#f4f3ef;text-shadow:0 1px 26px rgba(0,0,0,.28)}
.bleed-copy--dark,.bleed-copy--dark .micro{color:var(--ink)}
.bleed-cta{display:inline-block;margin-top:20px;font:9px var(--mono);letter-spacing:.12em;border-bottom:.8px solid currentColor;padding-bottom:4px}

/* The film. It sits over the poster at opacity 0 and only fades in once frames
   are actually playing, so the image never gives way to black while the file
   buffers, and never gives way at all if the film does not start.

   The slow drift is a transform, which the compositor runs off the main thread.
   The reference's grain is left out on purpose: a mix-blend-mode layer over a
   playing video has to be re-blended every frame, and that is what makes a
   phone stutter on scroll. At 6% it was barely visible anyway. */
.bleed--film{background:#232722}
.bleed--film .bleed-frame{background:#232722}
.bleed-video{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;display:block;
  opacity:0;transition:opacity .7s ease;transform:scale(1.08);pointer-events:none}
.bleed--film[data-playing] .bleed-video{opacity:1;animation:film-drift 22s ease-in-out infinite alternate}
.bleed--film[data-paused] .bleed-video{animation-play-state:paused}
@keyframes film-drift{from{transform:scale(1.08)}to{transform:scale(1.14)}}
.bleed-grade{position:absolute;inset:0;pointer-events:none;z-index:1;
  background:linear-gradient(180deg,rgba(9,14,10,.34),rgba(9,14,10,.18) 35%,rgba(9,14,10,.27) 70%,rgba(9,14,10,.42))}
.bleed-wordmark{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);margin:0;z-index:2;
  font:400 13px/1.4 var(--mono);letter-spacing:.32em;padding-left:.32em;white-space:nowrap;pointer-events:none}
.bleed-wordmark--light{color:#f1efe5;text-shadow:0 1px 12px rgba(0,0,0,.6)}
.bleed-wordmark--dark{color:#11110f}
.bleed--film .bleed-copy--light .bleed-cta{border-bottom-color:rgba(241,239,229,.5)}
.bleed-film-toggle{position:absolute;right:3.4vw;bottom:23px;z-index:2;min-height:44px;padding:0;
  border:0;background:none;cursor:pointer;color:#f1efe5;font:9.5px var(--mono);letter-spacing:.1em}
.bleed-film-glyph{margin-left:14px}
@media(prefers-reduced-motion:reduce){
  .bleed-video{transition:none}
  .bleed--film[data-playing] .bleed-video{animation:none}
}

@media(max-width:900px){
  /* Menu and search left, mark centred and wordless, watch and bag right. */
  :root{--nav-h:56px}
  .nav-bar{display:grid;grid-template-columns:1fr auto 1fr;align-items:center;gap:12px;padding:0 18px;height:56px}
  .nav-menu,.nav-search{display:none}
  .nav-left{display:flex;justify-self:start;gap:14px}
  .nav-toggle{display:inline-flex;align-items:center;min-height:44px}
  .nav-toggle .nav-glyph{width:20px;height:20px}
  .nav-search-toggle{display:inline-flex;align-items:center;padding:0}
  .nav-glyph{display:block}
  .nav-word{display:none}
  /* Phones lose the counters as well as the words. The fixed mobile bar
     (.mobile-bar) already carries a bag count and a total, so a second one in
     the corner is noise beside a 20px glyph. */
  .nav-num{display:none}
  .nav-brand{justify-self:center;gap:0}
  .nav-brand span{display:none}
  .nav-brand img{width:30px;height:30px}
  .nav-utils{margin-left:0;gap:18px;justify-self:end}
  /* One panel, not two stacked. Two half-height rows gave each picture a squat
     landscape frame on the one screen where the photography is most portrait —
     the opposite of the point. The second panel is desktop art; on a phone the
     first one gets the whole frame.

     Selected by class rather than :nth-child: .bleed-copy is a child of .bleed
     too, and an index selector would hide the headline on a hero that has no
     second image. */
  .bleed--split{grid-template-columns:1fr;grid-template-rows:auto}
  .bleed-frame--second{display:none}
  /* The mobile file's own measured ground and its own proportions. Falling back
     to the desktop shot's would paint a band of the wrong colour beside a
     different photograph, which reads as a mistake rather than as padding. */
  .bleed-frame{background:var(--shot-bg-m,var(--shot-bg,#ebe7df))}
  .bleed--fit .bleed-frame{aspect-ratio:var(--hero-ratio-m,var(--hero-ratio,1.5))}
  .bleed--tall,.bleed--full{height:min(72vh,720px)}
  .bleed-copy{left:20px;right:20px;bottom:26px;max-width:none}
  .bleed-wordmark{font-size:11px;letter-spacing:.23em;padding-left:.23em}
  .bleed-film-toggle{right:20px;bottom:11px}
  .bleed--film .bleed-copy{right:120px}
}

/* ---------------------------------------------------------------- SHELL -- */
.shell{min-height:100vh}
.spine{position:fixed;z-index:40;left:0;top:0;bottom:0;width:var(--spine);display:flex;flex-direction:column;background:var(--concrete);border-right:.6px solid var(--hair)}
.spine-head{height:58px;padding:17px 20px 0;display:flex;justify-content:space-between;align-items:flex-start}
.brand{font:11.5px var(--mono);letter-spacing:.015em}
.index-toggle{display:none;font:8px var(--mono);letter-spacing:.08em}
.spine-scroll{flex:1;overflow:auto;padding:10px 20px 30px;scrollbar-width:none}
.spine-scroll::-webkit-scrollbar{display:none}

/* ----------------------------------------------------------- SPINE TREE -- */
.tree{font:9.6px/1.44 var(--mono);letter-spacing:.002em}
.root{font-weight:700;margin:0 0 6px}
.branch{position:relative;margin:3px 0 8px 5px;padding-left:15px;border-left:.7px solid rgba(var(--ink-rgb),.62)}
.branch:before{content:"";position:absolute;left:-.5px;top:7px;width:9px;border-top:.7px solid rgba(var(--ink-rgb),.62)}
.branch-title{font-weight:600;padding:1px 0 3px}
.children{position:relative;padding-left:13px}
.children:before{content:"";position:absolute;left:0;top:1px;bottom:7px;border-left:.65px solid rgba(var(--ink-rgb),.38)}
.children>*{position:relative;padding:1px 0 1px 10px;color:var(--ink-2)}
.children>*:before{content:"";position:absolute;left:0;top:6px;width:7px;border-top:.65px solid rgba(var(--ink-rgb),.38)}
.sub{padding:0!important}
.sub:before{display:none}
.sub-title{font-weight:600}
.grandchildren{position:relative;padding-left:13px}
.grandchildren:before{content:"";position:absolute;left:0;top:1px;bottom:6px;border-left:.6px solid rgba(var(--ink-rgb),.24)}
.grandchildren>*{position:relative;padding:1px 0 1px 9px;color:var(--ink-3)}
.grandchildren>*:before{content:"";position:absolute;left:0;top:6px;width:6px;border-top:.6px solid rgba(var(--ink-rgb),.24)}
.deep{padding-left:15px!important}
.deeper{padding-left:27px!important;color:var(--muted)!important}
.deeper2{padding-left:39px!important;color:var(--ghost)!important}
.tree a:hover,.tree button:hover,.tree .active{opacity:.42}
.tree [aria-current="page"]{opacity:.42;font-weight:600}

/* Collapsible branches use <details> so they work without JavaScript. */
.branch>summary{list-style:none;cursor:pointer}
.branch>summary::-webkit-details-marker{display:none}

.find{margin-top:18px}
.find label{font:8px var(--mono);letter-spacing:.08em;color:var(--muted)}
.find>div{display:flex;margin-top:6px;border-bottom:.7px solid rgba(var(--ink-rgb),.52)}
.find input{width:100%;min-width:0;border:0;outline:0;background:transparent;padding:6px 0;font:9.5px var(--mono)}
.find button{font:12px var(--sans)}

.spine-spec{padding:8px 20px 7px;border-top:.6px solid var(--hair2);font:6.8px/1.45 var(--mono);letter-spacing:.045em;color:var(--muted)}
.spine-foot{height:44px;padding:0 20px;display:flex;align-items:center;gap:14px;border-top:.6px solid var(--hair);font:8px var(--mono);letter-spacing:.04em}
.spine-foot>*:last-child{margin-left:auto}

/* ---------------------------------------------------------------- STAGE -- */
.stage{margin-left:0;min-height:100vh;background:var(--concrete);
  transition:margin-left .2s ease}

/* ------------------------------------------------------ THEME TOGGLE ----- */
/* A mark, not two words. LIGHT / DARK sat in the menu as a third and fourth
   entry among the menu entries, which is what made the bar read as cheap.

   One drawing for both states: a circle with one half filled — the brightness
   mark — turned over when the theme turns over. No second file to keep in step,
   and no question of whether the icon shows the mode you are in or the one you
   would get, because it is the same object either way round.

   In the corner it keeps its glyph while WATCHLIST and BAG show words:
   .nav-glyph is display:none on desktop and this one overrides it. Being the
   only mark in a row of words is what sets it apart; the hairline finishes it. */
.nav-theme{padding-left:26px;border-left:.6px solid var(--hair2);color:inherit}
.nav-theme .theme-glyph{display:block}
.theme-glyph{transition:transform .28s ease}
.theme-toggle{opacity:.78;transition:opacity .15s;color:inherit}
.theme-toggle:hover{opacity:1}
:root[data-theme="dark"] .theme-glyph{transform:rotate(180deg)}
/* The bar's mark is desktop only. On a phone the row beside it is the watchlist
   and bag glyphs, which are places to go; a control that changes how the site
   looks does not belong in that row, and the sheet is where it lives instead. */
@media(max-width:900px){.nav-theme{display:none}}
/* In the sheet it sits ABOVE the foot's hairline rather than as its first row,
   and takes the margin-top:auto the foot used to carry — so it is pinned to the
   bottom of the menu list with the rule under it, not filed with BAG. */
.nav-sheet-theme{margin-top:auto;padding:0 20px 20px}
.nav-sheet-theme .theme-toggle{display:inline-flex;align-items:center;min-height:32px}
.nav-sheet-theme .theme-glyph{display:block;width:16px;height:16px}
@media(prefers-reduced-motion:reduce){.theme-glyph{transition:none}}

.topline{position:sticky;z-index:25;top:0;height:56px;display:grid;grid-template-columns:1fr auto 1fr;align-items:center;padding:0 34px;background:color-mix(in srgb,var(--concrete) 92%,transparent);backdrop-filter:blur(9px);font:8.5px var(--mono);letter-spacing:.045em;border-bottom:.6px solid var(--hair2)}
.topline>img{width:29px;height:29px;object-fit:contain;opacity:.9;filter:var(--mark-base)}
.topline>div:last-child{text-align:right}
.view{min-height:calc(100vh - 56px)}

/* ----------------------------------------------------------- HOMEPAGE --- */
/* Each movement is its own section so the client can reorder them, but they
   share the horizontal rhythm .home originally provided as one wrapper. */
.home-section{--pad-top:96px;--pad-bottom:56px;
  padding:var(--space-top,var(--pad-top)) 5.5vw var(--space-bottom,var(--pad-bottom));overflow:hidden}
.hero-mark--inline{position:static;width:48px;height:48px;margin:0 0 26px;opacity:.9;filter:var(--mark-base)}
.home-section--hero{--pad-top:58px}
.home-section:last-of-type{--pad-bottom:40px}
@media(max-width:900px){
  .home-section{--pad-top:72px;--pad-bottom:40px;padding-left:24px;padding-right:24px}
  .home-section--hero{--pad-top:45px}
}

/* His own art direction, recovered. These rules sat commented in the prototype's
   stylesheet with no markup ever rendering them — the hero, the wire-node
   diagram of the modular system, and the statement strip. */
.home{padding:58px 5.5vw 160px;overflow:hidden}
.hero{min-height:760px;position:relative}
.hero-copy{padding-top:12vh;max-width:760px}
.hero h1{font-size:clamp(78px,10vw,164px);font-weight:400;line-height:.82;letter-spacing:-.072em;margin:24px 0 34px}
.hero .lede{font:24px/1.25 var(--serif);max-width:500px;margin:0;color:var(--ink-2)}
.hero-links{display:flex;gap:22px;margin-top:34px;font:8px var(--mono);letter-spacing:.08em}
.hero-links button,.hero-links a{border-bottom:.6px solid var(--ink);padding-bottom:4px}
.hero-mark{position:absolute;right:9vw;top:7vh;width:55px;opacity:.85;filter:var(--mark-base)}
.hero-img{position:absolute;right:3vw;bottom:10px;width:min(31vw,420px)}
.hero-img img{width:100%;aspect-ratio:var(--tile);object-fit:contain;background:var(--shot-bg,var(--tile-bg));
  mix-blend-mode:multiply;filter:saturate(.72) contrast(.96)}
.hero-img figcaption{margin-top:8px;display:flex;justify-content:space-between;font:7px var(--mono);color:var(--muted)}
.home-thread{margin:0;min-height:520px;display:grid;grid-template-columns:minmax(180px,.42fr) 1fr;gap:11vw;align-items:start}
.thread-copy{padding-top:20px}
.thread-copy h2{font:400 clamp(48px,6vw,96px)/.92 var(--sans);letter-spacing:-.05em;margin:14px 0 22px}
.thread-copy p{font:20px/1.42 var(--serif);max-width:530px;color:var(--ink-2)}
.thread-diagram{position:relative;height:430px}
.wire{position:absolute;height:.7px;background:rgba(var(--ink-rgb),.64);transform-origin:left center}
.wire.w1{width:36%;left:7%;top:43%;transform:rotate(-18deg)}
.wire.w2{width:31%;left:40%;top:31%;transform:rotate(22deg)}
.wire.w3{width:30%;left:61%;top:43%;transform:rotate(-12deg)}
.wire-node{position:absolute;font:8px var(--mono);letter-spacing:.04em}
.wire-node:before{content:"";display:block;width:8px;height:8px;border:1px solid var(--ink);border-radius:50%;background:var(--concrete);margin-bottom:9px}
.wire-node.n1{left:4%;top:48%}
.wire-node.n2{left:39%;top:20%}
.wire-node.n3{left:63%;top:49%}
.wire-node.n4{right:4%;top:30%}
.wire-caption{position:absolute;right:4%;bottom:6%;font:17px/1.3 var(--serif);max-width:280px;color:var(--ink-2)}
.renaissance-strip{margin:0;display:grid;grid-template-columns:1fr 1fr;gap:8vw}
.renaissance-strip h2{font:400 clamp(52px,7vw,112px)/.86 var(--sans);letter-spacing:-.055em;margin:12px 0}
.renaissance-strip .quote{align-self:end;font:22px/1.32 var(--serif);max-width:520px}
.renaissance-strip .quote small{display:block;margin-top:24px;font:7.5px var(--mono);letter-spacing:.07em;color:var(--muted)}

/* --------------------------------------------------------- MARKET FEED -- */
/* ------------------------------------------------- SECTION SPACING ------ */
/* Two layers, and the distinction is the whole design.

   --pad-top / --pad-bottom is the BASELINE: this section's own rhythm, moved by
   the media queries the way any other responsive value is. --space-top /
   --space-bottom is the OVERRIDE, written inline on the root by
   snippets/section-space from the theme editor's two sliders.

   Written as var(--space-top, var(--pad-top)) rather than by declaring the
   baseline into --space-top, because an inline custom property beats a
   stylesheet one and a stylesheet one beats nothing: the fallback form lets a
   set slider win at every width, and lets an unset one — which is every section
   already saved in a JSON template — fall through to the baseline and look
   exactly as it did.

   The sliders are absolute, not additive. A slider that could only add would
   leave whatever number is written here as a floor nobody could get under,
   which is the complaint this is answering. */

.market{--pad-top:26px;--pad-bottom:56px;
  padding:var(--space-top,var(--pad-top)) 3.4vw var(--space-bottom,var(--pad-bottom))}
.market-intro{min-height:56px;display:grid;grid-template-columns:1fr auto;align-items:start;border-bottom:.6px solid var(--hair);padding-bottom:13px}
.market-intro h1{font:400 23px var(--sans);margin:0}
.market-intro p{font:15px var(--serif);color:var(--ink-3);margin:0}
.market-intro .count{font:8px/1.5 var(--mono);color:var(--muted);text-align:right}
/* Feeds and collection grids are the same layout now — one card, one grid. */
.feed{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));column-gap:22px;row-gap:0;padding-top:26px}

/* ------------------------------------------------------------- FACETS ----
   Filtering and sorting in the same materials as the rest of the page: 8px
   mono, hairlines, one underline for state, no chrome of its own.

   Every control is a real anchor with a real URL. The page filters with
   JavaScript off and a filtered view can be sent to somebody, which is the
   same property the rest of the site insists on. The price of anchors over a
   form is that two colours is two page loads — at three colour values that is
   the right trade, and it stops being it somewhere north of a hundred.

   The panel is a dropdown here and an accordion under 700px; see the note in
   that block for why. */
.facets{display:flex;flex-wrap:wrap;align-items:flex-start;gap:0 26px;padding:11px 0 0;
  font:8px var(--mono);letter-spacing:.06em;position:relative;z-index:6}
.facet{position:relative}
.facet>summary{list-style:none;cursor:pointer;display:inline-flex;align-items:center;gap:7px;
  padding:5px 0;border-bottom:.6px solid transparent}
.facet>summary::-webkit-details-marker{display:none}
.facet>summary:hover,.facet[open]>summary{border-bottom-color:var(--ink)}
.facet-n{color:var(--muted)}
/* The same plus-that-becomes-a-minus as the FAQ rows, at label scale. */
.facet-mark{position:relative;display:inline-block;width:7px;height:7px}
.facet-mark:before,.facet-mark:after{content:"";position:absolute;background:var(--ink);transition:transform .18s}
.facet-mark:before{left:0;right:0;top:3px;height:.8px}
.facet-mark:after{top:0;bottom:0;left:3px;width:.8px}
.facet[open] .facet-mark:after{transform:scaleY(0)}
.facet-panel{position:absolute;left:0;top:100%;min-width:174px;max-height:52vh;overflow:auto;
  background:var(--concrete);border:.6px solid var(--hair);padding:12px 14px 13px;
  display:flex;flex-direction:column;gap:9px;z-index:7}
.facet-panel a,.facet-panel>span{display:flex;justify-content:space-between;gap:22px;
  white-space:nowrap}
/* The rule hugs the word, not the row. Stretched the full width of the panel it
   reads as a divider between values rather than as the state of one — and it is
   the same underline the pager and the card's WATCH/BUY already use. */
.facet-panel a>span:first-child,.facet-panel>span>span:first-child{
  border-bottom:.6px solid transparent;padding-bottom:2px}
.facet-panel a:hover>span:first-child,
.facet-panel [aria-current]>span:first-child{border-bottom-color:var(--ink)}
.facet-count{color:var(--ghost)}
/* A value with nothing behind it stays visible and unclickable. The absence is
   information, and a list that reflows every time you touch it cannot be used. */
.facet-off{opacity:.32;pointer-events:none}
.facets-active{display:flex;flex-wrap:wrap;align-items:baseline;gap:8px 18px;padding:10px 0 0;
  font:8px var(--mono);letter-spacing:.06em}
.facets-active a{border-bottom:.6px solid var(--hair);padding-bottom:2px}
.facets-active a:hover{border-bottom-color:var(--ink)}
.facets-active .facet-clear{color:var(--muted)}
@media(prefers-reduced-motion:reduce){.facet-mark:before,.facet-mark:after{transition:none}}

.post{display:grid;grid-template-columns:104px minmax(0,1fr) 158px;gap:26px;align-items:start;position:relative;padding:22px 0 24px;border-bottom:.6px solid var(--hair2)}
.post-label{padding-top:1px}
.post-sku{font:10.5px var(--mono);margin-bottom:8px}
.post-label strong{display:block;font-size:14px;font-weight:400;line-height:1.2}
.post-label em{display:block;font:12.5px var(--serif);font-style:italic;color:var(--ink-3);margin-top:3px}
.post-label small{display:block;margin-top:8px;font:7.8px/1.5 var(--mono);color:var(--muted)}
.post-media{display:flex;gap:12px;align-items:flex-start;min-width:0;overflow:hidden;padding-right:2px}
.shot{border:0;background:transparent;flex:0 0 auto;position:relative;display:block}
.shot img{width:100%;height:100%;object-fit:contain;background:var(--shot-bg,#ebe7df);transition:transform .18s ease,filter .18s ease;filter:saturate(.92)}
.shot:hover img{transform:translateY(-3px);filter:saturate(1)}
/* Width only; the tile ratio comes from --tile. These ten were written as pairs
   of pixels, which meant the ratio lived in ten places that had to be recomputed
   by hand and could drift apart silently — six of them were already rounded off
   the true 4/3 rather than exact. */
.shot.hero-shot{width:148px;aspect-ratio:var(--tile)}
.shot.small-shot{width:100px;aspect-ratio:var(--tile)}
.post-quote{padding-top:2px;font-family:var(--mono);font-size:8px;line-height:1.6}
.quote-actions{display:flex;gap:13px;margin-top:11px}
.quote-actions button,.quote-actions a{font:8px var(--mono);border-bottom:.6px solid var(--ink);padding-bottom:2px;background:none}
.watching{opacity:.38}
.quick-sizes{display:flex;flex-wrap:wrap;gap:9px;margin-top:9px;font-family:var(--mono)}
.quick-sizes button,.quick-sizes span{font:8px var(--mono);letter-spacing:.04em;border-bottom:.6px solid transparent;padding-bottom:2px}
.quick-sizes button:hover{border-color:var(--ink)}
.quick-sizes button[disabled]{opacity:.35;cursor:not-allowed}
.quick-sizes .disabled{opacity:.3;text-decoration:line-through}

.post-number{position:absolute;left:-2.2vw;top:22px;font:7.5px var(--mono);color:var(--faint)}
.post:hover .post-number{color:var(--ink-2)}
.sold-out{opacity:.42}

/* --------------------------------------------------------- GRID VIEW ---- */
/* Collection browsing. Same materials as the feed — hairlines, mono micro-type,
   no rounded corners — but scannable in two dimensions instead of one. */
.grid{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));column-gap:22px;row-gap:0;padding-top:26px}
.grid-view .market-intro{margin-bottom:0}

/* Three shared rows: photograph, name, everything else. The card is a subgrid,
   so those tracks belong to the collection grid rather than to the card, and
   every card in a row gets the same three heights. A title that wraps to two
   lines therefore lifts the whole row's name track instead of pushing its own
   price down past its neighbour's — which is the misalignment you see on a
   collection page the moment one product is called Platform Track Jacket 01.

   Only the name needs its own track. Everything under it is fixed height, so
   three rows is enough and the body stays a flex column with its own rules.

   The fallback reserves two lines on every name instead: same alignment, at the
   cost of a blank line under the short ones. */
/* The default is the fallback, and it applies unconditionally.

   Subgrid only resolves when the card is a direct grid item of a grid, and one
   of the five places a card renders still is not: Complete the Look is a flex
   strip. There the property computes to none and the span is dropped — and
   because the browser DOES support subgrid, an @supports guard never fires
   either, so it would lose the alignment and its fallback at the same time.
   Reserving two lines here is what keeps it level.

   The carousel used to be in that list and is a grid now; the watchlist's
   wrapper div is lifted out with display:contents below. */
.card{display:flex;flex-direction:column;min-width:0;position:relative}
.card-name{line-height:1.5;min-height:3em}
/* The watchlist wraps each card in a div so its JS can hide one. display:contents
   lifts that wrapper out so the card is the grid item and gets the shared rows.
   The hidden state must be restated: the UA's [hidden] rule loses to any author
   rule, so without this every watched-out item would render. */
[data-watch-item]{display:contents}
[data-watch-item][hidden]{display:none}

/* Where it can resolve: three shared rows — photograph, name, body — so a title
   that wraps lifts the whole row's name track instead of pushing its own price
   out of line. row-gap:0 on the parent, so the space between card ROWS lives on
   the card; a row-gap would otherwise be inserted between the card's own rows. */
.feed,.grid{grid-auto-rows:min-content}
.feed>.card,.grid>.card,.carousel-row>.card{display:grid;grid-template-rows:subgrid;grid-row:span 3;
  padding-bottom:30px}
/* The shared row already equalises the name, so reserving two lines on top of it
   would add a blank line to every card in a row of short ones. */
.feed>.card>.card-name,.grid>.card>.card-name,
.carousel-row>.card>.card-name{min-height:0}
/* The watch mark sits over the photograph's top corner. It is the same pulse
   line as the header so the two read as one idea, and it fills in once the
   product is watched — which is also how it is removed again. */
/* NO IMAGE IS EVER CROPPED OR STRETCHED, anywhere on the site, whatever is
   uploaded. Every frame is object-fit:contain and carries --shot-bg, the
   measured backdrop of the photograph standing in it, so the leftover band
   is invisible rather than a gap. --shot-bg is set from snippets/shot-backdrop
   for known files and sampled at runtime by site.js for anything else. Where a
   frame is taller than the shot the band is split in two, above and below, and
   each half takes its own measured edge colour — one average is the wrong
   colour at both ends of any shot whose backdrop runs dark to light.

   Only half the catalog is shot 4:5 — a third is square and 15% is 8:9. With
   object-fit:cover a square packshot loses a quarter of its height, which took
   the necklines off. contain fits whatever arrives; the tile is tuned to the
   photography's own backdrop so the letterboxing does not read as a gap.

   Bound to .card-media>img as well as .card-img on purpose. The class alone
   is one deploy away from being wrong: when the stylesheet lands but the
   markup does not, the image falls out of the box and takes the neckline off
   every garment. The structural selector cannot drift from the markup. */
.card-media{position:relative;display:block;aspect-ratio:var(--tile);background:var(--shot-bg,var(--tile-bg));overflow:hidden}
.card-media>img,.card-img{width:100%;height:100%;object-fit:contain;display:block;filter:saturate(.92)}



/* Left-ranged, not centred. The block under the name is a column of figures and
   figures want a common left edge to read down; centring made every row start
   in a different place. */
.card-body{padding-top:11px;display:flex;flex-direction:column;align-items:flex-start;
  text-align:left;gap:0}
/* The name as a ruled cap line: micro mono over a hairline, so it behaves as the
   header of the block rather than a second price. */
/* padding-top, because the name is a direct child of .card now and no longer
   inherits .card-body's. Lifting it into its own grid row took its breathing
   space with it and sat it flush on the photograph. */
.card-name{display:block;width:100%;font:9px var(--mono);letter-spacing:.16em;
  text-transform:uppercase;color:var(--muted);padding-top:13px;padding-bottom:8px;
  margin-bottom:9px;border-bottom:.6px solid var(--hair2)}
/* Not align-self:start. The track is as tall as the longest name in the row, so
   a short name that hugs the top leaves its hairline higher than its neighbour's
   — two rules at two heights reads as broken. Stretched, every rule in the row
   lands on the same line and the slack sits above it. */
.card-name{display:flex;flex-direction:column;justify-content:flex-start}
.card-delta{font:9px var(--mono);letter-spacing:.13em;color:var(--muted);margin-top:4px}
.card-delta-sep{opacity:.45;padding:0 1px}
.card-ref{color:var(--ghost)}
.card-swatch{display:block;margin-top:9px;width:13px;height:13px;border:.6px solid rgba(var(--ink-rgb),.28);background-size:cover;background-position:center}
/* The price is set exactly like the name and separated from it by colour alone —
   name muted, price ink. Nothing on the card is allowed to shout; the figure is
   the only thing in the block that is black, which is enough to find it. */
.card-price{font:9px var(--mono);letter-spacing:.16em;color:var(--ink);
  font-variant-numeric:tabular-nums}

/* Read down a column, not across a row. Three figures on one left edge with the
   label beside its number, rather than the number at one end of the card and its
   label at the other. */
.card-stats{margin:10px 0 0;display:grid;gap:2px}
.card-stats>div{display:flex;gap:6px;font:9px var(--mono);letter-spacing:.13em;color:var(--muted)}
.card-stats dt,.card-stats dd{margin:0}
.card-stats dt{color:var(--ink);font-variant-numeric:tabular-nums}
.card-acts{display:flex;gap:16px;margin-top:11px;font:9px var(--mono);letter-spacing:.15em}
.card-acts a,.card-acts button{color:var(--ink);background:none;border:0;padding:0 0 2px;
  border-bottom:.7px solid var(--ink);cursor:pointer;font:inherit;letter-spacing:inherit}
.card-acts button.watching{opacity:.55}

/* ------------------------------------------------------- WATCHLIST CARD -- */
/* The one card on the site that is not a card for sale.

   Two figures, both measured. The bar's fill is sizes actually in stock in this
   colourway, with the dot breathing at the fill's edge — the only animation on
   any card, and the one this card carried before the panel around it was cut.
   The line above it is the real move in the product's price since the visitor
   pressed WATCH, written by site.js from a figure it recorded at that moment.

   Not a market history and not pretending to be one: it starts when you start
   watching, it lives in this browser, and the label says SINCE WATCHED rather
   than CHG for exactly that reason. Nothing here is seeded from a variant id. */
.card-pulse{width:100%;margin-top:11px;display:flex;flex-direction:column;gap:7px}
.pbar{position:relative;height:2px;background:var(--hair2);display:block}
.pfill{position:absolute;inset:0 auto 0 0;background:var(--ink)}
.pdot{position:absolute;top:50%;width:5px;height:5px;margin:-2.5px 0 0 -2.5px;border-radius:50%;
  background:var(--ink);animation:avail-pulse 1.9s ease-in-out infinite}
@keyframes avail-pulse{0%,100%{transform:scale(1);opacity:1}50%{transform:scale(2.2);opacity:.32}}
/* The old panel had no guard here and ran three of these per card. This is one
   per card, on one page, and it still stops when asked. */
@media(prefers-reduced-motion:reduce){.pdot{animation:none}}
.card-pulse-meta{display:flex;align-items:baseline;justify-content:space-between;gap:10px;
  font:9px var(--mono);letter-spacing:.13em;color:var(--muted)}
[data-since-watched]{color:var(--ink);font-variant-numeric:tabular-nums}
[data-since-watched][hidden]{display:none}


/* ------------------------------------------------------------ SKU INDEX -- */
.archive{--pad-top:26px;--pad-bottom:56px;
  padding:var(--space-top,var(--pad-top)) 3.4vw var(--space-bottom,var(--pad-bottom))}
.archive-head{min-height:92px;display:flex;justify-content:space-between;align-items:flex-start;border-bottom:.6px solid var(--hair);padding-bottom:13px}
.archive-head h1{font:400 21px var(--sans);margin:0}
.archive-head span{font:8px var(--mono);color:var(--muted)}
.archive-row{min-height:154px;display:grid;grid-template-columns:126px minmax(0,1fr);gap:24px;align-items:start;margin:0;padding:18px 0 20px;border-bottom:.6px solid var(--hair2)}
.archive-label{font:10px/1.45 var(--mono)}
.archive-label strong{display:block;font:400 12.5px var(--sans);line-height:1.2;margin-top:6px}
.archive-label small{display:block;font-size:7.5px;margin-top:7px;color:var(--muted)}
.archive-images{display:flex;gap:11px;align-items:flex-start;overflow:hidden}
.archive-images a{width:92px;aspect-ratio:var(--tile);display:block}
.archive-images a:nth-child(1){width:108px;aspect-ratio:var(--tile)}
.archive-images img{width:100%;height:100%;object-fit:contain;background:var(--shot-bg,#e8e4dc);filter:saturate(.92)}

/* ---------------------------------------------------------------- PDP --- */
/* Two columns: the photography scrolls, the buy panel stays. Previously the
   title and buy block sat in a header band with a full-width spread of six
   images below it, so the page ran past 4000px and the control you needed
   scrolled away from the thing you were looking at. */
/* Half the screen for the look, half for the details, with the media column
   capped at the width that makes a 4:5 box exactly one viewport tall — so the
   whole garment is inside the first view, head to hem, with nothing cropped.

   50% and not 50vw: vw counts the scrollbar and the 1fr track does not, so 50vw
   splits 50.5/49.5 on Windows rather than in half.

   minmax(0,1fr) and not 1fr: a bare 1fr floors at min-content, and .pdp-tablist
   is flex-wrap:nowrap, so its widest label sets a floor the grid would otherwise
   push out sideways to honour. .feed, .grid and .post already say minmax(0,1fr)
   for the same reason.

   The vh line before the svh line is NOT redundant. An unparsed svh invalidates
   the whole declaration, and grid-template-columns then falls back to none — one
   column, with the buy panel underneath a gallery several thousand pixels long.
   svh and not dvh: dvh re-lays-out as the mobile chrome hides, which drags the
   scroll-snap points around mid-gesture.

   The 360px floor is for short viewports — 1440x700, or a laptop with devtools
   open — where one-screenful would otherwise shrink the look to a postage stamp.
   Safe against clamp's min-beats-max rule because the phone breakpoint takes
   over at 900px, where 50% is still 450px. */
.pdp{display:grid;align-items:start;
  grid-template-columns:clamp(360px, min(50%, calc((100vh  - var(--announce-h)) * 4 / 5)), 50%) minmax(0,1fr);
  grid-template-columns:clamp(360px, min(50%, calc((100svh - var(--announce-h)) * 4 / 5)), 50%) minmax(0,1fr)}
.pdp-media{position:relative;min-width:0}
/* The frame is 4:5 and it is never taller than the viewport, so whatever the
   screen's own proportions the whole product is inside the first view. Width is
   the binding constraint, not height: `min(column width, the width that makes a
   4:5 box exactly one screen tall)`. Setting a max-height instead would clamp
   the height and leave aspect-ratio to widen the frame past 4:5.

   4:5 because it is what half the catalogue is already shot at, and because it
   is the ratio the re-export is briefed to. It does NOT make the sources match:
   a third of the library is square and 15% is 8:9, so the padded band narrows
   (square 25% -> 20%, 8:9 15.6% -> 10%) but does not go away. shot-backdrop
   gets more load-bearing here, not less. */
.pdp-shots{display:flex;flex-direction:column;gap:2px}
/* The pad is two colours, split at the halfway line. object-fit:contain centres
   the shot, so the bands it leaves are one above the midpoint and one below, and
   each gets the colour of the edge it actually touches. A single average paints a
   visible bar across the 46 photographs whose backdrop runs dark to light —
   headwear-duo-model-black is #0d0d0d at the top and #c1bdba at the bottom.
   50/50 is always the right split: for a frame ratio R and a shot ratio r > R
   each band is (1 - R/r)/2 of the height, which is strictly under half.
   The shorthand stays as the flat fallback for a shot the lookup does not know. */
.pdp-shot{margin:0;width:100%;
  aspect-ratio:var(--tile);background:var(--shot-bg,var(--tile-bg));
  background-image:linear-gradient(var(--shot-bg-top,var(--shot-bg,var(--tile-bg))) 0 50%,
                                   var(--shot-bg-bot,var(--shot-bg,var(--tile-bg))) 50% 100%)}
.pdp-shot img,.pdp-shot-img{width:100%;height:100%;object-fit:contain;display:block}

/* Position indicator, over the photograph's right edge. Replaced by a progress
   rule under the image on phones, where the gallery scrolls sideways. */
.pdp-dots{position:sticky;top:calc(50vh - 20px);float:right;height:0;margin:0 16px;
  display:flex;flex-direction:column;gap:9px;z-index:2}
/* flex:0 0 5px, not height alone. The column is zero-height so it overlays the
   gallery rather than pushing it, and in a column flex container the height is
   the MAIN size — so a shrinkable item collapses to nothing inside it. */
.pdp-dots button{flex:0 0 5px;width:5px;height:5px;padding:0;border:0;border-radius:50%;
  background:var(--ink);opacity:.22;cursor:pointer;transition:opacity .18s}
.pdp-dots button[aria-current="true"]{opacity:.9}
.pdp-progress{display:none}

/* No overscroll-behavior:contain. The panel is a scroll port, and containing it
   means a wheel over the right-hand column stops dead at the end of the panel
   instead of chaining to the page — so the footer was unreachable unless you
   moved the pointer onto the photography first. Chaining is the default; the
   panel scrolls until it is read, then the page carries on. */
/* The panel starts below the bar rather than under it. The gallery is meant to
   run beneath a transparent header; a column of type is not.

   Scoped to desktop, and it has to be. A media query adds no specificity, so
   this rule (0,3,1) outranks the phone rule that resets this padding to 26px
   (0,1,0), and the guard is true on every product page.
   The panel was getting calc(46px + 56px) = 102px of dead white on a phone,
   where it sits under a full-height image and nowhere near the nav. The offset
   only means anything up here, where the section is pulled up under the bar. */
@media(min-width:901px){
  body.hero-overlay .pdp-panel{padding-top:calc(46px + var(--nav-h))}

  /* The photography is its own scroll port, pinned for one screenful. A wheel
     over the images moves the images; a wheel anywhere else moves the page, on
     down to Complete the Look and the footer. That is the whole ask, and
     overscroll-behavior:contain is what delivers the second half of it — without
     it the gallery hands its momentum to the page at either end and you get both
     at once.

     top:0 and a full 100svh, not top:var(--nav-h). The section is already pulled
     up under the transparent bar and the first shot is meant to run beneath it;
     sticking below the nav would push the photograph down and undo that.

     The overflow goes on .pdp-shots rather than .pdp-media on purpose: site.js
     already listens for scroll on [data-gallery], so the dots keep painting with
     no change. On .pdp-media the listener would be on the wrong element. */
  .pdp-media{position:sticky;top:0;height:calc(100svh - var(--announce-h))}
  .pdp-shots{height:100%;overflow-y:auto;overscroll-behavior:contain;
    scroll-snap-type:y proximity;scrollbar-width:none}
  .pdp-shots::-webkit-scrollbar{display:none}
  .pdp-shot{scroll-snap-align:center}

  /* position:sticky needs a scrolling ancestor to travel against, and .pdp-media
     no longer moves — so the dots are placed, not stuck. */
  .pdp-dots{position:absolute;right:16px;top:50%;transform:translateY(-50%);
    float:none;height:auto;margin:0}
}
/* Symmetric side padding. It was 4vw right against 3.4vw left, which put another
   77px at 1920 and 102px at 2560 between the last hairline and the screen edge,
   on top of the inner cap below. padding-top stays 46px — the
   @media(min-width:901px) overlay rule adds --nav-h to exactly this value. */
.pdp-panel{position:sticky;top:var(--nav-h);align-self:start;padding:46px 3.4vw 60px 3.4vw;
  max-height:calc(100vh - var(--nav-h));overflow:auto}
/* No cap. clamp(340px,30vw,460px) stopped growing at a 1533px viewport while the
   panel track kept widening, so past that the hairlines froze at 460px and the
   dead space to their right grew about a pixel for every two of screen — 239px
   at 1440, 531px at 1920, 861px at 2560. The bands run the full content box now
   and the rules reach the edge.

   What gets capped instead is the content that would read badly at that width:
   prose keeps its measure, and the size run keeps a sane spread rather than
   throwing six labels across 900px. */
.pdp-panel-inner{max-width:none}
/* The size row and the read-only figures share one measure so they do not strand
   themselves across a wide panel. The buy button is deliberately NOT capped: it
   runs the full band, ending exactly where the hairlines end. That is a wide slab
   of solid black at 1920 and it is meant to be — it is the one control on the page
   and it should read as the terminus of the stack, not float short of the rules. */
.pdp-panel .sizes--spread{max-width:540px}
.pdp-stats,.pdp-delta{max-width:540px}
.pdp-panel .pdp-add{max-width:none}
/* One hairline between each group, the same one that sits under a card's name,
   so the panel reads as a stack of bands rather than a single run of everything.
   .opt carries the class too, so the colour and size groups are bands without
   restructuring the loops that emit them. */
/* 9px, not 14. Two adjacent bands each contribute their padding, so 14 put 28px
   plus a hairline between every group — five groups of that is most of the panel.
   The rule is what separates them; the space only has to let it breathe. */
.pdp-band{padding:9px 0;border-bottom:.6px solid var(--hair2)}
.pdp-band:first-child{padding-top:0}
.pdp-band--last{border-bottom:0}
/* The product name in the card's cap line. Still an h1 — the document outline is
   unchanged and search engines read the text, not the size — but it is a label
   here, and the price is what leads. */
/* 11px, not the card's 9. A card is one of four in a row and its name is a label;
   here it is the subject of the page, and the price with it. Everything else on
   the panel stays at 9 so the two of them carry the difference. */
/* 16px under the title, against 7px above it. The eyebrow and the name are one
   thing — a collection and the product in it — so they sit close; the price is a
   separate fact and was reading as a third line of the name at 7px. */
.pdp-title{font:11px var(--mono);letter-spacing:.16em;text-transform:uppercase;
  color:var(--muted);margin:0 0 16px}
.pdp-delta{font:9px var(--mono);letter-spacing:.13em;color:var(--muted);margin-top:4px}
.pdp-delta-sep{opacity:.45;padding:0 1px}
.pdp-delta s{text-decoration:line-through;opacity:.7}
.pdp-stats{margin:0;display:grid;gap:3px}
.pdp-stats>div{display:flex;gap:6px;font:9px var(--mono);letter-spacing:.13em;color:var(--muted)}
.pdp-stats dt,.pdp-stats dd{margin:0}
.pdp-stats dt{color:var(--ink);font-variant-numeric:tabular-nums}
.micro{font:7.5px var(--mono);letter-spacing:.09em;color:var(--muted)}

.pdp-panel .market-price{font:11px var(--mono);letter-spacing:.16em;color:var(--ink);
  font-variant-numeric:tabular-nums}



/* Depth ladder. A row per size: label, a track whose fill is that size's stock
   against the deepest size in the run, and the count. A sold-out size keeps its
   row and shows an empty track, because that is the answer someone came for. */
.opt{margin:0}
.opt>span{display:block;font:7.5px var(--mono);letter-spacing:.09em;color:var(--muted)}
/* Only the size run spreads, echoing the tab row below the panel. The colour row
   keeps its natural gap: with two colourways, space-between throws Black and
   Cream to opposite edges of the band with a void between them. */
.pdp-panel .sizes--spread{justify-content:space-between;gap:6px}
/* On a phone every control gets a 44px touch box, which is far taller than an
   8.5px word — so a border-bottom draws at the foot of the box, 16px adrift of
   the text it is meant to mark. The hit area is worth keeping, so the selected
   rule is a pseudo-element pinned just under the word instead. Resolves the same
   on desktop, where there is no min-height. */
.pdp-panel .sizes a{position:relative;line-height:1;border-bottom:0;padding-bottom:0}
.pdp-panel .sizes a::after{content:"";position:absolute;left:0;right:0;
  top:calc(50% + 8px);height:1px;background:currentColor;opacity:0;transition:opacity .15s}
.pdp-panel .sizes a.active::after,.pdp-panel .sizes a:hover::after{opacity:1}
.pdp-panel .sizes a.unavailable::after{opacity:0}
.pdp-panel .sizes a.unavailable.active::after{opacity:.75}
.sizes{display:flex;flex-wrap:wrap;gap:9px;margin:7px 0 0}
.sizes a,.sizes button{font:8.5px var(--mono);border-bottom:.6px solid transparent}
.sizes .active,.sizes a:hover{border-color:var(--ink)}
.sizes .disabled{opacity:.3;text-decoration:line-through}
/* .disabled is a combination that does not exist; .unavailable is one that
   does but is out of stock. The second stays clickable — selecting it is how
   you find out, and rendering it inert meant that with nothing in stock the
   whole size row did nothing and no size could show as chosen. */
.sizes .unavailable{opacity:.42;text-decoration:line-through}
.sizes .unavailable.active{opacity:.75;border-color:var(--ink)}
/* Swapping a variant is a fetch; this is the only thing that says so. */
/* Swapping a variant is a fetch; this is the only thing that says so. The
   gallery carries it too now — it was the half of the page being rebuilt while
   only the other half admitted to being busy. */
.pdp[data-loading] .pdp-panel-inner{opacity:.55;transition:opacity .12s}
.pdp[data-loading] .pdp-media{opacity:.62}
.pdp-media{transition:opacity .2s ease}

/* No margin-top: .buy-row is itself the last band, so 22px stacked on its own
   9px of band padding. */
.buy-row{display:flex;flex-wrap:wrap;align-items:center;gap:12px}
.buy-row form{display:flex;align-items:center;gap:12px;flex:1 1 100%}
/* The button carries the price, as the reference does. Solid black so it is
   unmistakably the primary control rather than a third underlined word. */
/* Solid, and the only solid thing on the page. It was a ruled control while it
   sat beside a quantity box and needed to stop shouting; with the box gone it is
   the single action on the panel, and a panel made entirely of hairlines gives it
   nothing to be distinct against. Colour is what makes it findable. */
.pdp-add{flex:1 1 auto;display:flex;align-items:center;justify-content:space-between;gap:16px;
  min-height:46px;padding:0 16px;border:0;background:var(--ink);color:var(--paper);
  font:9.5px var(--mono);letter-spacing:.14em;cursor:pointer;transition:opacity .15s}
.pdp-add:hover{opacity:.86}
.pdp-add[disabled]{opacity:.32;cursor:not-allowed}
.pdp-add-price{letter-spacing:.06em;font-variant-numeric:tabular-nums}
.pdp-watch{font:8.5px var(--mono);letter-spacing:.1em;border-bottom:.7px solid var(--ink);padding-bottom:3px}
.pdp-watch.watching{opacity:.55}

/* Tabs. Without JavaScript the tablist stays hidden and all three panels render
   stacked under their headings, so no content sits behind a dead control. */
.pdp-tabs{margin-top:26px;border-top:.6px solid var(--hair)}
/* One continuous rule under the whole row, with a mark that slides along it.
   An underline that jumps from label to label reads as three separate things;
   a rule with a travelling mark reads as one control with a position in it.
   Type is small and widely tracked so three labels spread across the column
   instead of crowding the middle — the width between them is the design. */
.pdp-tablist{display:none;gap:14px;padding-top:16px;padding-bottom:11px;flex-wrap:nowrap;
  justify-content:space-between;position:relative;border-bottom:.6px solid var(--hair2)}
.pdp-tabs[data-ready] .pdp-tablist{display:flex}
/* Tab and panel are deliberately DIFFERENT type now. Sharing one spec — same
   family, size and tracking, separated only by colour — made the whole block
   read as one undifferentiated grey field: a label and a paragraph in 9.5px
   tracked mono are hard to tell apart, and the row and its contents merged.

   So: the tab stays a label, in micro mono with the theme's tracking, and the
   prose under it goes back to the serif at a size you can actually read a
   sentence in. .details-list stays mono on purpose — it is upcased spec data,
   not prose, and it belongs to the label register. The tabs are spread across
   the column rather than bunched at its left edge. */
/* nowrap and a gap that gives before the labels do: three tabs are a row of
   three, and a label breaking onto a second line pushed the whole tablist to two
   rows of ragged height. Less of a risk now SHIPPING is the longest of them, but
   the constraint is about the row, not about any one word. */
.pdp-tablist button{font:8.5px var(--mono);letter-spacing:.15em;text-transform:uppercase;
  color:var(--muted);padding:6px 0;
  margin:-6px 0;background:none;border:0;cursor:pointer;white-space:nowrap;
  transition:color .2s ease}
.pdp-tabline{position:absolute;left:0;bottom:-.6px;height:1.2px;width:0;background:var(--ink);
  pointer-events:none;transform:translateX(0);
  transition:transform .32s cubic-bezier(.4,0,.2,1),width .32s cubic-bezier(.4,0,.2,1)}
@media(prefers-reduced-motion:reduce){.pdp-tabline{transition:none}}
.pdp-tablist button[aria-selected="true"]{color:var(--ink)}
.pdp-tabpanel{padding-top:18px}
.pdp-tabhead{font:9.5px var(--mono);letter-spacing:.09em;text-transform:uppercase;
  color:var(--ink);margin:18px 0 0;font-weight:400}
.pdp-tabs[data-ready] .pdp-tabhead{display:none}
.pdp-tabpanel .rte{font:15px/1.6 var(--serif);color:var(--muted);max-width:46ch}

/* ------------------------------------------------- SYSTEM DIAGRAM ------- */
/* A spine with a node for every anchor. The rule runs the height of the list
   and each node sits on it with a short arm out to its label, so the piece's
   whole relationship to the system reads down one line: where it takes a
   module, on which axis, how many, and which modules.

   Nodes carry the axis in their shape — a wide bar for a horizontal anchor, a
   tall one for a vertical — so the drawing says something the label repeats
   rather than the other way round. */
.sysd{max-width:540px}
.sysd-head{display:flex;align-items:center;gap:9px;font:9px var(--mono);letter-spacing:.15em;
  color:var(--muted);padding-bottom:11px;border-bottom:.6px solid var(--hair2)}
.sysd-head span:first-child{color:var(--ink)}
.sysd-rows{list-style:none;margin:0;padding:0 0 0 22px;position:relative}
/* The spine. Inset so the nodes straddle it. */
.sysd-rows::before{content:"";position:absolute;left:0;top:14px;bottom:14px;
  border-left:.7px solid var(--hair)}
.sysd-row{position:relative;padding:14px 0;border-bottom:.6px solid var(--hair2)}
.sysd-row:last-child{border-bottom:0}
/* The node, and the arm that ties it to its label. */
.sysd-mark{position:absolute;left:-22px;top:19px;width:7px;height:7px;background:var(--ink)}
.sysd-mark--h{width:11px;height:3px;top:21px;left:-24px}
.sysd-mark--v{width:3px;height:11px;top:17px;left:-20px}
.sysd-mark::after{content:"";position:absolute;left:100%;top:50%;width:12px;
  border-top:.7px solid var(--hair)}
.sysd-body{display:flex;flex-direction:column;gap:3px}
.sysd-at{font:11px var(--mono);letter-spacing:.14em;color:var(--ink)}
.sysd-at em{font-style:normal;color:var(--muted);letter-spacing:.08em}
.sysd-axis{font:8.5px var(--mono);letter-spacing:.14em;color:var(--ghost)}
.sysd-takes{font:9px/1.6 var(--mono);letter-spacing:.09em;color:var(--muted)}
.sysd-foot{display:flex;gap:12px;margin-top:14px;padding-top:12px;
  border-top:.6px solid var(--hair2)}
.sysd-label{flex:0 0 auto;font:8.5px var(--mono);letter-spacing:.15em;color:var(--ink)}
.sysd-foot .sysd-takes{flex:1}
/* The axis glyph beside MODULE in the header. */
.sysd-axis-mark{display:inline-block;background:var(--ink)}
.sysd-axis-mark--v{width:3px;height:11px}
.sysd-axis-mark--h{width:11px;height:3px}

/* ------------------------------------------------- SHIPPING & RETURNS -- */
/* The Shipping tab is a rate table and a returns table, not prose. Both are
   ruled rows in the panel's own register: label left, figure right, a hairline
   between, exactly as .pdp-band sets the panel above.

   .ship-group is the grid and .ship-row is display:contents, so every row's
   cells share ONE set of column tracks and the figures line up down the panel.
   Three separate row grids would each size their own `auto` columns, and
   "Sep 8 – Sep 10" against "Sep 4 – Sep 7" would step the price column sideways
   between the two rows.

   That costs the border: a display:contents element paints nothing, so the rule
   is drawn per cell and the cells join it up. Hence no column-gap either — a gap
   would break the rule into segments. The 18px between columns is padding on the
   cells, which the border runs through. */
.ship-country,.ship-group,.ship-foot,.ship-note,.ship-links{max-width:540px}
.ship-country{margin-bottom:20px}
.ship-country-label{display:block;font:7.5px var(--mono);letter-spacing:.09em;
  color:var(--muted);margin-bottom:7px}
.ship-country-static{margin:0;font:9px var(--mono);letter-spacing:.13em;color:var(--ink);
  padding-bottom:7px;border-bottom:.6px solid var(--hair)}
/* Ranged left with the rest of the panel. The references centre this row; the
   panel was deliberately ranged left in e2f677a and a centred control would be
   the only centred thing on it. */
.ship-country-form{position:relative;display:block}
.ship-country select{width:100%;border:0;border-bottom:.6px solid var(--hair);
  background:transparent;font:9px var(--mono);letter-spacing:.13em;color:var(--ink);
  padding:0 20px 7px 0;cursor:pointer;appearance:none}
.ship-country select:focus-visible{outline:1px solid var(--ink);outline-offset:2px}
/* The footer's selects carry appearance:none and no indicator at all, which is
   survivable on a two-word readout and not on a country list. Drawn rather than
   an image so it takes currentColor and needs no asset. */
.ship-country-form::after{content:"";position:absolute;right:2px;bottom:11px;
  width:5px;height:5px;border-right:.9px solid var(--muted);border-bottom:.9px solid var(--muted);
  transform:rotate(45deg);pointer-events:none}

.ship-group{display:grid;grid-template-columns:1fr auto;gap:0}
.ship-group--when{grid-template-columns:1fr auto auto}
/* The header row. Same tracks and same rule as any other row — what marks it
   as a header is the register, not weight: bold or ink here would make the
   column names the loudest line in a table whose content is the point. The rule
   under it is --hair rather than --hair2, so the head is divided from the body
   a shade more firmly than the rows are from each other. */
.ship-row--head>*{border-bottom-color:var(--hair);padding-top:0;padding-bottom:6px;
  font:7.5px var(--mono);letter-spacing:.09em;color:var(--ghost);text-transform:uppercase}
/* Column names sit over their columns, so they range the way the figures do. */
.ship-row--head>*+*{text-align:right}
/* The footnote sits between the two tables, so it breaks the adjacency the
   first of these relies on — without the second selector the returns table
   opens hard against "Free shipping over $150" and reads as part of it. */
.ship-group+.ship-group,.ship-foot+.ship-group{margin-top:26px}
.ship-row{display:contents}
.ship-row>*{padding:11px 0;border-bottom:.6px solid var(--hair2);align-self:baseline}
.ship-row>*+*{padding-left:18px}
.ship-row:last-child>*{border-bottom:0}
.ship-name{font:9px var(--mono);letter-spacing:.13em;color:var(--muted)}
.ship-price{font:9px var(--mono);letter-spacing:.13em;color:var(--ink);
  font-variant-numeric:tabular-nums;white-space:nowrap;text-align:right}
.ship-foot{font:9px var(--mono);letter-spacing:.13em;color:var(--muted);margin:9px 0 0}
.ship-when{font:9px var(--mono);letter-spacing:.13em;color:var(--muted);
  font-variant-numeric:tabular-nums;white-space:nowrap;text-align:right}
.ship-note{font:11.5px/1.55 var(--serif);color:var(--muted);max-width:40ch;margin-top:16px}
.ship-note p{margin:0 0 9px}
.ship-note p:last-child{margin-bottom:0}
.ship-note a{color:var(--ink);border-bottom:.6px solid var(--hair)}
.ship-links{font:9px var(--mono);letter-spacing:.13em;color:var(--muted);margin:14px 0 0}
/* The rule is a pseudo-element pinned just under the word, not a border, for
   the same reason .sizes a carries one: these links are in the 44px touch list
   below, and a border-bottom draws at the foot of that box — sixteen pixels
   adrift of the text it belongs to. Resolves identically on desktop, where
   there is no min-height. */
.ship-links a{position:relative;line-height:1;color:var(--ink)}
.ship-links a::after{content:"";position:absolute;left:0;right:0;top:calc(50% + 8px);
  height:.6px;background:var(--hair);transition:background .15s}
.ship-links a:hover::after{background:var(--ink)}
.ship-sep{opacity:.45;padding:0 4px}

/* The collection this product belongs to, above its name. */
.pdp-home{display:block;font:9px var(--mono);letter-spacing:.18em;color:var(--ghost);
  margin:0 0 7px;border-bottom:.6px solid transparent}
.pdp-home:hover{color:var(--muted);border-bottom-color:var(--hair2)}
.pdp-tabpanel .rte p{margin:0 0 13px}
.pdp-tabpanel .rte a{color:var(--ink);border-bottom:.6px solid var(--hair)}
.details-list{font:9.5px/1.85 var(--mono);letter-spacing:.09em;color:var(--muted);margin-top:18px}
.details-list a{border-bottom:.6px solid rgba(var(--ink-rgb),.3)}
.details-list+.details-list{margin-top:24px}


/* ------------------------------------------------------- CAROUSEL ROW -- */
/* A collection as a row rather than a grid. Card width is derived from how many
   the merchant wants in view, so the gutter never has to be guessed. */
.carousel{--pad-top:44px;--pad-bottom:44px;
  padding:var(--space-top,var(--pad-top)) 3.4vw var(--space-bottom,var(--pad-bottom))}
/* .market-intro carries the layout, the hairline and the height; this only has
   to set the heading, which .market-intro styles as an h1 and this section
   renders as an h2 — the page already has an h1 above it. */
.carousel-head{margin-bottom:0}
.carousel-head h2{font:400 23px var(--sans);margin:0}
.carousel-head .carousel-arrows{align-self:start}
.carousel-arrows{display:flex;gap:8px;flex:0 0 auto}
.carousel-arrows button{width:30px;height:30px;padding:0;border:.6px solid var(--hair);background:transparent;
  font:11px var(--mono);color:var(--ink);cursor:pointer;transition:border-color .15s}
.carousel-arrows button:hover{border-color:var(--ink)}
.carousel-arrows button[disabled]{opacity:.3;cursor:default}
/* A grid that scrolls, not a flex strip that scrolls.

   The card's real layout is three shared rows — photograph, name, body — and it
   is bound to .feed>.card and .grid>.card because subgrid only resolves when the
   card is a direct grid item of a grid. In a flex strip the property computes to
   none and the span is dropped, so every card in the carousel sized its own name
   track: one row of cards, three different heights of rule, three different
   price lines. The markup was already identical to the feed's — it renders
   through the same snippet — and only this was different.

   grid-auto-flow:column with grid-auto-columns says the same thing the width
   calc said, in the one display mode where the card can be what it is. Snap,
   overflow and the hidden scrollbar are written out here rather than borrowed
   from .scroller, because .scroller's display:flex is the whole problem. */
.carousel-row{display:grid;grid-auto-flow:column;
  grid-auto-columns:calc((100% - (var(--per-view,2) - 1) * 22px) / var(--per-view,2));
  grid-template-rows:auto auto auto;column-gap:22px;padding:26px 0 4px;
  overflow-x:auto;overflow-y:hidden;scroll-snap-type:x mandatory;
  scrollbar-width:none;-webkit-overflow-scrolling:touch}
.carousel-row::-webkit-scrollbar{display:none}
.carousel-row>.card{scroll-snap-align:start}
.carousel-row:focus-visible{outline:1px solid var(--ink);outline-offset:4px}

/* --------------------------------------------------- COMPLETE THE LOOK -- */
/* A horizontal snap row — the same primitive the phone gallery uses. */
.scroller{display:flex;overflow-x:auto;scroll-snap-type:x mandatory;
  scrollbar-width:none;-webkit-overflow-scrolling:touch}
.scroller::-webkit-scrollbar{display:none}
.scroller>*{scroll-snap-align:start;flex:0 0 auto}
.suggest{display:block;padding:70px 3.4vw 20px}
.suggest:empty{display:none}
.suggest-head h2{font:400 26px var(--sans);letter-spacing:-.03em;margin:0 0 18px}
.suggest-row{gap:22px}
.suggest-cell{width:clamp(190px,22vw,260px)}

/* ------------------------------------------------------ HOUSE PROMISES -- */
/* Three standing claims closing the product page. Centred columns on a hairline
   band, in the storefront's own materials — no cards, no rules between them and
   no glyphs. There was one 20x20 icon per promise; drawn at that size in a
   stroke this light they read as clip art rather than as part of the drawing,
   and they cost 35px of height each for the privilege. A headline and one short
   line is the whole thing. */
.promises{display:grid;grid-template-columns:repeat(3,1fr);gap:5vw;
  padding:54px 3.4vw 58px;margin-top:44px;border-top:.6px solid var(--hair2)}
.promise{text-align:center;max-width:36ch;margin-inline:auto}
.promise h2{font:400 clamp(17px,1.5vw,22px)/1.25 var(--sans);letter-spacing:-.025em;margin:0}
.promise p{margin:11px 0 0;font:9.5px/1.85 var(--mono);letter-spacing:.09em;color:var(--muted)}

/* --------------------------------------------------- SUPPORT PAGES ------ */
/* Contact, Track Order, Returns. Set in the storefront's own materials: hairline
   rules, mono micro-labels, no boxes and no cards. A bordered input would be the
   only one on the site. */

/* The one solid action a support page gets. Same shape as the buy button,
   because a shopper who has come here to do something should recognise it. */
.page-action{display:inline-flex;align-items:center;gap:14px;min-height:46px;padding:0 22px;
  background:var(--ink);color:var(--paper);font:9.5px var(--mono);letter-spacing:.14em;
  transition:opacity .15s}
.page-action:hover{opacity:.86}
.page-action--quiet{background:transparent;color:var(--ink);padding:0;min-height:0;
  border-bottom:.7px solid var(--ink);letter-spacing:.1em;margin-top:26px}

/* Form beside the ways to reach us, not above them: someone who wants an email
   address should not have to scroll past a form to find one. */
.contact-grid{display:grid;grid-template-columns:minmax(0,1fr) 210px;gap:6vw;
  margin-top:44px;align-items:start}
.contact-field{display:flex;flex-direction:column;gap:8px;margin-bottom:26px}
.contact-field label span{color:var(--ghost);letter-spacing:.09em}
.contact-field input,.contact-field textarea{
  width:100%;border:0;border-bottom:.7px solid rgba(var(--ink-rgb),.52);outline:0;background:transparent;
  color:var(--ink);font:13px/1.5 var(--mono);letter-spacing:.01em;padding:7px 0;resize:vertical}
.contact-field input:focus,.contact-field textarea:focus{border-bottom-color:var(--ink)}
.contact-field textarea{min-height:120px}
.contact-send{margin-top:6px;min-height:46px;padding:0 26px;border:0;background:var(--ink);
  color:var(--paper);font:9.5px var(--mono);letter-spacing:.14em;cursor:pointer;transition:opacity .15s}
.contact-send:hover{opacity:.86}
.contact-note{font:9.5px/1.8 var(--mono);letter-spacing:.08em;margin:0 0 22px;
  border-left:2px solid var(--hair);padding-left:14px}
.contact-note--ok{border-left-color:var(--ink);color:var(--ink)}
.contact-note--error{border-left-color:var(--alert);color:var(--alert)}
.contact-aside{display:flex;flex-direction:column;gap:22px;padding-top:4px}
.contact-line{display:flex;flex-direction:column;gap:5px;font:11px var(--mono);letter-spacing:.01em}
.contact-line a{border-bottom:.6px solid var(--hair)}
.contact-line a:hover{border-bottom-color:var(--ink)}

/* One order per row, hairline-separated. The state reads first because that is
   the question the page exists to answer. */
.track-list{display:flex;flex-direction:column;margin-top:40px;border-top:.6px solid var(--hair)}
.track-order{padding:22px 0;border-bottom:.6px solid var(--hair2)}
.track-head{display:flex;align-items:baseline;justify-content:space-between;gap:16px}
.track-name{font:400 17px var(--sans);letter-spacing:-.02em;border-bottom:.7px solid transparent}
.track-name:hover{border-bottom-color:var(--ink)}
.track-state{display:flex;align-items:center;gap:14px;margin-top:11px;flex-wrap:wrap}
.track-badge{font:9px var(--mono);letter-spacing:.11em;text-transform:uppercase;
  border:.6px solid var(--hair);padding:5px 10px}
.track-shipments{display:flex;flex-direction:column;gap:9px;margin-top:16px}
.track-shipment{display:flex;align-items:baseline;gap:14px;flex-wrap:wrap}
.track-link{font:11px var(--mono);letter-spacing:.01em;border-bottom:.6px solid var(--hair)}
.track-link:hover{border-bottom-color:var(--ink)}
.track-link--none{color:var(--muted);border-bottom-color:transparent}
.track-pending{margin:16px 0 0;font:9.5px/1.8 var(--mono);letter-spacing:.08em;color:var(--muted);max-width:60ch}
.track-signin{margin-top:40px;display:flex;flex-direction:column;align-items:flex-start;gap:26px;
  border-top:.6px solid var(--hair);padding-top:34px}
.track-signin .rte{max-width:58ch;margin:0}
.track-help{color:var(--muted);margin:0}
.track-help a{border-bottom:.6px solid var(--hair)}

@media(max-width:760px){
  .contact-grid{grid-template-columns:1fr;gap:44px}
  .contact-aside{border-top:.6px solid var(--hair2);padding-top:28px}
  .track-head{flex-direction:column;gap:5px}
}

/* -------------------------------------------------------- PAIR PRODUCTS -- */
.compose{--pad-top:52px;--pad-bottom:56px;
  padding:var(--space-top,var(--pad-top)) 4.5vw var(--space-bottom,var(--pad-bottom))}
.compose h1{font:400 clamp(54px,7vw,104px)/.84 var(--sans);letter-spacing:-.06em;margin:14px 0 26px}
.compose>p{font:22px/1.38 var(--serif);max-width:630px;color:var(--ink-2)}
.compose-grid{margin-top:62px;display:grid;grid-template-columns:1fr 1fr;gap:7vw}
.compose-column>span{font:8px var(--mono);color:var(--muted)}
.compose-options{margin-top:20px;display:flex;flex-wrap:wrap;gap:20px}
.compose-option{width:112px;text-align:left}
/* height:auto is load-bearing. Without it, image_tag's height attribute is a
   definite height, aspect-ratio is ignored, and this frame takes the source
   file's own proportions — the same fault the bag had. */
.compose-option img{width:112px;height:auto;aspect-ratio:var(--tile);object-fit:contain;
  background:var(--shot-bg,#e6e1d9);filter:saturate(.8)}
.compose-option small{display:block;margin-top:7px;font:7.5px/1.4 var(--mono)}
.compose-option.active img{outline:.8px solid var(--ink);outline-offset:5px}
.compose-summary{margin-top:68px;display:flex;justify-content:space-between;align-items:end;border-top:.7px solid var(--hair);padding-top:15px;font:8px var(--mono)}
.compose-summary strong{font:400 26px var(--sans);display:block;margin-top:5px}
.compose-summary button{border-bottom:.7px solid var(--ink);padding-bottom:3px}

/* --------------------------------------------------------------- BAG ---- */
.drawer{position:fixed;z-index:100;right:0;top:0;bottom:0;width:min(430px,94vw);background:var(--paper);transform:translateX(101%);transition:.25s ease;padding:22px 26px;display:flex;flex-direction:column}
.drawer.open{transform:none}
.drawer-head,.drawer-foot{display:flex;justify-content:space-between;align-items:center;font:8.5px var(--mono)}
/* -------------------------------------------------------- FREE SHIPPING -- */
/* How far the bag is from the free rate. Pinned above the list, because the
   list scrolls and a target you scroll past is not a target.

   The bar is the watchlist's — .pbar / .pfill / .pdot, with the dot breathing at
   the fill's edge — so the store draws a quantity against a target exactly one
   way. Sized against the line's own type, not the drawer's 8.5px chrome: it is
   an argument for adding something to the bag, and it has to be read. */
.drawer-ship{padding:18px 0 0;display:flex;flex-direction:column;gap:9px}
.drawer-ship-line{font:11.5px var(--mono);letter-spacing:.11em;color:var(--muted)}
.drawer-ship-line b{font-weight:400;color:var(--ink);font-variant-numeric:tabular-nums}
/* Met: the sentence is the reward, so it takes ink and stops being a shortfall. */
.drawer-ship--met .drawer-ship-line{color:var(--ink);letter-spacing:.14em}
.drawer-ship--lg{padding:0 0 30px;max-width:820px}
.drawer-ship--lg .drawer-ship-line{font-size:12.5px}

.drawer-items{flex:1;overflow:auto;padding:26px 0}
.drawer-empty{font:18px Georgia,serif;color:var(--ink-3)}
.drawer-foot{border-top:.6px solid var(--hair);padding-top:16px;align-items:baseline}
/* The total was a bare 22px figure with nothing saying what it counted. */
.drawer-total{display:flex;flex-direction:column;gap:4px}
.drawer-total small{font:7.5px var(--mono);letter-spacing:.12em;color:var(--muted)}
.drawer-total b{font:400 22px var(--sans);letter-spacing:-.02em;font-variant-numeric:tabular-nums}
.drawer-foot button{border-bottom:.7px solid var(--ink);padding-bottom:3px}

/* ------------------------------------------------------------- BAG LINE -- */
/* One line of the bag, in the pullout and on /cart. The archive's row: a rule
   under each line, the name and the price on one baseline, and everything else
   on the two lines beneath.

   THE CHIP IS SQUARE — the only frame on the site that is not 4:5, and the
   reason is the photography rather than the grid. At 56px this is a chip, not a
   photograph; a third of the catalogue is shot square and a sixth at 8:9, and
   in a 4:5 chip both of those get a band above and below the garment. That
   banding is what still read as broken after the stretching was fixed. Square
   bands nothing top or bottom, and the widest shots lose a few pixels at the
   sides instead, where a band reads as margin. Still contain: nothing cropped.

   The FRAME carries the ratio and the image fills it, exactly as .card-media,
   .bleed-frame, .archive-images and .ps-thumb do. NOT width plus aspect-ratio
   on the <img>: image_tag writes a height ATTRIBUTE from the source file, and a
   box with a definite width and a definite height ignores aspect-ratio
   altogether — the frame then takes the photograph's own height, which measured
   72x200 instead of 72x90 and is what put a beige band above and below every
   garment in the bag. An attribute cannot defeat a frame. */
/* Two knobs, and everything in the line is derived from them.

   The bag shipped at the card's own 9px micro-mono, which is right on a card
   sitting under a photograph you are already looking at and wrong in a panel you
   opened to check what you are buying — the client could not read it. Up 30%.

   Written as variables rather than as a dozen edited numbers because this is the
   second time the size has moved: the next adjustment is these two lines. */
.bag-line{
  --bag-tile:72px;      /* was 56 */
  --bag-type:11.5px;    /* was 9 */
  display:grid;grid-template-columns:var(--bag-tile) minmax(0,1fr) auto;
  column-gap:18px;row-gap:9px;padding:20px 0;border-bottom:.6px solid var(--hair2)}
.bag-line:first-child{padding-top:0}
.bag-tile{grid-row:span 3;display:block;width:var(--bag-tile);aspect-ratio:1;
  background:var(--shot-bg,var(--tile-bg));overflow:hidden}
.bag-tile img{width:100%;height:100%;object-fit:contain;display:block}
.bag-name{grid-column:2;align-self:start;font:var(--bag-type)/1.5 var(--mono);letter-spacing:.14em;
  text-transform:uppercase;color:var(--ink)}
.bag-price{grid-column:3;align-self:start;text-align:right;font:var(--bag-type) var(--mono);
  letter-spacing:.12em;color:var(--ink);font-variant-numeric:tabular-nums;white-space:nowrap}
.bag-variant{grid-column:2/4;font:var(--bag-type) var(--mono);letter-spacing:.11em;color:var(--muted)}
.bag-qty{grid-column:2;display:flex;gap:12px;align-items:center;
  font:var(--bag-type) var(--mono);letter-spacing:.09em;color:var(--muted)}
.bag-qty b{font-weight:400;color:var(--ink);font-variant-numeric:tabular-nums}
.bag-qty a{color:var(--ink);border-bottom:.6px solid var(--hair);padding:0 2px}
.bag-qty a:hover{border-bottom-color:var(--ink)}
/* At a quantity of one there is nothing to decrement to except deletion, and
   REMOVE is the control that says so. */
.bag-qty .disabled{opacity:.28}
.bag-remove{grid-column:3;justify-self:end;align-self:center;font:var(--bag-type) var(--mono);
  letter-spacing:.13em;color:var(--muted);border-bottom:.7px solid var(--hair);padding-bottom:3px}
.bag-remove:hover{color:var(--ink);border-bottom-color:var(--ink)}

/* The dropdown IS the colourway line — it used to sit under a text line saying
   the same thing. Sold-out sizes stay listed and disabled rather than being
   dropped, so the shopper can see the size exists and is gone. */
.bag-size{position:relative;display:inline-block}
.bag-size select{border:0;border-bottom:.6px solid var(--hair);background:transparent;
  font:var(--bag-type) var(--mono);letter-spacing:.11em;color:var(--ink);
  padding:3px 19px 4px 0;cursor:pointer;appearance:none}
.bag-size::after{content:"";position:absolute;right:2px;top:50%;margin-top:-2px;
  border:4px solid transparent;border-top-color:var(--ink);pointer-events:none}
.bag-size select:hover{border-bottom-color:var(--ink)}
.bag-size select:disabled{opacity:.4;cursor:progress}
.bag-size select:focus-visible{outline:1px solid var(--ink);outline-offset:2px}

/* /cart is 1440 wide and the line is not. Capped to a reading measure, with a
   chip big enough for the room. */
.bag-lines{max-width:820px;margin-top:26px}
.bag-line--lg{--bag-tile:94px;--bag-type:12.5px;column-gap:22px;row-gap:10px;padding:26px 0}
.scrim{position:fixed;inset:0;z-index:90;background:var(--scrim);opacity:0;pointer-events:none;transition:.2s}
.scrim.open{opacity:1;pointer-events:auto}
.toast{position:fixed;z-index:120;left:50%;bottom:24px;transform:translate(-50%,10px);opacity:0;background:var(--ink);color:var(--paper);padding:9px 12px;font:7px var(--mono);transition:.2s}
.toast.show{opacity:1;transform:translate(-50%,0)}

/* ------------------------------------------------------------- FOOTER --- */
.foot{border-top:.7px solid var(--hair);margin-top:80px;padding:44px 3.4vw 52px;font-family:var(--mono)}
.foot-top{display:grid;grid-template-columns:1.1fr 1fr 1fr 1.4fr;gap:40px 32px;align-items:start}
.foot-cols{display:contents}
/* The mark is one black file with no light version, so it is inverted rather
   than swapped. --mark-base is none in light and invert(1) in dark; without it
   these four are a black glyph on a dark ground. The nav's mark is deliberately
   NOT on this token — it answers to the photograph under a transparent bar, and
   a pale shot wants the black mark whichever theme the site is in. */
.foot-brand img{width:34px;height:34px;object-fit:contain;opacity:.9;filter:var(--mark-base)}
/* A row of marks under the newsletter field, aligned to its left edge. 18px is
   the site's glyph size — the header's search, watchlist and bag all run at it. */
.foot-social{display:flex;gap:16px;margin-top:20px}
.foot-social a{display:inline-flex;align-items:center;justify-content:center;
  width:26px;height:26px;color:var(--muted);transition:color .15s}
.foot-social a:hover{color:var(--ink)}
.foot-social svg{width:18px;height:18px;display:block}

.foot h2{font:8px var(--mono);letter-spacing:.12em;color:var(--muted);font-weight:400;margin:0 0 15px}
.foot-col ul{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:9px}
.foot-col a{font:10.5px var(--mono);letter-spacing:.01em;color:var(--ink-2)}
.foot-col a:hover{color:var(--ink)}

.foot-news{max-width:340px}
.foot-field{display:flex;align-items:center;gap:12px;border-bottom:.7px solid rgba(var(--ink-rgb),.52)}
.foot-field input{flex:1;min-width:0;border:0;outline:0;background:transparent;padding:7px 0;font:10.5px var(--mono)}
.foot-field input::placeholder{color:var(--ghost)}
.foot-field button{font:8px var(--mono);letter-spacing:.1em;white-space:nowrap}
.foot-note{font:8px var(--mono);letter-spacing:.08em;color:var(--muted);margin:10px 0 0}
.foot-error{color:var(--alert)}

.foot-bottom{margin-top:46px;padding-top:16px;border-top:.6px solid var(--hair2);display:flex;justify-content:space-between;align-items:flex-end;gap:28px;flex-wrap:wrap}
.foot-legal{display:flex;flex-direction:column;gap:8px;font:7.5px var(--mono);letter-spacing:.06em;color:var(--muted)}
.foot-policies{display:flex;gap:14px;flex-wrap:wrap}
.foot-policies a{color:var(--ink-2)}
.foot-policies a:hover{color:var(--ink)}
.foot-meta{display:flex;align-items:center;gap:24px;flex-wrap:wrap}
/* Country and language. The single-market case is a readout rather than a
   one-option dropdown, so the row reads the same either way. */
.locale{display:flex;gap:22px;flex-wrap:wrap;align-items:center}
.locale--stacked{flex-direction:column;align-items:flex-start;gap:12px}
.locale-form{display:flex;align-items:center;gap:6px}
.locale-key{font:8px var(--mono);letter-spacing:.06em;color:var(--muted)}
.locale--stacked .locale-key{font-size:10.5px;letter-spacing:.1em}
.locale-static{margin:0;font:8px var(--mono);letter-spacing:.06em;color:var(--ink);display:flex;gap:6px;align-items:center}
.locale--stacked .locale-static,.locale--stacked .locale select{font-size:10.5px;letter-spacing:.1em}
.locale select{border:0;border-bottom:.6px solid var(--hair);background:transparent;font:8px var(--mono);
  letter-spacing:.06em;padding:5px 18px 5px 0;color:var(--ink);cursor:pointer;appearance:none}
.locale select:focus-visible{outline:1px solid var(--ink);outline-offset:2px}
.foot-locale select{border:0;border-bottom:.6px solid var(--hair);background:transparent;font:8px var(--mono);letter-spacing:.06em;padding:5px 18px 5px 0;color:var(--ink);cursor:pointer;appearance:none}
.foot-pay{display:flex;gap:7px;align-items:center;flex-wrap:wrap}
.foot-pay-icon,.foot-pay svg{height:19px;width:auto;display:block}

@media(max-width:900px){
  /* Reordered for a phone: newsletter, then both menus level with each other,
     then the copyright, and the mark last and centred. display:contents lifts
     the desktop wrapper out of the way so one flex order controls the lot. */
  .foot{padding:38px 20px 40px;margin-top:56px;display:flex;flex-direction:column}
  .foot-top{display:contents}
  .foot-news{order:1;max-width:none}
  .foot-cols{order:2;display:grid;grid-template-columns:1fr 1fr;gap:30px 20px;align-items:start;margin-top:38px}
  /* display:contents like .foot-top, so the two halves of the bottom row can be
     ordered independently and the legal block can fall below the mark. The
     wrapper's own border and padding go with the box, so .foot-meta carries them
     now — it is what leads the group. */
  .foot-bottom{display:contents}
  /* Country over language rather than beside it. Inline, the pair wrapped
     unevenly against the payment icons at phone width; stacked they read as
     two rows of one row. align-items goes to flex-start so the icons sit
     level with the top of the pair instead of centring against it.
     Desktop keeps them inline, where the bottom row has the width for it. */
  .foot-meta{order:3;margin-top:38px;padding-top:16px;border-top:.6px solid var(--hair2);
    align-items:flex-start}
  .foot-meta .locale{flex-direction:column;align-items:flex-start;gap:10px}
  .foot-brand{order:4;margin-top:30px;padding-top:24px;border-top:.6px solid var(--hair2);
    display:flex;flex-direction:column;align-items:center;gap:16px}
  .foot-legal{order:5;margin-top:26px;align-items:flex-start}
}

/* --------------------------------------------------------- PAGINATION --- */
.pagination{display:flex;gap:16px;justify-content:center;padding:52px 0 0;font:8px var(--mono);letter-spacing:.06em}
.pagination a,.pagination span{padding-bottom:3px}
.pagination a:hover,.pagination .current{border-bottom:.7px solid var(--ink)}

/* --------------------------------------------------------- PASSWORD ----- */
/* Pre-launch gate. Same materials as the storefront — hairlines, mono micro
   labels, one oversized sans statement — with no spine, so the page reads as a
   closed shutter rather than a catalogue you cannot open. */
/* The lock. Shopify is supposed to serve this route through layout/password.liquid,
   which renders content_for_layout and nothing else — and that file is correct. It
   is not enough: the site header and footer have shown up on this page anyway, and
   a session that is already past the gate appears to get theme.liquid instead. So
   do not depend on which layout wins. theme.liquid sets
   class="template-{{ request.page_type | handle }}", so body.template-password is
   present under BOTH layouts, which makes it the one hook that always holds.

   Target the Shopify section wrappers by id. {% section %} always emits
   #shopify-section-<name>, a stable contract; the sections' own class names are not
   one — the announcement bar's is .tick-chg and the cart drawer's is .mobile-bar,
   neither of which reads as chrome. spine-nav and topline are the older markup that
   is still live on main. Named selectors, not a :not() sweep: a missed selector
   leaves a stray bar, an over-broad one blanks the page. */
body.template-password #shopify-section-announcement-bar,
body.template-password #shopify-section-header-nav,
body.template-password #shopify-section-footer,
body.template-password #shopify-section-cart-drawer,
body.template-password #shopify-section-spine-nav,
body.template-password #shopify-section-topline,
body.template-password .nav,
body.template-password .foot{display:none!important}
body.template-password .stage{min-height:0}
.gate{min-height:100vh;display:grid;grid-template-rows:1fr auto;padding:0 clamp(22px,5vw,64px)}
/* No rule across the foot and no band. The login sits at the bottom because that
   is where it belongs, not because it is a footer — anything full-width with a
   hairline over it reads as site chrome, which is the whole complaint. */
.gate-foot{display:flex;justify-content:flex-end;align-items:flex-end;gap:24px;
  font:8.5px var(--mono);letter-spacing:.055em;padding:16px 0 30px}
.gate-footnote{margin-right:auto;color:var(--muted)}
.gate-body{display:flex;flex-direction:column;justify-content:center;max-width:760px;padding:8vh 0}
.gate-mark{width:44px;height:44px;object-fit:contain;opacity:.9;margin-bottom:34px;filter:var(--mark-base)}
.gate h1{font:400 clamp(46px,7.4vw,116px)/.86 var(--sans);letter-spacing:-.062em;margin:0;text-wrap:balance}
.gate-lede{font:21px/1.36 var(--serif);color:var(--ink-2);max-width:44ch;margin:26px 0 0}

.gate-count{margin-top:clamp(44px,7vh,80px)}
.gate-count .micro,.gate-signup .micro{margin-bottom:13px}
.count-row{display:flex;align-items:flex-start;gap:clamp(20px,4vw,46px);border-top:.7px solid var(--hair);padding-top:15px}
.count-cell{display:flex;flex-direction:column;gap:7px}
.count-cell strong{font:400 clamp(38px,6vw,68px)/1 var(--sans);letter-spacing:-.05em;font-variant-numeric:tabular-nums}
.count-cell span{font:7.5px var(--mono);letter-spacing:.14em;color:var(--muted)}

.gate-signup{margin-top:clamp(38px,6vh,64px);max-width:420px}
.gate-field{display:flex;align-items:center;gap:10px;margin-top:8px;border-bottom:.7px solid rgba(var(--ink-rgb),.52)}
.gate-field input{flex:1;min-width:0;border:0;outline:0;background:transparent;padding:8px 0;font:11px var(--mono);letter-spacing:.02em}
.gate-field input::placeholder{color:var(--ghost)}
.gate-field button{font:14px var(--sans);line-height:1;padding:0 2px}
.gate-note{font:8px var(--mono);letter-spacing:.1em;margin:10px 0 0;color:var(--muted)}
.gate-error{color:var(--alert)}

.gate-pass{min-width:190px}
.gate-pass .gate-field{margin-top:6px}
.gate-pass .gate-field input{font-size:10px;padding:5px 0}

@media (prefers-color-scheme:dark){
  .gate-error{color:var(--alert)}
}
@media(max-width:640px){
  .gate-foot{flex-direction:column;align-items:flex-start;gap:22px}
  .count-row{flex-wrap:wrap;gap:22px}
  .gate-body{padding:6vh 0}
}

/* --------------------------------------------------------- RTE / PAGES -- */
.rte{font:19px/1.45 var(--serif);color:var(--ink-2)}
.rte p{margin:0 0 22px}
.rte a{border-bottom:.6px solid var(--hair)}
.static-page{padding:42px 4vw 90px;max-width:820px}
.static-head{margin:0 0 34px}
.static-head .micro{margin-bottom:14px}
.static-page h1{font:400 clamp(48px,6.2vw,96px)/.88 var(--sans);letter-spacing:-.055em;margin:0}

/* Where an installed app renders — on a page, and under the buy row on a
   product. Given a hairline above it and room to breathe, so a widget the theme
   has never seen still arrives looking placed rather than pasted. Nothing here
   dictates the app's own type or colour; that is the app's to set. */
.static-blocks,.pdp-apps{display:flex;flex-direction:column}
.static-blocks{gap:34px;margin-top:44px}
.static-app{border-top:.6px solid var(--hair2);padding-top:30px}
.pdp-apps{gap:16px;margin-top:20px}
.pdp-app:empty,.static-app:empty{display:none}

/* ------------------------------------------------------------ RESPONSIVE - */
@media(max-width:1180px){
  :root{--spine:278px}
  .grid,.feed{grid-template-columns:repeat(3,minmax(0,1fr))}
  .post{grid-template-columns:92px minmax(0,1fr) 132px;gap:20px}
  .shot.hero-shot{width:132px;aspect-ratio:var(--tile)}
  .shot.small-shot{width:90px;aspect-ratio:var(--tile)}
}

@media(max-width:900px){
  :root{--spine:0px}
  .home{padding:45px 24px 110px}
  .hero{min-height:720px}
  .hero-copy{padding-top:12vh}
  .hero h1{font-size:72px}
  .hero-img{width:48vw;right:0}
  .home-thread{grid-template-columns:1fr;margin-top:80px}
  .thread-diagram{height:340px}
  .renaissance-strip{grid-template-columns:1fr}
  .grid,.feed{grid-template-columns:repeat(2,minmax(0,1fr));column-gap:16px}
  .facets{gap:0 18px}
  .feed>.card,.grid>.card,.carousel-row>.card{padding-bottom:24px}
  .spine{width:min(326px,90vw);transform:translateX(-102%);transition:.22s;box-shadow:20px 0 50px rgba(0,0,0,.08)}
  .spine.open{transform:none}
  .spine-spec{padding-left:20px;padding-right:20px}
  .index-toggle{display:block;position:fixed;z-index:55;left:14px;top:18px}
  .stage{margin-left:0}
  .topline{padding:0 17px 0 66px}
  .topline>div:first-child{display:none}
  .market,.archive{--pad-top:24px;--pad-bottom:44px;padding-left:20px;padding-right:20px}
  .market-intro{min-height:48px}
  .post{grid-template-columns:84px 1fr;gap:14px}
  .post-quote{grid-column:2;display:flex;gap:18px;align-items:baseline;padding-top:9px}
  .quote-meta{margin:0}
  .post-media{overflow-x:auto;padding-bottom:7px;scrollbar-width:none}
  .post-media::-webkit-scrollbar{display:none}
  .archive-row{grid-template-columns:110px 1fr}
  .archive-images{overflow-x:auto}
  /* One column, and the gallery turns sideways: one image at a time, snapped,
     with a progress rule under it instead of the dot column. */
  .pdp{grid-template-columns:1fr}
  .pdp-shots{flex-direction:row;overflow-x:auto;scroll-snap-type:x mandatory;
    gap:0;scrollbar-width:none;-webkit-overflow-scrolling:touch}
  .pdp-shots::-webkit-scrollbar{display:none}
  /* 9/16 written out, NOT --tile:9/16. Setting the variable here is the obvious
     move — it is exactly how --nav-h:56px is written a few rules up — and it
     would silently reframe every card, thumbnail and drawer tile on the site.

     The frame fills the phone; the shot is still contained inside it and padded
     with its own two edge colours. It is NOT cropped to fit: a 9:16 centre crop
     takes the garment on 107 of the 130 photographs, both sleeves off the black
     tee and off every square flat-lay, which is the same failure as cutting off
     a head. Cover becomes safe only once the re-export gives the shots real side
     room. Bands until then: 14.8% each end on a 4:5 source, 18.4% on 8:9,
     21.9% on a square. */
  .pdp-shot{flex:0 0 100%;width:auto;aspect-ratio:9/16;scroll-snap-align:center}
  .pdp-dots{display:none}
  /* The progress rule belongs under the gallery, not above it. Markup order is
     progress-then-shots so the desktop dots can be sticky, and with the header
     overlaid an unmoved rule sits behind the transparent bar. */
  .pdp-media{display:flex;flex-direction:column}
  .pdp-shots{order:1}
  .pdp-progress{order:2}
  .pdp-progress{display:block;height:1.5px;background:var(--hair2)}
  .pdp-progress span{display:block;height:100%;width:0;background:var(--ink);transition:width .12s linear}
  .pdp-panel{position:static;max-height:none;overflow:visible;padding:26px 22px 40px}
  /* Name, price, then the date on its own line beneath them. Three columns of
     tracked mono do not fit the panel at 375, and the type floor below bumps
     them further. The rule moves to the date with it, so each row is still one
     hairline rather than two. */
  .ship-group--when .ship-row>.ship-name,
  .ship-group--when .ship-row>.ship-price{border-bottom:0;padding-bottom:0}
  .ship-group--when{grid-template-columns:1fr auto}
  .ship-group--when .ship-row>.ship-when{grid-column:1/-1;padding:3px 0 11px}
  .ship-col--when{display:none}
  /* Stacked, because three columns of prose at phone width is three columns of
     two words. */
  .promises{grid-template-columns:1fr;gap:26px;padding:34px 22px 38px;margin-top:30px}
  .pdp-panel-inner{max-width:none}


  .buy-row form{flex-wrap:wrap}

  .suggest{padding:44px 22px 20px}
  /* One card and a glimpse of the next, so the row reads as scrollable without
     an arrow to tell you so. */
  .carousel{--pad-top:36px;--pad-bottom:36px;padding-left:22px;padding-right:22px}
  .carousel-arrows{display:none}
  /* One card and a glimpse of the next. A grid track now, not a width on the
     child — the card has to stay a grid item or it loses the shared rows. */
  .carousel-row{grid-auto-columns:74%}
  .suggest-cell{width:74%}


  /* And on the bag bar, which is the third thing it lands on. That bar only
     exists below 700px, so the lift is scoped to the same width — above it the
     bar is display:none and the launcher has nothing to clear.

     54px of button and a 14px gap. FIRST of the two, and the order is still
     load-bearing: both selectors can match at once — the bag bar is up while the
     drawer is open — they are the same specificity, and source order decides.
     The drawer needs the taller lift, so it goes last and wins. */
  @media(max-width:700px){
    body:has(.mobile-bar:not([hidden])) #ShopifyChat,
    body:has(.mobile-bar:not([hidden])) #shopify-chat,
    body:has(.mobile-bar:not([hidden])) #chat-button-iframe,
    body:has(.mobile-bar:not([hidden])) .shopify-chat-container{
      bottom:calc(68px + env(safe-area-inset-bottom,0px))!important;
    }
  }
  /* The same launcher lands on the bag's foot, where it covered the subtotal in
     the client's screenshots. The drawer is 22px of padding, a ~46px total and
     16px above the rule; 96px clears it with a gap. */
  body:has(.drawer.open) #ShopifyChat,
  body:has(.drawer.open) #shopify-chat,
  body:has(.drawer.open) #chat-button-iframe,
  body:has(.drawer.open) .shopify-chat-container{
    bottom:calc(96px + env(safe-area-inset-bottom,0px))!important;
  }
  .compose{--pad-top:52px;--pad-bottom:56px;padding-left:24px;padding-right:24px}
  .compose-grid{grid-template-columns:1fr;gap:80px}
}

@media(max-width:560px){
  body{font-size:14px}
  .home{padding-top:24px}
  .hero h1{font-size:58px}
  .hero .lede{font-size:20px}
  .hero-img{position:relative;width:70vw;margin:90px 0 0 auto;right:auto;bottom:auto}
  .hero-mark{right:0}
  .topline>div:last-child{font-size:7.2px}
  .market-intro{display:flex;justify-content:space-between}
  .market-intro .count{display:none}
  .post{display:block}
  .post-label{width:190px;margin-bottom:14px}
  .post-media{gap:10px;overflow-x:auto}
  .post-quote{display:flex;margin-top:8px;gap:12px}
  .shot.hero-shot{width:122px;aspect-ratio:var(--tile)}
  .shot.small-shot{width:86px;aspect-ratio:var(--tile)}
  .archive-row{display:block}
  .archive-label{min-height:52px}
  .archive-images{margin-top:10px;overflow-x:auto}
  .product-title h1{font-size:52px}
}

/* ============================================================ MOBILE ====
   Its own layout, not a squeezed desktop. The terminal row does not survive a
   375px viewport: the gutter number collided with the SKU, the image strip
   reserved height for shots that could not fit, and the readout crushed onto
   one line. Below 700px each row restacks into a card — same markup, no second
   template to keep in sync — and the type comes off the floor.
   ===================================================================== */
@media(max-width:700px){
  /* Type floor. 8px monospace is unreadable on a phone.

     The card sits at 9px rather than 10: its block is a column of figures read
     as a group, not prose, and at 10px it was the loudest thing on a collection
     page. 9 is a deliberate step below the floor for those five classes only —
     everything else here stays at 10. */
  body{font-size:15px}
  .micro,.post-sku,.post-label small,.quote-delta,.quote-meta,.post-quote,
  .archive-label small,.ship-country-label,.ship-row--head>*{font-size:10px;line-height:1.55}
  .card-name,.card-delta,.card-stats>div,.card-acts,
  .pdp-delta,.pdp-stats>div,
  .ship-name,.ship-price,.ship-when,.ship-foot,.ship-links{font-size:9px;line-height:1.55}
  .quote-actions button,.quote-actions a,.card-actions button,.card-actions span,
  .quick-sizes button,.quick-sizes span,.card-sizes button,.card-sizes span,
  .sizes a,.sizes button,.product-buy .buy-row button{font-size:11px}
  /* Same floor for the tabs and their copy — 9.5px mono is a label size on a
     desktop and a squint on a phone. */
  /* The lift is for the spec list only. The tab labels are deliberately micro
     and the serif prose is already readable; 11px would shrink one and bloat
     the other. */
  .pdp-tabpanel .details-list{font-size:11px}
  .pdp-tabpanel .rte{font-size:16px;max-width:none}
  .ship-country-static,.ship-country select{font-size:11px}
  .ship-note{font-size:12px;max-width:none}

  /* Every control clears the 44px minimum. .card-actions and .card-sizes are
     kept only because other markup may still emit them; the live card's controls
     are .card-acts, which was missing here — its WATCH and BUY were getting the
     type floor but no touch target at all. */
  .quote-actions button,.quote-actions a,.card-actions button,
  .card-acts a,.card-acts button,
  .quick-sizes button,.card-sizes button,.sizes a,.sizes button,
  .product-buy .buy-row button,.spine-foot button,.spine-foot a,
  .ship-links a{
    min-height:44px;display:inline-flex;align-items:center
  }
  /* A select cannot be inline-flex and keep its own box, so it takes the
     minimum directly. The caret is positioned off the bottom edge, so it moves
     with the taller control rather than needing its own phone rule. */
  .ship-country select{min-height:44px}
  .quick-sizes,.card-sizes,.sizes{gap:6px 14px}

  /* Grid view matches. */
  .grid,.feed{grid-template-columns:repeat(2,minmax(0,1fr));column-gap:14px}
  .feed>.card,.grid>.card,.carousel-row>.card{padding-bottom:30px}

  /* Nav as a full-screen sheet rather than a 326px slice of 9.6px monospace. */
  .spine{width:100vw;box-shadow:none;border-right:0}
  .spine-scroll{padding:14px 24px 40px}
  .tree{font-size:13px;line-height:1.75}
  .tree a,.tree button,.tree span{line-height:1.75}
  .branch-title,.sub-title{font-size:13px}
  .deep{padding-left:18px!important}
  .deeper{padding-left:32px!important}
  .deeper2{padding-left:46px!important}
  .find input{font-size:13px;padding:11px 0}
  .find label,.spine-foot{font-size:10px}
  .index-toggle{font-size:10px;min-height:44px;display:inline-flex;align-items:center;
    background:var(--concrete);padding:0 10px;border:.6px solid var(--hair)}

  /* The bar becomes an accordion rather than a row of dropdowns. This is the
     .faq-row pattern already shipped in this theme, so it carries none of the
     positioning risk a fixed panel would, and with COLOUR / SIZE / SORT it is
     three rules deep rather than a wall of controls above the first garment.

     These rules live in the 700 block and NOT in the 560 block above, which is
     authored earlier in the file: at 375px a 700 rule wins any same-property
     conflict on equal specificity, so splitting them across the two would be a
     cascade puzzle for no gain. */
  .facets{display:block;padding:12px 0 0;border-top:.6px solid var(--hair2);font-size:10px}
  .facet{display:block;border-bottom:.6px solid var(--hair2)}
  .facet>summary{display:flex;justify-content:space-between;width:100%;min-height:44px;
    border-bottom:0}
  .facet[open]>summary{border-bottom:0}
  .facet-panel{position:static;max-height:none;border:0;padding:0 0 10px;gap:0}
  .facet-panel a,.facet-panel>span{min-height:44px;align-items:center;font-size:11px}
  .facets-active{font-size:10px;padding-top:12px;gap:0 16px}
  .facets-active a{min-height:44px;display:inline-flex;align-items:center}

  /* Room for the bag bar. A margin, not padding: padding here is the section
     spacing setting, and a slider dragged down would have put the last card
     back under the bar. */
  .market,.archive,.product,.compose{margin-bottom:64px}
  /* ...and room under the footer for it — but only while there is one. The
     bar renders hidden on an empty cart, which is most visits, and an
     unconditional 104px left the copyright stranded a bar's height above
     the bottom of the page with nothing underneath it. site.js keeps the
     hidden attribute honest as the cart changes, so the clearance follows
     the bar in and out. Without :has the copyright keeps the footer's own
     40px and a full cart can overlap it — the same trade this stylesheet
     already takes for the hero-overlay rule. */
  body:has(.mobile-bar:not([hidden])) .foot{padding-bottom:104px}
}

/* The bag bar itself — hidden entirely on desktop, where the spine carries it. */
.mobile-bar{display:none}
@media(max-width:700px){
  .mobile-bar{
    display:block;position:fixed;z-index:80;left:0;right:0;bottom:0;
    background:var(--ink);
  }
  .mobile-bar[hidden]{display:none}
  .mobile-bar button{
    width:100%;min-height:54px;display:flex;align-items:center;justify-content:space-between;
    gap:16px;padding:0 22px;color:var(--concrete);
    font:11px var(--mono);letter-spacing:.1em;
  }
  .drawer{width:100vw}
}

@media(prefers-reduced-motion:reduce){
  *,*::before,*::after{animation-duration:.01ms!important;transition-duration:.01ms!important}
}

/* --------------------------------------------------- ABOUT / CONCEPT -- */
/* The client's own spec, finished. These selectors were carried over from the
   prototype's stylesheet and sat commented out here because no markup ever
   rendered them; sections/system-explainer is that markup. Values are theirs.
   margin-left:var(--spine) is dropped — it offset a fixed rail that no longer
   exists, and would now indent the page by 304px against nothing. */
.about-main{min-height:100vh;padding:76px 7vw 130px}
.about-mark{display:flex;justify-content:center}
.about-mark img{width:43px}
.about-hero{min-height:auto;padding-top:11vh}
.about-hero h1{font:400 clamp(48px,7vw,110px)/.9 var(--sans);letter-spacing:-.055em;margin:18px 0 34px;max-width:14ch}
.about-hero h1 em{font-family:var(--serif);font-weight:400}
.about-hero p{font:25px/1.3 var(--serif);max-width:600px}
.two-col{display:grid;grid-template-columns:.35fr 1fr;gap:11vw;margin:120px 0;max-width:1100px}
.two-col p{font:21px/1.5 var(--serif);max-width:700px;margin:0 0 22px}
.about-statement{margin:150px 0;max-width:1200px}
.about-statement span{font:8px var(--mono);letter-spacing:.1em;color:var(--muted)}
.about-statement h2{font:400 clamp(44px,6vw,96px)/.92 var(--sans);letter-spacing:-.055em;margin:16px 0}
.about-footer-note{margin-top:220px;padding-top:14px;border-top:.7px solid var(--hair);display:flex;justify-content:space-between;font:8px var(--mono)}
.about-footer-note small{display:block;color:var(--muted);margin-top:4px}
.concept{--pad-top:52px;--pad-bottom:56px;
  padding:var(--space-top,var(--pad-top)) 4.5vw var(--space-bottom,var(--pad-bottom))}
.future-list{margin-top:100px;max-width:700px}
.future-list>div{display:grid;grid-template-columns:40px 1fr auto;padding:16px 0;border-bottom:.6px solid var(--hair);font:8px var(--mono);letter-spacing:.06em}
.future-list .micro{margin-bottom:10px}
.archive-stamp img{width:34px}

/* The diagram the .concept-flow frame was reserved for: nodes on a rule, each
   opening its own description. Without JavaScript every panel is visible. */
.concept-flow{margin-top:90px;position:relative;max-width:920px}
.flow-track{list-style:none;margin:0;padding:0;display:grid;
  grid-template-columns:repeat(var(--nodes,4),1fr);position:relative}
.flow-track:before{content:"";position:absolute;left:0;right:0;top:5px;border-top:.7px solid var(--hair)}
.flow-node{position:relative;min-width:0}
.flow-node button{display:flex;flex-direction:column;align-items:flex-start;gap:14px;
  width:100%;padding:0;border:0;background:transparent;text-align:left;cursor:pointer;
  font:8px var(--mono);letter-spacing:.1em;color:var(--muted)}
.flow-dot{width:9px;height:9px;border-radius:50%;border:.8px solid var(--ink);
  background:var(--concrete);position:relative;z-index:1;transition:background .16s}
.flow-node button[aria-expanded="true"]{color:var(--ink)}
.flow-node button[aria-expanded="true"] .flow-dot{background:var(--ink)}
.flow-node button:hover .flow-dot{background:var(--ink)}
.flow-detail{margin-top:46px;min-height:150px}
.flow-panel h2{font:400 30px var(--sans);letter-spacing:-.03em;margin:0 0 12px}
.flow-panel .rte{font:19px/1.45 var(--serif);color:var(--ink-2);max-width:620px}
.flow-link{display:inline-block;margin-top:16px;font:8px var(--mono);letter-spacing:.12em;
  border-bottom:.7px solid var(--ink);padding-bottom:3px}
/* Only once the script has taken over does hiding become safe. */
.concept-flow[data-ready] .flow-panel{display:none}
.concept-flow[data-ready] .flow-panel[data-open]{display:block}
.concept-flow:not([data-ready]) .flow-panel+.flow-panel{margin-top:34px}

/* The manifesto, from the client's about.html.

   It is the whole first screen, on a phone and on a desktop: the client wants
   the page to open on it and nothing else. The type grows with the screen, so
   the lines fill that screen rather than sitting small in the middle of it,
   and a cue at the foot says the page goes on. --memo-size drives every size
   in it, and the width is counted in characters of that size, so the longest
   line never wraps as the type grows.

   It grows with the height as well as the width. The block stands about 35
   times the type size tall, plus roughly 210px of bar and padding, so a wide
   but short laptop screen would otherwise push the end note below the fold. */
.memo{--memo-size:clamp(13px,min(.45rem + .75vw,(100svh - 210px) / 37),20px);
  position:relative;max-width:calc(var(--memo-size) * 36);margin:0 auto;
  min-height:calc(100svh - var(--nav-h) - var(--space-top,var(--pad-top)));
  padding:24px 0 72px;display:flex;flex-direction:column;justify-content:center}
.memo + .about-hero{padding-top:clamp(56px,9vh,96px)}
.memo-cue{position:absolute;left:0;right:0;bottom:22px;width:max-content;margin:0 auto;
  display:flex;align-items:center;gap:10px;min-height:44px;
  font:8.5px var(--mono);letter-spacing:.2em;color:var(--muted);
  animation:settle .8s ease both;animation-delay:calc(1500ms + var(--i,0) * 60ms)}
.memo[data-live] .memo-cue{animation-delay:calc(300ms + var(--i,0) * 70ms)}
.memo-cue span{display:inline-block;animation:cue-drop 1.8s ease-in-out infinite}
.memo-cue:hover{color:var(--ink)}
@keyframes cue-drop{0%,100%{transform:translateY(-2px)}50%{transform:translateY(3px)}}
/* Until site.js takes over the lines stay hidden, so the real words are never
   painted and then scrambled on top of. The delayed fade is only the fallback
   for a script that never arrives; normally data-live cancels it within a
   frame or two and the lines appear as noise, decoding. */
.memo .line{margin:0 0 1.05em;font:400 var(--memo-size)/1.75 var(--mono);
  letter-spacing:.005em;animation:settle .6s ease both;
  animation-delay:calc(1500ms + var(--i,0) * 60ms)}
.memo .sign{margin:2.2em 0 0;font:max(9.5px,calc(var(--memo-size) * .62)) var(--mono);letter-spacing:.22em;text-transform:uppercase;
  color:var(--muted);animation:settle .8s ease both;
  animation-delay:calc(1500ms + var(--i,0) * 60ms)}
.memo .about-footer-note{margin-top:max(40px,calc(var(--memo-size) * 3));gap:20px;letter-spacing:.09em;
  font-size:max(8px,calc(var(--memo-size) * .5));
  animation:settle .8s ease both;animation-delay:calc(1500ms + var(--i,0) * 60ms)}
.memo[data-live] .line{animation:none;opacity:0}
.memo[data-live] .line[data-in]{opacity:1}
.memo[data-live] .sign,.memo[data-live] .about-footer-note{animation-delay:calc(300ms + var(--i,0) * 70ms)}
.memo .about-footer-note>span:last-child{text-align:right}
@keyframes settle{from{opacity:0;transform:translateY(6px)}to{opacity:1;transform:none}}
/* The scrambling word. Inline-block so site.js can pin its width and the line
   never reflows while the characters are random. */
.memo .g{display:inline-block;white-space:pre;cursor:default;transition:color .18s ease}
.memo .g.on{color:var(--muted)}
@media(prefers-reduced-motion:reduce){
  .memo .line,.memo .sign,.memo .about-footer-note,.memo-cue,.memo-cue span{animation:none}
}
/* A phone's type cannot grow — the longest line is already the width of the
   screen at 13px — so the screen is filled with air between the lines instead. */
@media(max-width:900px){
  .memo{padding:12px 0 72px}
  .memo .line{margin-bottom:1.9em}
}

/* ------------------------------------------------------------------ FAQ -- */
.faq{--pad-top:56px;--pad-bottom:60px;
  padding:var(--space-top,var(--pad-top)) 4.5vw var(--space-bottom,var(--pad-bottom));max-width:900px}
.faq-head h1{font:400 clamp(40px,5.4vw,84px)/.95 var(--sans);letter-spacing:-.05em;margin:0 0 18px}
.faq-intro{font:20px/1.45 var(--serif);color:var(--ink-2);max-width:600px;margin-bottom:44px}
.faq-list{border-top:.6px solid var(--hair)}
.faq-row{border-bottom:.6px solid var(--hair)}
.faq-row>summary{list-style:none;cursor:pointer;display:grid;grid-template-columns:38px 1fr 20px;
  align-items:baseline;gap:8px;padding:20px 0;min-height:44px}
.faq-row>summary::-webkit-details-marker{display:none}
.faq-n{font:8px var(--mono);letter-spacing:.08em;color:var(--muted)}
.faq-q{font:400 17px var(--sans);letter-spacing:-.01em}
.faq-mark{position:relative;width:11px;height:11px;justify-self:end;align-self:center}
.faq-mark:before,.faq-mark:after{content:"";position:absolute;background:var(--ink);transition:transform .18s}
.faq-mark:before{left:0;right:0;top:5px;height:.8px}
.faq-mark:after{top:0;bottom:0;left:5px;width:.8px}
.faq-row[open] .faq-mark:after{transform:scaleY(0)}
.faq-a{padding:0 0 24px;margin-left:46px;max-width:600px;font:16px/1.6 var(--serif);color:var(--ink-2)}
.faq-a p{margin:0 0 11px}
.faq-foot{margin-top:44px;font:15px/1.6 var(--serif);color:var(--muted)}

/* ==========================================================================
   RESERVED — ABOUT / CONCEPT
   Still-unbuilt selectors from the prototype's stylesheet, kept as the spec for
   a later about page.

/* .about-main{margin-left:var(--spine);min-height:100vh;padding:76px 7vw 130px}
.about-mark{display:flex;justify-content:center}
.about-mark img{width:43px}
.about-hero{min-height:690px;padding-top:17vh}
.about-hero h1{font:400 clamp(64px,8vw,132px)/.86 var(--sans);letter-spacing:-.06em;margin:18px 0 34px}
.about-hero h1 em{font-family:var(--serif);font-weight:400}
.about-hero p{font:25px/1.3 var(--serif);max-width:600px}
.two-col{display:grid;grid-template-columns:.35fr 1fr;gap:11vw;margin:160px 0;max-width:1100px}
.two-col p{font:21px/1.5 var(--serif);max-width:700px;margin:0 0 22px}
.about-statement{margin:210px 0;max-width:1200px}
.about-statement span{font:8px var(--mono)}
.about-statement h2{font:400 clamp(56px,7vw,110px)/.88 var(--sans);letter-spacing:-.055em;margin:16px 0}
.about-footer-note{margin-top:220px;padding-top:14px;border-top:.7px solid var(--hair);display:flex;justify-content:space-between;font:8px var(--mono)}
.about-footer-note small{display:block;color:var(--muted);margin-top:4px}
/* (the second copy of this rule said the same thing and is gone) */
.concept-flow{margin-top:130px;position:relative;height:360px;max-width:920px}
.concept-copy{margin-top:120px;max-width:900px;display:grid;grid-template-columns:.4fr 1fr;gap:8vw}
.future-list{margin-top:100px;max-width:700px}
.future-list div{display:grid;grid-template-columns:40px 1fr auto;padding:16px 0;border-bottom:.6px solid var(--hair);font:8px var(--mono)}
.archive-stamp img{width:34px}
   ========================================================================== */
