/* ABRADO site. Black, white, one pink. Switzer for everything, IBM Plex Mono only for catalogue numbers and measured figures.
   Contrast: pink #FF2E8A on black 5.7:1 (any size); pink on white 3.5:1 (24 px+ or fills only); #B3005A on white 6.9:1 (small pink text). */
@font-face{font-family:"Switzer";src:url("../fonts/Switzer-400.woff2") format("woff2");font-weight:400;font-display:swap}
@font-face{font-family:"Switzer";src:url("../fonts/Switzer-500.woff2") format("woff2");font-weight:500;font-display:swap}
@font-face{font-family:"Switzer";src:url("../fonts/Switzer-700.woff2") format("woff2");font-weight:700;font-display:swap}
@font-face{font-family:"IBM Plex Mono";src:url("../fonts/IBMPlexMono-400.woff2") format("woff2");font-weight:400;font-display:swap}

:root{
  --black:#0B0B0C;--white:#FFFFFF;--pink:#FF2E8A;--pink-text:#B3005A;--grey:#5E5E5E;--rule:#E3E3E3;--field:#F4F4F4;
  --sans:"Switzer",system-ui,-apple-system,"Helvetica Neue",Arial,sans-serif;--mono:"IBM Plex Mono",ui-monospace,Menlo,monospace;
  --gut:clamp(20px,5vw,80px);--max:1200px;--s1:8px;--s2:16px;--s3:24px;--s4:40px;--s5:64px;--s6:96px;
  --ease:cubic-bezier(.2,.7,.2,1)}
*{box-sizing:border-box}
html{-webkit-text-size-adjust:100%;scroll-behavior:smooth}
@media (prefers-reduced-motion:reduce){html{scroll-behavior:auto}}
/* the grain: one small tiled SVG of hairline scratches at the 34 degree sanding angle, under everything, covered by the
   black and grey bands; nothing moves. Round 3 (a bit stronger, on request): grain-v2.svg, the same pattern at 6 to 8 %
   (tools/grain_v2.py). To revert to round 2 (2.5 to 4.5 %), swap the two body lines below. */
body{margin:0;background:var(--white) url("../img/grain-v2.svg") 0 0/240px 240px repeat;color:var(--black);font:400 18px/1.5 var(--sans);font-feature-settings:"ss01","cv11"}
/* body{margin:0;background:var(--white) url("../img/grain.svg") 0 0/240px 240px repeat;color:var(--black);font:400 18px/1.5 var(--sans);font-feature-settings:"ss01","cv11"} */
body.nograin{background-image:none}
img,svg,canvas{display:block;max-width:100%}
a{color:inherit}
button{font:inherit;color:inherit}
:focus-visible{outline:3px solid var(--pink);outline-offset:3px}
.sr{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap}
.wrap{max-width:var(--max);margin-inline:auto;padding-inline:var(--gut)}
p{max-width:62ch;margin:0 0 1em}
.mono{font-family:var(--mono);font-size:15px;line-height:1.5;color:var(--grey)}
.lead{font-size:clamp(20px,2.2vw,24px);line-height:1.4;max-width:56ch;margin:0 0 var(--s3)}
h1,h2,h3{font-weight:700;letter-spacing:-.035em;line-height:1;margin:0;text-wrap:balance}
h1{font-size:clamp(44px,7.2vw,100px);letter-spacing:-.045em;line-height:.9;overflow-wrap:anywhere}
h2{font-size:clamp(32px,4.4vw,56px);margin-bottom:var(--s3)}
h3{font-size:clamp(22px,2.4vw,26px);letter-spacing:-.02em;line-height:1.15;margin-bottom:var(--s1)}

/* header */
.top{position:sticky;top:0;z-index:20;background:var(--white);border-bottom:1px solid var(--rule)}
.top .wrap{display:flex;align-items:center;justify-content:space-between;height:68px}
.top .logo{display:flex;align-items:center;height:44px;text-decoration:none}
.top .logo img{height:20px;width:auto}
.top nav ul{display:flex;align-items:center;gap:clamp(4px,2vw,16px);margin:0;padding:0;list-style:none}
.top nav a,.top nav button{display:inline-flex;align-items:center;height:44px;padding:0 12px;font-weight:500;text-decoration:none;border:0;background:none;cursor:pointer;white-space:nowrap}
@media (max-width:420px){.top nav a,.top nav button{padding:0 8px}.top .logo img{height:17px}.top nav .buy{padding:0 16px}}
.top nav .buy{background:var(--black);color:var(--white);padding:0 20px;border-radius:999px}
.top nav .buy.pinkfill{background:var(--pink);color:var(--black)}
.top nav .buy.pinkfill:hover{background:var(--black);color:var(--white)}
@media (min-width:600px){.top .logo img{height:24px}}

/* buttons: one shape, black; the hover is the scrape (see .scrape). Round 4: no flex gap and no room for foreign children:
   the store's overlay script (loaded on the first hover) appends an empty <span class="logo-full"> to every checkout
   link, and a 12 px gap turned that into a pill 12 px wider than before the hover, for good. Every Buy pill (header,
   card, hero) shares these two rules, so each always ends at its original size. */
.btn>:empty,.top nav .buy>:empty{display:none}
.btn{position:relative;display:inline-flex;align-items:center;justify-content:center;height:60px;padding:0 30px;border:0;border-radius:999px;background:var(--black);color:var(--white);font-weight:500;font-size:19px;text-decoration:none;cursor:pointer;overflow:hidden;isolation:isolate}
.btn.secondary{background:none;color:var(--black);box-shadow:inset 0 0 0 2px var(--black)}
.btn.pinkfill{background:var(--pink);color:var(--black)}
/* round 6 (D2): the secondary outlined "+ Cart" next to every Buy. Round 8: "Added ✓" once the device is in the cart (cart.js), no
   "View" text; the button holds both labels in one grid cell (visibility swaps them), so its width is the wider label's and nothing
   reflows when the label changes. The click plays the pink fill (.filling: the same slanted sheet as the hero's hover, 350 ms in,
   held to 400 ms by cart.js) to completion, then the label settles. Every data-add button (card, hero, /plugins, "Also pressed")
   has the sheet for the click; only .hotfill (the device hero) also has it on hover. */
