/* ═══════════════════════════════════════════════════════════════════════════════════════════
   LEORVE — THE WEBSITE'S DESIGN SYSTEM. One file, linked by every page in static_v2.

   WHY THIS FILE EXISTS. Until now each of the five pages carried its OWN copy of the tokens,
   pasted (the quiz page says so in its own comment: "copied verbatim from index.html"). They had
   already drifted — three different localStorage keys for the theme, four different radius
   scales, and a green that only three of them had. A pasted token block is a design system that
   is wrong somewhere and nobody knows where. This is the one copy.

   THE SYSTEM IS PAPER AND INK, AND THAT IS THE OWNER'S SECOND CORRECTION, NOT A THIRD IDEA.
   A warm sand + clay conversion (ground #FAF7F2, accent #B4532A) was started across this tree and
   STOPPED MID-RUN when the owner rejected the colour. His words, verbatim:
       "dont do orange make it simple white or black like in this https://mnml.la/ and make more
        visualize like the screenshot see how the text in this website is in simple white or black"
   So the ramp below is WHITE GROUND, BLACK INK, and the interface carries NO HUE AT ALL except the
   two status tokens named further down. The clay family (#B4532A / #9C4522 / #FFE9D6) and the sand
   grounds (#FAF7F2 / #F0EAE1 / #F6F0E7 / #EDE3D5 / #E4D8C6) are DELETED from this file and from
   every page that loads it; the --accent* NAMES survive because ~1,900 declarations across seven
   pages speak them, and they now resolve to ink. The catalogue dyes, the skin tones and the
   photography remain the only chroma on a Leorve screen — as they were before the clay pass.
   WHAT THE HOUSE RULES NOW SAY, because the file used to argue for two that are retired and one
   that is back:
     · "dead-neutral monochrome, every ground/surface/ink token exactly R = G = B" — BACK IN SPIRIT
       AND MEASURED SLIGHTLY LOOSER, and the difference is the owner's own values rather than a
       licence. The greys he named are STONE neutrals, not pure ones: --paper-2 #F5F5F4 differs by
       1 between R and B, --line #E6E6E4 by 2, --line-2 #D4D4D1 by 3. Asserting exact equality
       would mean rewriting the palette he specified, so the machine-checked rule is a channel
       SPREAD of at most 3 in 255 — below any perceptual threshold, and two orders of magnitude
       tighter than the clay it replaces (#B4532A spreads 138, the sand ground #FAF7F2 spreads 8).
       tests/test_static_v2_palette.py computes it. The warm-ramp rule that briefly stood here
       (hue 20–45°, saturation ≤ 0.11) is gone with the clay.
     · "theme comes from data-theme on <html>" (light + dark) — STILL RETIRED. There is ONE theme.
       No dark block, no toggle, no data-theme rule, no cc_theme, no prefers-color-scheme (except
       prefers-reduced-motion, which is untouched) anywhere in static_v2.
     · EVERY text/ground pair that ships is measured against WCAG AA before it ships, and it is
       measured against the WORST ground in the system, not against white.
       tests/test_design_tokens_resolve.py computes it.
   tests/test_model_shot.py's R=G=B assertion is about WHITE GARMENT RENDERS — product truth, not
   interface — and is deliberately untouched.
   (The greys before the clay pass were #ececec / #1b1b1b. This is not a return to them: the ground
   is pure white now and the ladder inverts, see the note on --paper. If a value here and a value
   in the ramp below ever disagree again, the ramp is what ships and this is what is wrong.)

   WHAT THIS REPLACES, and it is worth naming because the pages still speak its vocabulary:
   a sage/pine palette (--gold #47664c, --sage #86a277, --rose #a7b099, --brass #b98a49) on a
   parchment ground #eef0e6. Three of its values were failing contrast outright — --outline
   #9aa189 was the token 70 declarations across the site used for BODY TEXT and it measured
   2.33:1 on the page ground; --gold-light #86a277 as text measured 2.45:1. Both are now ramp
   greys that clear AA with room. See the measured table under LEGACY ALIASES.

   HOW THE PAGES CONSUME IT. They do not learn a new vocabulary. Section 2 re-points every legacy
   token name (--bg, --fg, --gold, --surface, --outline …) at the ramp, so ~1,900 existing
   declarations across the five pages move onto the monochrome system without a markup change.
   New work should reach for the RAMP names (--paper, --ink, --line, --accent); the aliases are
   there so nothing had to be rewritten to be correct, not as a second system.

   THEME. THERE IS NO THEME SIGNAL IN THIS FILE, which is stronger than the rule it replaces.
   No data-theme rule, no @media(prefers-color-scheme) rule, no `.dark` class, no boot script and
   no toggle on any page. One ramp, declared once, below. The paragraph this replaces said
   data-theme was "the ONLY theme signal"; the seven per-page boot scripts that resolved it, the
   `cc_theme` key they all wrote, and the seven toggle buttons they bound are deleted with it.
   ═══════════════════════════════════════════════════════════════════════════════════════════ */

/* ═══ 1. THE RAMP ═══════════════════════════════════════════════════════════════════════════
   ONE LIGHT THEME, AND IT IS PAPER AND INK. The :root[data-theme="dark"] block that used to follow
   this one is DELETED, not commented out, and so is the theme signal itself. Measured contrast is
   written beside each value that carries type, because a number nobody can check is a number that
   goes quietly wrong.
   EVERY NUMBER IN THIS SECTION WAS RE-MEASURED FOR THE WHITE RAMP. The figures that stood here
   were computed against the sand grounds and every one of them was wrong the moment the grounds
   changed — a stale contrast table is worse than none, because it is the thing the next person
   trusts instead of measuring. The three grounds that carry type are #FFFFFF (--paper / --card),
   #F5F5F4 (--paper-2 / --card-2) and #EDEDEB (the .ink band's recessed plane). */
:root{
  /* PAPER — PURE WHITE, and THE LADDER STILL INVERTS. --paper and --card are the SAME value now,
     which is the whole visual idea: there is no plane above white, so planes RECESS toward grey
     rather than lifting toward white. --card-2 is the INSET FIELD inside a card (an input, a
     track, a resting stepper) and it is the same step as --paper-2 — ONE recessed plane, not two
     nearly-equal ones. The file's own spacing note applies to value as well as to space: nine
     nearly-equal steps read as an accident.
     Adjacent steps, measured: paper→paper-2 1.091, paper-2→the .ink band's paper-2 1.075.
     A WHITE CARD DROPPED ON --paper IS NOW A 1.000:1 RECTANGLE — not a faint plane, LITERALLY THE
     SAME PIXELS. It MUST carry 1px solid var(--line-2). This was the single most repeated mistake
     in the sand ramp and it is no longer a mistake, it is an impossibility: without the hairline
     there is no card. The owner's reference (mnml.la) draws every card exactly this way, which is
     why "a hairline instead of a shadow where possible" is the instruction and not a preference. */
  --paper:#FFFFFF; --paper-2:#F5F5F4; --card:#FFFFFF; --card-2:#F5F5F4;
  /* THE HAIRLINES, AND THERE ARE STILL THREE. --line (1.250:1 on white) divides INSIDE a plane;
     --line-2 (1.485:1) is the plane's OWN edge and is not optional on a white card; --line-3 is
     the CONTROL boundary — 3.545 / 3.249 / 3.024 on paper / paper-2 / the .ink band's recess,
     clearing WCAG 1.4.11's 3:1 on every ground the house has. Use it on input, select, textarea,
     an unchecked box and an unchecked radio: anything whose border IS the control, where the
     border is the only thing saying a field is there. --line-2 is deliberately NOT promoted to
     3:1 — it carries 58 card borders in this file alone and a 3:1 hairline on all of them is a
     wireframe. #888888 rather than a lighter grey because 3:1 has to hold on the WORST ground,
     and #8F8F8F (the obvious pick against white) measures 2.759:1 on the .ink band's recess. */
  --line:#E6E6E4; --line-2:#D4D4D1; --line-3:#888888;
  /* THE TYPE RAMP, AND IT IS CUT AGAINST THE WORST GROUND IN THE SYSTEM (#EDEDEB, the .ink band's
     recessed plane) rather than against white. That is the whole reason --muted clears AA
     everywhere instead of being a large-text-only trap.
     THE OWNER'S BRIEF NAMES --ink-3 #767676 AND SAYS "4.5:1 on white — do not go lighter for
     text". #767676 measures 4.542:1 on white and he is right about white; it measures 4.164:1 on
     --paper-2 and 3.875:1 on the .ink band, i.e. it FAILS AA on two of the three grounds this
     site actually puts meta text on. So the token ships one step darker at #6B6B6B — 4.546:1 on
     the worst ground — which keeps the RULE he stated and drops only the literal that breaks it.
     --ink-3 is declared below in the alias block as the name his brief uses.
     Measured paper/card · paper-2/card-2 · the .ink band's recess:
       --ink     18.883 / 17.309 / 16.108
       --ink-2    9.740 /  8.928 /  8.309
       --ink-dim  6.484 /  5.944 /  5.531
       --muted    5.329 /  4.885 /  4.546
     --muted is still the DECORATIVE / INACTIVE token by convention. It is not a trap. */
  --ink:#111111; --ink-2:#444444; --ink-dim:#5E5E5E; --muted:#6B6B6B;
  /* THE EMPHASIS TRIO, AND ALL THREE ARE INK AGAIN. Under clay these genuinely differed, because
     #B4532A failed AA as text and #9C4522 did not; with no hue there is nothing to split. The
     NAMES survive — 177 declarations across the seven pages spend them — and they resolve to the
     one ink the page is already set in:
       --accent      the FILL: button, focus ring, meter, active marker, a border.
       --accent-deep the TEXT cut: links, price, emphasis. Same value, kept as a separate name so
                     the fill/text split stays authored and a future accent can re-split it.
       --accent-soft WAS the reserved spark over photography (#FFE9D6). Over a scrim the house's
                     type is plain white and nothing else, so this is --on-image now.
     EMPHASIS IS SIZE, WEIGHT AND SPACE HERE, NEVER COLOUR. That is the one sentence to keep: with
     the hue gone there is no colour left to step with, so a designer reaching for "the accent
     colour" on this site is reaching for something that does not exist. Reach for a size step, a
     rule, or the editorial band in §8. */
  --accent:#111111; --accent-deep:#111111; --accent-soft:#FFFFFF; --on-accent:#FFFFFF;
  /* --on-ink IS THE OWNER'S NAME for what --on-accent has always been: the type that rides a black
     fill. Both are declared so new work can write the word the brief uses. */
  --on-ink:#FFFFFF;
  /* HOVER LIFTS OFF BLACK, PRESSED GOES TO IT. On a #111111 fill there is nowhere darker to go
     that anyone can see, so the conventional "darken on hover" is a no-op here and the direction
     inverts: hover lightens to #333333 (white on it is 12.635:1, comfortably AA) and the press
     lands on true black. Both still carry white type, which is what the sand ramp's note was
     really protecting. */
  --accent-hover:#333333; --accent-press:#000000;
  /* THE WASHES. These two replace the ~20 rgba(33,33,33,…) literals that were scattered across the
     five pages as selected-tile fills, focus tints and icon-ring gradients. They are mixes of
     --ink, so they are greys: 6% composites to #F2F2F2 on white (1.098 above ground), 12% to
     #E5E5E5 (1.233). TEXT ON EITHER WASH IS --ink. A selected tile carries ink in its BORDER and
     its MARK and a grey wash behind it; the label stays ink.
     THE MIX PERCENTAGES DROPPED (10/18 → 6/12) because the wash now sits on WHITE rather than on
     sand: the same alpha over a lighter ground reads as a heavier plate, and a selected tile that
     looks filled-in is the thing that made the clay version feel loud. */
  --accent-wash:color-mix(in srgb, var(--ink) 6%, transparent);
  --accent-wash-2:color-mix(in srgb, var(--ink) 12%, transparent);
  /* THE RESERVED HIGHLIGHT keeps its name — 23 rules across the house depend on it — and it is
     white, which is what it resolves to over the only ground that can hold it: a photograph. */
  --brass:#FFFFFF;
  /* SEMANTIC STATUS — AND THESE TWO ARE THE ONLY COLOURED TOKENS LEFT IN THE INTERFACE. The
     owner's instruction is explicit: keep --warn and --danger for error and warning states, and
     nothing else in the interface may carry a hue. --ok is --ink, so a settled state reads in
     confident type and the WORD carries it.
     THEY NO LONGER HAVE TO SEPARATE THEMSELVES FROM AN ACCENT, which is what the paragraph this
     replaces spent itself on: there is no accent hue to be a cousin of. They have to separate
     from INK, and they do — a saturated crimson and a dark amber against a neutral page are the
     two most legible status cues available, and they are the only chroma in the chrome, so they
     cannot be mistaken for decoration.
     Measured paper/card · paper-2/card-2 · the .ink band's recess:
       --warn   7.148 / 6.553 / 6.098      --danger  7.899 / 7.241 / 6.739
     Do not add a third, and do not spend either one on anything that is not a warning or an
     error — a hue anywhere else on this site is now a bug, not a taste question. */
  --ok:#111111; --warn:#6B5518; --danger:#9E1B33;
  /* The type that sits ON the danger fill — a destructive button's hover, a toast. It needed its
     own name when --danger had a night cut that white could not ride (white on #ca9384 was
     2.62:1). There is ONE cut now and white rides it at 7.899:1, so the name survives only because
     the pages already speak it. */
  --on-danger:#FFFFFF;

  /* ELEVATION, AND THE TINT IS NEUTRAL BLACK AGAIN. The warm rgba(74,56,40) tint went with the
     sand grounds: a brown shadow under a card on a PURE WHITE page is a visible tea stain, and it
     was one of the two things that still said "orange" after the fills were neutralised. Every
     stop below is rgba(0,0,0,·).
     AND THEY ARE LIGHTER THAN THEY WERE. The owner's instruction is a hairline instead of a shadow
     wherever a hairline will do, and --line-2 is now the thing that DEFINES a card (see --paper).
     So the shadow stopped carrying plane separation and went back to being what it should be — a
     whisper of contact — at roughly half the old alphas. A card that needs more than this wants a
     border, not a bigger blur.
     ONE HONEST NOTE ON THE LIT TOP INSET: on --card #FFFFFF a white top highlight is a NO-OP,
     because there is nothing above white, and now that --paper is white too it is a no-op on the
     page as well. It is kept ONLY because --card-2, --paper-2 and the .ink band still take it; if
     those ever go white the inset should be deleted rather than left as a line that draws nothing.
     The SHADED bottom inset still works: rgba(0,0,0,.05) over white composites to #F2F2F2,
     1.098:1 below the card. */
  --shadow-1:0 1px 2px -1px rgba(0,0,0,.06), 0 12px 32px -22px rgba(0,0,0,.14),
    inset 0 1px 0 rgba(255,255,255,.90), inset 0 -1px 0 rgba(0,0,0,.05);
  --shadow-2:0 2px 6px -2px rgba(0,0,0,.08), 0 24px 56px -38px rgba(0,0,0,.20),
    inset 0 1px 0 rgba(255,255,255,.92), inset 0 -1px 0 rgba(0,0,0,.06);
  /* FOUR LAYERS, NOT TWO: a 1px contact shadow, the wide ambient float, a LIT top hairline and a
     SHADED bottom one. The light source is ABOVE, always, and it is the same source the page
     ambient below uses. Every rule that already names --shadow-1 gets it for free.
     THE BLOOM is the ambient lift under an object that floats FREE of the page — the sticky bar,
     and only the sticky bar. A light room has no way to glow AROUND an object, only to cast
     further under it, so this is a deeper pool of the same neutral shadow. */
  --bloom:0 12px 36px -20px rgba(0,0,0,.16);
  /* THE AMBIENT - the light on the page. Two stops with a flat plateau at each end, so a sticky
     band that paints the ground can name --ground-top / --ground-foot and land on the identical
     value instead of seaming. The literals are precomputed composites of --paper with the two
     alphas: white over rgba(255,255,255,.55) is white, and over rgba(0,0,0,.035) it is #F7F7F7,
     1.061:1 below the page. CHANGE ONE, CHANGE ITS PAIR. */
  --amb-top:rgba(255,255,255,.55);
  --amb-foot:rgba(0,0,0,.035);
  --ground-top:#FFFFFF;
  --ground-foot:#F7F7F7;

  /* GLASS - AND IT IS NOW ACTUALLY GLASS.
     82% opaque is a tinted panel, not glass. Probed on the running app before this pass, a pane
     over the page composited to within ONE VALUE IN 255 of its own fill, and the two panes whose
     mix base was --paper composited to EXACTLY their fill - a GPU blur per frame for zero pixels
     of difference. app/index.html carries the full measurement; this file carries the same cure.

     --glass       A PANE OVER PHOTOGRAPHY. Mixed from --card, 68%.
     --glass-paper A PANE OVER THE PAGE. IT WAS MIXED FROM --card-2 AND THAT IS NOW WRONG: in the
                   inverted ladder --card-2 sits BELOW --paper, so a pane made of it would
                   composite DARKER than the page it floats over and read as a shadow rather than
                   as glass. It is repointed to --card and opened from 72% to 86%.

     AND ON A WHITE PAGE IT CANNOT SEPARATE BY LIGHTNESS AT ALL. A white pane at 86% over white
     composites to WHITE — 1.000:1, not the 1.059:1 the sand ground still gave it. ON A WHITE
     GROUND A PANE HAS NOWHERE TO GO; --glass-line (1.250:1 against the pane) and --shadow-1 are
     the ONLY things that identify it, and neither is optional. That is a real constraint of this
     palette, not a bug to tune away by pushing the opacity higher until the blur stops meaning
     anything.

     saturate(1.02) is unchanged and stays near 1: chrome that enriches the product's colour is a
     lie in a house whose colour is the product's. The brightness(.62) smoked-glass stage is
     DELETED with the night theme — it existed only to let dark transparency survive a blown-out
     frame, and there is no dark pane now.

     QUIET TYPE ON A PANE IS --ink-2, NEVER --ink-dim, below 18.66px. Unchanged.

     The -2 fallbacks are FULLY SOLID rather than 95% translucent: where no backdrop filter can be
     drawn, a translucent panel is not a degraded pane but an unreadable one. */
  --glass:color-mix(in srgb, var(--card) 68%, transparent);
  --glass-2:var(--card);
  --glass-paper:color-mix(in srgb, var(--card) 86%, transparent);
  --glass-paper-2:var(--card);
  --glass-line:#E6E6E4; --glass-ink:var(--ink);
  --glass-blur-px:20px;
  --glass-blur-fx:saturate(1.02);

  /* Type printed on imagery. WHITE, AND IT DOES NOT FOLLOW THE PAGE — a photograph is a
     photograph. THE SCRIM HAS NO HUE: black alpha only, because a tinted veil over a garment
     shifts the garment's colour and garment colour is product truth. The owner's instruction says
     the same thing in one line — "never a tinted veil". This, and a film, are the only places a
     dark ground survives in the house, and §8's editorial band is the main one. */
  --on-image:#FFFFFF; --on-image-sh:0 1px 12px rgba(0,0,0,.35);

  /* ── the radius scale: 14 / 20 / 28 / 36 ────────────────────────────────────────────────
     FOUR VALUES, the app's, and tighter than the six the pages used to carry (6/10/16/22/32/40).
     A 40px corner on a coloured card reads generous; the same corner on a grey one reads as a
     consumer app, because with the hue gone the SHAPE is doing proportionally more of the
     talking. --r the everyday control, --r-lg a sheet or small panel, --r-xl the image card,
     --r-2xl the card and the hero. */
  --r:14px; --r-lg:20px; --r-xl:28px; --r-2xl:36px;
  --r-img:var(--r-xl); --r-sq:var(--r); --r-pill:999px;

  /* ONE CURVE. The house's, everywhere. --ease and --ease-out are kept as separate names because
     the pages name both, and they are deliberately the same value: two curves in one product is
     how a house starts sounding like two houses. The overshoot spring the old pages carried
     (cubic-bezier(.34,1.56,.64,1)) is gone — a control that bounces past its mark and comes back
     is the single loudest thing a quiet interface can do. */
  --ease:cubic-bezier(.16,1,.3,1);
  --ease-out:cubic-bezier(.16,1,.3,1);

  /* ── the spacing rhythm: three values, and nothing else ─────────────────────────────────
     Vertical space is the largest single lever on perceived price, and the way it goes wrong is
     not stinginess but VARIETY — nine nearly-equal gaps read as an accident. A page that wants a
     fourth value wants a new movement instead. --sp-mov is the most important number here: it is
     now the ONLY thing separating two movements, so it has to read as silence rather than as a
     margin. The Row and Totême run more. */
  --sp-mov:72px;      /* movement → movement */
  --sp-head:28px;     /* section head → its content */
  --sp-item:14px;     /* item → item inside a group */

  /* Three faces, exactly as the app loads them. --serif is ITALIC-ONLY and ACCENT-ONLY: it has
     one job, the accent noun in a headline. Fraunces' fvar default for wght is 900, so never
     name `opsz` without naming `wght` in the same declaration. */
  --display:'Bricolage Grotesque',system-ui,-apple-system,'Segoe UI',sans-serif;
  --serif:'Fraunces',Georgia,serif;
  --body:'Instrument Sans',system-ui,-apple-system,'Segoe UI',sans-serif;

  /* The UA hint for native controls, scrollbars and form widgets. There is one theme now, so there
     is one value — and it is declared HERE rather than per page, which is what the seven boot
     scripts used to do badly. */
  color-scheme:light;
}

