/* LotPost landing page.
   Nexus V22 / Kavalsia Inc. / Siavash Sadighi

   -- THE 2026-09-04 CUT, nine sections to six ---------------------------------
   The page keeps the product's identity (true black, hold amber, Archivo, the
   lot-ground shader) and keeps every fact on it. What changed is how much of it
   there is. Siavash: "keep the whole website super simple and direct and easy to
   use ... proper screenshots of the platform after the simplifying design ...
   hundred percent all screen optimized. Nothing will break in two lines
   unequally. Letter spacing and line height should be premium, and phones
   should be a bit smaller in mobile."

     SIX SECTIONS: hero, how it works, what you get, price, questions, sign up.
     The workbench mockup, the five-node pipeline, the three-way comparison, the
     honest-rules band and the return calculator are gone. The three lines a
     reader was actually comparing on and the honest sentence about pacing moved
     into "what you get"; the pacing arithmetic moved into a question.

     THE SCREENSHOTS ARE REAL. site/assets/shots/ is captured off the running
     product by site/shots/build.mjs. Every frame carries its own file's aspect
     ratio with object-fit: contain on a --lp-surface-2 well, so nothing is
     cropped and nothing letterboxes. A landscape screen in a portrait box is a
     recorded trap on this page and it cost seventy per cent of a photograph's
     width the last time.

     ONE WEBGL SURFACE, NOT TWO. The lot ground stays and is the hero's whole
     backdrop. The bay shader that lit the features photograph came off with the
     photograph: the section it belonged to is six rows of text now and a shader
     behind text is noise. See site/lot.js for the same note at the code.

   TYPESETTING IS A GATE, NOT A STYLE. proof/typeset.mjs renders this page at
   eleven widths from 360 to 1920 and holds it to: no overflow, no clipped
   glyph, balance on every heading and the deck, pretty on every paragraph, a
   wrapped heading's shortest line at least 45% of its longest, every control on
   one line, a letter-spacing and line-height table per type role, and phone
   floors (h1 34-38px and body 15-16px under 480, tap targets 44px). Everything
   in the token block below that looks like an odd number is one of those
   measurements, not a preference.

   BORDERLESS. No painted rim: no `border` above width 0, no `outline` at rest,
   no `box-shadow` with a 0 to 2px spread standing in for one. Every separation
   is a fill plus space.

   MOTION. Nothing on this page is hidden by a class a scroll has to remove.
   lot.js sets the start state itself once GSAP is loaded and ScrollTrigger has
   measured the element. Script blocked, JavaScript off, reduced motion,
   Save-Data, a headless renderer: all five get the finished page. --lp-m-dur
   collapses to 0ms under prefers-reduced-motion, which proof.mjs walks rather
   than asserts.

   WIDTHS. FOUR breakpoints and no others: 480, 768, 1024, 1280. `(hover: hover)`
   and `prefers-reduced-motion` are capabilities, not widths, and stay. Every
   size is a clamp on :root and no rule below :root writes a literal px
   font-size. site/proof.mjs holds both by probe.

   Verified at 360, 768, 1024, 1280 and 1600 by site/proof.mjs, and at eleven
   widths by proof/typeset.mjs. */
/* ===================================================================== face */

/* One variable file, weights 400 to 700, latin subset. `swap`, not `block`:
   this is a page a stranger loads over a network once, where a flash of the
   fallback beats a blank headline. The panel uses `block` for the opposite
   reason, which is in sidepanel/tokens.css. */
@font-face {
  font-family: 'Archivo';
  font-style: normal;
  font-weight: 100 900;
  font-display: swap;
  src: url('assets/fonts/archivo-latin-var.woff2') format('woff2');
}

/* =================================================================== tokens */