.btn.addcart{padding:0 24px}
.btn[data-add]{display:inline-grid;grid-template-columns:minmax(0,auto);align-items:center;justify-items:center}
.btn[data-add] .lbl{grid-area:1/1;white-space:nowrap}
.btn[data-add] .l2,.btn[data-add].incart .l1{visibility:hidden}
.btn[data-add].incart .l2{visibility:visible}
.btn.addcart.incart{box-shadow:inset 0 0 0 2px var(--black);color:var(--black)}
.btn[data-add]::before{content:"";position:absolute;top:0;bottom:0;left:-30%;right:-30%;z-index:-1;background:var(--pink);transform:translateX(-100%) skewX(-14deg);transition:transform .35s var(--ease);pointer-events:none}
.btn[data-add].filling::before{transform:translateX(0) skewX(-14deg)}
.btn[data-add].filling{color:var(--black)}
.btn[data-add].filling.scrape::after{background:repeating-linear-gradient(0deg,var(--black) 0 2px,transparent 2px 7px,var(--white) 7px 8px,transparent 8px 13px)}
@media (prefers-reduced-motion:reduce){.btn[data-add]::before{transition:none}}
/* round 8: the header's Buy (n) bumps once in the moment the label settles (transform only) */
.top nav .buy.bump{animation:bump .3s var(--ease) 1}
@keyframes bump{0%{transform:scale(1)}40%{transform:scale(1.09)}100%{transform:scale(1)}}
@media (prefers-reduced-motion:reduce){.top nav .buy.bump{animation:none}}
.btn.small{height:44px;padding:0 16px;font-size:16px}
.top nav .buy .cart-count{margin-left:0;white-space:pre}
.link{display:inline-flex;align-items:center;min-height:44px;font-weight:500;text-decoration:underline;text-decoration-thickness:2px;text-underline-offset:4px}

/* scrape hover: a band of horizontal scuff lines sweeps across once (transform only, composited), like a surface being scuffed.
   Variant A (in use): pink and white hairlines. Variant B (test page): broken dashes. */