/* THE NIGHT RAMP THAT USED TO STAND HERE IS DELETED, NOT COMMENTED OUT — a commented-out theme is
   a theme somebody re-enables. It was a second complete ramp (--paper #1b1b1b … --on-danger
   #1b1b1b) plus its own four-layer shadows, a luminous bloom, its own ambient pair and a
   brightness(.62) smoked-glass stage. Every one of those values is gone from the product, and so
   is the attribute that selected them. There is no [data-theme] rule left in this file.
   THE OWNER'S DECISION, IN HIS WORDS: "Remove dark theme and black theme, make whole website to a
   one theme and completely better colour and visuals." */

/* ── INK: the STATEMENT band, opted into with class `ink` on a section ──────────────────────
   THE CLASS NAME IS NOT LITERALLY TRUE AND THE OWNER HAS NOW RULED ON IT TWICE. The original band
   was a near-black plate measuring 15.29:1 against the page — "the most dramatic cut available
   here". The clay pass made it deep sand. The owner's instruction for THIS pass names it directly:
   no .ink black plate. So it is not coming back as a plate, and it is not a tinted band either.
   .ink IS A MATERIAL STATEMENT, NOT A COLOUR ONE. What makes it read as a movement is that it is
   FULL-BLEED, that it carries a rule or an eyebrow, and that its type steps up in scale — not that
   it is a different colour of dark. On this ramp it is a quiet grey band: #F5F5F4 against a white
   page, 1.091:1, with its own recessed plane at #EDEDEB.
   THE DRAMA LIVES IN §8, THE EDITORIAL BAND. A full-bleed photograph with white type over a black
   scrim is the house's loud surface, and it is the device the owner's screenshots actually asked
   for. A dark ground survives ONLY where it is a photograph or a film — never as a fill.
   The band survives as a TOKEN SCOPE so that none of the ~1,900 declarations inside it learns a
   second vocabulary — which is why a scoped class still beats a parallel token set.
   THE INK RAMP DOES NOT MOVE INSIDE THE BAND, and that is the whole reason the type ramp above is
   cut against #EDEDEB rather than against white: measured on the band's two grounds --ink 17.309 /
   16.108, --ink-2 8.928 / 8.309, --ink-dim 5.944 / 5.531, --muted 4.885 / 4.546 — every one still
   AA — so the band re-declares only its GROUNDS, its LINES and its GLASS.
   Use it TWICE per page at most; scarcity is what makes it a statement rather than a stripe. */