:root {
  color-scheme: dark;

  /* --- surfaces: the extension's ladder, measured against a true-black ground.
         bg -> surface 1.19 . surface -> surface-2 1.11 . surface -> surface-3
         1.19 . and bg -> surface-2 1.07, which is over the 1.06 floor and is
         what lets a recess sit on the bare page rather than only inside a
         card. See sidepanel/tokens.css for the derivation. --- */
  --lp-bg:          #000000;
  --lp-surface:     #14171E;
  --lp-surface-2:   #0A0C10;
  --lp-surface-3:   #232830;

  /* --- ink --- */
  --lp-ink:         #F4F6F9;
  --lp-ink-2:       #B0B8C4;
  --lp-muted:       #959DA9;

  /* --- accent --- */
  --lp-accent:      #F2A93B;
  --lp-accent-ink:  #F5B45A;
  --lp-accent-solid:#F2A93B;
  --lp-accent-on:   #1A1103;
  --lp-accent-12:   rgba(242, 169, 59, 0.12);
  --lp-accent-20:   rgba(242, 169, 59, 0.20);
  /* The one alpha added for the day strip. At 0.20 over a card fill the amber
     lands on luma 45 against surface-3's 40, so the lit half of the day and
     the dark half read as the same bar. 0.42 separates them. Same accent, a
     fourth stop on it, not a new colour. */
  --lp-accent-42:   rgba(242, 169, 59, 0.52);

  /* THE FOCUS HALO, and it is a CONTRAST value rather than a fill. Queue #124:
     a halo painted in --lp-accent-20 measures 1.43:1 against the ground it
     sits on, and WCAG 1.4.11 wants 3:1 for a control's own boundary -- a ring
     a keyboard user cannot see is a ring that is not there. site/auth.css
     solved the same problem first and landed on 0.60 alpha, which measures
     4.03:1; this is that value, so the two sheets ring a focused control the
     same way. The 0.12/0.20/0.52 stops above stay what they are: they are
     FILLS behind text, and a fill's job is to sit under its own foreground,
     not to be seen from across a desk. */
  --lp-halo:        rgba(242, 169, 59, 0.60);

  /* THE DESTINATION'S BLUE, added 2026-09-04. Two values, one job: the
     Publish button and the storefront glyph on the hero's listing card, which
     are the marketplace's half of that picture. Nothing else on this page may
     use them -- the product's accent is the amber above and this is a
     quotation, the way a screenshot would be. #0866FF is Marketplace's own
     button blue and carries #FFFFFF at 4.82:1; the lighter one it ships on a
     white page does not clear 4.5 and was not used. */
  --lp-mkt:         #0866FF;
  --lp-mkt-on:      #FFFFFF;
  --lp-mkt-ink:     #7FB0FF;

  --lp-success:     #3FC98A;
  --lp-success-ink: #55D398;
  --lp-success-tag: #182B29;
  --lp-danger-ink:  #FF8F84;
  --lp-danger-tag:  #2F2429;

  /* --- type --- */
  --lp-face: 'Archivo', -apple-system, "SF Pro Text", "Segoe UI", system-ui, sans-serif;
  /* THE MACHINE FACE, added 2026-09-07. It was written out once, inline, in
     `.lp-code`, and nothing else on the page could reach it. A stock number, a
     photo count and a tally are DATA -- a reader scans them, they are never
     read as words -- and setting them in the same humanist sans as the prose
     is what made the page's figures read as copy. Same stack that was already
     in .lp-code, promoted to a token so the surfaces can share it. */
  --lp-face-mono: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;

  /* --- lift: offset + blur, never a zero-offset halo, never a 1px rim.
         On #000000 a shadow can only subtract, so the depth comes from the lit
         side and the shadow is what stops a floating card sitting flat. --- */
  --lp-lift:   0 1px 2px rgba(0, 0, 0, 0.6),  0 6px 20px rgba(0, 0, 0, 0.55);
  --lp-lift-2: 0 2px 6px rgba(0, 0, 0, 0.55), 0 18px 44px rgba(0, 0, 0, 0.6);
  --lp-lift-3: 0 6px 14px rgba(0, 0, 0, 0.6), 0 44px 96px rgba(0, 0, 0, 0.72);

  /* THE MAST'S SCROLLED PLATE, AND IT IS THE ONLY FILL THE BAR EVER WEARS.
     `--lp-mast-fill: rgba(8, 9, 12, 0.72)` was retired 2026-09-05: the header
     sits ON the hero and carries nothing at rest, so the at-rest token had no
     reference left in the sheet and a declared-but-unpainted colour is drift
     that reads as a live decision. This value is what `.lp-mast.is-down`
     actually paints past 24px of scroll, and it is a token rather than a
     literal so DESIGN.md can carry it and the detector can resolve it. */
  --lp-mast-fill-down: rgba(6, 7, 9, 0.94);

  /* --- THE MARKETING TYPE SCALE ---------------------------------------
     ONE fluid scale. Every size on this page is one of the tokens below,
     each a clamp() and NO rule below :root writes a literal px font-size.

     THE FLOORS MOVED ON 2026-09-04 AND THEY ARE MEASUREMENTS, NOT TASTE.
     proof/typeset.mjs check 7 renders the page at 360, 390 and 430 and holds
     the h1 to 34-38px and EVERY paragraph to 15-16px. The old scale read 40px
     and 17.5px there and failed all three widths. Siavash asked for the same
     thing in his own words -- "phones should be a bit smaller in mobile" -- so
     h1, lead and body now hold a flat phone value all the way to 480 and start
     climbing from there. That is why the h1 and the lead interpolate from 480
     rather than from 360: below 480 they are the floor, and the floor is the
     phone design.

       token     360-480   768     1280+   line-height  weight
       h1           36      50      76      1.04         700
       h2           29      37      48      1.10         700
       h3         19.5      21      23      1.20         600
       lead         16    17.6    20.5      1.50         400
       body         16    16.7      18      1.55         400
       small        14    14.5      15      1.30         500
       btn          16    16.5      17      1.00         600

     site/proof.mjs check 11 reads all seven back out of the browser at 360,
     768 and 1280 and fails on any that leaves its band, with a mutation arm;
     proof/typeset.mjs check 6 reads the tracking and the leading off the live
     elements at eleven widths. --- */
  --lp-mt-h1:    clamp(36px,   12.00px + 5.000vw, 76px);
  --lp-mt-h2:    clamp(29px,   21.56px + 2.065vw, 48px);
  --lp-mt-h3:    clamp(19.5px, 18.13px + 0.380vw, 23px);
  --lp-mt-lead:  clamp(16px,   13.30px + 0.5625vw, 20.5px);
  --lp-mt-body:  clamp(16px,   14.80px + 0.250vw, 18px);
  --lp-mt-small: clamp(14px,   13.61px + 0.109vw, 15px);
  --lp-mt-btn:   clamp(16px,   15.61px + 0.109vw, 17px);
  /* The one number a visitor came to find, and the only thing on this page set
     this large. Two of them sit side by side, so it is smaller than the single
     figure the one-plan page carried. */
  --lp-mt-price: clamp(40px,   33.74px + 1.739vw, 56px);

  /* THE STAGE'S OWN SCALE, and it is deliberately not the page's.
     .lp-stage is a PICTURE of three marketplace listing cards at the size the
     product's own surfaces run (sidepanel/tokens.css runs 11 to 19px). Scaling
     that artwork up to the page's reading sizes would stop it being a picture
     of the product. So the stage gets four tokens of its own, on :root like
     everything else, and the "no body text under 16 on a phone" rule is held
     over the page's PROSE, which is what a reader reads and what the gate
     measures (it counts <p> elements; nothing in the stage is one). Nothing
     outside .lp-stage and the mast may use these. */
  --lp-mt-ui-t: 10px;   /* a state pill: posted, queued */
  --lp-mt-ui-1: 11px;
  --lp-mt-ui-2: 12px;
  --lp-mt-ui-3: 13px;
  --lp-mt-ui-4: 15px;   /* the marketplace's own Publish control */

  /* --- TRACKING AND LEADING, PER ROLE ---------------------------------
     BUILD-PLAN-v2 section 4 names four roles and their values, and
     proof/typeset.mjs check 6 measures the computed value off the live
     elements at eleven widths rather than reading this file. The page read
     -0.036em on the h1 and -0.028em on every h2 until 2026-09-04: tight
     enough that the headline's counters closed up at 76px and the section
     headings lost the word gap at 29px. These are the values in the plan and
     they are the same four site/auth.css already holds its own pages to, so
     the landing page and the sign-up page are typeset alike. --- */
  --lp-track-display: -0.025em;
  --lp-track-h2:      -0.015em;
  --lp-track-h3:      -0.012em;
  --lp-track-label:    0.01em;

  --lp-mlh-body:    1.55;
  /* 1.50 and not 1.47: the gate holds every <p> on this page to 1.5-1.6,
     which is the reading band, and a lead is read. */
  --lp-mlh-lead:    1.50;
  --lp-mlh-display: 1.04;
  --lp-mlh-h2:      1.10;
  --lp-mlh-h3:      1.20;
  /* A label is not prose: it is short, it is scanned, and it sits in a row.
     1.3 with +0.01em is what BUILD-PLAN-v2 section 4 names for the role. */
  --lp-mlh-label:   1.30;
  /* The craft floor's body measure, 65 to 75ch, resolved in the block's OWN
     font. A ch on a container is measured in the container's inherited size,
     not in the type it holds, so these live on the text block itself. */
  --lp-read:        68ch;
  --lp-measure:     62ch;

  /* --- THE MARKETING SPACE SCALE --------------------------------------
     One ladder, fluid from m5 up, and every vertical gap on the page is one
     of these. m1 to m4 are fixed because a 4px gap does not want to be 5px
     on a wide screen; everything that separates BLOCKS is fluid, because
     that is the part a phone and a desktop genuinely disagree about.

       token    360   768   1280+     its job
       m1         4     4       4     a hairline gap inside a control
       m2         8     8       8     icon to label
       m3        12    12      12     label to value
       m4        16    16      16     h2 to lead, list rows, grid gap floor
       m5        16    24      32     GRID GAP, column gap inside a row
       m6        24  27.6      32     CARD PADDING
       m7        32    39      48     block gap between two stacked groups
       m8        40  50.6      64     LEAD TO CONTENT
     --- */
  --lp-m1:  4px;
  --lp-m2:  8px;
  --lp-m3: 12px;
  --lp-m4: 16px;
  --lp-m5: clamp(16px,  8.94px + 1.961vw, 32px);
  --lp-m6: clamp(24px, 20.87px + 0.870vw, 32px);
  --lp-m7: clamp(32px, 25.74px + 1.739vw, 48px);
  --lp-m8: clamp(36px, 28.17px + 2.174vw, 56px);

  /* THE SECTION RHYTHM, and it is one number per width for every section on
     the page. 64 at 360, 96 at 768, 136 at 1280 and flat above. Two variants
     and no others: -tight steps one rung DOWN (x0.75) and -air one rung UP
     (x1.25). Before this pass the page carried seven different section
     intervals at 360 (90/56, 0/44, 58, 44, 58, 88, 58, 58, 27) because the
     value was derived from four different space tokens through three
     different multipliers. site/proof.mjs now reads computed padding-block
     off every .lp-sec and fails on any value outside the three-rung ladder. */
  /* COMPACTED 2026-09-03 evening, on Siavash's call: the page carried too
     much air around everything. The ladder is unchanged in SHAPE and every
     rung still comes off this one token; only the number moved, 64/96/136
     down to 48/74/104, a flat 24 percent off every interval on the page at
     every width. The three-rung rule and the probe that enforces it are
     untouched, because the probe reads the ratio and not the value. */
  --lp-sec: clamp(48px, 26.09px + 6.087vw, 104px);

  /* The other half of "less space around": a narrower gutter and a wider
     measure, so the same window gives the content 1240px instead of 1180 and
     spends 32px a side instead of 40 getting there. */
  --lp-gut: clamp(18px, 12.52px + 1.522vw, 32px);
  --lp-max: 1240px;
  --lp-mast-h: clamp(64px, 60.87px + 0.870vw, 72px);

  /* --- marketing shape --- */
  --lp-r-tile:  18px;
  --lp-r-frame: 22px;
  --lp-r-stage: 28px;
  --lp-r-dev:   30px;   /* the browser window shell */
  --lp-r-scr:   22px;   /* its screen, = shell minus its 8px bezel */
  --lp-r-in:    10px;
  /* THE CODE CHIP, AND IT IS DELIBERATELY SMALLER THAN --lp-r-in. `.lp-code`
     is an inline span at 2px/7px padding, so it stands about 20px tall; at
     10px it reads as a pill rather than as a chip, which is the wrong shape
     for a literal command a reader has to recognise on screen. 6px was a raw
     literal in the sheet until 2026-09-05 and is a named rung now. */
  --lp-r-chip:   6px;
  --lp-r-pill: 999px;

  /* --- marketing control --- */
  --lp-m-ctl: 48px;
  --lp-m-tap: 44px;   /* the floor for anything a thumb has to hit */
  --lp-m-dur: 180ms;
  /* The ground's reveal. A separate token and not a hardcoded 600ms: every
     transition on this page has to collapse under prefers-reduced-motion, and
     a literal duration anywhere is one this file cannot switch off. */
  --lp-m-dur-slow: 600ms;
  --lp-m-ease: cubic-bezier(0.22, 0.7, 0.3, 1);
  /* THE GAP BETWEEN ONE ROW LANDING AND THE NEXT, for the "how it works" panel
     assembling itself on a step change. A token and not a literal for the same
     reason --lp-m-dur-slow is one: it has to collapse under reduced motion, and
     a delay is the one part of a transition that a 0ms DURATION does not
     silence -- with a live delay and a dead duration a row would sit invisible
     and then snap, which is worse than the motion it replaced. */
  --lp-m-stag: 55ms;

  /* How far a field is filled, 0 to 1. It ships at 1 so the workbench is a
     finished listing with the script blocked; lot.js drops it to 0 only after
     ScrollTrigger has measured the section, then scrubs it back up. */
  --lp-f: 1;
}