.scrape,.scrape-b{position:relative;overflow:hidden;isolation:isolate}
.scrape::after,.scrape-b::after{content:"";position:absolute;top:0;bottom:0;left:0;width:55%;pointer-events:none;opacity:0;z-index:1;transform:translateX(-100%);
  -webkit-mask:linear-gradient(90deg,transparent,#000 30% 70%,transparent);mask:linear-gradient(90deg,transparent,#000 30% 70%,transparent)}
.scrape::after{background:repeating-linear-gradient(0deg,var(--pink) 0 2px,transparent 2px 7px,var(--white) 7px 8px,transparent 8px 13px)}
.scrape.secondary::after,.band.black .scrape::after{background:repeating-linear-gradient(0deg,var(--pink) 0 2px,transparent 2px 7px,var(--black) 7px 8px,transparent 8px 13px)}
.scrape-b::after{background:repeating-linear-gradient(0deg,var(--pink) 0 1px,transparent 1px 4px);
  -webkit-mask:linear-gradient(90deg,transparent,#000 30% 70%,transparent),repeating-linear-gradient(90deg,#000 0 12px,transparent 12px 20px,#000 20px 26px,transparent 26px 44px);-webkit-mask-composite:source-in;
  mask:linear-gradient(90deg,transparent,#000 30% 70%,transparent),repeating-linear-gradient(90deg,#000 0 12px,transparent 12px 20px,#000 20px 26px,transparent 26px 44px);mask-composite:intersect}
.scrape:hover::after,.scrape:focus-visible::after{animation:scrape .35s var(--ease) 1 forwards}
.pinkfill.scrape::after{background:repeating-linear-gradient(0deg,var(--black) 0 2px,transparent 2px 7px,var(--white) 7px 8px,transparent 8px 13px)}
.scrape-b:hover::after,.scrape-b:focus-visible::after{animation:scrape .5s linear 1 forwards}
@keyframes scrape{0%{opacity:.95;transform:translateX(-100%)}100%{opacity:.95;transform:translateX(280%)}}
@media (prefers-reduced-motion:reduce){.scrape:hover::after,.scrape:focus-visible::after,.scrape-b:hover::after,.scrape-b:focus-visible::after{animation:none;opacity:.7;transform:translateX(40%)}}
/* round 7 (1): the device hero's Buy and "+ Cart", and only those two, fill with pink on hover. The fill is a wipe: a pink sheet
   with a slanted leading edge slides in from the left (transform only, 350 ms, the scrape's ease) under the scuff lines, and the
   pill resolves to solid pink with black text. Hover off wipes it out again. Every other pill keeps the round-6 hover (black, or
   white on a black surface). Reduced motion: the pink appears at once. */
.btn.hotfill::before{content:"";position:absolute;top:0;bottom:0;left:-30%;right:-30%;z-index:-1;background:var(--pink);transform:translateX(-100%) skewX(-14deg);transition:transform .35s var(--ease);pointer-events:none}
.btn.hotfill{transition:color .2s var(--ease) .08s}
.btn.hotfill:hover::before,.btn.hotfill:focus-visible::before{transform:translateX(0) skewX(-14deg)}
.btn.hotfill:hover,.btn.hotfill:focus-visible{color:var(--black)}
.btn.hotfill.scrape::after{background:repeating-linear-gradient(0deg,var(--black) 0 2px,transparent 2px 7px,var(--white) 7px 8px,transparent 8px 13px)}
@media (prefers-reduced-motion:reduce){.btn.hotfill::before,.btn.hotfill{transition:none}}
.scrape:hover,.scrape-b:hover{background:var(--black)}
.btn.secondary.scrape:hover{background:none}
/* round 6 (A1): a pill must never vanish into its surface on hover. The pink pill (Buy, Continue to payment) turns white
   with black text; on a black surface (.band.black, .pay, .frame.dark) every pill hovers to white with black text. */
.btn.pinkfill.scrape:hover,.btn.pinkfill.scrape:focus-visible{background:var(--white);color:var(--black)}
.band.black .scrape:hover,.band.black .scrape-b:hover,.pay .scrape:hover,.dark .scrape:hover{background:var(--white);color:var(--black)}
.band.black .btn.secondary.scrape:hover,.pay .btn.secondary.scrape:hover,.dark .btn.secondary.scrape:hover{background:var(--white);color:var(--black)}

/* hero (home and device page): text left, cover right; cover first on the phone */
.hero{display:grid;gap:var(--s4);padding-block:var(--s4) var(--s6);align-items:center}
.hero .cover{order:-1}
.hero .copy p{margin-bottom:var(--s3)}
.hero .price{font-size:clamp(32px,4vw,48px);font-weight:500;letter-spacing:-.03em;line-height:1;margin:var(--s2) 0 var(--s3)}
.hero .actions{display:flex;flex-wrap:wrap;gap:var(--s2) var(--s3);align-items:center}
.hero .catline{margin-top:var(--s3);font-size:16px;color:var(--grey);max-width:52ch}
.catline .mono{font-size:16px;color:var(--pink-text)}
figcaption{font-size:16px;color:var(--grey)}
.pagehead{padding-block:var(--s4) var(--s3)}
.pagehead h1{font-size:clamp(40px,7vw,96px)}
.pagehead .lead{margin-top:var(--s3)}
.mt12{margin-top:12px}.mt16{margin-top:16px}.mt24{margin-top:24px}.mt32{margin-top:32px}
@media (min-width:900px){.hero{grid-template-columns:minmax(0,1.05fr) minmax(0,.95fr);gap:var(--s5)}.hero .cover{order:0;justify-self:end}}
/* round 7 (4): the tighter device hero: a smaller cover and less padding above and below, so the screenshot band starts on screen at
   1440 x 900 (the band's top edge and the top of the device are in the first screen) */
.hero.tight{padding-block:var(--s3) var(--s4);gap:var(--s3)}
.hero.tight .cover{max-width:300px}
.hero.tight .lead{margin-bottom:var(--s2)}
.hero.tight .price{margin:var(--s1) 0 var(--s2)}
.hero.tight .catline{margin-top:var(--s2)}
@media (min-width:900px){.hero.tight{grid-template-columns:minmax(0,1.15fr) minmax(0,.85fr);gap:var(--s5);padding-block:var(--s4) var(--s5)}.hero.tight .cover{max-width:420px}}

/* the cover (round 5): black square, the device symbol centred (build.py → cover_geometry), one bottom row: the number
   alone on the left in pink mono ("001"), the version and pressing on the right. No title on the cover: the name is
   printed next to or under it everywhere it appears. All vector, no image, nothing moves. */
.cover{width:100%;max-width:640px;aspect-ratio:1;background:var(--black);color:var(--white);position:relative;overflow:hidden;container-type:inline-size}
.cover svg{position:absolute;inset:0;width:100%;height:100%}
.cover .cnum{position:absolute;left:6%;bottom:6%;font-family:var(--mono);font-size:max(13px,3.2cqw);line-height:1.4;color:var(--pink)}
.cover .cver{position:absolute;right:6%;bottom:6%;font-family:var(--mono);font-size:max(13px,3.2cqw);line-height:1.4;color:var(--white);text-align:right}
@container (max-width:219px){.cover .cnum,.cover .cver{display:none}}   /* below 220 px the 13 px floor cannot fit the row: symbol only (favicon-size uses) */

/* bands and sections */
.band{padding-block:var(--s6);border-top:1px solid var(--rule)}
.band.tight{padding-block:var(--s5)}
.band.field{background:var(--field);border-top:0}
.band.black{background:var(--black);color:var(--white);border-top:0}
.band.black .mono{color:#B9B9B9}
.band.black .lead{color:var(--white)}
.two{display:grid;gap:var(--s4)}
@media (min-width:860px){.two{grid-template-columns:minmax(0,1fr) minmax(0,1fr);gap:var(--s5)}}
.shot figure{margin:0}
.shot figure{display:grid;justify-items:center;text-align:center}
/* the device screenshot band (round 3: 816 CSS px on a laptop, the container's width on a phone; round 8: 408; round 8b: 612, and
   the band is first on the page). Round 4: no caption, one short mono line under it. The run-out grooves (.shot.runout, on since
   round 5) sit behind the screenshot, the grain over them, the screenshot on top; round 8b draws them on a canvas (rings.js). */
.shot{position:relative;overflow:hidden}
.shot figure{display:grid;justify-items:center;align-content:center;text-align:center}
/* round 8b: the screenshot (the 816 px 2x crop of the real v1.1 capture with Live's title bar) is shown at 612 CSS px (--shot-w from
   the build, scale 1.5; --shot-scale 2 previews 816); the container's width on a phone */
.shot .stage{position:relative;width:min(var(--shot-w,612px),100%)}
.shot picture{position:relative;z-index:2;display:block;width:100%}
.shot img{width:100%;height:auto;border-radius:6px}
.shot .shotline{margin:var(--s3) 0 0;font-size:14px;text-align:center}
/* round 6 (B2): the groove layer is 2.4x the image width at 9 % black. Round 7 (5): evenly spaced concentric rings in four tracks with
   three lead-out gaps (build.py → groove_geometry), no label. Round 8b: the band is the FIRST thing on the device page, directly under
   the header, and tight: little space above and below the device (the whole band and the device's name fit on screen at 1440 x 900);
   the grooves are drawn by rings.js on one <canvas> covering the band (canvas.grooves, device pixels, redrawn every frame; nothing is
   transformed), and the .rings box with the build's still SVG is the no-JavaScript fallback, hidden once the canvas is up. */
.shot.runout{padding-block:clamp(36px,4.5vw,64px)}
.shot.runout .wrap{position:relative}
.shot canvas.grooves{position:absolute;inset:0;width:100%;height:100%;z-index:0;pointer-events:none}
.shot .rings{position:absolute;left:50%;top:50%;width:calc(2.4 * min(816px, 100vw - 2 * var(--gut)));max-width:none;aspect-ratio:1;transform:translate(-50%,-50%);z-index:0;pointer-events:none}
.shot .rings[hidden]{display:none}
.shot .rings svg{position:absolute;inset:0;width:100%;height:100%}
.shot.runout::after{content:"";position:absolute;inset:0;z-index:1;background:url("../img/grain-v2.svg") 0 0/240px 240px repeat;pointer-events:none}
@media (max-width:599px){.shot.runout{padding-block:36px}}

/* home: brand hero (wordmark, the brand line, the plant readout), the catalogue cards, three principles, the pressing log.
   Motion rule: one moving element per screen. Hero: the O turns, one turn per 20 s (motion.js drives it with the Web
   Animations API so hover can speed it up without a jump; this CSS animation runs only without JavaScript); the readout
   types in once on load on a pointer device (readout.js) and is then still. Catalogue: the cover turns over to the
   screenshot on hover, pointer devices only. Nothing else moves. Nothing about one device on home except its card
   and its pressing-log line. */
.brandhero{padding-block:var(--s5) var(--s5);display:grid;gap:var(--s4)}
.brandhero .wm{width:min(100%,640px);height:auto;margin:0 0 var(--s4);color:var(--black);overflow:visible}
.brandhero .lead{font-size:clamp(22px,2.6vw,30px);max-width:36ch;margin:0}
/* the readout: three or four mono lines, grey label, black value, the latest number in pink; no > character, no cursor, no
   dots. Top right of the hero on a laptop; under the brand line on a phone (static). Lines never wrap: the block is
   as wide as its longest line, so the reveal clips nothing out of the layout. */
.readout{font-family:var(--mono);font-size:15px;line-height:1.7;color:var(--black);display:grid;gap:2px;align-content:start;justify-self:start}
.readout .line{display:grid;grid-template-columns:6.5ch minmax(0,1fr);gap:12px;white-space:nowrap}
.readout .k{color:var(--grey)}
.readout .num{font-weight:400;color:var(--pink-text)}
@media (min-width:900px){.brandhero{grid-template-columns:minmax(0,1fr) auto;align-items:start}.readout{justify-self:end;padding-top:8px}}
@media (max-width:599px){.readout{font-size:14px}.readout .line{grid-template-columns:6ch minmax(0,1fr);white-space:normal}}
html:not(.js) .o-spin{animation:turn 20s linear infinite}
@keyframes turn{to{transform:rotate(360deg)}}
@media (prefers-reduced-motion:reduce){html:not(.js) .o-spin{animation:none}}
.cards{display:grid;gap:var(--s4)}
.card{display:grid;gap:var(--s2);align-content:start}
.card .cover-link{display:block;text-decoration:none}
.card .cover{max-width:none}
/* the card flip (round 4, replaces the sleeve pull-out, which slid over the next slot). The slot (.sleeve) is a square
   with perspective; .flip turns about its vertical axis, 450 ms ease-in-out, on hover and keyboard focus, back on leave.
   Front: the square cover. Back: the real screenshot at the card's width (height = width x 169/408), centred in the
   slot, nothing behind it, so the page and its grain show through. During the first 90 degrees the cover is clipped
   top and bottom (clip-path, never squashed) so that edge-on it is exactly the screenshot's height; coming back it
   unclips during the last 90 degrees. Transform and clip-path only: the slot keeps its footprint and nothing below it
   moves. Backface hidden on both faces. On touch the back does not exist and the tap opens the device page.
   Reduced motion: an instant swap, no turn. The clip of 29.29 % = (1 - 169/408) / 2 of the card height. */
.sleeve{position:relative;display:block;aspect-ratio:1;perspective:1200px}
.sleeve .flip{position:absolute;inset:0;transform-style:preserve-3d}
.sleeve .cover{position:absolute;inset:0;max-width:none;backface-visibility:hidden;-webkit-backface-visibility:hidden}
.sleeve .record{display:none}
@media (hover:hover) and (pointer:fine){
  .sleeve .flip{transition:transform .45s cubic-bezier(.45,0,.55,1);will-change:transform}
  .sleeve .record{display:block;position:absolute;left:0;right:0;top:50%;transform:rotateY(180deg) translateY(-50%);backface-visibility:hidden;-webkit-backface-visibility:hidden;background:none}
  .sleeve .record img{width:100%;height:auto;display:block}
  .sleeve .cover{clip-path:inset(0 0 0 0);transition:clip-path .225s cubic-bezier(.3,0,.7,1) .225s}
  .sleeve:hover .flip,.sleeve:focus-visible .flip{transform:rotateY(180deg)}
  .sleeve:hover .cover,.sleeve:focus-visible .cover{clip-path:inset(var(--rec-clip,29.29%) 0 var(--rec-clip,29.29%) 0);transition-delay:0s}   /* round 8: --rec-clip from the build = (1 - image height / width) / 2 */
  @media (prefers-reduced-motion:reduce){.sleeve .flip,.sleeve .cover{transition:none}}
}
.card h2{margin:var(--s1) 0 0;font-size:clamp(32px,3.6vw,48px)}
.card h2 a{text-decoration:none}
.card h2 a:hover{color:var(--pink-text)}
.card p{margin:0;max-width:40ch}
.card .buyrow{display:flex;flex-wrap:wrap;gap:var(--s2) var(--s3);align-items:center;margin-top:var(--s1)}
.card .price{font-size:28px;font-weight:500;letter-spacing:-.02em}
.card.next .cover.blank{background:none;box-shadow:inset 0 0 0 2px var(--rule)}
.card.next .cnum{color:#767676}
.card.next .soon{margin:0;color:#767676}
/* round 8: the card's "$5 · Buy · + Cart" row is 297 px wide (the "+ Cart" pill is as wide as "Added ✓", 125 px) and must stay on one
   row at every width; two columns from 760 px (310 px cards) and three from 1200 px (317 px cards) keep it so (700 and 1000 wrapped it) */
@media (min-width:760px){.cards{grid-template-columns:repeat(2,minmax(0,1fr));gap:var(--s5)}}
@media (min-width:1200px){.cards{grid-template-columns:repeat(3,minmax(0,1fr))}}
.principles{display:grid;gap:var(--s3)}
.principles p{margin:0;max-width:48ch}
.principles a{text-decoration-thickness:2px;text-underline-offset:4px}
.principles a:hover{color:var(--pink-text)}
@media (min-width:860px){.principles{grid-template-columns:repeat(2,minmax(0,1fr));gap:var(--s3) var(--s5)}}

/* round 7 (6): "Turn it up. / That is the manual." breaks after "up." at every width and nowhere else: each sentence is a block */
.manual-h{text-wrap:initial}
.manual-h span{display:block;white-space:nowrap}   /* measured: "That is the manual." is 261 px at 390 (32 px type) in a 350 px column, so no size step is needed */

/* figures: three numbers from saved results, mono, one rule above each, no tiles */
.figures ul{list-style:none;margin:0;padding:0;display:grid;gap:var(--s3)}
.figures li{border-top:2px solid var(--black);padding-top:var(--s2);display:grid;gap:6px;align-content:start}
.figures .num{font:500 clamp(36px,4vw,52px)/1 var(--mono);letter-spacing:-.02em;white-space:nowrap}
.figures span{font-size:17px;color:var(--grey);max-width:28ch}
@media (min-width:700px){.figures ul{grid-template-columns:repeat(3,minmax(0,1fr));gap:var(--s4)}}

/* pressing log: catalogue lines, built from devices/*.json */
.pressings{list-style:none;margin:0;padding:0}
.pressings li{display:grid;grid-template-columns:3.2em minmax(0,1fr);align-items:baseline;gap:12px;border-top:1px solid var(--rule);padding:14px 0;font-size:17px}
.pressings .rest{display:flex;flex-wrap:wrap;gap:4px 12px;align-items:center}
.pressings li:last-child{border-bottom:1px solid var(--rule)}
.pressings .num{font-family:var(--mono);color:var(--pink-text)}
.pressings a{font-weight:500;text-decoration:none}
.pressings a:hover{color:var(--pink-text);text-decoration:underline;text-underline-offset:4px;text-decoration-thickness:2px}
.pressings .sep{color:var(--grey)}
.pressings .dim .num,.pressings .dim .rest{color:#767676}
.pressings .dim{min-height:52px}
/* round 6 (E1): the newest line types in once (log.js) with a block cursor; the next-number line keeps a slow cursor that blinks
   (1.2 s, steps) once the typing is done; steady under reduced motion and without JavaScript.
   Round 7 (7): the 002 line's cursor sits in its own slot (.slot, 1.2 em by 1.5 em), centred in it both ways, and the slot is
   centred on the text line. With site.json → log_disc on, the slot holds a small turning disc (the ABRADO O, 20 px, one turn
   per 8 s, grey, still under reduced motion) instead of the cursor: something being pressed. */
.pressings .slot{display:inline-flex;align-items:center;justify-content:center;width:1.2em;height:1.5em}
.pressings .cur{display:block;width:.55em;height:1em;background:currentColor}
.pressings .latest .cur.typing{display:inline-block;vertical-align:-.15em;margin-left:2px;background:var(--black)}
.pressings .logdisc{width:20px;height:20px;color:#767676;transform-origin:50% 50%;animation:turn 8s linear infinite}
.pressings .dim.hasdisc .slot{width:24px;height:24px}
@media (prefers-reduced-motion:reduce){.pressings .logdisc{animation:none}}
.pressings .dim .cur{opacity:0}
.pressings .dim .cur.steady{opacity:1}
.pressings .dim .cur.blink{opacity:1;animation:cur-blink 1.2s steps(1,end) infinite}
html:not(.js) .pressings .dim .cur{opacity:1}
@keyframes cur-blink{50%{opacity:0}}
@media (prefers-reduced-motion:reduce){.pressings .dim .cur.blink{animation:none;opacity:1}}

/* statement (home): what ABRADO is, big type */
.statement{font-size:clamp(28px,4vw,52px);font-weight:500;letter-spacing:-.035em;line-height:1.08;max-width:22ch;margin:0 0 var(--s4)}
.statement2{font-size:clamp(20px,2.2vw,24px);line-height:1.4;max-width:48ch;margin:0;padding-top:var(--s3);border-top:2px solid var(--black)}

/* release line */
.release{display:flex;flex-wrap:wrap;gap:var(--s2) var(--s4);align-items:baseline}
.release .num{font-family:var(--mono);font-size:clamp(20px,2.4vw,26px);color:var(--pink-text)}
.band.black .release .num{color:var(--pink)}

/* modes and controls as definition lists, not tiles; round 5: the manual paragraph above the four modes */
.modes-intro{margin:0 0 var(--s4)}
.modes-intro p{max-width:62ch;margin:0 0 .8em;font-size:19px;line-height:1.5}
.modes-intro p:last-child{margin:0}
.dl{margin:0;display:grid;gap:var(--s2)}
.dl div{display:grid;grid-template-columns:72px minmax(0,1fr);gap:var(--s2);border-top:1px solid var(--rule);padding-top:var(--s2)}
.dl dt{font-weight:700;font-size:clamp(28px,3vw,40px);letter-spacing:-.04em;line-height:1}
.dl dd{margin:0;max-width:52ch}
.dl.controls div{grid-template-columns:minmax(110px,160px) minmax(0,1fr)}
.dl.controls dt{font-size:22px;letter-spacing:-.02em}
.dl.controls dd .mono{margin:0 0 2px}

/* measured table */
table{width:100%;border-collapse:collapse;font-size:17px}
th{text-align:left;font-weight:500;color:var(--grey);padding:0 16px 10px 0;border-bottom:2px solid var(--black)}
td{vertical-align:top;padding:14px 16px 14px 0;border-bottom:1px solid var(--rule)}
td.num{font-family:var(--mono);white-space:nowrap;font-size:17px}
@media (max-width:719px){
  table,thead,tbody,tr,td{display:block}thead{display:none}
  tr{padding:12px 0;border-bottom:1px solid var(--rule)}td{padding:2px 0;border:0}
  td::before{content:attr(data-l);display:block;font-size:14px;color:var(--grey)}td.num{font-size:22px}
  td[data-l="What"]::before,td[data-l="Meaning"]::before{display:none}td[data-l="What"]{font-weight:700}}
ol.steps{margin:0;padding-left:1.2em;max-width:52ch}
ol.steps li{margin-bottom:var(--s1)}
/* spec sheet (round 4): the same hairlines and grey labels as the measured table, values in mono; the install steps are
   the same sheet with 01 02 03 as the labels and the step text in sans */
.spec{margin:0;border-top:2px solid var(--black)}
.spec div{display:grid;grid-template-columns:minmax(88px,7.5em) minmax(0,1fr);gap:var(--s2);padding:14px 0;border-bottom:1px solid var(--rule);font-size:17px}
.spec dt{color:var(--grey);font-weight:500}
.spec dd{margin:0;font-family:var(--mono);font-size:15px;line-height:1.6;max-width:52ch}
.spec.steps div{grid-template-columns:3em minmax(0,1fr)}
.spec.steps dt{font-family:var(--mono);font-size:15px;line-height:1.6;font-weight:400}
.spec.steps dd{font-family:var(--sans);font-size:17px;line-height:1.5}
@media (max-width:479px){.spec div{grid-template-columns:minmax(0,1fr);gap:4px}.spec.steps div{grid-template-columns:3em minmax(0,1fr);gap:var(--s2)}}

/* questions, on /questions and on each device page: one accordion component of <details>, closed by default */
.qa{display:grid;gap:var(--s3)}
.qa div{border-top:1px solid var(--rule);padding-top:var(--s2)}
.qa p{margin:0;max-width:60ch}
.qa h3{margin-bottom:6px}
.qa.acc{gap:0}
.qa.acc details{border-top:1px solid var(--rule)}
.qa.acc details:last-child{border-bottom:1px solid var(--rule)}
.qa.acc summary{display:grid;grid-template-columns:minmax(0,1fr) 28px;align-items:center;gap:var(--s2);min-height:64px;padding:12px 0;cursor:pointer;list-style:none}
.qa.acc summary::-webkit-details-marker{display:none}
.qa.acc summary h3{margin:0}
.qa.acc summary::after{content:"";width:16px;height:16px;justify-self:end;background:
  linear-gradient(var(--black),var(--black)) center/16px 2px no-repeat,linear-gradient(var(--black),var(--black)) center/2px 16px no-repeat;transition:transform .2s var(--ease)}
.qa.acc details[open] summary::after{transform:rotate(45deg)}
.qa.acc summary:hover h3{color:var(--pink-text)}
.qa.acc summary:focus-visible{outline:3px solid var(--pink);outline-offset:2px}
.qa.acc details p{padding:0 0 var(--s3);max-width:60ch}
@media (prefers-reduced-motion:reduce){.qa.acc summary::after{transition:none}}
.questions section{padding-block:var(--s4)}
.questions section + section{border-top:1px solid var(--rule)}
@media (min-width:900px){.questions section{display:grid;grid-template-columns:minmax(0,.8fr) minmax(0,1.2fr);gap:var(--s5);align-items:start}.questions section h2{position:sticky;top:92px}}

/* player */
.player{--dim:#D9D9D9;display:grid;grid-template-columns:auto auto 1fr;grid-template-areas:"disc play switch" "wave wave wave";gap:var(--s3) var(--s3);align-items:center}
/* round 6 (A3, B3): the "Stopped: / Playing:" line is gone from view; it stays in the page as a visually hidden live region.
   The listen band ends one spacing unit (--s4) under the closed accordion. */
.band.black.listen{padding-bottom:var(--s4)}
.player .disc{grid-area:disc;width:96px;height:96px}
.player .disc svg{width:100%;height:100%;transform-origin:50% 50%}
/* round 4: the disc's angle is the clip's position (one turn per clip, back at 0 at the end), set by player.js from the
   audio clock; nothing here animates it */
.player .switch{grid-area:switch;display:grid;grid-template-columns:1fr 1fr;height:72px;padding:0;border:2px solid var(--black);border-radius:999px;background:var(--white);cursor:pointer;position:relative;overflow:hidden;isolation:isolate}
.player .switch>.l,.player .switch>.r{position:relative;z-index:1;display:flex;align-items:center;justify-content:center;font-weight:700;font-size:clamp(18px,2.4vw,24px);letter-spacing:-.02em;padding:0 12px;transition:color .18s var(--ease)}
.player .switch>.knob{position:absolute;z-index:0;top:0;left:0;width:50%;height:100%;background:var(--black);border-radius:999px;transition:transform .2s var(--ease),background .2s var(--ease)}
.player .switch[aria-checked="true"] .knob{transform:translateX(100%);background:var(--pink)}
.player .switch[aria-checked="false"] .l{color:var(--white)}
.player .switch[aria-checked="true"] .r{color:var(--black)}
.player .wave{grid-area:wave;position:relative;width:100%;height:88px;cursor:pointer}
.player .wave svg{position:absolute;inset:0;width:100%;height:100%;transition:opacity .15s var(--ease)}
.player .wave use{fill:currentColor}
.player .wave .dim{color:var(--dim)}
.player .wave .hot{color:var(--black)}
.player[data-src="0"] .wave .w1,.player[data-src="1"] .wave .w0{opacity:0}
.player .play[disabled]{opacity:.5;cursor:default}
.note{margin:var(--s3) 0 0;font-size:16px;color:var(--grey);max-width:60ch}
.band.black .note{color:#B9B9B9}
/* "What is it doing?" under the player (round 3 loudness note, round 4 title; round 5 spacing): one borderless <details>,
   closed by default, the title with a plus; the body has room: a gap under the title, 17 px at 1.65 line-height, a space
   between the two sentences, the pointer line on its own, a 52 ch measure */
.note-acc{margin:var(--s3) 0 0;max-width:56ch}
.note-acc summary{display:inline-grid;grid-template-columns:minmax(0,auto) 16px;align-items:center;gap:14px;min-height:44px;cursor:pointer;list-style:none}
.note-acc summary::-webkit-details-marker{display:none}
.note-acc summary h3{margin:0;font-size:17px;font-weight:500;letter-spacing:0;line-height:1.3}
.note-acc summary::after{content:"";width:14px;height:14px;background:
  linear-gradient(currentColor,currentColor) center/14px 2px no-repeat,linear-gradient(currentColor,currentColor) center/2px 14px no-repeat;transition:transform .2s var(--ease)}
.note-acc[open] summary::after{transform:rotate(45deg)}
.note-acc summary:hover h3{color:var(--pink)}
.note-acc summary:focus-visible{outline:3px solid var(--pink);outline-offset:2px}
.note-acc .body{padding:var(--s2) 0 var(--s1)}
.note-acc .body p{margin:0 0 .9em;font-size:17px;line-height:1.65;color:#D2D2D2;max-width:52ch}
.note-acc .body p:last-child{margin-bottom:0}
.note-acc .body .pointer{margin-top:var(--s2);font-size:16px;color:#B9B9B9}
.note-acc p{margin:4px 0 0;font-size:16px;color:#B9B9B9;max-width:60ch}
@media (prefers-reduced-motion:reduce){.note-acc summary::after{transition:none}}
.player .play{grid-area:play;height:64px;padding:0 28px 0 22px;border-radius:999px;border:0;background:var(--black);color:var(--white);font-weight:500;font-size:19px;display:inline-flex;align-items:center;gap:12px;cursor:pointer;justify-self:start;position:relative;overflow:hidden;isolation:isolate}
.player .play i{width:0;height:0;border-left:16px solid currentColor;border-top:10px solid transparent;border-bottom:10px solid transparent;position:relative;z-index:1}
.player.playing .play i{width:16px;height:18px;border:0;background:currentColor}
.player .play span{position:relative;z-index:1}
.player .status{grid-area:status;font-size:16px;color:var(--grey);min-height:1.5em;display:flex;align-items:center;gap:12px}
/* on the black band the waveform is white: dim white unplayed, full white played (both takes) */
.band.black .player{--dim:rgba(255,255,255,.42)}
.band.black .player .wave .hot{color:var(--white)}
.band.black .player .switch{background:var(--black);border-color:var(--white)}
.band.black .player .switch>.l,.band.black .player .switch>.r{color:var(--white)}
.band.black .player .switch>.knob{background:var(--white)}
/* round 6 (A2): the label over the white knob is always black, the other label white; hover only tints the idle label pink.
   These rules outrank the generic ones above (they carry .band.black), so the active side never shows a blank white block. */
.band.black .player .switch[aria-checked="false"] .l,.band.black .player .switch[aria-checked="true"] .r{color:var(--black)}
.band.black .player .switch[aria-checked="false"] .r,.band.black .player .switch[aria-checked="true"] .l{color:var(--white)}
.band.black .player .switch:hover{background:var(--black)}
.band.black .player .switch[aria-checked="false"]:hover .r,.band.black .player .switch[aria-checked="true"]:hover .l{color:var(--pink)}
.player .switch[aria-checked="true"] .l,.player .switch[aria-checked="false"] .r{color:var(--black)}
.player .switch:hover{background:var(--white)}
.band.black .player .play{background:var(--white);color:var(--black)}
.band.black .player .status{color:#B9B9B9}
@media (max-width:599px){.player{grid-template-columns:auto 1fr;grid-template-areas:"play play" "switch switch" "wave wave" "disc disc"}.player .disc{width:40px;height:40px}.player .play{justify-self:stretch;justify-content:center}.player .switch{height:64px}}
@media (prefers-reduced-motion:reduce){.player .switch>.knob,.player .wave svg{transition:none}}

/* catalogue (private /plugins) */
.cat{display:grid;gap:var(--s4)}
.row{display:grid;gap:var(--s3);grid-template-columns:minmax(0,1fr);border-top:1px solid var(--rule);padding-top:var(--s4)}
.row .cover{max-width:280px}
.row .meta{display:flex;flex-direction:column;gap:var(--s2);justify-content:center}
.row .meta h2{margin:0;font-size:clamp(28px,3.4vw,44px)}
.row .meta h2 a{text-decoration:none}
.row .meta h2 a:hover{color:var(--pink-text)}
.row .meta p{margin:0}
.row .buyrow{display:flex;flex-wrap:wrap;gap:var(--s2);align-items:center;margin-top:var(--s1)}
.row .price{font-size:28px;font-weight:500;letter-spacing:-.02em}
@media (min-width:760px){.row{grid-template-columns:280px minmax(0,1fr)}}

/* signup */
.signup form{display:flex;flex-wrap:wrap;gap:var(--s2);max-width:560px}
.signup input{flex:1 1 240px;height:60px;padding:0 20px;font:inherit;border:2px solid var(--black);border-radius:999px;background:var(--white);color:var(--black);min-width:0}
.signup input::placeholder{color:var(--grey)}
.signup .msg{min-height:1.5em;margin-top:var(--s1)}

/* footer */
footer{background:var(--black);color:var(--white);padding-block:var(--s5)}
footer .wrap{display:grid;gap:var(--s4)}
footer .wm{height:28px;width:auto}
footer ul{list-style:none;margin:0;padding:0;display:flex;flex-wrap:wrap;gap:4px 20px}
footer ul.col{flex-direction:column}
footer a{display:inline-flex;align-items:center;min-height:44px;text-decoration:none;font-weight:500}
footer a:hover{color:var(--pink)}
footer small{display:block;max-width:60ch;font-size:15px;color:#B9B9B9;line-height:1.5}
@media (min-width:860px){footer .wrap{grid-template-columns:1fr auto;align-items:start;gap:var(--s6)}}

/* 404 */
.notfound{min-height:60vh;display:grid;align-content:center;padding-block:var(--s6)}

/* checkout and cart (round 6, C and D1): one framed page. Above the grid: the back link left, the page's mono label right. One
   12-column grid: the ORDER frame spans 7, the PAYMENT frame 5; same top and bottom line (the grid row stretches both frames to
   the taller one; the panel's content sits at the top). Every region framed by one hairline weight and labelled in small mono
   caps (the ELXR discipline, in the ABRADO look: white field, hairline black rules, pink only on the action). The order is a
   pressing-log table: one row per fact, grey sans label left, the value in mono right-aligned, no double rule. The cover is
   static (no flip, no hover; the flip stays on home and the device page). Tax and total are the checkout's numbers once the session
   has answered; "Calculated at payment" until then. On a phone the frames stack, order first. */
.checkout{padding-block:var(--s4) var(--s6)}
.toprow{display:flex;justify-content:space-between;align-items:center;gap:var(--s2);min-height:44px;margin-bottom:var(--s2)}
.back{display:inline-flex;align-items:center;min-height:44px;font-weight:500;text-decoration:none;color:var(--black)}
.back:hover{color:var(--pink-text);text-decoration:underline;text-decoration-thickness:2px;text-underline-offset:4px}
.back[hidden]{display:none}
.flabel{margin:0;font-size:12px;letter-spacing:.16em;text-transform:uppercase;color:var(--grey);line-height:1.5}
.co-grid{display:grid;gap:var(--s3)}
.frame{position:relative;border:1px solid var(--black);padding:var(--s4) var(--s3) var(--s3)}
.frame>.flabel{position:absolute;left:var(--s3);top:14px}
.frame.dark{background:var(--black);color:var(--white);border-color:var(--black)}
.frame.dark .flabel{color:#B9B9B9}
.order{display:grid;gap:var(--s3);align-content:start}
.order[hidden]{display:none}
.itemrow{display:grid;grid-template-columns:72px minmax(0,1fr);gap:var(--s2);align-items:center;padding-top:var(--s2)}
.itemrow .cover{max-width:72px}
.itemrow h1{font-size:clamp(30px,8vw,64px);line-height:1;letter-spacing:-.04em;margin:0;overflow-wrap:normal;word-break:normal}
.cart-h{font-size:clamp(36px,4.6vw,64px);line-height:1;letter-spacing:-.04em;margin:var(--s2) 0 0}
.facts{margin:0;border-top:1px solid var(--rule)}
.facts div{display:grid;grid-template-columns:max-content minmax(0,1fr);gap:var(--s2);padding:12px 0;border-bottom:1px solid var(--rule);font-size:17px;align-items:baseline}
.facts dt{color:var(--grey);font-weight:500}
.facts dd{margin:0;text-align:right;font-size:15px;line-height:1.5;color:var(--black)}
.facts .grand dt{color:var(--black);font-weight:700}
.facts .grand dd{font-size:17px;color:var(--black)}
.facts[hidden]{display:none}
.facts.items .item{grid-template-columns:minmax(0,1fr) auto;align-items:center}
.facts.items dd{white-space:nowrap}
.facts.items dt{display:grid;grid-template-columns:48px minmax(0,1fr);gap:var(--s2);align-items:center;color:var(--black)}
.facts.items dt a{grid-column:2;font-weight:700;text-decoration:none;font-size:20px;letter-spacing:-.02em}
.facts.items dt a:hover{color:var(--pink-text)}
.facts.items dt .dimtext{grid-column:2;font-size:13px;color:var(--grey)}
.facts.items .thumb{grid-row:1/3;width:48px}
.facts.items .thumb .cover{max-width:48px}
.facts.items .remove{display:inline-block;margin-left:12px;border:0;background:none;padding:0;min-height:44px;font:inherit;font-size:14px;color:var(--grey);text-decoration:underline;text-underline-offset:3px;cursor:pointer}
.facts.items .remove:hover{color:var(--pink-text)}
.taxnote{margin:0;font-size:15px;color:var(--grey);max-width:44ch}
.taxnote[hidden],.empty[hidden],.paynote[hidden]{display:none}
.order .empty{margin:var(--s2) 0 0}
.bundle{margin-top:var(--s3)}
.bundle h3{font-size:17px;margin-bottom:var(--s1)}
.bundle ul{list-style:none;margin:0;padding:0;border-top:1px solid var(--rule)}
.bundle li a{display:flex;justify-content:space-between;gap:var(--s2);min-height:44px;align-items:center;padding:8px 0;border-bottom:1px solid var(--rule);text-decoration:none;font-weight:500}
.bundle li a:hover{color:var(--pink-text)}
/* round 7 (3): the "Also pressed" upsell slot on /cart, built only with two or more released devices (build.py → also_pressed) */
.also{margin-top:var(--s3);border-top:1px solid var(--rule);padding-top:var(--s2)}
.also[hidden]{display:none}
.also>.flabel{margin-bottom:var(--s2)}
.also ul{list-style:none;margin:0;padding:0;display:grid;gap:var(--s2)}
.also li{display:grid;grid-template-columns:48px minmax(0,1fr) auto;gap:var(--s2);align-items:center}
.also li[hidden]{display:none}
.also .cover{max-width:48px}
.also .meta{display:grid}
.also .meta a{font-weight:700;text-decoration:none;font-size:18px;letter-spacing:-.02em}
.also .meta a:hover{color:var(--pink-text)}
.also .dimtext{font-size:13px}
.pay{display:grid;gap:var(--s3);align-content:start}
.pay>p{margin:0;max-width:44ch;color:#D2D2D2;font-size:17px}
.pay>p.flabel{color:#B9B9B9}
.cardmarks{list-style:none;margin:0;padding:0;display:flex;flex-wrap:wrap;gap:10px}
.cardmarks li{width:60px;height:36px}
.cardmarks svg{width:100%;height:100%}
.pay form{margin:var(--s1) 0 0}
.pay form[hidden]{display:none}
.pay .btn{min-width:220px}
.pay .btn[disabled]{opacity:.5;cursor:default}
.pay .paystatus{margin:0;min-height:1.5em;color:#B9B9B9;font-size:15px}
.pay .paynote{margin:0;font-size:15px;color:#B9B9B9;max-width:44ch}
.pay .handled{margin:var(--s1) 0 0;font-size:15px;color:#B9B9B9;max-width:44ch}   /* round 8: the one line that names the checkout, under the button */
.pay .paymeta{list-style:none;margin:var(--s2) 0 0;padding:var(--s2) 0 0;border-top:1px solid rgba(255,255,255,.18);display:grid;gap:6px;font-size:14px;color:#B9B9B9}
.pay .paymeta a{color:var(--white)}
.pay .paymeta a:hover{color:var(--pink)}
@media (min-width:900px){
  .co-grid{grid-template-columns:repeat(12,minmax(0,1fr));gap:var(--s3);align-items:stretch}
  .orders{grid-column:1/8}.pay{grid-column:8/13}
  .frame{padding:var(--s5) var(--s4) var(--s4)}
  .frame>.flabel{left:var(--s4);top:18px}
  .itemrow{grid-template-columns:128px minmax(0,1fr);gap:var(--s4)}
  .itemrow .cover{max-width:128px}
  .itemrow h1{font-size:clamp(36px,4.6vw,64px)}
}

/* thanks, terms, privacy */
.legal .pagehead .lead{margin-top:var(--s3)}
.legal .pagehead .mono{margin-top:var(--s2)}
.legal section{padding-block:var(--s4);border-top:1px solid var(--rule)}
.legal section h2{font-size:clamp(24px,2.6vw,32px);margin-bottom:var(--s2)}
.legal section p{max-width:62ch}
.legal section div p:last-child{margin-bottom:0}
.legal a{text-decoration-thickness:2px;text-underline-offset:4px}
.legal a:hover{color:var(--pink-text)}
@media (min-width:900px){.legal section{display:grid;grid-template-columns:minmax(0,.8fr) minmax(0,1.2fr);gap:var(--s5);align-items:start}.legal section h2{margin:0}}