.ink{
  --paper:#F5F5F4; --paper-2:#EDEDEB; --card:#FFFFFF; --card-2:#F5F5F4;
  --line:#E2E2E0; --line-2:#D0D0CD; --line-3:#888888;
  --glass-line:#E2E2E0;
  /* The band re-declares its own glass. A custom property containing var() is substituted where it
     is DECLARED, so :root's --glass resolved against :root's --card once and inherited downward -
     survivable while a pane was 82% opaque, not survivable now that one genuinely shows through. */
  --glass:color-mix(in srgb, #FFFFFF 68%, transparent);
  --glass-2:#FFFFFF;
  --glass-paper:color-mix(in srgb, #FFFFFF 86%, transparent);
  --glass-paper-2:#FFFFFF;
}
/* THE SCOPED NIGHT OVERRIDE THAT USED TO STAND HERE IS DELETED. It existed for one arithmetic
   reason: at night the PAGE was already a near-black, so a near-black statement band measured
   1.04:1 against it and a full-bleed section simply was not there; the override lifted it to a
   raised pool at 1.324:1 and pulled the quiet type up with it. THERE IS NO NIGHT PAGE NOW, so
   there is nothing for the band to lift off. */

/* ═══ 2. LEGACY ALIASES ═════════════════════════════════════════════════════════════════════
   Every token name the five pages already speak, re-pointed at the ramp above. This is the whole
   de-hue: ~1,900 existing declarations move without a markup change.

   THE MAPPING IS BY ROLE, NOT BY LIGHTNESS, and the roles were measured off the pages rather
   than guessed. Counted across the five pages in static_v2 before this file existed:
     --outline         71 uses, 70 of them `color:` → it is BODY TEXT, not a border. #9aa189 on
                       the old ground measured 2.33:1 and failed AA on every ground the site has
                       (2.60:1 on a card, 2.16:1 on the recessed inset). It becomes --ink-dim:
                       6.484:1 on paper and on a card, 5.944:1 on the recessed inset. Re-measured
                       for the white ramp: the sand figures that stood here went wrong the moment
                       the grounds changed, which is why a stale table is worse than none.
     --outline-light   67 uses, 58 of them a border  → --line-2, the visible hairline.
     --outline-softer  54 uses, 52 of them a border  → --line, the quiet hairline.
     --fg-soft         59 uses, all `color:`         → --ink-2, the strong secondary.
     --gold           255 uses (163 text / 62 border / 45 fill) → --accent. It was the accent and
                       it stays the accent; the accent is ink. SEE THE NOTE ON THE SPLIT BELOW —
                       the fill/text sorting done for clay is kept even though both cuts currently
                       resolve to the same ink.
     --gold-light      34 uses, 31 of them `color:`  → --ink-dim. #86a277 measured 2.45:1 on paper.
   The three gradient tokens resolve to FLAT tones. A gradient is decoration in this system and
   the negative list forbids it; the pages keep naming --grad-warm and --grad-gold, and what they
   get is a surface. The one gradient kept is --grad-hero-overlay, which is not decoration but a
   SCRIM: type over a photograph needs it to stay readable, and IT HAS NO HUE — black alpha only,
   because a tinted veil over a garment shifts the garment's colour and garment colour is product
   truth. It is the same reason the Studio stage never took the chrome's tint. */
:root{
  --bg:var(--paper);
  --fg:var(--ink);
  --fg-soft:var(--ink-2);
  --surface:var(--card);
  --surface-low:var(--paper-2);
  --surface-high:var(--card-2);
  --surface-dim:var(--line);
  --outline:var(--ink-dim);
  --outline-light:var(--line-2);
  --outline-softer:var(--line);
  --primary:var(--accent);             /* the INK fill. --ink → clay → --ink; it is ink again */
  --on-primary:var(--on-accent);
  /* --ink-3, THE OWNER'S NAME FOR THE META TEXT TOKEN. His brief names paper / paper-2 / card /
     line / line-2 / ink / ink-2 / ink-3 / on-ink / on-image, and eight of those ten are already
     ramp names above. This is the ninth, pointed at --muted; --on-ink is the tenth and is declared
     in the ramp beside --on-accent. See the ramp's type note for why the literal moved from
     #767676 to #6B6B6B. */
  --ink-3:var(--muted);
  /* THE OLD ACCENT FAMILY, AND ALL FOUR ARE ONE VALUE AGAIN. Under clay --gold and --gold-deep had
     to differ, because #B4532A measured 4.170:1 as text and failed AA on the sand band while
     #9C4522 cleared it. With no hue there is nothing to split: every one of these resolves to
     --ink, and EMPHASIS IS SIZE AND SPACE, which is what this note said before the clay pass and
     says again.
     THE SPLIT IS KEPT AS TWO NAMES ON PURPOSE. 137 var(--gold) and 33 var(--gold-deep)
     declarations across the seven pages were sorted by property in the clay pass — fills and
     borders onto --gold, `color:` onto --gold-deep — and that sorting is real work that should
     not be thrown away just because the two values currently agree. If an accent hue is ever
     re-introduced it re-splits along a line that is already drawn. Keep writing --gold-deep for
     `color:` and --gold for a background or a border. */
  --gold:var(--accent);
  --gold-deep:var(--accent-deep);
  --gold-hover:var(--accent-hover);    /* pointer hover on an INK fill — it LIGHTENS, see the ramp */
  --gold-press:var(--accent-press);
  --gold-dim:var(--ink-2);
  --gold-light:var(--ink-dim);
  --rose:var(--ink-dim);
  --sage:var(--ink-dim);
  --error:var(--danger);
  --card-bg:var(--card);
  /* HOVER RECESSES A PLANE, and that is forced rather than chosen: --card is pure white and white
     has nowhere to lift to. --card-2 #F5F5F4 is 1.091:1 BELOW the card. */
  --card-hover:var(--card-2);
  --invert-bg:#F5F5F4;                 /* = the .ink band's ground, and not an inversion at all:
                                          1.091:1 against the page where it was 15.29:1 before the
                                          black plate was removed. The one call site
                                          (index.html's studio tile) takes its icon and its 2px
                                          border from --ink instead, or it reads as plain ink on
                                          grey and loses its point. */
  --invert-fg:var(--ink);
  --outline-strong:var(--line-3);      /* NEW — the CONTROL boundary, 3:1 on every ground */
  --glass-bg:var(--glass);
  --glass-border:var(--glass-line);
  --glass-blur:var(--glass-blur-px);
  /* Flat, on purpose — see the note above. */
  --grad-warm:var(--paper);
  --grad-gold:var(--accent);
  --grad-rose:var(--paper-2);
  --grad-hero-overlay:linear-gradient(140deg,rgba(0,0,0,.06) 0%,rgba(0,0,0,.58) 100%);
  /* The shadow ladder the pages name, all four resolving onto the two real recipes. --shadow-luxe
     was a GREEN glow (rgba(71,102,76,.35)), then a warm brown; the tint follows the ramp's and the
     ramp has no hue, so it is black alpha. A brown shadow on a white page is a tea stain. */
  --shadow-xs:0 1px 2px rgba(0,0,0,.04);
  --shadow-sm:0 2px 6px rgba(0,0,0,.05),0 1px 2px rgba(0,0,0,.03);
  --shadow-md:var(--shadow-1);
  --shadow-lg:var(--shadow-2);
  --shadow-luxe:var(--shadow-2);
  /* Six radius names onto four values. Nothing is rounder than the app: 14 / 14 / 20 / 20 / 28 / 36.
     ONLY THREE LINES BELONG HERE, AND THAT IS THE WHOLE FIX. The pages name six radii
     (--r-xs/-sm/-md/-lg/-xl/-2xl, once 6/10/16/22/32/40) and the scale at line 151 already OWNS
     three of those names, so only --r-xs, --r-sm and --r-md are missing and need aliasing. This
     block used to write all six, and the three extra lines were
         --r-lg:var(--r-xl); --r-xl:var(--r-2xl); --r-2xl:var(--r-2xl);
     every one of which was wrong in the same way: A CUSTOM PROPERTY CANNOT READ THE VALUE IT IS
     SHADOWING. Both :root blocks have the same specificity, so this one wins on source order and
     `var(--r-xl)` here resolves to THIS block's --r-xl, never the 28px above — the aliases chained
     into each other instead of onto the scale. --r-2xl:var(--r-2xl) made that literal: a cycle,
     which the spec makes invalid at computed-value time, so --r-2xl became guaranteed-invalid and
     took --r-xl, --r-lg, --r-md and --r-img (= var(--r-xl)) down with it.
     MEASURED, on /shop, getComputedStyle(document.documentElement) before the repair:
       --r 14px  --r-xs 14px  --r-sm 14px  --r-sq 14px  --r-pill 999px
       --r-md ""  --r-lg ""  --r-xl ""  --r-2xl ""  --r-img ""
     An empty custom property makes `border-radius:var(--r-lg)` an INVALID DECLARATION at
     computed-value time, which falls back to the initial value — 0. AT THE MOMENT OF THE REPAIR,
     twenty-four declarations across this file and the seven pages that load it resolved through
     these names, and twenty-three of them were painting a square corner while their rule asked
     for 20, 28 or 36. (That count is a census of the damage, not a budget: /shop has since added
     two more on purpose — see below.) The twenty-fourth is index.html's .skip-to-content, which
     wrote `var(--r-md,10px)` and so quietly took the fallback — the one place a radius survived,
     and the reason the failure looked like a design rather than a defect.
     The blast radius is NARROWER THAN THE NAMES SUGGEST, and worth recording so the next person
     does not go looking: 22 of the 24 were in static_v2/index.html (/reading), one was shop.html's
     .stylewith, one is .lv-frame below. /quiz, /wardrobe, /account, /styling and /membership name
     none of these five tokens anywhere, so nothing on them moved when this was repaired. What DID
     move beyond the repair is a separate decision the owner took once the names resolved again:
     /shop's .card-img and .gal-shot were `border-radius:0` and now ask for --r-2xl, so the garment
     photograph carries the same 36px corner on /shop as it does in the flagship's lookbook, which
     links straight to it. shop.html records the reversal and the argument it replaces.
     SETTING --r-2xl:36px HERE WOULD HAVE HIDDEN THE BUG, NOT FIXED IT. The chain would resolve, and
     resolve every upper name to the same number: --r-xl 36, --r-lg 36, --r-md 36. Six names onto
     TWO values and the whole house at one corner — the opposite of what the first line claims. The
     names have to stop shadowing; they must not start resolving. */
  --r-xs:var(--r); --r-sm:var(--r); --r-md:var(--r-lg);
  --ease-spring:var(--ease);           /* the overshoot is gone; see --ease above */
  --font-display:var(--display);
  --font-serif:var(--serif);
  --font-body:var(--body);
  --font-label:var(--body);
  --noise-opacity:0;                   /* the grain overlay is decoration; it is off */
}
/* A SECOND NIGHT BLOCK STOOD HERE — three aliases (--shadow-xs, --shadow-sm and a heavier
   --grad-hero-overlay) that only existed because the page behind them was near-black. It is
   deleted with the theme. The scrim keeps ONE cut now, the light one, because a photograph is a
   photograph whatever the page around it is doing. */

/* ═══ 3. THE PAGE ═══════════════════════════════════════════════════════════════════════════ */
*{box-sizing:border-box;-webkit-tap-highlight-color:transparent}
/* THE TWO HUES TAILWIND'S PLAY CDN SMUGGLES IN, AND WHY THIS RULE IS NOT PARANOIA.
   Three of the five pages load the Play CDN, whose preflight sets two colours nothing in this
   house ever asked for:
     *,::before,::after{border-color:#e5e7eb}   gray-200: |R-G| 2, |R-B| 6
     input::placeholder,textarea::placeholder{color:#6b7280}  gray-500: |R-G| 7, |R-B| 16
   Probed live on /wardrobe: 138 elements were computing the first one and ZERO were painting it,
   because every one of them has border-width 0 — so this is a latent trap rather than a live
   violation, and it is exactly the kind that goes live the day somebody writes `border-t` (a
   WIDTH utility) without a colour utility beside it and gets a blue-grey hairline on a
   dead-neutral page. /shop and /account already write bare `border-t` in three places.
   THE SELECTOR CARRIES A TYPE, DELIBERATELY. The CDN injects its stylesheet AFTER this file, so a
   bare `*{...}` at specificity (0,0,0) would lose the tie on source order — as the tailwind.config
   comment at the top of each page already warns about for font families. `html *` is (0,0,1) and
   wins whatever the order. */
html *,html ::before,html ::after{border-color:var(--line)}
/* --ink-dim, NOT --muted. This file reserves --muted for DECORATIVE and INACTIVE marks and says
   so where it is declared; a placeholder is neither — it is the only instruction in an empty
   field. On the grounds inputs actually sit on, --muted measured under 4.5:1 on the ramp this
   replaced, so the hint in the sign-up fields on /account and the add-item fields on /wardrobe sat
   just below the readable threshold. --ink-dim is 5.944:1 on --paper-2 and 6.484:1 on --card. */
html input::placeholder,html textarea::placeholder{color:var(--ink-dim)}
html{scroll-behavior:smooth}
/* AN ANCHOR LANDS BELOW THE CAPSULE (v7 verify site1-13, measured: /membership's "FREE" eyebrow sat
   under the bar at 1440 and 1024, /reading's focused occasion card at -4px under its fixed header).
   --lv-nav-safe is the settled capsule plus its air, from /static/lv-capsule.css, which every page
   links and which declares this same rule; it is said here too so a page that ever loses that sheet
   still keeps anchors clear of whatever bar it has. 80px is the desktop value. */
html{scroll-padding-top:var(--lv-nav-safe,80px)}
/* NO AMBIENT LIGHT ON THIS PAGE, AND THAT IS A DECISION RATHER THAN AN OMISSION.
   app/index.html paints a two-stop neutral ambient on #app — a fixed light source above the frame
   — because an app screen is exactly one viewport tall and was measuring 82.6% ONE FLAT VALUE.
   This is a six-thousand-pixel marketing page and neither half of that argument transfers:
     · body IS the scroller here, so the same gradient would either travel with the content (a
       moving light, which is the cheap effect this house does not ship) or need
       background-attachment:fixed, which repaints the viewport on every scroll frame on mobile.
     · Several sections paint their own full-bleed --paper ground. A gradient underneath them
       would seam at every one, which is the same failure the app had to solve with plateaus.
   --amb-top / --amb-foot / --ground-top / --ground-foot ARE declared above, so the four surfaces
   carry one token vocabulary and a fixed piece of chrome here can name them. The page does not
   spend them. If this page ever reads flat, the answer is the same one the app's own note gives:
   a bigger photograph and more whitespace. */
body{margin:0;background:var(--paper);color:var(--ink);
  font:400 15px/1.55 var(--body);letter-spacing:.005em;
  -webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;
  text-rendering:optimizeLegibility;overflow-x:hidden}
/* THE FOOTER STAYS ON THE FLOOR OF A SHORT PAGE (v7 verify site1-30): a /styling refusal, an empty
   /account or a one-question /taste ended ~760px down a 900px window with the dot field showing under
   the footer, which reads as a page that stopped loading. A column whose content block takes the
   spare height puts the footer at the bottom of the window — and changes nothing on a long page,
   where there is no spare height to take. Only body's direct content blocks grow (`main`, or the
   #page wrapper /shop uses); everything else on body is positioned (the capsule's rail, the skip
   link, dialogs in the top layer) or is the footer itself. 100vh first for an engine without dvh.
   THE WIDTH IS SAID, AND THAT IS MEASURED (build_SHARED/check_pages.mjs, flex vs block): a flex item
   with auto side margins is sized to FIT ITS CONTENT, not to the line, so /styling's centred
   `.wrap{max-width;margin:0 auto}` main shrank 1180 → 1047px and /wardrobe's, while it was still
   loading, to 182px. Block layout's own rule is "fill the line, then clamp to max-width, then
   centre", which is exactly what stretch / fill-available say; 100% is the fallback for an engine
   with neither, and it is only ever reached on a main with no side margins of its own. */
body{display:flex;flex-direction:column;min-height:100vh;min-height:100dvh}
body > main, body > #page{flex:1 0 auto;width:100%;width:-webkit-fill-available;width:-moz-available;width:stretch}
/* NO TRANSITION ON THE GROUND. The pages used to carry `transition:background .6s, color .6s` on
   <body>, which meant every theme change repainted the entire viewport for 600ms and, worse,
   that the FIRST paint after the boot script set the theme animated from the default. A theme is
   a state, not a movement. */
h1,h2,h3,h4{font-family:var(--display);font-feature-settings:"kern";letter-spacing:-.02em;
  line-height:1.1;margin:0}
h1,h2{font-weight:700}
img{max-width:100%;display:block}
a{color:inherit}
button,input,select,textarea{font:inherit;color:inherit}
:focus-visible{outline:2px solid var(--accent);outline-offset:3px;border-radius:var(--r)}
/* A HEADING THAT IS FOCUSED TO BE READ IS NOT A CONTROL (v7 verify site1-31). The pages move focus to
   a step's h1 (tabindex="-1") so a screen reader announces where it is; Chrome's :focus-visible then
   drew the 2px ring round the whole heading after any key press — a box round a sentence nobody can
   press. The announcement is the point and stays; the ring goes, on programmatic targets only. */
[tabindex="-1"]:is(h1,h2,h3):focus{outline:none}
/* On a photograph --accent is near-black on a dark scrim and the ring all but disappears.
   Anything printed on imagery gets a white one instead. */
.on-photo :focus-visible,.hero :focus-visible{outline-color:var(--on-image)}
::selection{background:var(--accent);color:var(--on-accent)}
::-webkit-scrollbar{width:6px;height:6px}
::-webkit-scrollbar-track{background:transparent}
::-webkit-scrollbar-thumb{background:var(--line-2);border-radius:3px}
/* Gated like every hover in the house (v7 verify site2 NEW-2): a touch browser fires :hover on tap. */
@media (hover:hover) and (pointer:fine){ ::-webkit-scrollbar-thumb:hover{background:var(--muted)} }
.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;
  clip:rect(0 0 0 0);clip-path:inset(50%);white-space:nowrap;border:0}
.num,.font-label{font-variant-numeric:tabular-nums}

/* ═══ 4. THE SHARED TYPE ROLES ══════════════════════════════════════════════════════════════
   Four, and a page that wants a fifth wants a new movement instead. These are the app's names so
   a reader moving between the two products meets the same three sizes doing the same three jobs. */
.eyebrow{font:500 11px/1.2 var(--body);letter-spacing:.22em;text-transform:uppercase;
  color:var(--ink-dim);margin:0 0 var(--sp-item)}
.sec{font:700 clamp(28px,4.2vw,44px)/1.06 var(--display);letter-spacing:-.02em;color:var(--ink);
  margin:0}
/* THE STATEMENT: 30–50% larger than the section head beside it, which is NN/g's measured figure
   for an element that is meant to be read as the one thing on the screen. It is a SIZE step, not
   a colour one — there is no colour left to step with. */
.statement{font:700 clamp(38px,7vw,72px)/1.02 var(--display);letter-spacing:-.03em;
  color:var(--ink);margin:0}
/* A HEAD INSIDE A CARD OR A PANEL — the one step between the section head and body copy, and it
   is NAMED in this pass rather than added by it. A live audit of the five pages found twenty-five
   distinct font sizes rendering on the homepage alone (8, 9, 10, 11, 12, 13, 13.5, 14, 15, 16, 17,
   18, 19, 20, 22, 24, 27, 30, 48, 60, 120, 140 …), seventeen on /shop and twelve on /wardrobe.
   That is the same accumulation the app's type block describes — no one size visibly wrong, the
   aggregate exactly what "not quite right" feels like. These five roles are the whole ladder a
   page needs; a page that reaches for a sixth wants a new movement instead. */
.lv-sub{font:600 21px/1.25 var(--display);letter-spacing:-.015em;color:var(--ink);margin:0}
.lede{font:400 clamp(16px,1.6vw,19px)/1.6 var(--body);color:var(--ink-2);max-width:56ch;margin:0;
  text-wrap:pretty}
/* text-wrap:pretty on the reading roles is the cheapest typographic improvement available here:
   it stops a one-word last line, which is the most common thing that makes a two-sentence deck
   look unconsidered. It degrades to `wrap` everywhere it is not supported. */
.lv-body{font:400 15px/1.6 var(--body);color:var(--ink-dim);margin:0;text-wrap:pretty}
.fine{font:400 13px/1.5 var(--body);color:var(--ink-dim);margin:0;text-wrap:pretty}
.font-serif{font-family:var(--serif);font-feature-settings:"liga","dlig","kern";
  letter-spacing:-.015em;line-height:1.1}
.font-display{font-family:var(--display);font-weight:500;letter-spacing:-.01em;line-height:1.08}
.text-gold{color:var(--accent-deep)}   /* the name survives; the colour is ink */

/* ═══ 5. THE IMAGE FRAME ════════════════════════════════════════════════════════════════════
   NOTHING REFLOWS AS A PICTURE ARRIVES. Every photograph on this site sits in a box that already
   knows its shape, so the layout below it never moves when the file lands. That is the single
   biggest cause of the "jank" this redesign is answering, and it is fixed by geometry rather
   than by an effect.

   THE RATIOS ARE PORTRAIT AND THERE ARE THREE OF THEM. Measured off the reference set: H&M 2:3,
   COS 2:3, Uniqlo 3:4, Everlane 4:5. Nobody in that set uses a square. `--lv-ratio` lets one
   frame class serve all three so the site runs ONE ratio at several SCALES — SSENSE's move —
   rather than several ratios at one scale.

   THE FADE IS THE ONLY ENTRANCE. opacity alone, 240ms, and it is applied FROM JS: the hidden
   state is never in this stylesheet, so a page whose script fails still shows every picture.
   A LAZY IMAGE IS NEVER HIDDEN — loading="lazy" defers the fetch to a decision that does not
   always arrive, and an image whose load never starts would be stranded invisible. */
.lv-frame{display:block;position:relative;overflow:hidden;
  aspect-ratio:var(--lv-ratio,2/3);background:var(--paper-2);border-radius:var(--r-img)}
.lv-frame > img{width:100%;height:100%;object-fit:cover;display:block}
.lv-frame.r-45{--lv-ratio:4/5}
.lv-frame.r-34{--lv-ratio:3/4}
.lv-frame.r-11{--lv-ratio:1/1}       /* swatches and thumbs only, never a garment */
.lv-frame.is-fading > img{opacity:0}
.lv-frame > img.is-ready{opacity:1;transition:opacity .24s linear}
/* A frame whose file is missing collapses to the paper well with its caption still readable,
   rather than to a broken-image glyph. */
.lv-frame.is-missing > img{display:none}

/* ═══ 6. MOTION ═════════════════════════════════════════════════════════════════════════════
   WHAT "SMOOTH" MEANS HERE, because three redesigns were rejected for getting this wrong: every
   failed attempt answered "premium" with MORE MOTION. Stillness reads as expensive. So the rule
   below is not a performance tweak, it is the design.

   ONLY transform AND opacity, on any scroll or interaction path. Never width, height, top, left,
   margin or filter — each of those is laid out or painted on the main thread every frame, and a
   scroll that is competing with layout is the stutter this is here to remove.

   will-change is set only WHILE something animates and is never parked on an idle element: a
   parked will-change holds a compositor layer for the life of the page and costs memory on
   exactly the low-end phones it was meant to help. */
/* .lv-rise IS DELETED, AND IT WAS A LOADED GUN RATHER THAN MERELY DEAD CODE.
   It hid unconditionally — `.lv-rise{opacity:0}` — and was revealed by `.lv-rise.is-in`, a class
   NOTHING IN THE REPOSITORY EVER ADDED: no markup carried `lv-rise`, no script wrote `is-in`.
   So the rule cost nothing while unused and would have made any element permanently invisible the
   moment somebody reached for the class, with no error and nothing to grep for. That is the same
   failure the scroll reveals in index.html were rewritten to remove; leaving the pattern lying
   about in the shared stylesheet would have re-taught it.
   IF A RISE-ON-SCROLL IS WANTED AGAIN: write the hidden state from JS, or gate it on a class the
   script adds only after `new IntersectionObserver` has returned — index.html's html.js-reveal
   block is the worked example, and app/core.js's html.io-fallback states the contract. */

/* PREFERS-REDUCED-MOTION LANDS EVERYTHING ON ITS FINISHED, VISIBLE STATE. Not "no animation" —
   VISIBLE. The failure mode this guards against is an entrance whose start state is opacity:0:
   turning the animation off there leaves the content permanently invisible, which is not a
   reduced experience but a broken one. Hence `animation:none` paired with an explicit
   opacity:1 / transform:none, and `!important` because the start states are written inline on
   several pages as animation-delay pairs. */
@media (prefers-reduced-motion: reduce){
  html{scroll-behavior:auto}
  *,*::before,*::after{
    animation-duration:.001ms !important;
    animation-iteration-count:1 !important;
    transition-duration:.001ms !important;
    scroll-behavior:auto !important}
  .fade-up,.hero-text-anim,.reveal,.st-piece,.lb-card{
    opacity:1 !important;transform:none !important}
  .lv-frame > img{opacity:1 !important}
}

/* ═══ 7. THE SHARED CHROME ══════════════════════════════════════════════════════════════════ */
/* A PANE OVER PHOTOGRAPHY. --glass-blur-fx is saturate alone; the smoked-glass brightness stage it
   used to carry was the night cut and went with the night theme. */
.lv-glass{background:var(--glass);
  -webkit-backdrop-filter:blur(var(--glass-blur-px)) var(--glass-blur-fx);
  backdrop-filter:blur(var(--glass-blur-px)) var(--glass-blur-fx);
  border:1px solid var(--glass-line);color:var(--glass-ink);box-shadow:var(--shadow-1)}
/* A PANE OVER THE PAGE - mixed from --card-2, so it sits ABOVE the ground rather than matching it.
   Reach for this one for a toolbar or a panel, and for .lv-glass over an image. */
.lv-glass-paper{background:var(--glass-paper);
  -webkit-backdrop-filter:blur(var(--glass-blur-px)) var(--glass-blur-fx);
  backdrop-filter:blur(var(--glass-blur-px)) var(--glass-blur-fx);
  border:1px solid var(--glass-line);color:var(--ink);box-shadow:var(--shadow-1)}
/* ── THE STICKY CHROME IS RETIRED (v7, owner brief 27 Oct 2026: "that glass tab … should be of this
   same design in every section and page") ─────────────────────────────────────────────────────────
   .lv-chrome — the flat sticky bar the seven pages drew, with its .nav-mid six, .nav-right glyphs,
   .nav-bag, .nav-burger and .nav-menu panel, the travelling-pill block and the 900–1279 fluid band —
   is DELETED, not commented out. Every page now draws the flagship's floating capsule from ONE shared
   pair, /static/lv-capsule.css + /static/lv-nav.js (NAV_SPEC.md), which the Studio and the legal
   pages link too; tests/test_capsule_everywhere.py fails if any page re-declares it or if a retired
   class comes back. What stayed here: the lockup's nav cut (.lv-lock, below — test_identity reads it)
   and the materials (.lv-glass / .lv-glass-paper), which other page furniture still wears.
   THE OLD Z-INDEX NOTE STILL HOLDS FOR THE CAPSULE: it takes the bar's slot, z 100, so every layer the
   pages built under .lv-chrome (the Plus sheet, the wardrobe's selection bar, the quiz's foot) still
   sits where it was drawn to sit.

   THE FALLBACK IS A SOLID SURFACE, not a slightly-less-transparent one: with no blur behind it, a
   translucent panel shows whatever it sits on at full sharpness directly under the type. --card is
   white and carries --ink at 18.883:1.
   THE FALLBACK IS KEYED ON THE TOKEN, NOT ON A LIST OF CLASSES, and that is the difference between a
   fallback that works and one that works until somebody writes another pane: #navbar.is-stuck on the
   homepage and .gal-nav on /shop named var(--glass) DIRECTLY, so a class-keyed @supports left them as
   68%-translucent panels with an unblurred photograph under their type. Re-declaring the CUSTOM
   PROPERTY covers every consumer at once, including the next one, because a custom property is
   substituted where it is DECLARED.
   AND IT IS NOT THE "REDUCE TRANSPARENCY" PATH. The note that stood here claimed it was; it was false
   (v7 verify site1-19 / site2-18): @supports answers what the ENGINE can do, never what the customer
   asked their OS for, so a customer with Reduce Transparency on got live glass on every static page.
   That setting has its own block below, the twin of the Studio's, the app's and the flagship's. */
@supports not ((backdrop-filter:blur(1px)) or (-webkit-backdrop-filter:blur(1px))){
  /* TWO SCOPES, NOT FOUR. This list used to name :root, the night :root, .ink and the lifted night
     .ink, because a custom property is substituted where it is DECLARED and each scope declares
     its own pane. Two of those scopes went with the dark theme. */
  :root,.ink{
    --glass:var(--glass-2);
    --glass-paper:var(--glass-paper-2);
  }
  .lv-glass{background:var(--glass-2)}
  .lv-glass-paper{background:var(--glass-paper-2)}
}
/* THE CUSTOMER ASKED FOR SOLID SURFACES (prefers-reduced-transparency): the same solids as the
   no-blur fallback, and no blur at all — on the token, so every pane that names it follows. */
@media (prefers-reduced-transparency: reduce){
  :root,.ink{
    --glass:var(--glass-2);
    --glass-paper:var(--glass-paper-2);
    --glass-blur-px:0px;
  }
  .lv-glass{background:var(--glass-2);-webkit-backdrop-filter:none;backdrop-filter:none}
  .lv-glass-paper{background:var(--glass-paper-2);-webkit-backdrop-filter:none;backdrop-filter:none}
}
/* UNDER A MODAL NOTHING BLURS TWICE (AUTH_SPEC critic C17, the phone blur budget): a page pane is
   under ::backdrop, so its own blur is invisible work the compositor still pays for. Every modal on
   these pages is a <dialog> opened with showModal(), so the hook is the dialog itself; a pane INSIDE
   the open dialog keeps its glass. A browser without :has() drops this rule only. */
html:has(dialog:modal) :is(.lv-glass,.lv-glass-paper):not(dialog:modal *){-webkit-backdrop-filter:none;backdrop-filter:none}

/* THE TWO BUTTONS, AND THE BRIEF NAMES THEM EXACTLY: "Primary button = --ink fill, --on-ink text.
   Secondary = transparent with --line-2 hairline." That is what these two rules already were once
   the accent went back to ink, so the only real change is the HOVER.
   IT WAS `opacity:.86` AND THAT IS WRONG ON A BLACK FILL. Opacity fades the button toward whatever
   is behind it, which on a white page means a black button hovers to #3B3B3B *and drags its white
   label to 86% with it* — the label loses contrast at the exact moment the pointer is on it.
   Hovering the BACKGROUND token instead moves only the fill: #111111 → #333333, white type 18.883
   → 12.635, still far above AA. The press lands on true black.
   v7: ONE BUTTON LANGUAGE (verify site2-25). This was 13px UPPERCASE tracked .1em while the capsule's
   "Get my looks", the account dialog, the plan sheet and the flagship all press a sentence-case pill
   with a lit top edge and a cast shadow — two vocabularies for one verb, a page apart. It is that pill
   now (the flagship's .cc-btn / lv-auth.css .lva-btn numbers). And the hover is gated (v7 verify site2
   NEW-2): a phone fires :hover on tap and leaves it stuck, which left the fill grey after a press. */
.lv-btn{display:inline-flex;align-items:center;justify-content:center;gap:8px;
  padding:14px 26px;border:1px solid var(--accent);border-radius:var(--r-pill);cursor:pointer;
  background:var(--accent);color:var(--on-accent);
  font:600 14px/1 var(--body);letter-spacing:.01em;
  box-shadow:inset 0 1px 0 rgba(255,255,255,.16),inset 0 -1px 0 rgba(0,0,0,.40),
    0 1px 2px rgba(17,17,17,.22),0 10px 22px -12px rgba(17,17,17,.55);
  text-decoration:none;transition:background-color .2s var(--ease),transform .2s var(--ease),
    border-color .2s var(--ease)}
.lv-btn:active{background:var(--accent-press);transform:scale(.985)}
/* The secondary states its own background back, because the rule above it now sets one. */
.lv-btn.ghost{background:transparent;color:var(--ink);box-shadow:none;
  border:1px solid var(--line-2);padding:14px 26px}
.lv-btn.ghost:active{background:transparent}
.lv-rule{height:1px;background:var(--line);border:0;margin:0}
/* AN INLINE TEXT LINK: ink, underlined, offset so the rule clears the descenders. The brief says
   "Links = --ink, underlined", and this is that rule as a CLASS rather than as a bare `a`
   selector — deliberately. A blanket `a{text-decoration:underline}` on these seven pages would
   underline every nav item, every card wrapper and every photographic tile, because almost every
   anchor here wraps a BOX rather than a run of words. The rule belongs on the links that are
   actually prose. */
.lv-link{color:var(--ink);text-decoration:underline;text-underline-offset:3px;
  text-decoration-thickness:1px}
@media (hover:hover) and (pointer:fine){
  .lv-btn:hover{background:var(--accent-hover);border-color:var(--accent-hover)}
  .lv-btn.ghost:hover{background:transparent;border-color:var(--ink)}
  .lv-link:hover{text-decoration-thickness:2px}
}

/* ═══ THE IDENTITY ═══════════════════════════════════════════════════════════════════════════
   The owner's logo: a gold script monogram over LEORVE in thin tracked capitals. Two parts, and
   they are different KINDS of thing, so they are built differently:

     THE SIGNATURE is a drawing. app/brand/leorve-script.svg, traced from the artwork, 595x308.
     THE NAME is TYPE. "LEORVE" set in Julius Sans One. Not a picture of type.

   IT LIVES HERE ONCE. Five pages each carried their own lockup block; this is the whole identity,
   in the shared sheet every page already links.

   ── THE SIGNATURE ─────────────────────────────────────────────────────────────────────────────
   IT IS A MASK, NOT A BACKGROUND IMAGE, and that is the whole reason there is only one file.
   The drawing is a single-colour silhouette, so painting it means painting ONE shape — and a
   mask does exactly that: the SVG supplies the shape, `background` supplies the colour, and
   `currentColor` makes it follow whatever ink the surrounding text is already using. No second
   cut for night, no [data-theme] rule here at all, no chance of the two files drifting apart.
   The retired two-tone mark needed a light cut and a dark cut precisely because it was two
   colours and a mask cannot carry two.
   Both the prefixed and unprefixed properties are set, in that order; Safari still wants
   -webkit-mask and ignoring it silently paints a solid rectangle rather than the logo.

   THE GOLD IS A SEPARATE FILE, AND IT IS SPENT, NOT SPREAD. The artwork's #efc368 measures
   1.72:1 on --paper #ececec — not illegible-as-a-logo, but a squint, and a smudge once small.
   So the metal keeps its gradient in leorve-script-gold.png (a raster: a traced vector cannot
   carry a gradient, and the gradient is what makes it read as metal rather than a yellow line)
   and appears ONLY where it has a dark ground: the app's opening over photography, and a footer
   on ink. Every other surface — every nav, every bar — uses the masked cut in the page's own
   ink, so the chrome stays dead-neutral. That is how a house spends a metal: in one place, so it
   still means something.

   THE BOX CARRIES THE RATIO. aspect-ratio:595/308 is the traced bounding box, so height alone is
   set and width follows — the signature can never be stretched, and its space is reserved before
   the file decodes, so nothing reflows on load. */
.lv-mark{display:block;height:var(--lv-h,30px);aspect-ratio:595/308;background:currentColor;
  -webkit-mask:center/contain no-repeat url("/app/brand/leorve-script.svg");
  mask:center/contain no-repeat url("/app/brand/leorve-script.svg")}
/* THE GOLD CUT. Add .lv-mark-gold ALONGSIDE .lv-mark, on a dark ground only. It replaces the
   mask with the real metal, so it is a raster and cannot be recoloured — which is the point. */
.lv-mark-gold{background:center/contain no-repeat url("/app/brand/leorve-script-gold.png");
  -webkit-mask:none;mask:none}

/* ── THE NAME, AS TYPE ──────────────────────────────────────────────────────────────────────────
   Julius Sans One (Luciano Vergara / LatinoType, SIL OFL — see app/brand/JULIUS-SANS-ONE-OFL.txt).
   Chosen by comparing candidates against the artwork letter by letter, not by eye: it matches the
   thin monoline weight, the circular O and the R's straight kicked-out leg. Poiret One, which the
   previous identity used, is visibly wrong here — its capitals are more delicate and Art Deco.

   SELF-HOSTED AND SUBSET TO FIVE GLYPHS — 'ELORV', 2,700 bytes. Not the CDN: a wordmark that
   arrives late is a wordmark that pops. The unicode-range is the guard — applied to text that is
   not the house name, the browser falls back per glyph instead of drawing tofu.

   THE ARTWORK MEASURES 0.71em, and the product ships 0.44 — see --lv-track below for why.
   The measurement: Julius Sans One set against the artwork by binary search on the
   aspect ratio: the artwork's LEORVE is 9.912 wide to its cap height, the face's natural setting
   is 5.007, and 0.7115em closes the gap. That is 14.2px between letters at a 20px name — very
   wide, and that IS the logo; do not "tidy" it.

   THE NEGATIVE MARGIN IS NOT A HACK, and at this tracking it matters more than usual: CSS
   letter-spacing appends its space after EVERY character including the last, so an untreated
   LEORVE carries a full --lv-track tail — most of a whole letter of empty space. Left in, it pushes the
   word off-centre when stacked and puts nothing, rather than the final E, against whatever sits
   to its right. Both values are em of this element's own font-size, so this cancels exactly. */
@font-face{font-family:"Leorve Wordmark";
  src:url("/app/brand/julius-leorve.woff2") format("woff2");
  font-weight:400;font-style:normal;font-display:swap;
  unicode-range:U+45,U+4C,U+4F,U+52,U+56}
/* THE TRACKING IS A TOKEN, AND IT IS NOT THE ARTWORK'S NUMBER. Measured off the supplied logo it
   is 0.71em — but that is DISPLAY tracking, set for a mark drawn a thousand pixels wide. Carried
   unchanged onto an 11px nav it scatters the word into six separate letters that the eye has to
   reassemble, and it eats most of a phone's header doing it. 0.44em keeps the logo's character —
   it still reads as deliberately, unusually wide — while letting LEORVE read as one word.
   Letting the space between letters shrink as type gets smaller is ordinary optical sizing, not a
   deviation: a display cut and a text cut of the same face differ in exactly this way.
   It is a custom property so a genuinely large use (a splash, a print sheet) can raise it back
   toward the artwork's 0.71 without touching six files. */
.lv-word{font-family:"Leorve Wordmark",var(--serif),Georgia,serif;font-weight:400;
  --lv-track:.44em;
  font-size:var(--lv-word,15px);letter-spacing:var(--lv-track);margin-right:calc(-1 * var(--lv-track));
  line-height:1;color:var(--ink);white-space:nowrap}

/* NO TRADEMARK MARK ON THE WORDMARK.
   This rule used to append a ™ to every .lv-word, on the reasoning that the supplied artwork pack
   shipped ™ lockups. The owner has confirmed it does not — the artwork is the signature and the
   name, and nothing else. A mark the brand does not actually use is worse than no mark: it is a
   claim printed on every page of the site that the artwork itself does not make.

   Removed rather than commented out at the call sites, because the whole point of drawing it in
   CSS was that one rule governs all five pages. Reinstating it is this one block:
       .lv-word::after { content: "2"; font-size: .5em; letter-spacing: 0;
                         vertical-align: super; margin-left: .18em; }
   ™ AND NOT ®, if it ever comes back: ® asserts a REGISTERED mark and displaying it before
   registration completes is an offence, while ™ merely claims the mark. */

/* ── THE LOCKUPS ────────────────────────────────────────────────────────────────────────────────
   The artwork is STACKED — signature over name — and the footer cut reproduces that. The nav cut
   cannot: even at 0.44em the name alone is 8.08 cap-heights wide, so a horizontal lockup at a
   readable size would eat most of a phone's header. So the nav sets the name SMALLER than the
   signature suggests and lets the signature lead, which is the right emphasis anyway — the script
   is the recognisable thing, the capitals are the caption. */
.lv-lock{display:inline-flex;align-items:center;gap:12px;text-decoration:none;color:inherit;
  --lv-h:26px;--lv-word:11px;margin-right:18px}
.lv-lockstack{display:inline-flex;flex-direction:column;align-items:center;gap:10px;
  text-decoration:none;color:inherit;--lv-h:52px;--lv-word:14px}
.lv-lockstack .lv-wm-tag{margin-top:12px}
/* Registered wording, always spelled out in full, and REAL TEXT rather than part of the picture:
   it is a sentence the site is making, not a shape. Fraunces italic, the house accent face. */
.lv-wm-tag{font-family:var(--serif);font-style:italic;font-weight:400;font-size:12px;
  letter-spacing:.05em;color:var(--ink-dim);line-height:1}


/* A NUMBERED SEQUENCE, set as type rather than drawn as a widget. The counter is a hairline
   number in the margin; there is no circle, no chip and no icon, because three of those in a
   column is a diagram and what this is is a sentence in three parts. */
.lv-steps{list-style:none;counter-reset:lv;margin:0;padding:0;
  display:flex;flex-direction:column;gap:18px}
.lv-steps > li{counter-increment:lv;position:relative;padding-left:30px;
  font:400 13px/1.6 var(--body);color:var(--ink-dim)}
.lv-steps > li::before{content:counter(lv,decimal-leading-zero);position:absolute;left:0;top:1px;
  font:500 11px/1.5 var(--body);letter-spacing:.12em;color:var(--muted);font-variant-numeric:tabular-nums}
.lv-steps > li b{font-weight:600;color:var(--ink)}

/* THE THEME SWITCH IS GONE. Two classes drew it — .lv-toggle on five pages and .lv-theme on
   /reading — and both are deleted with the theme they switched. A control with one position is
   not a control. */


/* ═══════ THE 44px FLOOR FOR THE SHARED CHROME ═══════
   Measured at 375x812 across /shop, /reading, /wardrobe and /account — the four pages that share
   this sheet — sixteen controls came in under the 44px both Apple and Google publish as the
   minimum comfortable target: the wordmark at 26 tall, product titles at 21 and every footer link at
   17 among them. (The bar's own controls — account, bag, burger — live in the capsule now, which
   draws every one of them as a 44px circle at every pointer: lv-capsule.css .lv-circ.)

   Text controls get a PSEUDO-ELEMENT: a wordmark, product titles, footer links. Growing them with
   padding would move the layout they sit in and change the spacing the design was drawn to. An
   ::after with a negative vertical inset adds hit area on top of the layout while occupying none of
   it. AND THE EXPANSION IS VERTICAL ONLY. These sit in rows 20px apart; a sideways inset would reach
   into the neighbour and take taps meant for it.

   The footer's row-gap goes 20 -> 28 for the same reason. Those links wrap to two rows with 17px
   of text and a 20px gap, a 37px pitch, so a 44px hit area would have overlapped the row above by
   7px and the lower row would have won every contested tap. 28px of gap makes the pitch 45 and the
   targets fit between the rows instead of fighting over them. */
@media (pointer: coarse) {
  .lv-lock, .card-name, .foot .links a, .crumb a { position: relative; }
  .lv-lock::after, .card-name::after, .foot .links a::after, .crumb a::after {
    content: ""; position: absolute; left: 0; right: 0;
    top: 50%; transform: translateY(-50%); height: 44px;
    z-index: 0;                     /* behind the element's own text, never over it */
  }

  .foot .links { row-gap: 28px; }

  /* ── AND NO FIELD BELOW 16px ────────────────────────────────────────────────────────────────
     iOS Safari zooms the page when a text field whose computed font-size is under 16px takes
     focus, and it does NOT zoom back out afterwards — the customer is left on a page they have to
     pinch to read, mid-form. studio.html and admin.html have carried this guard for a while;
     /reading is the page that never got it. Measured there at 375px: nineteen fields, twelve of
     them zoom-triggering types (ten text, two select), every one computing to 15px. They are
     hidden until the manual-entry flow opens, which is exactly when a customer is typing.
     Declared here rather than per page so the next page to grow a form inherits it. */
  /* !important, for the reason studio.html and admin.html already carry it: the pages' own field
     rules are CLASS selectors and a bare element selector loses to them on specificity. Measured
     without it, /reading still reported twelve fields at 15px — the rule was present and silently
     outranked. */
  input, select, textarea { font-size: 16px !important; }

  .lv-btn { min-height: 44px; }

  /* ── TWO MORE THE FIRST PASS MISSED, BOTH MEASURED BY HIT-TESTING RATHER THAN BY READING CSS ──
     Probing with elementFromPoint at the centre +/- 22px says what a thumb actually hits, and it
     found /quiz's exit and /wardrobe's "See Plus" link at 17px and 22px tall. min-height rather than
     an overlay: growing the box is one declaration and cannot reach into a neighbour the way a 44px
     band can. Neither is in a stacked list, so nothing below them moves. */
  .skip-link { display: inline-flex; align-items: center; min-height: 44px; }
  .wd-link { min-height: 44px; }

  /* /reading's footer links are `a` inside a flex row; its gap-8 row already leaves 32px between
     rows, wide enough that a 44px band clears its neighbour. (Its #navbar header, which this block
     also used to reach, is gone: the capsule replaced it.) */
  footer .flex > a { position: relative; }
  footer .flex > a::after {
    content: ""; position: absolute; left: -10px; right: -10px;
    top: 50%; transform: translateY(-50%); height: 44px;
    z-index: 0;
  }
}

/* ── LEORVE PLUS: THE SHEET AND THE FREE-READING LINE ────────────────────────────────────────
   Drawn by /static_v2/js/lv-plus.js on every page that can be refused by a Plus gate, so it lives
   in the one sheet those pages share rather than in five <style> blocks that would drift. A BOTTOM
   SHEET on a phone, because that is where the thumb already is when a tap is refused; a centred card
   from 640px. Monochrome like everything else: the scrim is a neutral, the primary is --accent.
   v7: IT IS GLASS NOW, THE ACCOUNT DIALOG'S (owner brief: "more glass effect in pop up"; verify
   site1-18, site2-25). The card wears .lv-crystal and lv-auth.css draws the pane — fill, hairline,
   specular rim, the frost veil that clears once the card has landed — so the two pop-ups a refused
   customer can meet are one material. What is below is only the sheet's own layout and the solid
   plate it falls back to when lv-auth.css is not on the page (the class then styles nothing, and the
   --card-2 plate here is what shows). It is the FALLBACK now: with lv-auth.js on the page a refusal
   opens the account dialog itself (lv-plus.js open()).
   QUIET TYPE ON THE GLASS IS --ink-2, NEVER --ink-dim OR --muted: the pane is .72 white (.80 on a
   phone), which over the black bound composites to 184 — #444 holds 4.89:1 there, #5E5E5E and
   #6B6B6B fall to 3.3 and 2.7. The eyebrow, the close glyph and the fine print moved up a step. */
.lvp{margin:auto auto 0;padding:0;border:0;width:100%;max-width:100%;max-height:92dvh;
  background:transparent;color:var(--ink);overflow:visible}
/* LITERALS, NOT var(): ::backdrop inherits custom properties only on recent engines. The page is
   dimmed and softly blurred behind the pane, the account dialog's own backdrop; a phone dims only
   (one live layer — the card). */
.lvp::backdrop{background:rgba(8,8,8,.32);-webkit-backdrop-filter:blur(10px) saturate(.9);backdrop-filter:blur(10px) saturate(.9)}
/* THE SCROLLER IS INSIDE THE CARD (.lvp-scroll), as in the account dialog: overflow on the card
   itself would clip the crystal's specular rim, which sits ON the 1px border (inset -1px). */
.lvp-card{position:relative;display:flex;flex-direction:column;background:var(--card-2);border:1px solid var(--line);
  border-radius:28px 28px 0 0;box-shadow:var(--shadow-2);max-height:92dvh}
.lvp-scroll{overflow:auto;overscroll-behavior:contain;border-radius:inherit;
  padding:26px 22px calc(20px + env(safe-area-inset-bottom))}
@media (min-width:640px){
  .lvp{margin:auto;width:min(460px,calc(100% - 48px))}
  .lvp-card{border-radius:28px}
  .lvp-scroll{padding:30px 30px 24px}
}
@media (max-width:767.98px){
  .lvp::backdrop{-webkit-backdrop-filter:none;backdrop-filter:none;background:rgba(8,8,8,.36)}
}
@media (prefers-reduced-transparency:reduce){
  .lvp::backdrop{-webkit-backdrop-filter:none;backdrop-filter:none;background:rgba(8,8,8,.40)}
}
.lvp-x{position:absolute;top:12px;right:12px;width:44px;height:44px;display:grid;place-items:center;
  border:1px solid rgba(17,17,17,.10);border-radius:999px;background:rgba(255,255,255,.55);
  box-shadow:inset 0 1px 0 rgba(255,255,255,.9);color:var(--ink);cursor:pointer;z-index:3}
.lvp-x svg{width:18px;height:18px;fill:none;stroke:currentColor;stroke-width:1.6;stroke-linecap:round}
.lvp-x:focus-visible,.lvp-btn:focus-visible,.lvp-later:focus-visible{outline:2px solid var(--accent);outline-offset:3px}
.lvp-eyebrow{margin:0;font:700 11px/1.2 var(--body);letter-spacing:.2em;text-transform:uppercase;color:var(--ink-2)}
.lvp-title{margin:12px 44px 0 0;font:600 26px/1.12 var(--display);letter-spacing:-.02em;color:var(--ink)}
.lvp-detail{margin:12px 0 0;font:400 15px/1.55 var(--body);color:var(--ink-2)}
.lvp-incl{list-style:none;margin:16px 0 0;padding:0;border-top:1px solid rgba(17,17,17,.08)}
.lvp-incl li{padding:10px 0 10px 22px;border-bottom:1px solid rgba(17,17,17,.08);position:relative;
  font:400 14px/1.5 var(--body);color:var(--ink)}
.lvp-incl li::before{content:"";position:absolute;left:2px;top:15px;width:9px;height:5px;
  border-left:1.6px solid var(--ink);border-bottom:1.6px solid var(--ink);transform:rotate(-45deg)}
.lvp-price{margin:14px 0 0;font:600 15px/1.3 var(--body);color:var(--ink);font-variant-numeric:tabular-nums}
.lvp-acts{margin-top:20px;display:grid;gap:10px}
.lvp-btn{display:flex;align-items:center;justify-content:center;min-height:52px;padding:0 20px;
  border-radius:999px;border:1px solid var(--accent);background:var(--accent);color:var(--on-accent);
  box-shadow:inset 0 1px 0 rgba(255,255,255,.16),inset 0 -1px 0 rgba(0,0,0,.40),
    0 1px 2px rgba(17,17,17,.22),0 10px 22px -12px rgba(17,17,17,.55);
  font:600 15px/1 var(--body);letter-spacing:.01em;text-decoration:none;cursor:pointer}
.lvp-btn.lvp-ghost{background:rgba(255,255,255,.55);color:var(--ink);border-color:rgba(17,17,17,.16);
  box-shadow:inset 0 1px 0 rgba(255,255,255,.9)}
.lvp-btn:active{transform:scale(.985)}
/* `hidden` loses to display:flex above; said again so the unused second button is not an empty pill. */
.lvp-btn[hidden]{display:none}
.lvp-later{min-height:44px;border:0;background:transparent;color:var(--ink);cursor:pointer;
  font:500 14px/1 var(--body);text-decoration:underline;text-underline-offset:4px}
.lvp-fine{margin:10px 0 0;text-align:center;font:400 12.5px/1.5 var(--body);color:var(--ink-2)}
@media (hover:hover) and (pointer:fine){
  .lvp-btn:not(.lvp-ghost):hover{background:var(--accent-hover);border-color:var(--accent-hover)}
  .lvp-btn.lvp-ghost:hover{background:rgba(255,255,255,.80)}
  .lvp-x:hover{background:rgba(255,255,255,.85)}
  .lvp-later:hover{text-decoration-thickness:2px}
}
@media (prefers-reduced-motion:no-preference){
  .lvp[open] .lvp-card{animation:lvp-rise .32s var(--ease)}
  @keyframes lvp-rise{from{transform:translateY(24px);opacity:0}to{transform:none;opacity:1}}
}
@media (prefers-reduced-motion:reduce){ .lvp-btn:active{transform:none} }
/* THE FREE-READING LINE, above the first control of a reading. Quiet on purpose: it is a fact about
   the account, not a sales panel, and the sheet is where Plus is actually offered. */
.lvp-quota{margin:14px 0 0;font:400 13.5px/1.55 var(--body);color:var(--ink-2)}
.lvp-quota-link{color:var(--ink);font-weight:600;text-decoration:underline;text-underline-offset:3px}

/* ── THE MEMBER: CARD, PLAN ROWS, "YOUR MONTH", MONOGRAM (DESIGN_member §3) ─────────────────────
   Drawn by /static_v2/js/lv-plus.js on /account, /membership, /wardrobe, /reading and /shop, so its
   styles live beside the refusal sheet's in the one file those pages share.
   PREMIUM IS SHOWN BY MATERIAL, NOT BY COLOUR, VOLUME OR A BADGE (owner: "simple white or black like
   mnml.la"). Free draws no object; Plus is a white plate with a 1.5px ink edge; Premium an ink
   plate with an engraved inner hairline. No pill, no capsule, no uppercase tier anywhere. Every
   literal below is achromatic (channel spread 0): the scrim rgba(8,8,8,.4), on-ink text
   rgba(255,255,255,.72) (9.6:1 on #111), the engraved line rgba(255,255,255,.16), the swatch ring
   rgba(0,0,0,.12), and the exit curve. The only colour in any of these objects is the customer's
   own — their garment photographs and their taste swatches.
   STYLED ON data-rank, THE SERVED tier_rank, NEVER ON A TIER WORD. A rank above Premium is styled as
   Premium (the :not() chains), so a fourth tier never draws as Free.
   EVERY ENTRANCE IS AN ANIMATION THAT LANDS ON THE VISIBLE STATE (fill-mode backwards), never a
   resting opacity:0 a failed script would strand (tests/test_reveal_failsafe.py), and every one
   has its prefers-reduced-motion answer. Hover is inside (hover:hover) and (pointer:fine) only. */
html.lvm-lock{overflow:hidden}

/* THE MONOGRAM THAT STOOD HERE (.lvm-mono, and the /account ring drawn around it on the old bar) IS
   RETIRED with the bar it lived in (v7 NAV_SPEC §9.1). The capsule's account circle draws the initials,
   the Plus ring (rank 1), the Premium disc (rank 2+) and /account's own outer ring itself —
   lv-capsule.css a.lv-acct[data-rank] / a.lv-acct.on, painted by lv-nav.js from the served tier_rank —
   and it is the flagship's monogram, the same on every page. */

/* The member card (§3.3). Plus's edge is an INSET shadow, not a border, so the two plates are the
   same box and nothing shifts between tiers. */
.lvm-mc{position:relative;display:grid;grid-template-columns:1fr;box-sizing:border-box;min-height:188px;
  padding:22px 22px 18px;border-radius:var(--r-lg);text-align:left;background:var(--card);color:var(--ink)}
.lvm-mc[data-rank="1"]{box-shadow:inset 0 0 0 1.5px var(--ink)}
.lvm-mc:not([data-rank="0"]):not([data-rank="1"]){background:var(--ink);color:#FFFFFF;
  box-shadow:inset 0 0 0 7px var(--ink),inset 0 0 0 8px rgba(255,255,255,.16),var(--shadow-2)}
.lvm-mc-k{margin:0 0 14px;font:600 11px/1 var(--body);letter-spacing:.14em;text-transform:uppercase;opacity:.72}
.lvm-mc-tier{margin:0;font:600 26px/1.08 var(--display);letter-spacing:-.02em}
.lvm-mc-name{margin:6px 0 0;font:500 15px/1.3 var(--body)}
.lvm-mc-meta{margin:16px 0 0;font:400 13px/1.5 var(--body);color:var(--ink-dim)}
.lvm-mc-meta span{display:block}
.lvm-mc:not([data-rank="0"]):not([data-rank="1"]) .lvm-mc-meta{color:rgba(255,255,255,.72)}
.lvm-mc-act{justify-self:start;display:inline-flex;align-items:center;gap:6px;min-height:32px;margin:12px 0 0;
  padding:0;background:none;border:0;color:inherit;cursor:pointer;font:600 13px/1 var(--body);
  text-decoration:underline;text-decoration-thickness:1px;text-underline-offset:4px}
.lvm-mc-act svg{width:8px;height:8px;flex:none}
.lvm-mc-act:active{opacity:.7}
.lvm-mc-act:focus-visible{outline:2px solid currentColor;outline-offset:3px}
@media (pointer:coarse){.lvm-mc-act{min-height:44px}}

/* The plan rows, "What your plan includes" (§3.4). Always on paper, never on the ink card, so one
   set of colours serves every host. */
.lvm-pr{margin:0;padding:0;border-top:1px solid var(--line);max-width:640px}
.lvm-pr-row{display:grid;grid-template-columns:minmax(120px,40%) 1fr;column-gap:16px;align-items:baseline;
  padding:14px 0;border-bottom:1px solid var(--line)}
.lvm-pr-row dt{margin:0;font:500 14px/1.4 var(--body);color:var(--ink)}
.lvm-pr-row dd{margin:0;font:400 14px/1.45 var(--body);color:var(--ink-2);text-align:right;
  font-variant-numeric:tabular-nums}
.lvm-pr-sub{display:block;margin-top:2px;font-size:12px;line-height:1.45;color:var(--ink-dim)}
.lvm-pr-meter{grid-column:1/-1;height:2px;margin:10px 0 0;background:var(--line);border-radius:1px;overflow:hidden}
.lvm-pr-meter i{display:block;height:100%;width:100%;background:var(--ink);transform-origin:0 50%;
  transform:scaleX(var(--lvm-fill,1))}
.lvm-pr-fine{grid-column:1/-1;margin:8px 0 0;font:400 12px/1.45 var(--body);color:var(--ink-dim)}
.lvm-pr-foot{margin:6px 0 0}
.lvm-pr-manage{display:inline-flex;align-items:center;min-height:44px;font:600 14px/1 var(--body);
  color:var(--ink);text-decoration:underline;text-decoration-thickness:1px;text-underline-offset:4px}
.lvm-pr-manage:focus-visible{outline:2px solid var(--ink);outline-offset:2px}

/* "Your month" (§3.5): a 440px side sheet from 768px, a bottom sheet below it. The UA's modal
   max-width / max-height are reset or the side sheet stops short of the edges.
   v7: THE FLAGSHIP'S SHEET, IN ITS GLASS (owner brief "more glass … continuation"; NAV_SPEC). On / it
   is a Vellum pane floating 12px in from the edges (globals.css .lv-month.lv-vellum: --vellum-sheet-fill
   .92, blur 28 / 20 coarse); here it was an opaque edge-to-edge panel, so the same "Your month" door
   in the same capsule opened two different objects a page apart. The numbers are the flagship's,
   written as literals because this sheet has no Vellum tokens. AA at .92: the pane composites to 235
   over the black bound, where --ink-dim (the sheet's quietest text) holds 5.47:1. */
.lvm{position:fixed;inset:12px 12px 12px auto;margin:0;padding:0;
  width:min(440px,calc(100vw - 24px));max-width:none;height:auto;max-height:none;overflow:hidden;
  border-radius:28px;border:1px solid rgba(17,17,17,.08);
  background:linear-gradient(180deg,rgba(255,255,255,.55) 0%,rgba(255,255,255,0) 42%),rgba(255,255,255,.92);
  background-clip:padding-box;
  -webkit-backdrop-filter:blur(28px) saturate(.7);backdrop-filter:blur(28px) saturate(.7);
  box-shadow:inset 0 1px 0 rgba(255,255,255,.95),inset 0 0 0 1px rgba(255,255,255,.35),
    0 1px 2px rgba(17,17,17,.05),0 18px 40px -22px rgba(17,17,17,.28);
  color:var(--ink)}
/* LITERALS, NOT var(): ::backdrop inherits custom properties only on recent engines. */
.lvm::backdrop{background:rgba(8,8,8,.32);-webkit-backdrop-filter:blur(10px) saturate(.9);backdrop-filter:blur(10px) saturate(.9)}
@media (pointer:coarse){.lvm{-webkit-backdrop-filter:blur(20px) saturate(.7);backdrop-filter:blur(20px) saturate(.7)}}
/* Never see-through without a blur, and solid when the customer asked for solid surfaces. */
@supports not ((backdrop-filter:blur(1px)) or (-webkit-backdrop-filter:blur(1px))){ .lvm{background:#FAFAFA} }
@media (prefers-reduced-transparency:reduce){
  .lvm{background:#FAFAFA;-webkit-backdrop-filter:none;backdrop-filter:none}
  .lvm::backdrop{-webkit-backdrop-filter:none;backdrop-filter:none;background:rgba(8,8,8,.40)}
}
.lvm-card{display:flex;flex-direction:column;height:100%;max-height:inherit}
.lvm-head{flex:none;position:relative;display:flex;align-items:center;justify-content:space-between;
  min-height:64px;padding:0 12px 0 32px}
.lvm-grab{display:none}
.lvm-title{margin:0;font:600 15px/1.2 var(--body);letter-spacing:0;color:var(--ink)}
/* THE TIER OVER THE TITLE (v7 WARDROBE_SPEC §5 V12/V31; the app sheet's eyebrow): whose month it is, in
   the account dialog's eyebrow voice (lv-auth.css .lva-eyebrow) so the website's two glass sheets speak
   one way. --ink-2 on the .92 pane holds AA at 11px bold (the pane composites to >= 235). */
.lvm-ttl{min-width:0}
.lvm-eyebrow{margin:0 0 4px;font:700 11px/1.2 var(--body);letter-spacing:.2em;text-transform:uppercase;color:var(--ink-2)}
.lvm-eyebrow:empty{display:none}
.lvm-title:focus{outline:none}
.lvm-title:focus-visible{outline:2px solid var(--ink);outline-offset:2px}
.lvm-x{width:44px;height:44px;display:grid;place-items:center;border:0;border-radius:999px;
  background:transparent;color:var(--ink-dim);cursor:pointer}
.lvm-x svg{width:18px;height:18px;fill:none;stroke:currentColor;stroke-width:1.6;stroke-linecap:round}
.lvm-x:focus-visible{outline:2px solid var(--ink);outline-offset:2px}
.lvm-body{flex:1 1 auto;min-height:0;overflow-y:auto;overscroll-behavior:contain;padding:8px 32px 40px}
@media (max-width:767.98px){
  /* A phone: one live layer — the sheet. The page is dimmed, not blurred. */
  .lvm::backdrop{-webkit-backdrop-filter:none;backdrop-filter:none;background:rgba(8,8,8,.36)}
  .lvm{inset:auto 0 0 0;width:100%;height:auto;max-height:92svh;border:0;border-top:1px solid rgba(17,17,17,.08);
    border-radius:28px 28px 0 0}
  .lvm-card{max-height:92svh}
  /* The head is the drag handle: touch-action:none so the browser hands the finger to the sheet
     instead of scrolling the page under it. The body still scrolls natively. */
  .lvm-head{padding:14px 4px 0 16px;min-height:60px;touch-action:none}
  .lvm-grab{display:block;position:absolute;top:8px;left:50%;width:36px;height:4px;margin-left:-18px;
    border-radius:2px;background:var(--line-2)}
  .lvm-body{padding:4px 16px calc(24px + env(safe-area-inset-bottom))}
}

/* The blocks (§3.6): one hairline between them, sentence-case labels, nothing boxed inside the sheet. */
.lvm-b + .lvm-b{margin-top:20px;padding-top:20px;border-top:1px solid var(--line)}
/* SENTENCE-CASE SECTION LABELS (v7 WARDROBE_SPEC §5 V12; the app's .mb-k): with the tier now the sheet's
   ONE tracked eyebrow, five more tracked-capital labels under it gave every block the same shout and no
   hierarchy, and the app had already moved to these words in this weight. */
.lvm-k{margin:0 0 10px;font:600 13px/1.3 var(--body);letter-spacing:0;text-transform:none;color:var(--ink-2)}
.lvm-k2{margin-top:16px}
.lvm-lead{margin:0;font:400 15px/1.45 var(--body);color:var(--ink)}
.lvm-line{margin:0;font:400 15px/1.45 var(--body);color:var(--ink)}
.lvm-dim{margin:6px 0 0;font:400 13px/1.45 var(--body);color:var(--ink-dim)}
.lvm-fine{margin:8px 0 0;font:400 12px/1.45 var(--body);color:var(--ink-dim)}
.lvm-reason{margin:10px 0 0;max-width:40ch;font:400 14px/1.45 var(--body);color:var(--ink-2)}
.lvm-worn{font-size:13px;color:var(--ink-dim)}
.lvm-today{display:flex;gap:8px;margin:0;padding:0;list-style:none}
.lvm-today li{width:64px;height:80px;flex:none;border-radius:12px;overflow:hidden;background:var(--card-2);
  border:1px solid var(--line)}
.lvm-today img{width:100%;height:100%;object-fit:cover;display:block}
.lvm-figs{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:10px 16px;margin:0;padding:0;list-style:none}
.lvm-figs li{display:flex;align-items:baseline;gap:6px;min-width:0}
.lvm-n{font:500 20px/1 var(--display);letter-spacing:-.01em;color:var(--ink);font-variant-numeric:tabular-nums}
.lvm-l{font:400 14px/1.4 var(--body);color:var(--ink-2)}
.lvm-tname{margin:0;font:600 18px/1.2 var(--display);letter-spacing:-.01em;color:var(--ink)}
.lvm-sws{display:flex;gap:6px;margin:10px 0 0}
.lvm-sw{width:16px;height:16px;border-radius:50%;box-shadow:inset 0 0 0 1px rgba(0,0,0,.12)}
.lvm-mem{margin:0;padding:0;list-style:none}
.lvm-mem li{padding:8px 0;font:400 14px/1.45 var(--body);color:var(--ink-2)}
.lvm-mem li + li{border-top:1px solid var(--line)}
.lvm-mem b{font-weight:600;color:var(--ink)}
.lvm-last{margin:0;font:400 14px/1.45 var(--body);color:var(--ink-2)}
.lvm-acts{margin:12px 0 0}
/* nowrap: every .lvm-link is a two- or three-word action, and the sheet's narrow blocks folded "See
   saved looks" across two lines at 1440 (v3 verification), so the underline read as two links. */
.lvm-link{display:inline;padding:0;background:none;border:0;cursor:pointer;color:var(--ink);white-space:nowrap;
  font:600 13px/1.4 var(--body);text-decoration:underline;text-decoration-thickness:1px;text-underline-offset:4px}
.lvm-btn{display:inline-flex;align-items:center;justify-content:center;min-height:44px;padding:0 20px;
  border-radius:999px;border:1px solid var(--accent);background:var(--accent);color:var(--on-accent);
  font:600 14px/1 var(--body);text-decoration:none;cursor:pointer}
.lvm-btn.lvm-ghost{background:transparent;color:var(--ink);border-color:var(--line-2)}
.lvm-link:active,.lvm-btn:active{opacity:.7}
.lvm-link:focus-visible,.lvm-btn:focus-visible{outline:2px solid var(--ink);outline-offset:2px}
/* 44px under a coarse pointer without growing the line (§3.8). */
@media (pointer:coarse){.lvm-link{padding-block:12px;margin-block:-12px}}
.lvm-foot{margin:24px 0 0;padding-top:12px;border-top:1px solid var(--line)}
.lvm-err{padding:12px 0}
/* Still planes while the read is in flight — no shimmer (the house rule, account.html .skeleton). */
.lvm-sk{background:var(--paper-2);border:1px solid var(--line);border-radius:12px}
.lvm-sk + .lvm-sk{margin-top:20px}
.lvm-sk-1{height:132px}.lvm-sk-2{height:76px}.lvm-sk-3{height:88px}
.lvm-b .lvm-pr-meter{margin:10px 0 6px}

@media (hover:hover) and (pointer:fine){
  .lvm-mc-act,.lvm-link,.lvm-pr-manage{transition:text-decoration-thickness .16s var(--ease)}
  .lvm-mc-act:hover,.lvm-link:hover,.lvm-pr-manage:hover{text-decoration-thickness:2px}
  .lvm-btn:hover{opacity:.88}
  .lvm-x:hover{color:var(--ink)}
}
@media (hover:hover) and (pointer:fine) and (prefers-reduced-motion:no-preference){
  .lvm-mc-act svg{transition:transform .16s var(--ease)}
  .lvm-mc-act:hover svg{transform:translateX(2px)}
}

/* MOTION (§3.7). M3 side sheet 320ms in / 200ms out; M4 bottom sheet 360ms in / 240ms out; M6 the
   blocks' first arrival; M7 the meter filling to what is left. lv-plus.js closes the dialog on
   animationend (with a timer behind it), so an exit always finishes on screen. */
@keyframes lvm-side-in{from{transform:translateX(32px);opacity:0}to{transform:none;opacity:1}}
@keyframes lvm-out-side{from{transform:none;opacity:1}to{transform:translateX(24px);opacity:0}}
@keyframes lvm-sheet-in{from{transform:translateY(100%)}to{transform:none}}
@keyframes lvm-out-sheet{from{transform:none}to{transform:translateY(100%)}}
@keyframes lvm-fade-in{from{opacity:0}to{opacity:1}}
@keyframes lvm-out-fade{from{opacity:1}to{opacity:0}}
@keyframes lvm-in{from{opacity:0;transform:translateY(6px)}to{opacity:1;transform:none}}
@keyframes lvm-meter{from{transform:scaleX(0)}}
@media (prefers-reduced-motion:no-preference){
  .lvm[open]{animation:lvm-side-in .32s var(--ease) backwards}
  .lvm[open]::backdrop{animation:lvm-fade-in .24s linear backwards}
  .lvm.is-out{animation:lvm-out-side .2s cubic-bezier(.4,0,1,1) forwards}
  .lvm.is-out::backdrop{animation:lvm-out-fade .2s cubic-bezier(.4,0,1,1) forwards}
  .lvm-in{animation:lvm-in .28s var(--ease) backwards}
  .lvm-pr-meter i{animation:lvm-meter .6s var(--ease) backwards}
}
@media (prefers-reduced-motion:no-preference) and (max-width:767.98px){
  .lvm[open]{animation:lvm-sheet-in .36s var(--ease) backwards}
  .lvm.is-out{animation:lvm-out-sheet .24s cubic-bezier(.4,0,1,1) forwards}
}
/* REDUCED: opacity only, 120ms, both ways; no block entrance, no meter fill, no translate. */
@media (prefers-reduced-motion:reduce){
  .lvm[open],.lvm[open]::backdrop{animation:lvm-fade-in .12s linear backwards}
  .lvm.is-out,.lvm.is-out::backdrop{animation:lvm-out-fade .12s linear forwards}
  .lvm-in,.lvm-pr-meter i{animation:none}
}

/* ═══ 8. THE EDITORIAL BAND ══════════════════════════════════════════════════════════════════
   WHY THIS EXISTS. With the hue gone there is exactly one loud surface left in the house, and it
   is a photograph. The owner asked for two things in one sentence — "simple white or black" AND
   "make more visualize like the screenshot" — and those only reconcile one way: the PAGE gets
   quieter and the PICTURES get bigger. Every colour this pass removed has to be paid back in
   imagery or the site is just greyer than it was.

   THE DEVICE, from the owner's screenshots: ONE wide photograph, full-bleed; a small tracked-caps
   eyebrow, a very large headline in plain white, and an underlined "shop now →" link, all set on
   the image at the LEFT. That is the whole component. It is not a hero and it is not a card — it
   is a RULE between two stretches of type, and its job is to stop a page being six screens of
   paragraphs.

   IT IS BUILT AS ONE CLASS AND USED SIX TIMES, which is the point: the five pages each grew their
   own "band" (shop.html's .band, membership.html's .m-band, wardrobe.html's .wt-banner) and none
   of them look like each other. New work takes this.

   WHAT IT IS NOT: a tinted veil. The scrim is BLACK ALPHA ONLY, for the reason --on-image already
   records — a tint over a garment shifts the garment's colour, and garment colour is product
   truth. It is also never used to dress up a generated image as product photography: the caption
   and the copy say what the picture is, and the four Collection 01 frames are the only files on
   this site that may be captioned as the garment you are buying.

   ── THE CROP IS THE HARD PART, AND IT IS WORTH WRITING DOWN. Nearly every photograph this site
   owns is 4:5 PORTRAIT. A full-bleed band at 1440 is between 2.2:1 and 3.5:1, so a portrait file is
   cropped to roughly a THIRD of its own height and whatever was at the top and bottom is simply
   gone — AND, because the file is only 1200px wide, it is also UPSCALED onto a 1440 viewport, which
   is what the owner saw and called "very low quality". So:
     · --lv-ed-focus sets object-position PER BAND. It is a variable rather than a class because
       the right value is a property of the FILE, not of the layout, and the next person changing
       the copy must not have to rediscover it.
     · --lv-ed-h sets the band's height per band. A portrait file wants a TALLER band (less crop);
       a wide file can take a shorter one.
     · USE A WIDE FILE. site/band-wardrobe, band-studio and band-plans are 2400x1340 (16:9) with
       a 1600 and a 1000 derivative, each composed with an EMPTY LEFT THIRD for exactly this
       device. A band with a long stretch of type above or below it takes one of those; a portrait
       crop is a fallback, not a choice. The old share-card.webp (1200x630) is still wide but is
       half the pixels, so it lost /membership to band-plans.
   ── AND THE TYPE SIDE IS THE OTHER HALF. Below 760px the scrim turns VERTICAL, because a
   left-to-right scrim on a 390px screen has no room to fade and either veils the whole frame or
   leaves the headline on bare photograph.
   THE SCRIM IS NOT DECORATION. Every one of these files is a bright frame on a pale wall or a pale
   floor; white type on the unscrimmed ground measures between 1.2:1 and 1.4:1, i.e. nothing.
   The scrim is the only thing making the type readable, which is why it lives in the component and
   not in the pages.
   ── THE STOPS BELOW ARE MEASURED, AND THE FIRST SET WAS WRONG. The paragraph this replaces
   claimed "4.6:1 at the lightest scrimmed point" from an estimate. Measured properly — render each
   band, hide .lv-ed-in so only the photograph and the scrim paint, screenshot the band, then take
   the BRIGHTEST pixel inside each type element's box and compute white against it — the first set
   measured 1.74:1 on /styling's eyebrow at 390, 2.2–2.5:1 on four headlines at 390 and 3.68:1 on
   /shop's headline at 1440. Every one of those failed AA, on a component whose entire job is type
   on a photograph. getComputedStyle cannot find this: it reports what the cascade resolved, not
   what the photograph underneath happens to be doing, which is the trap CLAUDE.md records.
   The brightest pixel is the right statistic and not a pessimistic one: a blown highlight directly
   under a letter stroke is exactly what makes a headline unreadable.
   ── THE DESKTOP SCRIM WAS REBUILT SO THE PHOTOGRAPH WINS (owner complaint 3, against mnml.la).
   It used to run .90/.86/.66/.24/.08 across the full width: nine tenths black at the left edge and
   still an eighth of a stop of black at the RIGHT edge, which means the picture never actually
   appeared — /membership's band screenshotted as a plain black rectangle with type on it, and the
   owner's reference has the PHOTOGRAPH dominating. The stops below hold the same darkness where
   the type is (the left ~46%, which is where .lv-ed-in's 760px measure ends on a 1440 viewport)
   and then fall to ZERO by 80%, so the right half of every band is the photograph unmodified.
   MEASURED, by the method above, on the five bands that render without a session (/shop,
   /wardrobe, /styling, /account, /membership) x four elements: worst case 7.31:1 at 1440
   (/account's headline) and 12.11:1 at 390 (/wardrobe's eyebrow), against the 6.09:1 worst case of
   the set this replaces. The desktop figure improved DESPITE a lighter scrim because a 40px
   headline sits in a narrower, darker column than a 66px one did.
   /quiz's band is the one not in that run: it only renders after a completed quiz, so it is
   covered by the type scale and the stops but not by a measurement — measure it if you touch it.
   RE-MEASURE IF YOU CHANGE A STOP, A BAND'S HEIGHT, A TYPE SIZE, OR A PHOTOGRAPH. */
.lv-ed{position:relative;display:block;overflow:hidden;
  /* The plate under a photograph that has not decoded yet, and under a file that 404s. Near-black
     rather than --ink so the band never flashes a lighter rectangle than the image that lands. */
  background:#0C0C0C;color:var(--on-image)}
/* FULL-BLEED OUT OF A CONSTRAINED COLUMN. Five of the six call sites sit inside a max-width shell,
   and a band that stops at the text column is a picture, not a band. 100vw includes the scrollbar,
   so the box overhangs by half of it on each side; body{overflow-x:hidden} (§3) clips that, which
   is why this trick is safe HERE and would not be on a page without that rule. */
.lv-ed.bleed{width:100vw;max-width:100vw;margin-left:calc(50% - 50vw)}
.lv-ed > img{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;
  object-position:var(--lv-ed-focus,50% 50%)}
/* `sizes` ON A COVER BAND IS NOT `100vw`, AND THAT COST US THE OWNER'S "photo is very low quality"
   ALL OVER AGAIN. object-fit:cover scales the file by max(boxW/imgW, boxH/imgH), so on a phone —
   where the band is 100vw wide but --lv-ed-h holds it at a 420px FLOOR — the crop is driven by
   HEIGHT, not width. A 16:9 master covering 390x420 has to paint 752 CSS px wide and throw the
   rest away. sizes="100vw" told the browser 390, it fetched the 1000w derivative, and Chromium
   then blew it up: MEASURED 1.51x at DPR 2 and 2.26x at DPR 3 on /membership and /styling — the
   two pages that had just been given the new wide photography. The band looked soft on every
   phone for exactly the reason the letterboxed portrait did.
   THE RULE: a cover band's `sizes` is max(100vw, bandHeight x imageAspect), not its width.
   420 x 16/9 = 747, rounded to 752, and it only binds below that width because above it 100vw is
   already the larger of the two. It is on the markup and not here because `sizes` is an attribute.
   ONLY THE 16:9 BANDS NEED IT. /shop, /quiz and /account carry 4:5 portraits, where 420 x 0.8 =
   336 is under any phone's width, so their plain 100vw is already the bigger number and correct.
   RE-DERIVE IT IF --lv-ed-h's FLOOR MOVES OR A BAND CHANGES ASPECT. */
/* THE SCRIM, and it is a pseudo-element so the markup is <img> + one text box and nothing else.
   It paints above the image and below .lv-ed-in because all three are positioned and only the
   text box carries a z-index. */
/* THE MOBILE SCRIM IS ANCHORED TO THE BOTTOM IN PIXELS, NOT IN PERCENTAGES, AND THAT IS A
   MEASURED FIX. It was `180deg, .34 0%, .30 18%, .74 46%, .90 100%` — stops as a fraction of the
   BAND, while the type is laid out from the band's FOOT (justify-content:flex-end below 760px).
   Those two agree only at one band height. Shrinking the headline 66px → 40px and the bands with
   it moved every text block UP the gradient into the pale end: measured at 390 on the new
   photography, /membership's eyebrow fell to 3.15:1 and /wardrobe's to 3.48:1 — both under AA, on
   type that had been fine the day before, and nothing about the scrim itself had changed.
   Px stops from the bottom track the text instead: the dark reaches ~300px up, which is more than
   the tallest of these text blocks at 390 (eyebrow + two headline lines + three note lines + the
   44px link ≈ 275px), and then clears fast so the top ~120px of the band is the photograph. This
   is why every --lv-ed-h clamp on the six call sites now has a 420px FLOOR: below that the band is
   shorter than the text plus the clear strip, and the picture disappears on a phone again.
   Re-measured across the five bands x four elements at 390: worst case 12.11:1, against 3.15:1
   with the percentage stops and 6.09:1 before any of this. IF YOU CHANGE A TYPE SIZE OR A BAND
   HEIGHT, RE-MEASURE — that coupling is exactly what broke this. */
.lv-ed::after{content:"";position:absolute;inset:0;pointer-events:none;
  background:linear-gradient(0deg,rgba(0,0,0,.90) 0px,rgba(0,0,0,.86) 150px,
    rgba(0,0,0,.74) 300px,rgba(0,0,0,.26) 390px,rgba(0,0,0,.14) 100%)}
@media (min-width:760px){
  /* Dark where the type is, GONE by 80% — see the note above. The last stop is a true 0, not .08:
     an eighth of a stop of black across the whole frame is what made these bands read as tinted
     plates rather than as photographs. */
  .lv-ed::after{background:linear-gradient(90deg,rgba(0,0,0,.78) 0%,rgba(0,0,0,.74) 30%,
    rgba(0,0,0,.56) 46%,rgba(0,0,0,.18) 66%,rgba(0,0,0,0) 80%)}
}
/* text-align:left IS NOT REDUNDANT. The band is dropped into containers that centre their own
   type — quiz.html's .result is text-align:center — and flex alignment positions the BOX while
   text-align still lays out the lines inside it, so without this the headline sat left and its
   words ran centred. The device is left-set; say so here rather than at each call site. */
.lv-ed-in{position:relative;z-index:1;text-align:left;
  display:flex;flex-direction:column;align-items:flex-start;justify-content:flex-end;
  min-height:var(--lv-ed-h,clamp(420px,46vw,580px));
  padding:clamp(26px,4.4vw,72px);
  max-width:min(100%,760px)}
@media (min-width:760px){ .lv-ed-in{justify-content:center} }
/* NO ALPHA ON ANY OF THE THREE, and this is the same house rule the retired bar's link note measured:
   dimming small type is how you lose it. Over a photograph the quiet step is not a lighter white,
   it is a SMALLER one. --on-image-sh is the one concession, and it is a shadow rather than a fill
   so it costs the letterforms nothing.

   THE HEADLINE CAME DOWN FROM 66px TO 40px, AND THAT IS THE OWNER'S COMPLAINT 2 ANSWERED, not a
   taste change. At 66/1.02 with a 16ch measure, "Everything you own, in one place." was two lines
   of 60px-tall letters occupying a third of the band's height and roughly half its width — the
   type was the band and the photograph was its background, which is the exact inversion of the
   reference in complaint 3 (mnml.la: the PHOTOGRAPH dominates, the headline is moderate). The
   proportions now, on a 1440 viewport, are eyebrow 10.5 : headline 40 : note 15 : link 11 —
   a 3.8x step from eyebrow to headline where it used to be 6x.
   THE FLOOR MOVED TOO (32 → 26): on a 390 phone the old clamp resolved to 32px, which with the
   band's own padding left almost no photograph visible above the type.
   WEIGHT 600, NOT 700. At 40px the display face's bold reads as a shout where its semibold reads
   as a headline; the size drop would otherwise have been half-undone by the weight.
   THE MEASURE WIDENED 16ch → 20ch to keep the same two-line shape at the smaller size — a 16ch
   measure at 40px would have broken these headlines onto three and four lines. */
.lv-ed-eyebrow{margin:0 0 clamp(12px,1.4vw,18px);
  font:600 10.5px/1.2 var(--body);letter-spacing:.26em;text-transform:uppercase;
  color:var(--on-image);text-shadow:var(--on-image-sh)}
.lv-ed-title{margin:0;color:var(--on-image);
  font:600 clamp(26px,2.9vw,40px)/1.1 var(--display);letter-spacing:-.02em;
  max-width:20ch;text-wrap:balance;text-shadow:var(--on-image-sh)}
.lv-ed-note{margin:clamp(12px,1.3vw,16px) 0 0;max-width:42ch;
  font:400 clamp(14px,.95vw,15.5px)/1.65 var(--body);color:var(--on-image);
  text-wrap:pretty;text-shadow:var(--on-image-sh)}
/* THE LINK IS UNDERLINED, WHICH IS THE ONE THING THE SCREENSHOT MAKES UNAMBIGUOUS. min-height is
   the 44px floor §7 sets for every other control; the underline rides the GLYPHS, not the box, so
   growing the box does not push the rule away from the word. The arrow is outside the underlined
   span so the rule stops at the last letter, which is how the reference sets it. */
/* SMALL, and smaller than it was (12.5 → 11): the reference's link is a quiet "shop now →" under
   the headline, not a second heading. The 44px box stays — it is the §7 floor for every control —
   so shrinking the glyphs costs nothing in tap area. */
.lv-ed-cta{margin-top:clamp(16px,2vw,26px);
  display:inline-flex;align-items:center;gap:9px;min-height:44px;
  font:600 11px/1 var(--body);letter-spacing:.14em;text-transform:uppercase;
  color:var(--on-image);text-decoration:none;text-shadow:var(--on-image-sh)}
.lv-ed-cta > span{text-decoration:underline;text-underline-offset:7px;text-decoration-thickness:1px}
.lv-ed-cta svg{display:block;width:16px;height:16px;fill:none;stroke:currentColor;stroke-width:1.5;
  stroke-linecap:round;stroke-linejoin:round;transition:transform .3s var(--ease)}
@media (hover:hover) and (pointer:fine){
  .lv-ed-cta:hover > span{text-decoration-thickness:2px}
  .lv-ed-cta:hover svg{transform:translateX(5px)}
}
/* The house ring is --accent, which is ink, and ink on a scrimmed photograph is a ring nobody can
   see. §3 already redirects .on-photo and .hero; the band is the third surface that needs it. */
.lv-ed :focus-visible{outline:2px solid var(--on-image);outline-offset:4px}
/* A caption UNDER the band, on the page's own ground, for a picture that has to say what it is —
   e.g. "Collection 01 · Inner Peace, in Sand", or the note that a frame is a studio render rather
   than product photography. Outside the scrim on purpose: a legal or provenance line printed over
   a photograph at 12px is a line nobody reads. */
.lv-ed-cap{margin:10px 0 0;font:400 12px/1.5 var(--body);color:var(--ink-dim)}

/* ── THE PHOTOGRAPHIC TILE IS NOT IN THIS FILE, AND THAT IS ON PURPOSE ────────────────────────
   The band's grammar at tile scale — a frame, a black scrim at the foot, one tracked-caps label in
   white — is what "occasion tiles wherever occasions are chosen" asks for, and it has exactly ONE
   call site: the Outfit Creator's eight occasions in wardrobe.html (.wd-occ-tile). A first draft of
   this pass declared a shared .lv-tile here as well and nothing used it, because the real control
   is a <label> wrapping a <input type=radio> and its whole selected/focused behaviour is written
   against that structure. A shared class with no consumer is dead CSS; a shared class with ONE
   consumer is a second place to keep in step. So the rules live beside their markup, and this note
   is here so the next person does not "hoist" them back and find they have nothing to hoist.
   HOIST IT IF A SECOND SURFACE EVER GROWS ONE. The reading (styling.js) is the obvious candidate
   and is deliberately NOT it: its five rails include Active, which has no truthful photograph in
   api/static/site — travel is not active and styling.js says so where the rails are declared. */


/* ═══════ THE FOOTER — the vanilla twin of web/components/footer.tsx (SITE-01) ═══════════════════
   ONE FOOTER ON ALL SEVEN PAGES, STYLED ONCE, HERE. Before it, /styling, /wardrobe, /account and
   /membership ended with no footer at all, /taste's only <footer> was its sticky question bar, and
   /shop's listed the seven destinations but no policy — so no static_v2 page linked the Grievance
   Officer, and only /reading linked privacy, terms and contact. The Consumer Protection
   (E-Commerce) Rules 2020 want the grievance officer reachable, and the payment providers' crawlers
   read the policy links before issuing live keys; they do not run script, so these are plain <a>
   tags to the server-rendered /legal pages, written into each page's markup.
   THE DESTINATIONS ARE THE FLAGSHIP'S, THE LABELS ARE THE BAR'S. Three columns grouped by what a
   customer came to do, in the bar's own order and words (tests/test_site_nav_consistency.py
   NAV_ORDER), then the seven statutory pages and the company details. Every page carries the same
   markup; tests/test_static_v2_footer.py reads all seven.
   ON ITS OWN GROUND (--card over a --line rule) and position:relative with a z-index, because
   /styling's dot field is fixed behind everything and would otherwise show through. */
.lv-foot{position:relative;z-index:1;background:var(--card);border-top:1px solid var(--line);color:var(--ink-dim)}
.lv-foot-in{box-sizing:border-box;max-width:1180px;margin:0 auto;
  padding:40px 24px calc(24px + env(safe-area-inset-bottom));display:flex;flex-direction:column;gap:32px}
.lv-foot-top{display:flex;flex-direction:column;align-items:center;gap:32px}
.lv-foot-brand{display:flex;flex-direction:column;align-items:center;gap:16px}
.lv-foot .lv-lockstack{color:var(--ink)}
.lv-foot-copy{margin:0;font:500 11px/1.4 var(--body);letter-spacing:.16em;text-transform:uppercase;color:var(--ink-dim)}
/* Two columns on a phone (Style beside Shop, then Leorve), three from 640px — the flagship's rows. */
.lv-foot-cols{display:grid;grid-template-columns:repeat(2,auto);column-gap:48px;row-gap:24px}
.lv-foot-col{display:flex;flex-direction:column;align-items:flex-start}
.lv-foot-h{margin:0 0 6px;font:600 11px/1.3 var(--body);letter-spacing:.14em;text-transform:uppercase;color:var(--ink)}
/* 32px rows to a mouse, 44px to a thumb: the links are a stacked list, so growing the box is safe
   here where an overlay band would reach into the next row. */
.lv-foot-col a{display:inline-flex;align-items:center;min-height:32px;font:400 13px/1.3 var(--body);
  color:var(--ink-dim);text-decoration:none}
.lv-foot-legal{display:flex;flex-wrap:wrap;justify-content:center;column-gap:22px;
  border-top:1px solid var(--line);padding-top:12px}
.lv-foot-legal a{display:inline-flex;align-items:center;min-height:36px;font:400 11.5px/1.3 var(--body);
  letter-spacing:.04em;color:var(--ink-dim);text-decoration:none}
.lv-foot a:focus-visible{outline:2px solid var(--ink);outline-offset:2px;border-radius:4px}
@media (min-width:640px){
  .lv-foot-top{flex-direction:row;align-items:flex-start;justify-content:space-between}
  .lv-foot-brand{align-items:flex-start}
  .lv-foot-cols{grid-template-columns:repeat(3,auto)}
  .lv-foot-legal{justify-content:flex-start}
}
/* 44 BY 44 TO A THUMB (v7 verify site1-32): the rows were already 44px tall on a coarse pointer, but
   a short word — "Terms", "About" — was a 34px-wide target. min-width grows the box to the RIGHT
   only, so every word keeps its left edge in its column, and the 22px gap still keeps neighbours apart. */
@media (pointer:coarse){ .lv-foot-col a,.lv-foot-legal a{min-height:44px;min-width:44px} }
@media (hover:hover) and (pointer:fine){
  .lv-foot-col a,.lv-foot-legal a{transition:color .2s var(--ease)}
  .lv-foot-col a:hover,.lv-foot-legal a:hover{color:var(--ink)}
}

/* ═══════ THE PAGE TITLE — one treatment for the top h1 of every website page (SITE-18) ═══════
   The website's first headings were set in three faces: /styling's light grotesque with one
   Fraunces italic word, /wardrobe's and /account's heavy Bricolage display, and /membership's
   upright Fraunces. /styling's is the house's (one italic word per view, GLOSSARY rules), so it is
   the one declared here; the heavy display stays with the .lv-ed editorial bands, where a
   photograph carries the weight. Pages put this class on their top h1 and keep only spacing in
   their own rules. */
.lv-page-title{font-family:var(--display);font-weight:300;font-size:clamp(30px,3.6vw,48px);line-height:1.06;
  letter-spacing:-.025em;color:var(--ink);text-wrap:balance}
.lv-page-title i,.lv-page-title em{font-family:var(--serif);font-style:italic;font-weight:400;letter-spacing:-.01em}

/* ═══════ THE BYPASS LINK, declared once (SITE-08) ═══════
   /wardrobe and /account had no skip link at all, so a keyboard customer tabbed through the logo,
   six section links, the account glyph, the bag and the burger before reaching the page on every
   visit. Pages that carried their own copy (/styling, /taste, /membership, /reading) keep it; it
   says the same thing. Off-screen rather than display:none, which would drop it from the tab order. */
.skip-to-content{position:absolute;left:-9999px;top:0;z-index:2000;background:var(--ink);color:var(--on-ink);
  padding:12px 20px;border-radius:0 0 var(--r) 0;font:600 12px/1 var(--body);letter-spacing:.06em;text-decoration:none}
.skip-to-content:focus{left:0}