@media (prefers-reduced-motion: reduce) {
  :root { --lp-m-dur: 0ms; --lp-m-dur-slow: 0ms; --lp-m-stag: 0ms; }
}

/* ==================================================================== reset */

*, *::before, *::after { box-sizing: border-box; }

html { scroll-behavior: smooth; }
@media (prefers-reduced-motion: reduce) { html { scroll-behavior: auto; } }

body {
  margin: 0;
  background: var(--lp-bg);
  color: var(--lp-ink);
  font: 400 var(--lp-mt-body) / var(--lp-mlh-body) var(--lp-face);
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
  overflow-x: clip;          /* never `hidden`: it would kill the sticky mast */
  caret-color: var(--lp-accent);
  scrollbar-color: var(--lp-surface-3) transparent;
  scrollbar-width: thin;
}

/* The parts nobody draws still carry the design. */
::selection { background: var(--lp-accent-20); color: var(--lp-ink); }

h1, h2, h3, h4, p, ol, ul, dl, dd, figure, figcaption { margin: 0; }

/* TEXT-WRAP IS SET BY ROLE, HERE, ONCE. BUILD-PLAN-v2 section 4 names it and
   proof/typeset.mjs check 3 measures the computed value off every heading, deck
   and paragraph on the page at eleven widths: 93 of 101 read `wrap` before this
   rule existed. `balance` on a heading evens the rendered lines, which is the
   whole of "nothing will break in two lines unequally"; `pretty` on body copy
   costs nothing and kills the one-word last line. A component that needs
   something else says so at its own rule and is answerable to the gate. */
h1, h2, h3, .lp-deck { text-wrap: balance; }
p { text-wrap: pretty; }
ol, ul { padding: 0; list-style: none; }
img, svg { display: block; max-width: 100%; }
a { color: inherit; }
summary::-webkit-details-marker { display: none; }

:where(a, button, summary):focus-visible {
  outline: 2px solid var(--lp-accent);
  outline-offset: 3px;
  border-radius: var(--lp-r-in);
}
/* AN ACCENT RING ON AN ACCENT FILL IS NO RING. The primary button is
   --lp-accent-solid and the default focus outline above is --lp-accent, so a
   keyboard user tabbing to the one control this page is built around saw amber
   on amber. The ink is 12.6:1 against that fill. */
.lp-btn-go:focus-visible { outline-color: var(--lp-ink); }

/* THE SKIP LINK, REWRITTEN 2026-09-04, and both of the old rule's problems
   were real rather than gate noise.
     It was `width:1px; height:1px; overflow:hidden; clip-path:inset(50%)`.
     A 1px box with overflow:hidden holding a nine-character phrase is, by the
     only definition that matters, a CLIPPED GLYPH: proof/typeset.mjs check 2
     reads the text's own Range rects against its parent's box and reported it
     at all eleven widths, correctly. And its 1px height is a tap target of
     1px, which check 7 reported at every phone width, also correctly.
   Parked off the top of the viewport instead, at full size. It is a real 44px
   control that a keyboard finds and a pointer never meets; nothing is clipped
   because nothing is small, and `top` is negative rather than
   `inset-inline-start: -9999px`, which would put a node 9999px off the canvas
   and produce actual horizontal scroll in an RTL context (site/proof.mjs's
   overflow probe is right to report that one). Fixed, not absolute: it has to
   land on screen from wherever the reader has scrolled to. */
.lp-skip {
  position: fixed;
  top: 0;
  inset-inline-start: var(--lp-gut);
  z-index: 9;
  display: inline-flex;
  align-items: center;
  min-height: var(--lp-m-tap);
  padding: 0 var(--lp-m4);
  background: var(--lp-accent-solid);
  color: var(--lp-accent-on);
  border-radius: var(--lp-r-pill);
  font-size: var(--lp-mt-small);
  font-weight: 600;
  text-decoration: none;
  white-space: nowrap;
  transform: translateY(-200%);
  transition: transform var(--lp-m-dur) var(--lp-m-ease);
}
.lp-skip:focus { transform: translateY(var(--lp-m2)); }

/* =================================================================== layout */

.lp-wrap {
  width: 100%;
  max-width: calc(var(--lp-max) + var(--lp-gut) * 2);
  margin-inline: auto;              /* a max-width block does not centre itself */
  padding-inline: var(--lp-gut);
}
.lp-sec { padding-block: var(--lp-sec); }
.lp-sec-tight { padding-block: calc(var(--lp-sec) * 0.75); }
/* Every anchor target has to clear the sticky mast, or the heading it jumps to
   lands underneath it. One token, so the mast height and this cannot drift. */
.lp-sec[id] { scroll-margin-top: calc(var(--lp-mast-h) + var(--lp-m4)); }

/* MORE SPACE ABOVE A HEADING THAN BELOW IT, and only ONE gap under the head.
   The old sheet paid twice for the same break: .lp-head carried a bottom
   margin of 88px and then .lp-alt added a 56px top margin of its own, so the
   compare section opened a 144px hole between its lead and its first panel
   while every other section opened 88. The head owns the interval now and
   nothing under it adds a second one.
     h2 to lead        --lp-m4   16 at every width
     lead to content   --lp-m8   40 / 51 / 64 */
.lp-head { display: grid; gap: var(--lp-m4); margin-bottom: var(--lp-m8); }
.lp-head .lp-lede { max-width: var(--lp-read); }

/* ===================================================================== mast */

/* THE HEADER SITS ON THE HERO AND CARRIES NOTHING OF ITS OWN, 2026-09-05.
   Design lesson 36, in his words: "the header should be on the hero banner,
   not on that background", "remove any divider between header and body". Until
   now this bar painted a 0.72 plate and blurred AT REST, so
   the first thing on the page was a bar with its own surface sitting in front
   of the hero rather than on it. At rest it is now nothing: no fill, no blur,
   no shadow, no hairline. The mark, the nav pill and the button carry
   themselves.
   THE PLATE IS EARNED BY SCROLLING, not worn by default. Past 24px lot.js sets
   .is-down and the fill comes on, because from there the bar genuinely is over
   moving copy and a transparent bar would be text on text (design lesson 27,
   nothing covers text). That is a fill change and not a height change:
   animating height would move every anchor's scroll-margin underneath it on
   every frame. */
.lp-mast {
  position: sticky;
  top: 0;
  z-index: 5;
  background: transparent;
  transition: background-color var(--lp-m-dur) var(--lp-m-ease);
}
.lp-mast.is-down {
  background: var(--lp-mast-fill-down);
  -webkit-backdrop-filter: blur(18px) saturate(1.4);
          backdrop-filter: blur(18px) saturate(1.4);
}
/* A translucent bar over a page that scrolls under it is the one place a
   backdrop-filter earns its keep. Where it is unsupported the scrolled bar has
   to be fully opaque or the nav sits on top of moving copy. */
@supports not ((-webkit-backdrop-filter: blur(1px)) or (backdrop-filter: blur(1px))) {
  .lp-mast.is-down { background: #08090C; }
}

.lp-mast-in {
  height: var(--lp-mast-h);
  display: flex;
  align-items: center;
  gap: var(--lp-m6);
}

.lp-brand { display: inline-flex; align-items: center; gap: var(--lp-m2); min-height: var(--lp-m-tap); text-decoration: none; }
.lp-mark { width: 21px; height: 21px; flex: none; color: var(--lp-accent); }

.lp-signin {
  display: none;
  align-items: center;
  min-height: var(--lp-m-tap);
  padding: 0 var(--lp-m4);
  border-radius: var(--lp-r-pill);
  font-size: var(--lp-mt-small);
  font-weight: 600;
  color: var(--lp-ink-2);
  text-decoration: none;
  white-space: nowrap;
  transition: color var(--lp-m-dur) var(--lp-m-ease), background-color var(--lp-m-dur) var(--lp-m-ease);
}
.lp-signin:hover { background: var(--lp-surface); color: var(--lp-ink); }
.lp-word { font-size: var(--lp-mt-lead); font-weight: 700; letter-spacing: -0.02em; }

/* The nav is a pill of its own, floating on the bar rather than a row of bare
   links. It is a fill, so it stays borderless, and it reads as one control
   group instead of four loose words. */
.lp-nav {
  margin-inline: auto;
  display: none;
  gap: 2px;
  padding: 4px;
  background: var(--lp-surface-2);
  border-radius: var(--lp-r-pill);
}
.lp-navlink {
  display: inline-flex;
  align-items: center;
  min-height: var(--lp-m-tap);
  padding: 0 var(--lp-m4);
  border-radius: var(--lp-r-pill);
  font-size: var(--lp-mt-small);
  font-weight: 500;
  color: var(--lp-ink-2);
  text-decoration: none;
  white-space: nowrap;
  transition: background-color var(--lp-m-dur) var(--lp-m-ease), color var(--lp-m-dur) var(--lp-m-ease);
}
.lp-navlink:hover { background: var(--lp-accent-12); color: var(--lp-accent-ink); }

.lp-mast-end { margin-inline-start: auto; display: flex; align-items: center; gap: var(--lp-m2); }
/* THE BURGER AND THE SHEET ARE GONE, 2026-09-04, with the cut to six sections.
   Three stacked <span> bars inside one <button> measure as a THREE-LINE
   control, which proof/typeset.mjs check 5 reports at every width the burger
   painted at -- and it is not a false positive: the check reads how many line
   boxes a control's contents occupy, and this one occupied three. The honest
   fix is not an SVG in the same button; it is that a six-section page a reader
   can scroll in four screens does not need a disclosure holding a list of
   anchors to those six sections. Under 1024 the mast is the mark, sign in and
   the offer, and nothing else. */

/* ================================================================== buttons */

.lp-btn {
  min-height: var(--lp-m-ctl);
  padding: 0 var(--lp-m6);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  border: 0;
  border-radius: var(--lp-r-pill);
  font-size: var(--lp-mt-btn);
  font-weight: 600;
  line-height: 1;
  text-decoration: none;
  white-space: nowrap;
  cursor: pointer;
  transition: background-color var(--lp-m-dur) var(--lp-m-ease),
              color var(--lp-m-dur) var(--lp-m-ease),
              transform var(--lp-m-dur) var(--lp-m-ease);
}
.lp-btn svg { width: 18px; height: 18px; flex: none; }

/* THE PRIMARY SITS ABOVE THE PAGE. It was a flat amber pill on flat black: the
   one control the page exists to be pressed had less elevation than the cards
   under it, which is the wrong way round. The lift is --lp-lift, the page's own
   neutral shadow token, and it is NEUTRAL on purpose -- an accent-coloured
   shadow under a high-luminance accent is the glow the laws ban, and on #000000
   a shadow can only subtract, so what reads is the offset, not a halo. The
   hover deepens to the next rung rather than inventing a value. */
.lp-btn-go { background: var(--lp-accent-solid); color: var(--lp-accent-on); box-shadow: var(--lp-lift); }
.lp-btn-go:hover { background: var(--lp-accent-ink); transform: translateY(-1px); box-shadow: var(--lp-lift-2); }
.lp-btn-go:active { transform: none; box-shadow: var(--lp-lift); }

.lp-btn-quiet { background: var(--lp-surface); color: var(--lp-ink); }
.lp-btn-quiet:hover { background: var(--lp-surface-3); }

/* THE HERO'S SECOND ACTION. It used to go transparent from 480 up, on the
   argument recorded here that "two filled pills side by side are two
   primaries". THE PAGE ITSELF FALSIFIES THAT ARGUMENT: the price section puts
   a --lp-surface `Start as a salesperson` directly beside an amber
   `Start a dealership`, and nobody has ever had to ask which of those two is
   the point. --lp-surface is #14171E on a #000000 ground, a 1.19 step -- it is
   not a second primary, it is the quietest shape this page owns. What the
   transparent version actually produced was a naked word at button size
   floating beside a filled pill, with no box, no edge and no hover state until
   a pointer found it. Siavash, 2026-09-05, on that exact row: "this also look
   brokens and ver basic".
   The fill is the same at every width now, which is what the phone case below
   480 was already doing and what the price section has always done. One
   button role fewer, and the hero's two actions finally look like two actions
   of different weight rather than a control and a caption. */
/* AND THE SECOND ACTION IS A CONTROL, NOT A SHADOW. --lp-surface (#14171E) is a
   1.19 step off #000000, which is enough to separate a large card from the page
   and not enough to give a 48px pill a shape: measured beside the amber it read
   as a dark patch where a button ought to be, which is the "naked word" failure
   the note below already describes, wearing a fill. --lp-surface-3 (#232830) is
   the same ladder one rung up, 1.42 off the ground, and it is the fill the
   device bezel and the quiet chips already use, so nothing new is introduced.
   It is still unmistakably the quieter of the two: solid amber against a grey
   the eye reads as a surface. */
.lp-btn-bare { background: var(--lp-surface-3); color: var(--lp-ink); }
/* The hover is a LIFT and not a fourth fill: the ladder has three surface rungs
   and inventing a fourth for one state is a token nobody else can reach. Law 10
   asks for a lift anyway. */
.lp-btn-bare:hover { transform: translateY(-1px); box-shadow: var(--lp-lift); }
.lp-btn-bare:active { transform: none; box-shadow: none; }

/* The small button is still a TAP TARGET. It was 38px high, which no thumb
   floor allows; --lp-m-tap is the floor and the visual weight comes off the
   horizontal padding instead. */
.lp-btn-sm { min-height: var(--lp-m-tap); padding: 0 var(--lp-m4); font-size: var(--lp-mt-small); }
.lp-btn-wide { width: 100%; }

/* THE TRAP THIS PAGE PAID FOR. .lp-ground is a POSITIONED child with
   z-index:0; the hero's own copy is static. Positioned z-index:0 content paints
   in step 8 of the paint algorithm and static block content in step 4, so while
   .lp-ground was nested inside the hero it covered every word of it, at every
   width, with the boxes all measuring correctly and the stacking numbers
   reading right. Two guards, and both are needed: .lp-ground is a sibling of
   the section, and the section raises its own content explicitly. */
.lp-hero > .lp-wrap { position: relative; z-index: 1; }

/* NO min-height. A hero told to be a share of the window is a hero told to
   invent height it has no content for, and the surplus lands as a black band
   nobody put there. The height is the CONTENT plus the section rhythm rung,
   the same padding-block every other section pays. */
.lp-hero { position: relative; z-index: 1; display: grid; }
.lp-hero .lp-wrap { display: grid; align-items: center; }
.lp-hero-in > .lp-deck      { margin-top: var(--lp-m5); }
.lp-hero-in > .lp-hero-who  { margin-top: var(--lp-m4); }
.lp-hero-in > .lp-acts      { margin-top: var(--lp-m6); }

/* THE OFFER LINE UNDER THE BUTTON IS GONE, 2026-09-06. `.lp-hero-fine` was a
   real <small> carrying nothing but its rung on the margin ladder above, and
   the ladder is one rung shorter now. The offer itself is not dropped from the
   page: the pricing section states it in full and the JSON-LD carries it. */

/* --------------------------------------------------------------- headline */

/* PLAIN TEXT, ONE ELEMENT, AND THE BROWSER PICKS THE BREAK.
   It used to be two authored <span class="lp-line"> rows, each its own overflow
   box for a mask rise. An authored break is right at exactly one measure and
   wrong at the ten others proof/typeset.mjs renders, and its check 4 reads the
   shortest rendered line against the longest at every one of them: the old
   headline measured 0.26 at 390 and 430, where "filled from your lot." sat
   under a line four times its width. `text-wrap: balance` is the whole fix, and
   it can only do its job on a single uninterrupted text run.
   The accent rule and the accent-coloured second line went with the spans, and
   nothing is lost: the emphasis in this headline is the sentence. */
.lp-h1 {
  font-size: var(--lp-mt-h1);
  font-weight: 700;
  line-height: var(--lp-mlh-display);
  letter-spacing: var(--lp-track-display);
  text-wrap: balance;
  color: var(--lp-ink);
  /* 26ch AND NOT 22ch, AND IT IS A MEASUREMENT AGAINST THE NEW WORDS.
     Design lesson 81: a `ch` cap is a guess until measured against what it
     lands on, and a cap set against old words breaks new words. The old
     headline was 41 characters and sat on two lines at 22ch. The new one is
     49, and at 22ch it broke to three lines at 1280, which lesson 18 bans
     outright. 26ch holds it to two lines from 1024 up and three at 375, both
     inside the rule, and proof/typeset.mjs check 4 still reads the shortest
     rendered line against the longest at eleven widths. */
  max-width: 26ch;
}

.lp-h2 {
  font-size: var(--lp-mt-h2);
  font-weight: 700;
  line-height: var(--lp-mlh-h2);
  letter-spacing: var(--lp-track-h2);
  text-wrap: balance;
}
.lp-h4 {
  font-size: var(--lp-mt-h3);
  font-weight: 600;
  line-height: var(--lp-mlh-h3);
  letter-spacing: var(--lp-track-h3);
  text-wrap: balance;
}

/* A deck runs one step smaller and one weight lighter than the headline it
   sits under, or it competes with it. `balance` and not `pretty`: the gate
   holds .lp-deck to the heading rule, because a deck is read as a unit the way
   a heading is, and a ragged three-and-one under a balanced headline is the
   defect the headline was just fixed for. */
.lp-deck {
  font-size: var(--lp-mt-lead);
  line-height: var(--lp-mlh-lead);
  font-weight: 400;
  color: var(--lp-ink-2);
  max-width: 56ch;
  margin-inline: auto;
  text-wrap: balance;
}
.lp-lede {
  font-size: var(--lp-mt-lead);
  line-height: var(--lp-mlh-lead);
  font-weight: 400;
  color: var(--lp-ink-2);
  max-width: 56ch;
}
/* The line naming the two account types. It is the page's one place where the
   words "dealership" and "salesperson" are set against each other, and it is
   signup.html's own tile language, so a reader meets the same two words on
   both pages. */
/* `balance` AND NOT THE INHERITED `pretty`. This line sits under a balanced
   headline and a balanced deck, and `pretty` only guards the last line against
   a single-word orphan -- it let this one break as 12 words then "own logins.",
   a two-word tail under two evenly-set blocks. It is short enough to be read
   as a unit, which is the same reason .lp-deck carries balance. */
.lp-hero-who { color: var(--lp-muted); max-width: 52ch; margin-inline: auto; text-wrap: balance; }

/* FULL WIDTH ON A PHONE BY DEFAULT, released at 480. A content-width pill on a
   360 column leaves a ragged right edge and two different button widths
   stacked; the base case is the narrow one. */
.lp-acts { width: 100%; display: flex; flex-wrap: wrap; justify-content: center; gap: var(--lp-m3); }
.lp-acts .lp-btn { flex: 1 1 100%; }
.lp-get .lp-h4 { grid-column: 2; grid-row: 1; align-self: start; }
.lp-plan .lp-btn { margin-top: var(--lp-m2); }
.lp-code {
  padding: 2px 7px;
  border-radius: var(--lp-r-chip);
  background: var(--lp-surface-2);
  color: var(--lp-accent-ink);
  font-family: var(--lp-face-mono);
  font-size: 0.92em;
}

/* ================================================================== footer */

.lp-foot { padding-block: var(--lp-m8); background: var(--lp-surface-2); }
.lp-foot-in { display: flex; flex-wrap: wrap; align-items: center; gap: var(--lp-m4); }
.lp-foot .lp-brand { min-height: 0; }

/* ============================================================ small print */

/* The `small` role, and it is the fourth line of the type table in
   BUILD-PLAN-v2 section 4: +0.01em over 1.3, which is what a short scanned
   line wants and what a paragraph does not. It is a real <small> element
   rather than a class on a <p> for a reason that is not cosmetic: the gate
   holds every <p> on the page to reading leading (1.5-1.6) and a 15-16px floor
   on a phone, both of which are correct for prose and wrong for a version
   line. Naming the element is how the two roles stay separable. */
small, .lp-fine {
  display: block;
  font-size: var(--lp-mt-small);
  line-height: var(--lp-mlh-label);
  letter-spacing: var(--lp-track-label);
  color: var(--lp-muted);
}

/* ================================================================== widths

   FOUR breakpoints and no others: 480, 768, 1024, 1280. `(hover: hover)` and
   `prefers-reduced-motion` are capability queries, not widths, and stay.

   There is nothing here that changes a font size or a section interval: both
   are clamps on :root and they move continuously. What lives here is COLUMN
   COUNT and the two or three places a component genuinely has to restructure.
   That is the whole reason the old sheet disagreed with itself between screens:
   type stepped at one width and the grid holding it at another. */

/* --- 480: the phone stops being a single narrow column --------------- */
@media (min-width: 480px) {
  .lp-signin  { display: inline-flex; }
  .lp-acts    { width: auto; }
  .lp-acts .lp-btn { flex: 0 0 auto; }
  .lp-foot .lp-fine { margin-inline-start: auto; }
}

/* --- 1024: the desktop composition ---------------------------------- */
@media (min-width: 1024px) {
  .lp-nav      { display: flex; }
  .lp-mast-end { margin-inline-start: 0; }
}
