.ascmp { position: relative; border-radius: var(--ac-radius, 8px); overflow: hidden;
         background: var(--ac-bg, #1b1b1b); color: var(--ac-text, #e8e8e8);
         box-shadow: 0 4px 12px rgba(0,0,0,.5); text-align: left;
         display: flex; flex-direction: column; max-height: 520px; }
.ascmp-inner { position: relative; padding: 14px 16px; flex: 1 1 auto; min-height: 0;
               display: flex; flex-direction: column; }
.ascmp-head { margin: 0 0 2px; font-size: 16px; font-weight: 600; text-align: center; }
.ascmp-intro { margin: 0 0 10px; font-size: 12px; color: var(--ac-muted, #9a9a9a);
               text-align: center; }

/* ---- the A/B switch ------------------------------------------------------
   Two halves of one control rather than two buttons: it is a toggle between two
   states, and a segmented control says that without a label explaining it. */
/* GRID, not flex, and this is the whole trick.
   With `flex: 1 1 0` the container's intrinsic width is the SUM of both labels,
   and each half then takes 50% of that - so a long label paired with a short one
   ("Real performance" vs "VHorns") gets a half narrower than its own text and
   clips. A grid sized to `max-content` resolves both `1fr` columns to the width
   of the LARGER item instead, which is what "equal halves" was always meant to
   mean: as wide as the longest label, never narrower. */
.ascmp-ab { display: grid; grid-template-columns: 1fr 1fr; gap: 0;
            width: max-content; max-width: 100%;
            margin: 0 0 10px; border-radius: 999px;
            background: rgba(255,255,255,.07); padding: 3px; align-self: center; }
/* Generous horizontal padding on purpose: these two halves ARE the instruction.
   A visitor has to realise they can swap sides mid-playback, and a small target
   reads as a label rather than something to press. */
.ascmp-ab button {
  appearance: none; -webkit-appearance: none; border: 0; cursor: pointer;
  padding: 9px 30px; border-radius: 999px;
  /* Equal halves come from the grid above. 11ch is only a FLOOR, so two short
     labels still read as a balanced toggle rather than two tiny pills. */
  min-width: 11ch; text-align: center;
  background: transparent; color: var(--ac-muted, #9a9a9a);
  font-size: 12.5px; font-weight: 600; letter-spacing: .02em; white-space: nowrap;
  /* Only reachable on a screen narrower than the labels themselves, where the
     max-width:100% cap wins. An ellipsis is a better failure than text spilling
     out of the pill. */
  overflow: hidden; text-overflow: ellipsis;
  transition: background .18s ease, color .18s ease;
}
/* Buy back the width on phones before the ellipsis has to do any work: the
   padding is the first thing that should give, not the label. */
@media (max-width: 480px) {
  .ascmp-ab button { padding-left: 14px; padding-right: 14px; font-size: 12px; }
}
.ascmp-ab button.on { background: var(--ac-accent, #c9a227); color: #16160f; }
.ascmp-ab button:not(.on):hover { color: var(--ac-text, #e8e8e8); }

/* ---- transport + waveforms ---------------------------------------------- */
.ascmp-main { display: flex; align-items: center; gap: 12px; }
/* Sized against TWO stacked waveforms, not one: at 42px it read as an
   afterthought beside a 100px-tall display. */
.ascmp-play {
  flex: 0 0 auto; width: 66px; height: 66px; padding: 0; cursor: pointer;
  background: none; border: 0; color: var(--ac-accent, #c9a227);
  display: flex; align-items: center; justify-content: center;
  opacity: .92; transition: opacity .15s, transform .15s;
}
.ascmp-play:hover { opacity: 1; transform: scale(1.08); }
.ascmp-ico { width: 100%; height: 100%; display: block; }
.ascmp-play .ascmp-ico:last-child { display: none; }
.ascmp.is-playing .ascmp-play .ascmp-ico:first-child { display: none; }
.ascmp.is-playing .ascmp-play .ascmp-ico:last-child { display: block; }

/* Both waveforms, stacked. The inactive one stays visible but dimmed: the
   comparison is the product, so hiding half of it would defeat the object. */
.ascmp-waves { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 3px; cursor: pointer; }
.ascmp-wave { display: block; width: 100%; height: 46px; }
.ascmp-wave.dim { opacity: .38; }
.ascmp-side { font-size: 10px; letter-spacing: .06em; text-transform: uppercase;
              color: var(--ac-muted, #9a9a9a); line-height: 1.1; }
.ascmp-side.on { color: var(--ac-accent, #c9a227); }
.ascmp-time { flex: 0 0 auto; font-size: 12px; color: var(--ac-muted, #9a9a9a);
              font-variant-numeric: tabular-nums; white-space: nowrap; }

/* ---- the pair list ------------------------------------------------------- */
.ascmp-list { list-style: none; margin: 10px 0 0; padding: 0;
              flex: 1 1 auto; min-height: 0; overflow-y: scroll; }
/* Same reason as the demo player: macOS overlay scrollbars vanish, so a scrolled
   list gives no clue where you are. Styling the pseudo-element opts out. */
.ascmp-list::-webkit-scrollbar { width: 10px; }
.ascmp-list::-webkit-scrollbar-track { background: rgba(255,255,255,.07); border-radius: 5px; }
.ascmp-list::-webkit-scrollbar-thumb { background: rgba(255,255,255,.30); border-radius: 5px;
  border: 2px solid transparent; background-clip: content-box; }
.ascmp-list::-webkit-scrollbar-thumb:hover { background: rgba(255,255,255,.48);
  border: 2px solid transparent; background-clip: content-box; }
@supports (-moz-appearance: none) {
  .ascmp-list { scrollbar-width: thin;
                scrollbar-color: rgba(255,255,255,.30) rgba(255,255,255,.07); }
}
.ascmp-item { display: flex; align-items: center; gap: 10px; cursor: pointer;
              padding: 7px 8px; border-radius: 4px; font-size: 13px; }
.ascmp-item:hover { background: rgba(255,255,255,.07); }
.ascmp-item.current { background: rgba(255,255,255,.10); }
.ascmp-item.current .ascmp-it-title { color: var(--ac-accent, #c9a227); font-weight: 600; }
.ascmp-n { flex: 0 0 18px; text-align: right; color: var(--ac-muted, #9a9a9a); font-size: 11px; }
.ascmp-it-title { flex: 1; min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.ascmp-it-dur { flex: 0 0 auto; color: var(--ac-muted, #9a9a9a); font-size: 11px;
                font-variant-numeric: tabular-nums; min-width: 4.2em; text-align: right; }
/* The playing row shows "0:42 / 1:49", which is wider than a bare length -
   reserving the space stops the title column jumping when playback starts. */
.ascmp-item.current .ascmp-it-dur { min-width: 8.5em; }

@media (max-width: 700px) {
  .ascmp { max-height: 460px; }
  .ascmp-wave { height: 36px; }
  .ascmp-play { width: 52px; height: 52px; }
  .ascmp-ab button { padding: 8px 20px; font-size: 12px; }   /* still roomy, but it has to fit */
}
@media (prefers-reduced-motion: reduce) {
  .ascmp-play, .ascmp-ab button { transition: none; }
  .ascmp-play:hover { transform: none; }
}
/* ============================ TOKENS ============================
   Dark only - committed, not theme-following. The palette is shifted slightly
   COOL so the warm instrument artwork and the gold accent read against it.
   Not entirely flat any more: sections carry their own ground (see TONES). */
:root{
  color-scheme:dark;
  --ink:#101014;
  --line:#26262E; --line-2:#3A3A46;
  --text:#EFEFF2; --text-2:#A5A5B0; --muted:#74747F;
  /* Navigation rest state. --text-2 clears WCAG on this ink (7.8:1) and still
     reads dim for a LABEL YOU ARE HUNTING FOR - you scan a menu, you do not
     read it, and scanning wants contrast that body copy does not. This sits
     between the two so hover -> --text is still a visible step. */
  --text-1:#D8D8DF;
  --well:#000;
  --gold:#D99A44; --silver:#D5D5DB; --gold-ink:#17120A;
  --ok:#59C98A;
  /* Knobs a product may override (admin: Product > Colours). Each is a plain
     value here and a plain value there - the mixing that produces the four
     lifted grounds below happens in PHP (src/Palette.php), NOT in CSS: the
     grounds were built with color-mix(), which a browser older than 2023 does
     not know, and an unknown function drops the whole declaration rather than
     falling back. Every value this stylesheet emits is ordinary hex.
       --silver    the light end of the title gradient
       --grad-end  its colour end
       --gold      everything functional: buttons, prices, players, active states
       --tint      what the grounds are tinted with (a page-level colour, NOT
                   the button colour - one value cannot be both)
       --g-*       the four lifted grounds, and the tone-d glow */
  --grad-end:var(--gold);
  --tint:var(--gold);
  /* THE LADDER: four steps above the page colour, named by how far they go.
     Computed in PHP when a product sets a colour; these are the site's own. */
  --lift-1:#14141A; --lift-2:#16161D; --lift-3:#17171F; --lift-4:#1A1A24;
  --glow:rgba(217,154,68,.12);
  /* A surface, and a hover, expressed as a wash over the page rather than as a
     colour. White on a dark page and black on a light one - Palette flips them
     with the ground, or a panel that is "slightly lighter than the page" would
     be invisible the moment the page turns light. */
  --veil:rgba(255,255,255,.02); --veil-2:rgba(255,255,255,.06);
  /* OVER ARTWORK. A banner or a screenshot is not the page, so the scrim that
     makes text readable on it, the text itself and the play chip are their own
     set - and they flip with the page, because a dark scrim with white type on
     a bright page looks like a piece of another site. Palette rewrites these
     when the page colour is light. */
  --art-scrim:10,10,13; --art-text:#fff; --art-chip:#F4F4F6; --art-chip-ink:#101014;
  /* How hard a backdrop is washed toward the page colour. A photograph behind
     type has to sit BACK - it is a room, not the subject - so this is a real
     wash rather than a hint of one; on a bright page it goes further still,
     because a dark picture there would read as a hole (Palette raises it). */
  --art-veil:.68;
  /* THE BANNER'S OWN WASH: how dark the scrim across the bottom of the page
     banner gets at its very bottom (the stop above it scales with it). Per
     product (admin: Page banner > Banner overlay) because the artwork is: a
     banner that is already night-dark needs far less than a bright one. */
  --bhero-veil:.9;
  /* Which site logo the header shows: the bright mark on a dark page, the ink
     mark on a light one. Palette flips the pair with the ground. */
  --logo-bright:block; --logo-ink:none;
  /* How big a product's banner logo is drawn, as a multiple of the box
     .bhero-logo fits it into (admin: Product > Logo size, 100% = 1). */
  --logo-size:1;
  --measure:64ch;
  --s--1:.8125rem; --s--0:.875rem; --s-0:.9375rem; --s-1:1.0625rem; --s-2:1.3125rem;
  --s-3:1.75rem; --s-4:2.5rem; --s-5:3.5rem;
  --page:1300px; --dock:0px; --r:14px; --rs:10px;
  /* Declared with the other tokens because BODY reads it now - it used to be
     introduced two hundred lines down, beside the product bar that was its
     only reader. Same value, same meaning: the height of the fixed header. */
  --head-h:67px;
  --shadow-art:0 22px 46px -22px rgba(0,0,0,.9);
  --shadow-btn:0 10px 24px -8px rgba(0,0,0,.75);
}

/* ---------- 3. SECTION TONES ----------
   Each band sits on its own ground so the page has rhythm instead of one flat
   sheet. All four are within a few steps of each other - the shift should read
   as depth, never as stripes. */
/* THE SIX GROUNDS a band can sit on (admin: Sections > Ground, and Product >
   Section grounds for the fixed bands). Each is built from the ladder above, so
   "lifted" is a known step rather than a percentage buried in a gradient.

   Every one of them says, in its name, what it does at its own edges - which is
   what the picker in product.php needs to know: a band that ENDS on the base
   must not be followed by one that STARTS there, or the two meet at the same
   value and the join disappears.

     base       flat page colour            base -> base
     lift       flat, one step up           lift -> lift
     fade-down  light at the top            lift -> base
     fade-up    light at the bottom         base -> lift
     spot       a pool of light at the top  lift -> base
     glow       page colour, warm corner    base -> base                     */
/* --gnd-a and --gnd-b are the two stops the shape is drawn between, set on the
   band itself when someone has chosen them. The fallback in each var() is the
   ladder value that shape has always used, so a band nobody has touched is
   unchanged. A flat ground uses only the first; the warm corner takes the first
   as its glow and the second as the ground beneath it. */
.gnd-base{background:var(--gnd-a, var(--ink))}
.gnd-lift{background:var(--gnd-a, var(--lift-1))}
.gnd-fade-down{background:linear-gradient(180deg, var(--gnd-a, var(--lift-3)) 0%, var(--gnd-b, var(--ink)) 100%)}
.gnd-fade-up{background:linear-gradient(180deg, var(--gnd-a, var(--ink)) 0%, var(--gnd-b, var(--lift-2)) 100%)}
.gnd-spot{background:radial-gradient(125% 95% at 50% 0%, var(--gnd-a, var(--lift-4)) 0%, var(--gnd-b, var(--ink)) 62%)}
.gnd-glow{background:radial-gradient(72% 80% at 12% 0%, var(--gnd-a, var(--glow)), transparent 60%), var(--gnd-b, var(--ink))}

*{box-sizing:border-box}
html{background:var(--ink)}
body{
  margin:0; background:var(--ink); color:var(--text);
  font-family:"Plex Sans",system-ui,sans-serif; font-size:var(--s-0);
  line-height:1.7; -webkit-font-smoothing:antialiased;
  /* The room the fixed header no longer takes in the flow. Its height is fixed
     by .site-head .wrap (66px + the hairline), so this cannot drift out of
     step with the bar it is standing in for. */
  padding-top:var(--head-h); padding-bottom:var(--dock);
}
img{max-width:100%; display:block}
a{color:inherit; text-decoration:none}
:where(a,button,input,select):focus-visible{outline:2px solid var(--gold); outline-offset:3px}
.mono{font-family:"Plex Mono",ui-monospace,monospace; font-variant-numeric:tabular-nums}
.wrap{max-width:var(--page); margin-inline:auto; padding-inline:clamp(1.25rem,5vw,3rem)}
/* A PAGE THAT IS ONE COLUMN, not a page of bands: a document, a form, the
   install guide. The band still runs the full width - it is the ground - but
   what stands on it is narrowed and CENTRED, head included. Centring the column
   rather than the text is the whole point: the crumb, the title and the body
   keep one left edge to line up on, and the page stops looking like something
   that lost its right-hand half. */
.wrap.solo{max-width:min(96ch,100%)}
section.band{padding-block:clamp(2.75rem,6vw,5rem)}
/* A picture band's scrim is the page colour at both edges, so it belongs to the
   base family and the band after it is a lifted one - the same small step as
   everywhere else. No line: a rule between full-width bands reads as a divider,
   and these are not divided, they are next to each other. */
.rule{border:0; border-top:1px solid var(--line); margin:0}

/* the gradient lettering - the thing worth stealing from that page */
.grad{
  /* Composed HERE, not as a --grad token on :root. A custom property is
     substituted where it is DECLARED, so a --grad built at the root would have
     baked in the root's --grad-end and a per-product override lower down would
     do nothing. */
  background:linear-gradient(104deg, var(--silver) 0%, var(--silver) 20%, var(--grad-end) 82%);
  -webkit-background-clip:text; background-clip:text;
  color:transparent; -webkit-text-fill-color:transparent; display:inline-block;
}
/* The display face is a token because a product may bring its own (admin:
   Product > General > Title font). Poppins at 300 is the brand default and the
   fallback for every product that names nothing; a named family carries its own
   weight, because 300 is a Poppins fact, not a general one - most families do
   not have it, and the browser then synthesises or jumps to 400 and the title
   sits at the wrong colour weight against the gradient. */
.display{
  font-family:var(--font-display, Poppins,sans-serif);
  font-weight:var(--font-display-weight, 300); line-height:1.02;
  letter-spacing:.005em; margin:0; text-wrap:balance;
}
/* DESCENDERS. background-clip:text only paints the gradient inside the box,
   and at line-height 1.02 the box ends above the bottom of a g, p or y - so the
   gradient title lost the foot of every descender. The box is extended
   downwards by --grad-pad and the same amount is taken back with a negative
   margin, so nothing around the title moves. A title that needs space below it
   sets --grad-mb instead of margin-bottom (see product.php). Placed after
   .display, whose margin:0 would otherwise win. */
.grad{--grad-pad:.2em; padding-bottom:var(--grad-pad);
  margin-bottom:calc(var(--grad-mb, 0px) - var(--grad-pad))}
/* Every feature carries its own title at this size, so it is a shade smaller
   than a one-off hero title would be - ten of them at 5.5rem is a shouting match. */
.feature-title{font-size:clamp(2rem,5.2vw,4rem); text-transform:uppercase; letter-spacing:.01em; max-width:16ch}

/* A feature band whose image runs full-bleed behind the words. */
.feat-bg{position:relative; overflow:hidden; display:flex; align-items:center; min-height:clamp(420px,58vh,640px)}
/* THE BACKGROUND MUST NOT GROW WITH THE SECTION.
   height:100% + cover means opening a "more" panel makes the band taller and
   the browser re-crops and zooms the artwork to fill it - the picture visibly
   swells through the whole transition. Where a panel exists the image is given
   the band's OWN minimum height instead of its actual one, so it is the same
   picture open or closed and the extra height is ground, which the scrim
   already darkens to. Bands with no panel (the description lede) cannot change
   height and keep filling. */
.feat-bg:has(.more) .bgimg{height:clamp(420px,58vh,640px); bottom:auto}
.feat-bg .bgimg{
  position:absolute; inset:0; width:100%; height:100%; object-fit:cover;
  object-position:var(--pos,center); opacity:.55;
}
.feat-bg::after{
  content:""; position:absolute; inset:0; pointer-events:none;
  background:linear-gradient(90deg, rgba(var(--art-scrim),.96) 0%, rgba(var(--art-scrim),.82) 38%, rgba(var(--art-scrim),.3) 100%);
}
.feat-bg .wrap{position:relative; z-index:1; width:100%}
/* COLOUR GOES ON THE CONTAINER, NOT ON THE PARAGRAPHS.
   A colour set in the editor arrives as a <font color> or a coloured span
   inside - or around - the copy, and both are beaten by any author rule that
   names the <p> itself. Declaring it once on the block instead means the
   nearest colour in the content always wins, which is the whole point of
   setting one. */
.feat-bg .prose-legacy{color:var(--art-text)}
.feat-bg p{max-width:50ch; opacity:.92; margin:0 0 1.15rem; font-size:var(--s-1); line-height:1.75}
.feat-bg p:last-child{margin-bottom:0}
.feat-bg b{color:var(--art-text); font-weight:600}
@media (max-width:760px){
  .feat-bg::after{background:linear-gradient(180deg, rgba(var(--art-scrim),.6) 0%, rgba(var(--art-scrim),.94) 55%)}
}
/* inline feature image */
/* The extra detail each section carries on staging. Kept as a disclosure that
   expands in place - it stays scannable, and the depth is one click away for
   the people who actually want it. */
.more{margin-top:1.4rem}
.more > summary{
  list-style:none; cursor:pointer; display:inline-flex; align-items:center; gap:.6rem;
  font-size:var(--s--1); font-weight:600; letter-spacing:.04em; color:var(--gold);
  padding:.5rem 1.1rem; border:1px solid var(--gold); border-radius:999px;
  transition:background .15s;
}
.more > summary::-webkit-details-marker{display:none}
.more > summary::after{content:"+"; font-weight:400; font-size:1.05em; line-height:1}
.more[open] > summary::after{content:"\2212"}
.more > summary:hover{background:rgba(217,154,68,.12)}
/* Full width, not --measure: this is the long-form half of a section and it can
   run to several hundred words. */
.more-body{padding-top:1.2rem; max-width:none}
.more-body{color:var(--text-2)}
.more-body p{margin:0 0 1.1rem; font-size:var(--s-0); line-height:1.75}
.more-body p:last-child{margin-bottom:0}
.feat-bg .more-body{color:var(--art-text)}
.feat-bg .more-body p{opacity:.82}
.feat-shot{border-radius:var(--r); overflow:hidden; background:var(--well); border:1px solid var(--line)}
.feat-shot img{width:100%; display:block}
.eyebrow{font-family:"Plex Mono",monospace; font-size:.6875rem; letter-spacing:.2em; text-transform:uppercase; color:var(--muted)}
.gold{color:var(--gold)}
h2.sec{font-family:Poppins,sans-serif; font-weight:600; text-transform:uppercase; letter-spacing:.12em; font-size:var(--s-1); margin:0 0 2rem; color:var(--text)}
h2.sec.center{text-align:center}

/* ============================ HEADER ============================ */
/* FIXED, NOT STICKY, AND THAT IS THE FIX.
   A sticky header is laid out in the flow and repositioned as the page scrolls
   past it. The layout is exact - measured at every offset from 0 to 200, the
   header's top is 0 at all of them - but the repositioning is not always drawn
   in the same frame as the scroll it answers, so at the start of a gesture the
   bar travels a few pixels with the page and is clipped by the top of the
   window before it catches up. Promoting it with will-change did not help,
   which is the tell: there is nothing wrong with where it is put, only with
   when. A fixed element is never moved by a scroll at all, so there is nothing
   to lag.
   The flow then has to make room for it, which is what the padding on body is
   for; --head-h is the same number the product bar and the anchor offsets have
   always measured themselves against, so nothing else moves. */
.site-head{position:fixed; inset-inline:0; top:0; z-index:60; background:var(--ink);
  border-bottom:1px solid var(--line)}
.site-head .wrap{display:flex; align-items:center; gap:clamp(1rem,3vw,3rem); height:66px}
.logo img{width:138px; height:auto}
.logo .logo-bright{display:var(--logo-bright)}
.logo .logo-ink{display:var(--logo-ink)}
.nav{display:flex; align-items:center; gap:2rem; margin-right:auto}
.nav a{font-size:var(--s-0); color:var(--text-1); padding:.4rem 0}
.nav a:hover,.nav a[aria-current]{color:var(--text)}
.tools{display:flex; align-items:center; gap:1.5rem; font-size:var(--s--0); color:var(--text-1)}
.tools .cur{font-family:"Plex Mono",monospace; letter-spacing:.06em}
.tools .cur b{color:var(--text); font-weight:500}
.hsearch{
  display:flex; align-items:center; gap:.55rem; border:1px solid var(--line-2);
  border-radius:999px; padding:.4rem .9rem; min-width:210px;
  transition:border-color .15s;
}
.hsearch:focus-within{border-color:var(--text-2)}
.hsearch{position:relative}
.hsearch svg{width:15px; height:15px; flex-shrink:0; opacity:.55}
.hsearch input{
  border:0; background:transparent; color:var(--text); font:inherit;
  font-size:var(--s--0); width:100%; outline:none; padding:0;
}
.hsearch input::placeholder{color:var(--text-2)}
.hsearch input::-webkit-search-cancel-button{filter:invert(1); opacity:.4}
/* Set like EUR/USD beside it: all-caps mono. Mixed-case body text next to
   all-caps mono sits optically lower even when the boxes line up exactly (they
   did - same centre to a tenth of a pixel), because the eye aligns x-height
   against cap-height. Same face, same case, no ambiguity. */
.cart-chip{display:flex; align-items:center; gap:.5rem; color:var(--text);
  font-family:"Plex Mono",monospace; text-transform:uppercase; letter-spacing:.06em}
.cart-chip .n{font-family:"Plex Mono",monospace; font-size:.6875rem; font-weight:500; border:1px solid var(--line-2); border-radius:999px; min-width:1.4rem; height:1.4rem; display:grid; place-items:center; padding-inline:.3rem}

/* product bar, at the top the way Spitfire has it */
/* Sticks under the site header once the scroll reaches it. --stick is what the
   two sticky bars together occupy, and it is also what an anchored section has
   to clear - see scroll-margin-top below. */
:root{--probar-h:62px; --stick:calc(var(--head-h) + var(--probar-h))}
.probar{
  position:sticky; top:var(--head-h); z-index:55; background:var(--ink);
  border-top:1px solid var(--line); border-bottom:1px solid var(--line);
}
/* AN ANCHOR THAT LANDS UNDER THE STICKY BARS LOOKS LIKE AN ANCHOR THAT DID
   NOTHING. The page did scroll - the heading was just behind the header and the
   bar. Every anchored section reserves that height. */
[id^="sec-"]{scroll-margin-top:calc(var(--stick) + 12px)}
html{scroll-behavior:smooth}
@media (prefers-reduced-motion:reduce){ html{scroll-behavior:auto} }
.probar .wrap{display:flex; align-items:center; gap:1.5rem; min-height:60px; flex-wrap:wrap}
.probar .pname{font-weight:600}
.anchors{display:flex; gap:2rem; margin-right:auto}
.anchors a{font-size:var(--s-0); color:var(--text-1); padding:.4rem 0; position:relative}
.anchors a:hover{color:var(--text)}
.anchors a.on{color:var(--text)}
.anchors a.on::after{content:""; position:absolute; inset-inline:0; bottom:-1px; height:1px; background:var(--gold)}
/* The family strip under the bar: a single's bundle. One quiet line - it is a
   pointer, not a second buy box. */
.famstrip{border-bottom:1px solid var(--line); font-size:var(--s-0); color:var(--text-1)}
.famstrip .wrap{display:flex; flex-wrap:wrap; align-items:center; gap:.5rem 1.75rem; padding-block:.8rem}
.famstrip a{color:var(--text)}
.famstrip a:hover{color:var(--gold)}
.famstrip .fam-price{color:var(--gold); margin-left:.4rem; font-variant-numeric:tabular-nums}
.famstrip .fam-go{margin-left:auto; color:var(--gold)}
.probar .buy{display:flex; align-items:center; gap:1.25rem}

/* ============================ BUTTONS (flat, rounded, no shadow) ======== */
/* THE INSTALLER'S BUTTON NAMES ARE ALIASES, NOT A SECOND SKIN.
   The serial page renders the installer's own markup (a shared partial, so the
   two cannot drift) and that markup brings its class names with it. Those names
   are attached to the real button here instead of being re-styled inside that
   page's <style> block, where the copy drifted at once: it had no lift on
   hover, faded instead of brightening, and made the Cancel buttons as loud as
   Register. .ilok-page is set by that page and nothing else. */
.btn,
.ilok-page .login100-form-btn2,
.ilok-page .login100-form-btn3{
  font-family:"Plex Sans",sans-serif; font-size:var(--s-0); font-weight:600;
  padding:.75rem 1.6rem; cursor:pointer; white-space:nowrap; border-radius:var(--rs);
  border:1px solid var(--line-2); background:transparent; color:var(--text);
  display:inline-flex; align-items:center; gap:.6rem; justify-content:center;
  transition:background .18s ease, border-color .18s ease, color .18s ease,
             filter .18s ease, opacity .18s ease, transform .18s ease;
}
/* HOVER FILLS, IT DOES NOT JUST REDRAW AN EDGE. An outlined button used to
   move one hairline from --line-2 to --text-2 - a change so small that the
   .15s transition had nothing to carry, which is what read as "it just
   switched". The circular actions on the banner have always done the whole
   move (ground, edge and ink together, plus a pixel of lift) and that is the
   one that feels like something answered. Same move here.
   The :not() chain is the point of the selector: this fills buttons that have
   no fill of their own. A button that brings its own colour - gold, solid, the
   Revolut blue - keeps it and brightens instead, or hovering the payment
   button would turn it gold. */
.btn:not(.btn-solid):not(.btn-gold):not(.btn-revolut):hover,
.btn:not(.btn-solid):not(.btn-gold):not(.btn-revolut):focus-visible{
  background:var(--gold); border-color:var(--gold); color:var(--gold-ink);
}
.ilok-page .login100-form-btn2:not([type="submit"]):hover,
.ilok-page .login100-form-btn2:not([type="submit"]):focus-visible,
.ilok-page .login100-form-btn3:hover,
.ilok-page .login100-form-btn3:focus-visible{
  background:var(--gold); border-color:var(--gold); color:var(--gold-ink);
}
.btn:hover,.btn:focus-visible,
.ilok-page .login100-form-btn2:hover,.ilok-page .login100-form-btn2:focus-visible,
.ilok-page .login100-form-btn3:hover,.ilok-page .login100-form-btn3:focus-visible{transform:translateY(-1px)}
.btn:active,.ilok-page .login100-form-btn2:active,.ilok-page .login100-form-btn3:active{transform:translateY(0)}
.btn-solid{background:var(--text); border-color:var(--text); color:var(--ink)}
.btn-solid:hover{filter:brightness(1.08); color:var(--ink)}
.btn-gold,
.ilok-page input[type="submit"].login100-form-btn2{background:var(--gold); border-color:var(--gold); color:var(--gold-ink)}
/* Brightness, not opacity. Fading a button towards its background is the same
   gesture as disabling one, and over artwork it reads as the button flickering
   rather than lighting up. */
.btn-gold:hover,
.ilok-page input[type="submit"].login100-form-btn2:hover{filter:brightness(1.08); color:var(--gold-ink)}
.btn-sm,
.ilok-page .login100-form-btn2,
.ilok-page .login100-form-btn3{padding:.5rem 1.1rem; font-size:var(--s--1)}
/* THE PRICE IS PART OF THE BUTTON. It was a number standing next to one, which
   is two things to look at and neither of them large. Inside, the button is the
   biggest thing on the bar and says what it costs, as the banner mockup had it. */
/* OUTLINED PILL. Same shape language as the header search: a full radius and
   one 1px accent hairline, over a deep tint of that same accent. The price is
   split off by a hairline and carries the accent colour, so the button still
   says what it costs without a filled chip. Hover fills with the full accent.
   --gold / --ink are the product's UI and ground colours (Palette), so outline,
   tint and hover all follow each product's settings. */
.btn.btn-buy{
  /* The fill is the accent mixed into the page ground (both per-product, from
     Palette), so it stays a darker shade of the outline on every product. */
  background:color-mix(in srgb, var(--gold) 20%, var(--ink));
  border-color:var(--gold); color:var(--text); text-decoration:none;
  border-radius:999px; padding:.55rem .45rem .55rem 1.4rem; gap:0;
  font-weight:500; letter-spacing:.02em;
}
.btn-buy .pz{
  font-family:"Plex Mono",monospace; font-variant-numeric:tabular-nums;
  font-weight:500; letter-spacing:-.01em; color:var(--gold); text-decoration:none;
  margin-left:1.1rem; padding:.2rem 1rem .2rem 1.1rem;
  border-left:1px solid var(--gold);
  transition:color .18s ease, border-color .18s ease;
}
.btn.btn-buy:hover,.btn.btn-buy:focus-visible{
  background:var(--gold); color:var(--gold-ink); filter:none;
}
.btn-buy:hover .pz,.btn-buy:focus-visible .pz{color:var(--gold-ink); border-color:var(--btn-wash, rgba(0,0,0,.25))}
.btn-block{width:100%}

.pbtn{
  --sz:52px; width:var(--sz); height:var(--sz); flex-shrink:0; padding:0; border:0;
  border-radius:calc(var(--sz) * .29);
  background:var(--art-chip); box-shadow:var(--shadow-btn); cursor:pointer;
  display:grid; place-items:center; transition:transform .18s, background .18s;
}
.pbtn::after{
  content:""; border-left:calc(var(--sz)/4.4) solid var(--art-chip-ink);
  border-block:calc(var(--sz)/6.6) solid transparent; margin-left:calc(var(--sz)/14);
}
.pbtn:hover{transform:scale(1.07); filter:brightness(1.06)}
.pbtn.on::after{
  border:0; margin-left:0; background:var(--art-chip-ink);
  width:calc(var(--sz)/5.4); height:calc(var(--sz)/3.4);
  box-shadow:calc(var(--sz)/3.8) 0 0 var(--art-chip-ink); transform:translateX(calc(var(--sz)/-7.6));
}
/* on-page variant: same shape, but quiet - it is not sitting on artwork */
.pbtn.solid{background:transparent; border:1px solid var(--line-2); box-shadow:none}
.pbtn.solid::after{border-left-color:var(--text)}
.pbtn.solid:hover{background:transparent; border-color:var(--text-2)}
.pbtn.solid.on::after{background:var(--text); box-shadow:calc(var(--sz)/3.8) 0 0 var(--text)}

.auditions{display:flex; gap:2.5rem; flex-wrap:wrap; align-items:center}
.audition{display:flex; align-items:center; gap:.9rem; cursor:pointer; background:none; border:0; padding:0; font:inherit; color:var(--text); text-align:left}
.audition span{font-weight:600}
.audition small{display:block; font-weight:400; color:var(--muted); font-size:var(--s--1)}

/* ============================ BANNER HERO ============================
   The artwork IS the hero: it fills the whole block and the copy sits on top of
   it. The only gradient is a scrim across the bottom third, there to make the
   text legible - not to blend the image into the page. */
.bhero{
  position:relative; background:var(--well); overflow:hidden;
  min-height:clamp(460px,66vh,760px);
  display:flex; align-items:flex-end;
}
.bhero > img{
  position:absolute; inset:0; width:100%; height:100%; object-fit:cover; object-position:center;
}
/* ARRIVAL. On the product page the banner and the mark fade in once they have
   been decoded (product.php adds .reveal, then .in per picture). Only under
   .reveal, so nothing is hidden when the script does not run - and the home
   hero, which crossfades its own layers, never carries the class. */
.bhero.reveal > img,.bhero.reveal .bhero-logo{transition:opacity .6s ease}
.bhero.reveal > img:not(.in),.bhero.reveal .bhero-logo:not(.in){opacity:0}
@media (prefers-reduced-motion:reduce){
  .bhero.reveal > img,.bhero.reveal .bhero-logo{transition:none}
}
/* TWO LAYERS, CROSSFADING. The home hero stacks two images and swaps which one
   is opaque, so one instrument dissolves into the next instead of the picture
   leaving the block and coming back - which is what a single element fading out
   and in actually does, and what made the change read as a jump.
   .9s: long enough to be a dissolve rather than a cut, short enough that a
   ten-second rotation still feels like slides rather than a screensaver. */
.hero-layer{opacity:0; transition:opacity .9s ease}
.hero-layer.on{opacity:1}
/* The copy is faded separately and faster - it has to be GONE before the words
   change, or you read half of one line and half of the next. */
.bhero-txt{transition:opacity .22s ease}
.bhero.swapping .bhero-txt{opacity:0}
@media (prefers-reduced-motion:reduce){
  .hero-layer,.bhero-txt{transition:none}
  /* The colour still moves - it is what tells you the button is live. The
     travel does not. */
  .btn:hover,.btn:focus-visible,.btn:active,.tact:hover,.tact:focus-visible{transform:none}
}
.bhero::after{
  content:""; position:absolute; inset:0; pointer-events:none;
  /* The two stops keep their ratio (.55 : .9) as --bhero-veil moves, so a
     product turning the overlay down softens the whole fade rather than just
     its last few pixels. calc() inside an rgba() alpha is understood by every
     browser that understands custom properties. */
  background:linear-gradient(to bottom, transparent 42%,
    rgba(var(--art-scrim),calc(var(--bhero-veil) * .61)) 72%,
    rgba(var(--art-scrim),var(--bhero-veil)) 100%);
}
.bhero-copy{position:relative; z-index:1; width:100%}
.bhero-copy .wrap{display:flex; align-items:flex-end; gap:2.5rem; flex-wrap:wrap; padding-block:clamp(1.75rem,4vw,3rem)}
.bhero-txt{max-width:52ch; display:flex; flex-direction:column; gap:.75rem}
/* Fixed at exactly three lines. Without this the hero block is as tall as its
   copy, so a longer sentence (C7's) made that banner sit taller than the rest. */
.bhero-txt p{
  margin:0; font-size:var(--s-1); color:var(--art-text); opacity:.92;
  min-height:calc(3 * 1.7em);
  display:-webkit-box; -webkit-line-clamp:3; -webkit-box-orient:vertical; overflow:hidden;
}
/* The name as a picture, for the products drawn in their own face. It is the
   TITLE of the banner, so it is set at title size - several times the height of
   the name in type, not the same height. It was 68px at most, which on a SQUARE
   badge (VBrass ships a 740x740 roundel) rendered as a coin sitting beside the
   sentence rather than as the name of the instrument.
   A height and a width cap together, with object-fit:contain deciding between
   them, is what lets one rule serve both shapes: a square logo grows until it
   hits the height, a long wordmark until it hits the width, and neither can run
   off the banner. */
/* align-self, because the column stretches its children: without it the <img>
   box was the full width of the copy column and only the PICTURE inside it was
   the right size - a 165px roundel in a 491px element. */
/* display:block is an AUTHOR rule and [hidden] is a user-agent one, so the
   author wins: the home banner hides the mark on slides that have none, and
   without this pair it would show the previous instrument's. */
.bhero-logo[hidden]{display:none}
/* Both limits scale with --logo-size, so a wide wordmark that hits the width
   cap and a tall badge that hits the height cap shrink by the same amount when
   a product turns its logo down. The two numbers are ImageHelper::LOGO_BOX_*:
   the served copy is cut to twice this box, so keep them in step. */
.bhero-logo{
  display:block; align-self:flex-start; height:calc(clamp(96px,15vw,210px) * var(--logo-size)); width:auto;
  max-width:calc(min(560px,72vw) * var(--logo-size)); object-fit:contain; object-position:left center;
}
/* The copy is bottom-aligned, so a title three times taller grows upwards and
   the block ends flush against the section bar below. Lifting it is what keeps
   the picture visible under the words - and it is on the WRAP, so the buttons
   at the other end of the row rise with the text instead of dropping out of
   line with it. Products without a logo are untouched. */
.bhero.has-logo .bhero-copy .wrap{
  padding-bottom:calc(clamp(1.75rem,4vw,3rem) + clamp(1rem,3.2vw,3rem));
}
/* On the banner the eyebrow is the instrument's NAME, not a label above one,
   so it is set as a title rather than as fine print. */
.bhero-txt .eyebrow{
  color:var(--art-text); font-family:Poppins,sans-serif; font-weight:600; letter-spacing:.06em;
  font-size:clamp(1.25rem,2.6vw,1.9rem); text-transform:none;
}
.bhero-act{margin-left:auto; display:flex; gap:.9rem; flex-wrap:wrap; align-items:center}
/* On the hero the same three sit beside the outlined button, and they are the
   only controls on a picture that fills the screen - 46px read as the tile
   buttons had wandered onto the banner. They scale with the viewport now and
   keep a 50px floor, which is already above the 44px touch minimum. */
.bhero-act .tact{width:clamp(50px,4.2vw,62px); height:clamp(50px,4.2vw,62px)}
.bhero-act .tact svg{width:clamp(23px,1.95vw,29px); height:clamp(23px,1.95vw,29px)}
/* .pbtn - the big filled disc - is now the THEATRE's play button only, over a
   video still, where a large solid target is the whole convention. The heroes
   use .tact, the same hairline circle the catalogue tiles carry: one control in
   three places should look like one control. */
/* Outline buttons need a light edge once they sit on a photograph - but the
   filled variants must keep their dark ink, so exclude them explicitly. */
.bhero-copy .btn:not(.btn-solid):not(.btn-gold){border-color:var(--art-text); color:var(--art-text); opacity:.85}
.bhero-copy .btn:not(.btn-solid):not(.btn-gold):hover{
  opacity:1; background:var(--gold); border-color:var(--gold); color:var(--gold-ink);
}

/* THE STRIP'S HEIGHT IS FIXED, and that is the point of it being here rather
   than derived. It was `flex:1 0 128px` with `aspect-ratio:3.1/1`, so every
   thumbnail grew to fill the row and turned the spare width into HEIGHT: six
   slides gave a 96px strip, three gave 161px and two gave 241px. Removing one
   banner in the admin therefore made the strip visibly taller, which is not a
   thing anybody chose - and the count changes over time by design.
   Fixed height, thumbnails share the width. */
.strip{display:flex; gap:1px; background:var(--line); overflow-x:auto; height:clamp(76px,6.4vw,104px)}
.strip button{flex:1 1 0; min-width:104px; height:100%; border:0; padding:0; background:var(--well); cursor:pointer; position:relative; overflow:hidden}
.strip img{width:100%; height:100%; object-fit:cover; opacity:.34; transition:opacity .2s}
.strip button:hover img{opacity:.7}
.strip button[aria-current="true"] img{opacity:1}
/* The instrument's mark, centred on its thumbnail. It is the same file the
   hero shows, at a fraction of the tile: never wider than 70% of it nor taller
   than 56%, and it does not take the hover. Brighter than the art under it so
   the strip reads as a row of names, and full on the current one. */
.strip img.strip-logo{
  position:absolute; inset:0; margin:auto; width:auto; height:auto;
  max-width:70%; max-height:56%; object-fit:contain; opacity:.6; pointer-events:none;
}
.strip button:hover img.strip-logo{opacity:.9}
.strip button[aria-current="true"] img.strip-logo{opacity:1}
/* THE LINE UNDER THE CURRENT THUMBNAIL IS THE TIMER.
   Full width when nothing is rotating - it is just the marker then. While the
   banner rotates it grows from nothing to the full width over exactly the
   interval, so the page says when it is about to move without a countdown or a
   spinner anywhere.
   It restarts by itself: the pseudo-element belongs to whichever button carries
   aria-current, so moving that attribute to the next thumbnail creates a fresh
   one and the animation begins from zero. */
.strip button[aria-current="true"]::after{
  content:""; position:absolute; inset-inline:0; bottom:0; height:2px; background:var(--gold);
  transform-origin:left center;
}
.strip.rotating button[aria-current="true"]::after{
  animation:thumbtimer var(--slide-ms,7000ms) linear forwards;
}
/* Paused with the rotation itself - a bar that keeps filling while the timer is
   held is worse than no bar. */
.strip.rotating.paused button[aria-current="true"]::after{animation-play-state:paused}
@keyframes thumbtimer{from{transform:scaleX(0)} to{transform:scaleX(1)}}
@media (prefers-reduced-motion:reduce){
  .strip.rotating button[aria-current="true"]::after{animation:none}
}

/* ============================ FEATURE SECTIONS ============================
   Spitfire's shape: an enormous gradient title, then a simple two-up of plain
   words and a flat line drawing. No panels, no shadows, no boxes. */
.feature{display:grid; gap:clamp(2rem,5vw,4.5rem); grid-template-columns:minmax(0,1fr) minmax(0,1fr); align-items:center}
.feature.flip .fig,.feature.flip .feat-shot{order:-1}
.feature h3{font-family:Poppins,sans-serif; font-weight:600; font-size:var(--s-3); margin:0 0 1.1rem; color:var(--gold); letter-spacing:-.01em}
/* feature copy is the page's actual argument - it was set at UI size, which is
   too small to read comfortably at these measures */
.feature .prose-legacy{color:var(--text-2)}
.feature p{margin:0 0 1.15rem; max-width:var(--measure); font-size:var(--s-1); line-height:1.75}
.feature p:last-child{margin-bottom:0}
.feature b{color:var(--text); font-weight:600}
.fig{width:100%; color:var(--text-2)}
.fig svg{width:100%; height:auto; display:block}
.fig .lbl{font-family:"Plex Mono",monospace; font-size:11px; letter-spacing:.14em; fill:var(--muted); text-transform:uppercase}
.fig .hot{stroke:var(--gold)}
.fig .hotf{fill:var(--gold)}

/* ============================ CATEGORY GRID ============================
   Orientation in one screen: what the catalogue actually contains. Artwork does
   the work; the label sits on it rather than under it, so the row stays compact. */
.cats{display:grid; grid-template-columns:repeat(auto-fill,minmax(210px,1fr)); gap:clamp(.75rem,1.4vw,1.1rem)}
.cat{
  position:relative; display:block; aspect-ratio:5/3; border-radius:var(--rs);
  overflow:hidden; background:var(--well); box-shadow:var(--shadow-art);
}
.cat img{width:100%; height:100%; object-fit:cover; transform:scale(1.06); transition:transform .5s cubic-bezier(.2,.7,.3,1); opacity:.78}
.cat:hover img{transform:scale(1.13); opacity:.82}
.cat::after{content:""; position:absolute; inset:0; background:linear-gradient(180deg, rgba(var(--art-scrim),.05) 28%, rgba(var(--art-scrim),.9) 100%)}
.cat-lbl{position:absolute; inset-inline:0; bottom:0; padding:.85rem 1rem; z-index:1; display:flex; align-items:baseline; justify-content:space-between; gap:.5rem}
.cat-lbl b{font-family:Poppins,sans-serif; font-weight:600; font-size:var(--s-1); color:#fff}
.cat-lbl span{font-family:"Plex Mono",monospace; font-size:.6875rem; color:var(--art-text); opacity:.6}

/* ============================ CATALOGUE PLAYER ============================
   Modelled on the A/B comparison player: one big transport, a scrubable
   waveform, and the playlist underneath. Skinned to this template - gold only
   on the played portion, the active row and the transport. */
.player{
  /* One accent drives the transport, the lit waveform and the active row, so a
     variant retints the whole control by setting three properties. */
  --pl-acc:var(--gold); --pl-acc-ink:var(--gold-ink); --pl-acc-soft:rgba(217,154,68,.09);
  border:1px solid var(--line); border-radius:var(--r); background:var(--veil); overflow:hidden;
}
/* The A/B comparison is a different KIND of thing from a demo playlist, so it
   reads in a cool accent - enough to tell them apart at a glance, not enough to
   look like a second brand. (The admin colour designer is bypassed for now.) */
.player--cmp{--pl-acc:#6FA8C7; --pl-acc-ink:#08161d; --pl-acc-soft:rgba(111,168,199,.10)}
.pl-top{display:flex; align-items:center; gap:clamp(1rem,2.5vw,1.75rem); padding:clamp(1.1rem,2.4vw,1.6rem)}
/* SAME LANGUAGE AS THE ADD TO CART PILL: a full circle, one 1px accent
   hairline over a deep tint of that accent mixed into the page ground, glyph in
   the page's text colour; hover fills with the full accent. No shadow. --pl-acc
   follows the product's UI colour (Palette -> --gold), and --ink its ground. */
.pl-play{
  --sz:64px; width:var(--sz); height:var(--sz); flex-shrink:0; padding:0;
  border:1px solid var(--pl-acc); border-radius:50%;
  background:color-mix(in srgb, var(--pl-acc) 20%, var(--ink)); color:var(--text);
  cursor:pointer; display:grid; place-items:center;
  transition:transform .18s, background .18s, color .18s;
}
/* Glyph sized from --sz (the 64px values scaled), so the 38px dock button is not crowded. */
.pl-play::after{content:""; border-left:calc(var(--sz) * .25) solid currentColor; border-block:calc(var(--sz) * .172) solid transparent; margin-left:calc(var(--sz) * .0625)}
.pl-play:hover,.pl-play:focus-visible{background:var(--pl-acc); color:var(--pl-acc-ink); transform:scale(1.05)}
.pl-play.on::after{
  border:0; margin-left:0; width:calc(var(--sz) * .078); height:calc(var(--sz) * .297); background:currentColor;
  box-shadow:calc(var(--sz) * .14) 0 0 currentColor; transform:translateX(calc(var(--sz) * -.0625));
}
.pl-scope{flex:1; min-width:0; display:flex; flex-direction:column; gap:.5rem}
.pl-wave{display:flex; align-items:center; gap:2px; height:56px; cursor:pointer}
.pl-wave i{flex:1; background:var(--line-2); border-radius:1px; display:block; transition:background .08s}
.pl-wave i.lit{background:var(--pl-acc)}
.pl-list{list-style:none; margin:0; padding:0; max-height:308px; overflow-y:auto; border-top:1px solid var(--line)}
.pl-list::-webkit-scrollbar{width:8px}
.pl-list::-webkit-scrollbar-thumb{background:var(--line-2); border-radius:4px}
.pl-list li{
  display:grid; grid-template-columns:2.5rem minmax(0,1fr) auto auto;
  gap:1rem; align-items:center; padding:.7rem clamp(1.1rem,2.4vw,1.6rem);
  cursor:pointer; border-left:2px solid transparent;
}
.pl-list li + li{border-top:1px solid var(--line)}
.pl-list li:hover{background:var(--veil-2)}
.pl-list li.on{background:var(--pl-acc-soft); border-left-color:var(--pl-acc)}
.pl-i{font-family:"Plex Mono",monospace; font-size:.75rem; color:var(--muted); text-align:right}
.pl-list li.on .pl-i{color:var(--pl-acc)}
.pl-t{min-width:0; font-size:var(--s-0); color:var(--text); white-space:nowrap; overflow:hidden; text-overflow:ellipsis}
.pl-list li.on .pl-t{color:var(--pl-acc); font-weight:600}
.pl-src{font-size:var(--s--1); color:var(--muted); white-space:nowrap}
.pl-d{font-family:"Plex Mono",monospace; font-size:.75rem; color:var(--muted); width:2.7rem; text-align:right}
/* The product name on the home page's catalogue run is a link to that product. */
a.pl-link{color:var(--muted); text-decoration:none; border-bottom:1px solid transparent; transition:color .15s, border-color .15s}
a.pl-link:hover,a.pl-link:focus-visible{color:var(--pl-acc); border-bottom-color:currentColor}
.pl-list li.on a.pl-link{color:var(--text)}
.pl-list li.on a.pl-link:hover{color:var(--pl-acc)}
/* Home page: a 40-track catalogue run, so the playlist gets room for ~13 rows
   instead of ~6, and the waveform a little more presence to match. */
.player--tall .pl-wave{height:72px}
.player--tall .pl-list{max-height:min(620px, 70vh)}
@media (max-width:700px){
  .pl-list li{grid-template-columns:2rem minmax(0,1fr) auto}
  .pl-src{display:none}
  /* ...except the link: on a phone it drops under the title instead of vanishing. */
  .pl-list li a.pl-link{display:block; grid-column:2; grid-row:2; margin-top:-.6rem; font-size:.8rem; justify-self:start}
}

/* ============================ COMPARE PLAYER ============================
   Their A/B player: toggle between the real performance and the library, two
   labelled scopes, one transport, shared playlist. */
.cmp{border:1px solid var(--line); border-radius:var(--r); background:var(--veil); overflow:hidden}
.cmp-toggle{display:flex; justify-content:center; padding:clamp(1rem,2vw,1.4rem) 1rem .25rem}
.cmp-seg{display:inline-flex; background:var(--veil-2); border:1px solid var(--line); border-radius:999px; padding:3px}
.cmp-seg button{
  font:inherit; font-size:var(--s--1); font-weight:600; color:var(--text-2);
  background:transparent; border:0; border-radius:999px; padding:.5rem 1.4rem; cursor:pointer;
  transition:background .16s, color .16s;
}
.cmp-seg button:hover{color:var(--text)}
.cmp-seg button[aria-pressed="true"]{background:var(--gold); color:var(--gold-ink)}
.cmp-body{display:flex; align-items:center; gap:clamp(1rem,2.5vw,1.75rem); padding:clamp(1rem,2.4vw,1.5rem)}
.cmp-scopes{flex:1; min-width:0; display:flex; flex-direction:column; gap:.9rem}
.cmp-scope{display:flex; flex-direction:column; gap:.35rem; transition:opacity .2s}
.cmp-scope.off{opacity:.32}
.cmp-scope .lab{
  font-family:"Plex Mono",monospace; font-size:.625rem; letter-spacing:.18em;
  text-transform:uppercase; color:var(--muted);
}
.cmp-scope.on .lab{color:var(--gold)}
.cmp-wave{display:flex; align-items:center; gap:2px; height:40px; cursor:pointer}
.cmp-wave i{flex:1; background:var(--line-2); border-radius:1px; display:block}
.cmp-scope.on .cmp-wave i.lit{background:var(--gold)}

/* Audio demos and the A/B comparison side by side - neither needs full width,
   and together they are the "listen" half of the page. */
.listen2{display:grid; grid-template-columns:repeat(2,minmax(0,1fr)); gap:clamp(1.75rem,3.5vw,3rem); align-items:stretch}
/* Both columns are one flex stack so the two players end level. The DEMO
   player sets the height - its playlist carries a min-height and the
   comparison's does not, so the row is sized by the demos and the comparison
   stretches to match, rather than the taller of the two winning. */
.listen2 > div{display:flex; flex-direction:column; min-width:0}
/* One height for both, set here rather than by whichever player happens to
   have more tracks: --listen-h is the demo player's own natural height (its
   transport plus the 308px playlist it had before the two were paired), and
   the comparison simply matches it. Each playlist scrolls inside it. */
/* +100px on the pair: at 26rem the playlists showed barely four rows. */
.listen2{--listen-h:calc(26rem + 100px)}
.listen2 .player{height:var(--listen-h); display:flex; flex-direction:column}
.listen2 .pl-list{flex:1; max-height:none; min-height:0; overflow-y:auto}

/* One line under each heading saying what the player is. Two lines would put
   the two players out of step, so it is clamped to one. */
.sec-sub{
  margin:-1.5rem 0 1.5rem; text-align:center; color:var(--muted); font-size:var(--s--1);
  white-space:nowrap; overflow:hidden; text-overflow:ellipsis;
}

/* A/B lanes: both takes drawn, the audible one lit. The transport sits between
   them so neither reads as the primary. */
.cmp-lanes{flex:1; min-width:0; display:flex; flex-direction:column; gap:.6rem}
.cmp-lane{
  font:inherit; text-align:left; border:1px solid transparent; background:none; cursor:pointer;
  padding:.35rem .5rem; border-radius:var(--rs); display:block; width:100%;
  transition:border-color .2s, background .2s;
}
.cmp-lane:hover{background:var(--veil)}
.cmp-lane.on{border-color:var(--line-2); background:var(--veil-2)}
.cmp-lab{
  display:block; font-family:"Plex Mono",monospace; font-size:.625rem; letter-spacing:.16em;
  text-transform:uppercase; color:var(--muted); margin-bottom:.3rem;
}
.cmp-lane.on .cmp-lab{color:var(--pl-acc)}
.cmp-lane .pl-wave{height:34px; display:flex; opacity:.4; transition:opacity .2s}
.cmp-lane.on .pl-wave{opacity:1}
.col-lede{margin:-1.25rem 0 1.5rem; color:var(--text-2); font-size:var(--s-0)}
/* minmax(0,...) not 1fr: a bare 1fr is minmax(auto,1fr) and refuses to shrink
   below the player's intrinsic width, which overflows the page on a phone */
@media (max-width:1000px){ .listen2{grid-template-columns:minmax(0,1fr)} }

/* ============================ INTERFACE CAROUSEL ============================
   Centre-stage coverflow: the active screenshot is full size and its neighbours
   sit back, scaled and dimmed, so you can see there is more to look at. */
.cover{position:relative; padding-inline:clamp(0px,4vw,56px)}
.cv-view{overflow:hidden; padding-block:.5rem}
/* The endless wrap moves .on from a clone to the real item in the same frame
   the track is repositioned. Without this the scale/opacity transition on both
   items runs AFTER the slide has visibly finished - the "settles, shrinks, and
   grows again" artifact. */
.cv-track.nofx, .cv-track.nofx .cv-item{transition:none}
.cv-track{position:relative; display:flex; align-items:center; gap:clamp(.75rem,2vw,1.5rem); transition:transform .55s cubic-bezier(.22,.7,.24,1); will-change:transform}
.cv-item{
  flex:0 0 clamp(260px,58%,720px); margin:0; border-radius:var(--r); overflow:hidden;
  background:var(--well); border:1px solid var(--line);
  transform:scale(.86); opacity:.35; transition:transform .55s cubic-bezier(.22,.7,.24,1), opacity .45s, border-color .3s;
}
.cv-item.on{transform:scale(1); opacity:1; border-color:var(--line-2); box-shadow:var(--shadow-art)}
.cv-item img{width:100%; display:block}
/* The no-script fallback: the same pictures, simply laid out. */
.cv-plain{display:grid; grid-template-columns:repeat(auto-fit,minmax(260px,1fr)); gap:1rem; margin-top:1rem}
.cv-plain img{width:100%; height:auto; border-radius:var(--rs); display:block}
.cv-item figcaption{text-align:center;
  padding:.75rem 1rem; font-family:"Plex Mono",monospace; font-size:.6875rem;
  letter-spacing:.1em; text-transform:uppercase; color:var(--muted);
  border-top:1px solid var(--line); opacity:0; transition:opacity .35s;
}
.cv-item.on figcaption{opacity:1; color:var(--text-2)}
.cv-nav{
  position:absolute; top:calc(50% - 24px); width:46px; height:46px; border-radius:50%;
  border:1px solid var(--line-2); background:rgba(var(--art-scrim),.72); backdrop-filter:blur(6px);
  color:var(--text); cursor:pointer; z-index:2;
  display:grid; place-items:center; transition:background .18s, border-color .18s;
  padding:0;
}
/* The arrow is an SVG path in the markup, symmetric about the centre of its own
   viewBox - so centring the box centres the arrow, with nothing to nudge. It
   replaced two rotated borders, which centre a bounding box whose ink sits in
   one corner of it, and before that a typeset chevron, which sits wherever its
   font puts it in the em box. Both needed a fudge factor and both looked a
   little wrong anyway. */
.cv-nav svg{width:20px; height:20px; display:block}
.cv-nav:hover:not(:disabled){background:var(--gold); border-color:var(--gold); color:var(--gold-ink)}
.cv-nav:disabled{opacity:.28; cursor:default}
.cv-nav.prev{left:0} .cv-nav.next{right:0}
.cv-dots{display:flex; justify-content:center; gap:.5rem; margin-top:1.25rem}
.cv-dots button{
  width:8px; height:8px; padding:0; border:0; border-radius:50%; cursor:pointer;
  background:var(--line-2); transition:background .2s, transform .2s;
}
.cv-dots button[aria-selected="true"]{background:var(--gold); transform:scale(1.35)}

/* ============================ THEATRE VIDEO ============================
   One big stage; the strip below swaps a video into it rather than opening a
   second player. */
/* narrower than the page: there are rarely enough videos for the strip to match
   a full-width stage, and a full-width 16:9 stage is enormous on a wide screen */
.theatre{display:flex; flex-direction:column; gap:1rem; max-width:900px; margin-inline:auto}
.th-strip{justify-content:center}
.th-stage{position:relative; aspect-ratio:16/9; background:var(--well); border-radius:var(--r); overflow:hidden; box-shadow:var(--shadow-art)}
.th-stage img{width:100%; height:100%; object-fit:cover; opacity:.85}
.th-stage .pbtn{position:absolute; inset:0; margin:auto}
.th-cap{position:absolute; left:0; bottom:0; width:100%; padding:1.4rem 1.6rem; display:flex; flex-direction:column; gap:.15rem;
  background:linear-gradient(transparent, rgba(var(--art-scrim),.9)); pointer-events:none}
.th-cap b{font-family:Poppins,sans-serif; font-weight:600; font-size:var(--s-2); color:var(--art-text)}
.th-cap span{font-size:var(--s--1); color:var(--art-text); opacity:.7}
.th-strip{display:flex; gap:.75rem; overflow-x:auto; padding-bottom:.6rem}
.th-strip::-webkit-scrollbar{height:6px}
.th-strip::-webkit-scrollbar-thumb{background:var(--line-2); border-radius:3px}
/* thumbs share the stage width rather than overflowing it - with fewer videos
   they simply sit wider, up to a sane cap */
.th-thumb{flex:1 1 0; min-width:96px; max-width:180px; border:0; padding:0; background:none; cursor:pointer; text-align:center; display:flex; flex-direction:column; gap:.45rem}
.th-thumb .tw{position:relative; aspect-ratio:16/9; border-radius:var(--rs); overflow:hidden; background:var(--well); border:1px solid transparent}
.th-thumb img{width:100%; height:100%; object-fit:cover; opacity:.6; transition:opacity .2s}
.th-thumb:hover img{opacity:.9}
.th-thumb[aria-selected="true"] img{opacity:1}
.th-thumb[aria-selected="true"] .tw{border-color:var(--gold)}
.th-thumb small{font-size:.6875rem; color:var(--muted); line-height:1.35}
.th-thumb[aria-selected="true"] small{color:var(--text)}


/* ============================ VIDEO REVIEWS ============================ */
.vgrid{display:grid; grid-template-columns:repeat(auto-fill,minmax(240px,1fr)); gap:clamp(1rem,2vw,1.6rem)}
.vcard{display:flex; flex-direction:column; gap:.7rem}
.vthumb{position:relative; aspect-ratio:16/9; border-radius:var(--rs); overflow:hidden; background:var(--well); box-shadow:var(--shadow-art)}
.vthumb img{width:100%; height:100%; object-fit:cover; transition:transform .5s cubic-bezier(.2,.7,.3,1)}
.vcard:hover .vthumb img{transform:scale(1.06)}
.vthumb .pbtn{position:absolute; inset:0; margin:auto; --sz:44px}
.vcard{text-align:center}
.vcard b{font-size:var(--s-0); font-weight:600}
.vcard span{font-size:var(--s--1); color:var(--muted)}

/* ============================ REVIEWS ============================ */
.rvw{display:grid; gap:0; border-top:1px solid var(--line)}
.rv{padding:1.5rem 0; border-bottom:1px solid var(--line); display:grid; grid-template-columns:190px minmax(0,1fr); gap:2rem}
.rv-who b{display:block; font-size:var(--s-0); font-weight:600}
.rv-who time{font-family:"Plex Mono",monospace; font-size:.6875rem; color:var(--muted)}
.rv-stars{color:var(--gold); letter-spacing:.15em; font-size:var(--s--1); margin-top:.3rem}
.rv p{margin:0; color:var(--text-2); max-width:var(--measure)}
@media (max-width:760px){ .rv{grid-template-columns:1fr; gap:.5rem} }

/* compact feature list for the secondary points */
.fgrid{display:grid; grid-template-columns:repeat(auto-fit,minmax(280px,1fr)); gap:clamp(1.5rem,3vw,2.5rem)}
.fgrid h4{font-family:Poppins,sans-serif; font-weight:600; font-size:var(--s-1); margin:0 0 .5rem; color:var(--gold)}
.fgrid p{margin:0; color:var(--text-2); font-size:var(--s--1)}

/* ============================ TILES ("recently added") ============= */
.tiles{display:grid; gap:clamp(1.75rem,3.5vw,3rem); grid-template-columns:repeat(auto-fill,minmax(310px,1fr))}
.tile{display:flex; flex-direction:column}
/* The one place a shadow earns its keep - it lifts the tile off the band. */
.tile-well{position:relative; aspect-ratio:16/9; background:var(--well); overflow:hidden; display:grid; place-items:center; border-radius:var(--r); box-shadow:var(--shadow-art)}
.tile-shot{grid-area:1/1; width:100%; height:100%; display:block}
.tile-well img{width:100%; height:100%; object-fit:cover; transform:scale(1.07); transition:transform .5s cubic-bezier(.2,.7,.3,1)}
.tile:hover .tile-well img{transform:scale(1.12)}
/* Centred over the artwork, and out of the way until the tile is approached. */
.tile-well .pbtn{grid-area:1/1; place-self:center; --sz:52px; opacity:.9; transition:transform .18s, background .18s, opacity .2s}
.tile:hover .tile-well .pbtn,.tile-well .pbtn:focus-visible,.tile-well .pbtn.on{opacity:1}
/* THE TILE'S THREE ACTIONS.
   Bottom-right of the artwork, always visible, always in the same place. The
   single centred play button they replace landed wherever the picture's subject
   happened to be - over a saxophone's bell on one tile and empty sky on the
   next - and it faded IN on hover, which on a touch device meant it faded away
   the moment you reached for it. A corner is a corner on every tile. */
.tile-acts{
  /* ABSOLUTE, not a grid item aligned to the end. The well is a 16:9 box but
     its single grid row is sized by the artwork's own ratio (a 201x134 cached
     variant makes a 3:2 row), so the row is taller than the box and anything
     aligned to its end lands below the clip - which is exactly where these
     went, invisible under overflow:hidden. The well is positioned, so a corner
     offset is a corner offset whatever the picture measures. */
  position:absolute; right:.7rem; bottom:.7rem; z-index:2;
  display:flex; gap:.45rem;
}
.tact{
  width:38px; height:38px; flex-shrink:0; padding:0; cursor:pointer;
  display:grid; place-items:center; border-radius:50%;
  background:rgba(var(--art-scrim),.62); border:1px solid rgba(255,255,255,.14);
  color:var(--art-text); -webkit-backdrop-filter:blur(6px); backdrop-filter:blur(6px);
  transition:background .16s, border-color .16s, color .16s, transform .16s;
}
.tact svg{width:19px; height:19px; display:block; fill:currentColor; stroke:none}
/* The speaker's two arcs are strokes; its cone is a fill. */
.tact svg .w1,.tact svg .w2{fill:none; stroke:currentColor; stroke-width:1.7; stroke-linecap:round}
.tact:hover,.tact:focus-visible{background:var(--gold); border-color:var(--gold); color:var(--gold-ink); transform:translateY(-1px)}
/* Playing is a state, not a hover: it has to read without the pointer. */
.tact-spk.on{background:var(--gold); border-color:var(--gold); color:var(--gold-ink)}
.tile-well .absent{font-family:"Plex Mono",monospace; font-size:.6875rem; letter-spacing:.14em; text-transform:uppercase; color:#3A3A42; align-self:end; padding-bottom:1.1rem}
.tile-meta{position:absolute; left:0; bottom:0; width:100%; padding:.6rem .9rem; font-family:"Plex Mono",monospace; font-size:.6875rem; color:var(--art-text); background:linear-gradient(transparent,rgba(var(--art-scrim),.82)); display:flex; gap:1rem}
.tile-body{padding-top:1.15rem; display:flex; flex-direction:column; gap:.5rem; flex:1}
.tile-body h3{font-family:Poppins,sans-serif; font-weight:600; font-size:var(--s-2); margin:0}
.tile-body p{margin:0; color:var(--text-2); font-size:var(--s-0); flex:1; display:-webkit-box; -webkit-line-clamp:3; -webkit-box-orient:vertical; overflow:hidden}
/* "More info" and the price share one line at the foot of the tile. Baseline
   alignment, not centre: a mono figure and a sans link centred against each
   other sit visibly off, because the two faces do not share an x-height. */
.tile-foot{display:flex; align-items:baseline; justify-content:space-between; gap:1rem; padding-top:.35rem}
.tile-more{font-weight:600; display:inline-flex; align-items:center; gap:.5rem; align-self:flex-start}
.tile-price{
  font-family:"Plex Mono",monospace; font-variant-numeric:tabular-nums; font-weight:500;
  font-size:var(--s-0); color:var(--text); white-space:nowrap; letter-spacing:-.01em;
}
/* Free is a fact worth reading as one, so it takes the accent rather than
   sitting in the same ink as a number. */
.tile-price.free{color:var(--gold)}
.tile-price .was{font-weight:400; font-size:var(--s--1); color:var(--muted);
  text-decoration:line-through; margin-right:.4rem}
.tile-more::after{content:"\2192"; transition:transform .2s}
.tile:hover .tile-more::after{transform:translateX(4px)}

/* ============================ PAGE HEAD ============================ */
.pagehead{padding-block:clamp(2.5rem,6vw,4.5rem)}
.crumb{font-family:"Plex Mono",monospace; font-size:.6875rem; letter-spacing:.1em; text-transform:uppercase; color:var(--muted); display:flex; gap:.6rem; margin-bottom:1.5rem}
.crumb a:hover{color:var(--text-2)}
.pagehead .lede{margin:1rem 0 0; color:var(--text-2); max-width:var(--measure); font-size:var(--s-1)}
.toolbar{display:flex; align-items:center; gap:1rem; flex-wrap:wrap; margin-top:2.5rem; padding-top:1rem; border-top:1px solid var(--line)}
.toolbar label{margin-left:auto; display:flex; align-items:center; gap:.6rem; color:var(--muted); font-size:var(--s--1)}
select{font:inherit; font-size:var(--s--1); color:var(--text); background:transparent; border:1px solid var(--line-2); padding:.45rem 2rem .45rem .8rem; cursor:pointer; appearance:none; border-radius:var(--rs);
  background-image:linear-gradient(45deg,transparent 50%,currentColor 50%),linear-gradient(135deg,currentColor 50%,transparent 50%);
  background-position:calc(100% - 15px) 53%,calc(100% - 10px) 53%; background-size:5px 5px,5px 5px; background-repeat:no-repeat}

/* ============================ MEDIA ============================ */
.media{display:grid; grid-template-columns:minmax(0,1.5fr) minmax(0,1fr); gap:1.5rem; align-items:start}
.mvid{position:relative; background:var(--well); aspect-ratio:16/9; overflow:hidden; border-radius:var(--r)}
.mvid img{width:100%; height:100%; object-fit:cover; opacity:.9; transform:scale(1.06)}
.mvid .pbtn{position:absolute; inset:0; margin:auto}
.mcap{position:absolute; left:0; bottom:0; width:100%; padding:.7rem 1rem; font-family:"Plex Mono",monospace; font-size:.6875rem; letter-spacing:.06em; color:var(--art-text); background:linear-gradient(transparent,rgba(var(--art-scrim),.84))}
.demos{border:1px solid var(--line); border-radius:var(--r); overflow:hidden; display:flex; flex-direction:column; min-width:0}
.demos-h{display:flex; align-items:center; justify-content:space-between; gap:1rem; padding:.85rem 1.1rem; border-bottom:1px solid var(--line)}
.tracks{list-style:none; margin:0; padding:0; overflow-y:auto; flex:1; max-height:334px}
.tracks li{display:flex; align-items:center; gap:.85rem; padding:.62rem 1.1rem; cursor:pointer}
.tracks li + li{border-top:1px solid var(--line)}
.tracks li:hover .tk-name{color:var(--text)}
.tk-n{font-family:"Plex Mono",monospace; font-size:.6875rem; color:var(--muted); width:1.4rem; flex-shrink:0}
.tracks li.on .tk-n{color:var(--gold)}
.tk-name{flex:1; min-width:0; font-size:var(--s--1); color:var(--text-2); white-space:nowrap; overflow:hidden; text-overflow:ellipsis}
.tracks li.on .tk-name{color:var(--text)}
.tk-plays,.tk-dur{font-family:"Plex Mono",monospace; font-size:.6875rem; color:var(--muted); flex-shrink:0}
.tk-dur{width:2.7rem; text-align:right}

/* The opening statement, on the same full-bleed treatment as the feature bands.
   Its scrim is symmetric rather than left-weighted, because the text is centred -
   and it sits much darker than the hero so the photograph reads as atmosphere
   here rather than as a repeat of the shot directly above it. */
.lede-band .bgimg{opacity:.3; object-position:center 62%}
.lede-band::after{
  background:radial-gradient(120% 85% at 50% 50%, rgba(var(--art-scrim),.72) 0%, rgba(var(--art-scrim),.93) 62%, rgba(var(--art-scrim),.99) 100%);
}
.lede-band .prose-legacy{color:var(--art-text)}
.lede-band p{opacity:.9}
.lede-band .wrap{text-align:center}
/* the title reads better broken early; the prose does not */
.lede-band .feature-title{max-width:20ch; margin-inline:auto}
.lede-band p{max-width:88ch}
/* Through the .grad variables (see .grad): the title's own --grad-mb when it
   sets one, this band's default otherwise, less the descender padding. */
.lede-band .feature-title{margin:0 auto calc(var(--grad-mb, clamp(1.75rem,4vw,2.5rem)) - var(--grad-pad, 0px))}
.lede-band .prose-legacy{color:var(--text-2)}
.lede-band p{margin:0 auto 1.35rem; font-size:var(--s-1); line-height:1.8}
.lede-band p:last-child{margin-bottom:0}
.lede-band b{color:var(--text); font-weight:600}
.lede-pull{
  font-family:Poppins,sans-serif; font-weight:300; color:var(--gold) !important;
  font-size:clamp(1.25rem,2.6vw,1.75rem) !important; line-height:1.35 !important;
  margin-block:clamp(1.5rem,3vw,2rem) !important;
}

/* The blocks in the specs column are prose, not the page's body copy: one
   step down, so the technical description beside them stays the louder of the
   two. */
/* Two class names, not one: `.specs > .prose-legacy` sets the per-product text
   a step down, and these blocks are no longer direct children of .specs, so a
   single-class rule here loses to .prose-legacy's own size and the site-wide
   paragraphs came out LOUDER than the product's own words. */
.prose-legacy.specs-block{font-size:var(--s--0); color:var(--text-2)}
.specs-block p{margin:0 0 .7rem}
.specs-block p:last-child{margin:0}
/* The three site-wide blocks run ACROSS the full width under the per-product
   text, three to a row - they are three short parallel paragraphs and read
   better side by side than as one long column. A rule separates them from the
   product's own words above. Three or one, never two: with three blocks a
   two-column row leaves the last one sitting alone. */
.specs-blocks{grid-column:1/-1; margin-top:2.4rem; padding-top:1.8rem;
  border-top:1px solid var(--line);
  display:grid; grid-template-columns:repeat(3,minmax(0,1fr)); gap:clamp(1.6rem,3vw,3.2rem)}
.specs-blocks > div{min-width:0}
.specs-blocks .eyebrow{margin:0 0 .7rem}
@media (max-width:820px){
  .specs-blocks{grid-template-columns:1fr; gap:1.8rem; margin-top:2rem; padding-top:1.4rem}
}
.specs{display:grid; grid-template-columns:repeat(auto-fit,minmax(250px,1fr)); gap:0 clamp(2rem,5vw,4rem)}
.specs dl{margin:0; display:grid; grid-template-columns:1fr auto; align-content:start}
.specs dt,.specs dd{padding:.75rem 0; border-bottom:1px solid var(--line); margin:0}
.specs dt{color:var(--muted); font-size:var(--s--1)}
.specs dd{font-family:"Plex Mono",monospace; font-size:var(--s--1); text-align:right; color:var(--text)}
.chips{display:flex; flex-wrap:wrap; gap:.4rem}
.chip{font-family:"Plex Mono",monospace; font-size:.6875rem; border:1px solid var(--line-2); padding:.25rem .6rem; color:var(--text-2); border-radius:999px}
.avail{display:inline-flex; align-items:center; gap:.5rem; color:var(--ok); font-size:var(--s--1); font-weight:600}
.avail::before{content:""; width:7px; height:7px; border-radius:50%; background:var(--ok)}
.price{font-family:"Plex Mono",monospace; font-variant-numeric:tabular-nums; font-weight:500; letter-spacing:-.02em}

/* ============================ CART ============================ */
.cartgrid{display:grid; grid-template-columns:minmax(0,1fr) 340px; gap:clamp(2rem,5vw,4rem); align-items:start}
.lines{border-top:1px solid var(--line)}
.line{display:grid; grid-template-columns:130px minmax(0,1fr) auto; gap:1.5rem; align-items:center; padding:1.4rem 0; border-bottom:1px solid var(--line)}
.line-art{aspect-ratio:16/9; background:var(--well); overflow:hidden; border-radius:var(--rs)}
.line-art img{width:100%; height:100%; object-fit:cover; transform:scale(1.06)}
.line h3{font-family:Poppins,sans-serif; font-weight:600; margin:0 0 .2rem; font-size:var(--s-1)}
.line .sub2{font-family:"Plex Mono",monospace; font-size:.6875rem; color:var(--muted); letter-spacing:.04em}
.line-r{display:flex; flex-direction:column; align-items:flex-end; gap:.35rem}
.rm{font-size:var(--s--1); color:var(--muted); cursor:pointer; text-decoration:underline; text-underline-offset:3px}
.rm:hover{color:var(--text-2)}
.sum{border:1px solid var(--line); border-radius:var(--r); padding:1.6rem}
.sumrow{display:flex; justify-content:space-between; gap:1rem; padding:.5rem 0; font-size:var(--s--1); color:var(--text-2)}
.sumrow .mono{color:var(--text)}
.sumrow.tot{border-top:1px solid var(--line); margin-top:.75rem; padding-top:1rem; color:var(--text); font-size:var(--s-1)}
.sumrow.tot .price{font-size:var(--s-3); color:var(--gold)}
.coupon{display:flex; gap:.5rem; margin:0 0 1.25rem}
/* The pieces the cart view needs on top of the block above, which was written
   for the mockup before there was a cart to put it in. */
.line h3 a{color:var(--text)}
.line h3 a:hover{color:var(--gold)}
.line .sub2 .lbl{background:var(--gold); color:var(--gold-ink); border-radius:999px; padding:.1rem .5rem; margin-left:.4rem; letter-spacing:.04em}
.line .qty{display:inline-flex; align-items:center; gap:.55rem; margin-top:.7rem}
.line .qty span{font-family:"Plex Mono",monospace; font-size:.6875rem; letter-spacing:.1em; text-transform:uppercase; color:var(--muted)}
/* The browser's own number input is white-on-grey, which is unreadable here. */
.qty-input{
  font:inherit; font-size:var(--s--1); width:64px; text-align:center; padding:.4rem .3rem;
  background:transparent; border:1px solid var(--line-2); color:var(--text); border-radius:var(--rs);
  -moz-appearance:textfield;
}
.qty-input:focus{outline:none; border-color:var(--text-2)}
.line-r .price{font-size:var(--s-1)}
.cpn-err{margin:-.75rem 0 1rem; font-size:var(--s--1); color:#E88}
.sum-note{margin:.9rem 0 0; font-size:var(--s--1); color:var(--text-2)}
.sum .btn-block{margin-top:1.25rem}
@media (max-width:900px){
  .cartgrid{grid-template-columns:minmax(0,1fr)}
  .line{grid-template-columns:96px minmax(0,1fr); gap:1rem}
  .line-r{grid-column:2; flex-direction:row; align-items:center; justify-content:space-between}
}
input[type=text]{font:inherit; font-size:var(--s--1); flex:1; min-width:0; background:transparent; border:1px solid var(--line-2); color:var(--text); padding:.6rem .9rem; border-radius:var(--rs)}
input::placeholder{color:var(--muted)}

/* ============================ TRANSPORT ============================
   Appears only while something is playing. Flat: a hairline, no shadow. */
.dock{position:fixed; left:0; right:0; bottom:0; z-index:80; background:var(--ink); border-top:1px solid var(--line-2); transform:translateY(110%); transition:transform .3s cubic-bezier(.2,.8,.2,1)}
.dock.up{transform:none}
.dock .wrap{display:flex; align-items:center; gap:clamp(.7rem,1.5vw,1.25rem); min-height:66px; padding-block:.6rem}
.dk-art{width:70px; aspect-ratio:16/9; background:var(--well); overflow:hidden; border-radius:6px; flex-shrink:0}
.dk-art img{width:100%; height:100%; object-fit:cover; transform:scale(1.06)}
.dk-id{min-width:0; display:flex; flex-direction:column; flex-shrink:0; max-width:clamp(110px,16vw,220px)}
.dk-id b{font-family:Poppins,sans-serif; font-weight:600; font-size:var(--s-0); white-space:nowrap; overflow:hidden; text-overflow:ellipsis}
.dk-id span{font-size:var(--s--1); color:var(--muted); white-space:nowrap; overflow:hidden; text-overflow:ellipsis}
.dk-skip{
  width:34px; height:34px; flex-shrink:0; border:0; padding:0; background:none; cursor:pointer;
  color:var(--text-2); display:grid; place-items:center; border-radius:8px; transition:color .15s, background .15s;
}
.dk-skip svg{width:19px; height:19px}
.dk-skip:hover{color:var(--text); background:var(--veil-2)}
/* Denser bars need a hairline gap, or the gaps are most of what you see. */
.dk-wave{flex:1; min-width:60px; display:flex; align-items:flex-end; gap:1px; height:34px; cursor:pointer; padding-block:2px}
.dk-wave i{align-self:center}
.dk-wave i{flex:1 1 0; min-width:1px; background:var(--line-2); display:block; transition:background .1s}
.dk-wave i.lit{background:var(--gold)}
.dk-time{font-family:"Plex Mono",monospace; font-size:.6875rem; color:var(--muted); flex-shrink:0}
.dk-act{display:flex; align-items:center; gap:.6rem; flex-shrink:0}
.dk-close{background:none; border:0; color:var(--muted); cursor:pointer; font-size:1.3rem; line-height:1; padding:.25rem .5rem}
.dk-close:hover{color:var(--text)}

/* ============================ FOOTER (kept - you liked it) ========== */
.site-foot{border-top:1px solid var(--line)}
.foot-news{display:flex; align-items:center; gap:1.5rem; flex-wrap:wrap; padding-block:2rem; border-bottom:1px solid var(--line)}
.foot-news p{margin:0; color:var(--text-2); font-size:var(--s--1)}
.foot-news form{display:flex; gap:.5rem; flex-wrap:wrap; margin-right:1.5rem}
/* FOLLOW LINKS, at the far right and tight together.
   They sat between the sentence and the form, which put four circles right up
   against the email field - two different things to click, touching. The form
   keeps the left of the row with the sentence it belongs to, the icons take
   the auto margin and the far edge, and the gap between them is small enough
   that they read as ONE group rather than four separate buttons. */
.foot-social{display:flex; gap:.1rem; list-style:none; margin:0 0 0 auto; padding:0}
.foot-social a{
  /* 34px, not 38: the hit area is what separates the glyphs, so shrinking it
     is what brings them together. It goes back to 44 where the pointer is a
     finger - see the hover:none block. */
  display:grid; place-items:center; width:34px; height:34px; border-radius:50%;
  color:var(--muted); transition:color .15s, background .15s;
}
.foot-social svg{width:19px; height:19px; display:block; fill:currentColor}
.foot-social a:hover,.foot-social a:focus-visible{color:var(--gold); background:var(--veil-2)}
.foot-cols{display:grid; grid-template-columns:repeat(auto-fit,minmax(150px,1fr)); gap:2.5rem; padding-block:3rem}
.foot-cols h4{font-family:"Plex Mono",monospace; font-size:.6875rem; letter-spacing:.16em; text-transform:uppercase; color:var(--text); margin:0 0 1.1rem; font-weight:500}
.foot-cols ul{list-style:none; margin:0; padding:0; display:grid; gap:.55rem}
.foot-cols a{font-size:var(--s--1); color:var(--muted)}
.foot-cols a:hover{color:var(--text)}
.foot-base{padding-block:1.5rem; border-top:1px solid var(--line); font-family:"Plex Mono",monospace; font-size:.6875rem; color:var(--muted); letter-spacing:.06em}

/* ============================ NOTES ============================ */
.notes-list{display:grid; border-top:1px solid var(--line)}
.note{padding:1.6rem 0; border-bottom:1px solid var(--line); display:grid; grid-template-columns:210px minmax(0,1fr); gap:2rem}
.note .was{font-family:"Plex Mono",monospace; font-size:.6875rem; color:var(--muted); letter-spacing:.04em; line-height:1.6}
.note h4{margin:0 0 .4rem; font-size:var(--s-1); font-family:Poppins,sans-serif; font-weight:600}
.note p{margin:0; color:var(--text-2); font-size:var(--s--1); max-width:var(--measure)}

/* ============================ RESPONSIVE ============================ */
@media (max-width:1000px){
  .media,.feature,.cartgrid{grid-template-columns:1fr}
  .feature.flip .fig,.feature.flip .feat-shot{order:0}
  .nav,.anchors{display:none}
  .claims{grid-template-columns:1fr}
  .claims div + div{border-left:0; border-top:1px solid var(--line)}
  .note{grid-template-columns:1fr; gap:.5rem}
  .bhero-copy{margin-top:0}
}
@media (max-width:900px){
  /* collapse the search to its icon rather than dropping it */
  .hsearch{min-width:0; padding:.45rem}
  .hsearch input{display:none}
}
@media (max-width:640px){
  .bhero{min-height:clamp(380px,58vh,520px)}
  .bhero-act{margin-left:0}
  .dk-id,.dk-time{display:none}
  .line{grid-template-columns:96px minmax(0,1fr)}
  .line-r{grid-column:1/-1; flex-direction:row; align-items:center; justify-content:space-between}
  .foot-news form{margin-left:0; width:100%}
  /* On a phone the band stacks, so the icons lead the row rather than being
     pushed to an edge that no longer exists. */
  .foot-social{margin-left:0}
  .foot-news form{margin-right:0}
}
@media (prefers-reduced-motion:reduce){*{transition-duration:.01ms !important; animation-duration:.01ms !important}}

/* ============================ REAL-TEMPLATE CHROME ============================
   Pieces the mockup did not need: the live header's dropdown and mobile panel,
   the cart popup, and a wrapper for the 93 hand-written product descriptions. */
.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap;border:0}

/* header nav dropdown */
.nav-drop{position:relative; display:flex; align-items:center}
/* A drawn triangle, not a glyph: the character rendered at about six pixels and
   read as a speck of dust. */
.nav-drop > a{display:inline-flex; align-items:center; gap:.45rem}
.nav-drop > a .caret{
  width:0; height:0; border-inline:5px solid transparent; border-top:6px solid currentColor;
  opacity:.65; transition:opacity .2s, transform .2s;
}
.nav-drop:hover > a .caret,.nav-drop:focus-within > a .caret{opacity:1; transform:translateY(1px)}
.nav-panel{
  position:absolute; top:100%; left:-1rem; min-width:230px; display:none; z-index:20;
  background:var(--ink); border:1px solid var(--line); border-radius:var(--rs);
  padding:.5rem; flex-direction:column;
}
.nav-drop:hover .nav-panel,.nav-drop:focus-within .nav-panel{display:flex}
.nav-panel a{padding:.5rem .75rem; border-radius:6px; font-size:var(--s--0); color:var(--text-1)}
.nav-panel a:hover{background:var(--veil-2); color:var(--text)}
.nav-panel a.all{color:var(--gold); border-top:1px solid var(--line); margin-top:.35rem; padding-top:.7rem}

.tools .cur a{color:var(--muted)}
.tools .cur a.on{color:var(--text); font-weight:500}
.tools .acct{font-size:var(--s--1); color:var(--text-2)}
.tools .acct:hover{color:var(--text)}

/* mobile nav */
#nav-toggle{display:none; width:38px; height:38px; border:1px solid var(--line-2); border-radius:8px;
  background:transparent; cursor:pointer; padding:0; flex-direction:column; gap:4px; align-items:center; justify-content:center}
#nav-toggle span{display:block; width:16px; height:1.5px; background:var(--text)}
/* The menu hangs off a FIXED header now, so it no longer pushes the page down
   and cannot simply be as tall as it likes: a long panel would run off the
   bottom of the window with no way to reach the end of it. */
#mobile-nav{border-top:1px solid var(--line); background:var(--ink);
  max-height:calc(100vh - var(--head-h)); overflow-y:auto; overscroll-behavior:contain}
/* ONE PANEL AT A TIME. The viewport clips whichever two panels are mid-slide
   and animates between their heights; everything else about the menu is
   ordinary flow, so a panel is as tall as its own list and the page scrolls. */
.mnav-vp{position:relative; overflow:hidden; transition:height .28s ease}
.mnav{list-style:none; margin:0; padding:.75rem 0 1.25rem; display:grid}
/* display:grid is an AUTHOR rule and [hidden] a user-agent one, so without this
   every panel in the tree renders at once - which is the flat list this was
   built to replace, only longer. */
.mnav[hidden]{display:none}
.mnav li a{display:block; padding:.6rem clamp(1.25rem,5vw,3rem); font-size:var(--s-0); color:var(--text-1)}
.mnav li a:hover{color:var(--text)}
/* Only DURING a slide are the panels taken out of flow. Leaving them absolute
   would make the menu's height depend on JS having run. */
.mnav-panel.sliding{position:absolute; top:0; left:0; width:100%;
  transition:transform .28s ease, opacity .28s ease}
.mnav-panel.from-r{transform:translateX(100%); opacity:0}
.mnav-panel.from-l{transform:translateX(-100%); opacity:0}
.mnav-panel.to-l{transform:translateX(-100%); opacity:0}
.mnav-panel.to-r{transform:translateX(100%); opacity:0}
@media (prefers-reduced-motion:reduce){
  .mnav-vp,.mnav-panel.sliding{transition:none}
}
/* A row that opens a panel is a button, not a link: it goes nowhere, and a
   link that goes nowhere is the thing that makes a menu feel broken. */
.mnav li .mnav-into{
  display:flex; align-items:center; justify-content:space-between; gap:.7rem; width:100%;
  background:none; border:0; cursor:pointer; text-align:left; font:inherit;
  font-size:var(--s-0); color:var(--text-1);
  padding:.6rem clamp(1.25rem,5vw,3rem);
}
.mnav li .mnav-into:hover{color:var(--text)}
/* Drawn, like the chevrons in the desktop menu - the "\203A" glyph sits low in
   its line box and reads as sitting at the bottom of a padded row. */
.mnav-more,.mnav-arrow{
  width:7px; height:7px; flex:0 0 auto;
  border-right:1.6px solid currentColor; border-top:1.6px solid currentColor;
  transform:rotate(45deg); opacity:.6;
}
.mnav-arrow{transform:rotate(-135deg); opacity:.9; margin-right:.15rem}
/* The panel says where you are and how to leave, in that order of importance:
   the title is the heading, Back is the control beside it. */
.mnav-head{
  display:flex; align-items:center; gap:.9rem;
  padding:.2rem clamp(1.25rem,5vw,3rem) .7rem;
  border-bottom:1px solid var(--line); margin-bottom:.5rem;
}
.mnav-head b{font-family:Poppins,sans-serif; font-weight:600; font-size:var(--s-0); color:var(--text)}
.mnav-out{
  display:inline-flex; align-items:center; gap:.45rem; background:none; cursor:pointer;
  border:1px solid var(--line-2); border-radius:999px; padding:.35rem .8rem;
  font:inherit; font-size:var(--s--1); color:var(--text-2);
  transition:background .16s, border-color .16s, color .16s;
}
.mnav-out:hover{background:var(--gold); border-color:var(--gold); color:var(--gold-ink)}
.mnav li a.mnav-prod{display:flex; align-items:center; gap:.7rem}
.mnav-sec{padding:1rem clamp(1.25rem,5vw,3rem) .35rem; font-family:"Plex Mono",monospace;
  font-size:.6875rem; letter-spacing:.16em; text-transform:uppercase; color:var(--text-2)}

/* sub-categories on a category page */
.subcats{display:flex; flex-wrap:wrap; gap:.5rem; margin-top:1.25rem}
.subcats .chip{padding:.4rem .9rem; color:var(--text-2)}
.subcats .chip:hover{border-color:var(--text-2); color:var(--text)}

/* cart popup */
.mcart-pop{position:fixed; inset:0; z-index:200; background:rgba(6,6,8,.72); backdrop-filter:blur(4px); display:grid; place-items:center}
.mcart-pop[hidden]{display:none}
.mcart-box{background:var(--ink); border:1px solid var(--line-2); border-radius:var(--r);
  padding:2rem 2.25rem; text-align:center; max-width:420px; width:calc(100% - 2rem)}
.mcart-name{font-family:Poppins,sans-serif; font-weight:600; font-size:var(--s-2)}
.mcart-sub{color:var(--muted); margin:.3rem 0 1.4rem; font-size:var(--s--1)}
.mcart-act{display:flex; gap:.6rem; justify-content:center; flex-wrap:wrap}
.multibuy-note{color:var(--gold); font-size:var(--s--1); margin-bottom:1rem}

.ck-reopen-line{text-align:center; margin:0; padding:.75rem 1rem 1.25rem; font-size:.72rem; color:var(--muted)}
.ck-reopen-line a{text-decoration:underline}

/* tile heading links inherit, they are not separate controls */
.tile-body h3 a{color:inherit}
.tile-body h3 a:hover{color:var(--gold)}

/* The 93 imported descriptions are hand-written HTML against the OLD theme's
   classes. Scope them rather than reauthoring: they get readable defaults and
   cannot reach the rest of the page. */
.prose-legacy{color:var(--text-2); font-size:var(--s-1); line-height:1.75}
.prose-legacy img{max-width:100%; height:auto; border-radius:var(--rs); margin:1rem 0}
.prose-legacy a{color:var(--gold)}
/* AN AUTHOR'S COLOUR OUTRANKS OUR EMPHASIS COLOUR. Bold inside the copy is
   painted --text (or --art-text over a picture) so it lifts off the body grey;
   that rule also repainted the inside of a span the author had deliberately
   coloured, which reads as the colour picker being broken. Two classes' worth
   of specificity, so it beats `.lede-band b` and `.feat-bg b` wherever the copy
   ends up. Sanitizer side: RichHtml::promoteFont turns <font color> into the
   style attribute these selectors look for. */
.prose-legacy [style*="color"] b,.prose-legacy [style*="color"] strong,
.prose-legacy [style*="color"] i,.prose-legacy [style*="color"] em,
.prose-legacy [style*="color"] span,.prose-legacy [style*="color"] p,
.prose-legacy [style*="color"] li,.prose-legacy [style*="color"] h1,
.prose-legacy [style*="color"] h2,.prose-legacy [style*="color"] h3{color:inherit}
.prose-legacy h1,.prose-legacy h2,.prose-legacy h3{font-family:Poppins,sans-serif; font-weight:600; color:var(--text); line-height:1.25}
.prose-legacy table{width:100%; border-collapse:collapse; overflow-x:auto; display:block}
.prose-legacy td,.prose-legacy th{border-bottom:1px solid var(--line); padding:.6rem; text-align:left}
.home-intro{max-width:var(--measure); margin-inline:auto}

@media (max-width:1000px){
  #nav-toggle{display:flex}
}
@media (min-width:1001px){
  #mobile-nav{display:none !important}
}

/* ---------------- product page additions ---------------- */
.probar .was{font-family:"Plex Mono",monospace; font-size:var(--s--1); color:var(--muted); text-decoration:line-through}
/* the theatre play control is an <a> so fancybox can open it */
a.pbtn{text-decoration:none}
.th-cap b{display:block}
/* third-party review embeds: keep them inside their column */
.vdemos{display:grid; grid-template-columns:repeat(auto-fill,minmax(300px,1fr)); gap:clamp(1rem,2vw,1.75rem)}
.vdemo iframe,.vdemo img{max-width:100%; border-radius:var(--rs)}
.vdemo iframe{width:100%; aspect-ratio:16/9; height:auto; border:0}
/* write-a-review form */
.rv-form{display:grid; gap:1rem; max-width:52ch; margin-top:1rem}
.rv-form label{display:grid; gap:.4rem; font-size:var(--s--1); color:var(--text-2)}
.rv-form input[type=text],.rv-form textarea,.rv-form select{
  font:inherit; font-size:var(--s-0); background:transparent; border:1px solid var(--line-2);
  color:var(--text); padding:.65rem .9rem; border-radius:var(--rs); width:100%;
}
.rv-form textarea{resize:vertical}
.rv-form .btn{justify-self:start}
.rv-form input[type=email]{width:100%; box-sizing:border-box; font:inherit; font-size:var(--s-0);
  background:var(--lift-2); border:1px solid var(--line-2); border-radius:var(--rs);
  padding:.6rem .75rem; color:var(--text)}
/* "optional" is a property of the field, so it sits in the label rather than
   in a hint underneath where it would be read as part of the next question. */
.rv-form .opt{color:var(--muted); font-weight:400}
/* Said once, at the top of the form, in the colour of what happened. */
.demo-flash{margin:0 0 1.25rem; padding:.7rem .9rem; border-radius:var(--rs); font-size:var(--s-0)}
.demo-flash.is-ok{border:1px solid rgba(89,201,138,.35); background:rgba(89,201,138,.12); color:#7FD9A6}
.demo-flash.is-err{border:1px solid #6E6130; background:rgba(110,97,48,.2); color:var(--text-2)}
/* specs: the imported technical description spans, the boilerplate sits beside it */
.specs > .prose-legacy{font-size:var(--s-0)}

/* lazy images must not collapse their box before they arrive */
.cv-item img{aspect-ratio:3/2; object-fit:cover; height:auto}
.feat-shot img{aspect-ratio:3/2; object-fit:cover; height:auto}

/* ---------------- mini cart ---------------- */
#mini-cart-wrap{position:relative}
#mini-cart-dropdown{
  position:absolute; right:0; top:100%; margin-top:.6rem; width:min(360px,88vw);
  background:var(--ink); border:1px solid var(--line-2); border-radius:var(--r);
  padding:1rem; z-index:60; display:none; box-shadow:0 24px 60px -28px rgba(0,0,0,.9);
}
#mini-cart-wrap:hover #mini-cart-dropdown,
#mini-cart-wrap:focus-within #mini-cart-dropdown{display:block}
/* a gap between the chip and the panel would drop the hover; bridge it */
#mini-cart-dropdown::before{content:""; position:absolute; inset:-.6rem 0 100% 0}
#mini-cart-dropdown .mc-row{
  display:flex; justify-content:space-between; gap:1rem; padding:.4rem 0;
  font-size:var(--s--1); color:var(--text-2); border-bottom:1px solid var(--line);
}
#mini-cart-dropdown .mc-row:last-of-type{border-bottom:0}
#mini-cart-dropdown .mc-row span:last-child{font-family:"Plex Mono",monospace; color:var(--text); white-space:nowrap}
#mini-cart-dropdown .mc-empty{color:var(--muted); font-size:var(--s--1); padding:.4rem 0}
#mini-cart-dropdown .multibuy-note{color:var(--gold); font-size:var(--s--1)}
#mini-cart-dropdown .mc-actions{display:flex; gap:.5rem; margin-top:.9rem}
/* /cart/mini emits classic's .productlistbutton - restyle rather than fork the route */
#mini-cart-dropdown .productlistbutton{
  flex:1; text-align:center; font-family:"Plex Sans",sans-serif; font-size:var(--s--1);
  font-weight:600; padding:.6rem 1rem; border-radius:var(--rs);
  background:var(--gold); color:var(--gold-ink); border:1px solid var(--gold);
}
#mini-cart-dropdown .productlistbutton.mc-secondary{
  background:transparent; color:var(--text); border-color:var(--line-2);
}
#mini-cart-dropdown .productlistbutton:hover{opacity:.88}

/* ---------------- interface carousel: smaller stage ---------------- */
.cv-item{flex-basis:clamp(240px,42%,520px)}

/* ---------------- video reviews: even rows, one link colour ---------------- */
.vdemos{align-items:start}
.vdemo{display:flex; flex-direction:column; gap:.6rem}
/* third-party markup arrives with its own alignment and inline colours */
.vdemo p,.vdemo div{margin:0; text-align:left}
.vdemo a,.vdemo a:visited,.vdemo a font,.vdemo a span{color:var(--gold) !important}
.vdemo iframe{display:block}
/* Some embeds were pasted with a stray <br> or whitespace next to the iframe, so
   the wrapping <p> gained a line box and that cell ran ~30px taller than its
   neighbours. Zeroing the line-height on the media paragraph collapses those
   phantom boxes; the iframe is display:block so it keeps its own height. */
.vdemo > p:first-of-type{line-height:0; font-size:0}
/* The caption is a bare URL pasted by whoever added the review, so its length is
   arbitrary - one cell wrapping to two lines is what made the row look uneven.
   Every embed is the same 16:9 box, so clamping the caption to a single line is
   all that is needed for the row to line up. */
.vdemo > p:nth-of-type(2),.vdemo > div{
  white-space:nowrap; overflow:hidden; text-overflow:ellipsis;
  font-size:var(--s--1); color:var(--muted);
}

/* ---------------- categories menu: two levels, opened on hover ----------------
   The classic menu's behaviour in this template's clothes. Hover-only by
   design, as it was; the mobile panel below is the touch route. */
.nav-menu{
  position:absolute; top:100%; left:-1rem; margin-top:.4rem; display:none; z-index:60;
  list-style:none; padding:.4rem; margin-block:.4rem 0; min-width:230px;
  background:var(--ink); border:1px solid var(--line-2); border-radius:var(--rs);
  box-shadow:0 30px 70px -32px rgba(0,0,0,.9);
}
.nav-drop:hover .nav-menu,.nav-drop:focus-within .nav-menu{display:block}
/* bridges the gap under the trigger, so moving the pointer down does not close it */
.nav-menu::before{content:""; position:absolute; inset:-.6rem 0 100% 0}
/* Every row that can open a flyout is the box that flyout is positioned
   against. Without this on the SECOND level, a third-level panel resolved
   against the first-level <li> and opened on top of the second column. */
.nav-menu li,.nav-sub li{position:relative}
.nav-menu a,.nav-sub a{
  display:flex; align-items:center; gap:.6rem; padding:.5rem .75rem; border-radius:6px;
  font-size:var(--s--0); color:var(--text-1); white-space:nowrap;
}
.nav-sub li:hover > a,.nav-sub a:hover{background:var(--veil-2); color:var(--text)}
/* Product rows carry a thumbnail. The empty placeholder keeps the labels on one
   left edge when only some products have artwork. */
.nav-thumb{width:39px; height:26px; flex:0 0 39px; object-fit:cover; border-radius:3px; background:var(--well)}
.nav-thumb-none{display:block; border:1px solid var(--line)}
.nav-menu a:hover,.nav-menu li:hover > a{background:var(--veil-2); color:var(--text)}
/* A DRAWN chevron, like the caret in the bar above. The "\203A" glyph sits low
   in its own line box, so on a padded row it read as sitting at the bottom
   rather than beside the label. Two borders rotated 45deg cannot do that. */
/* .nav-more, NOT .more: the section expander two hundred lines up owns .more
   and gives it margin-top:1.4rem, which was pushing every chevron in the menu
   to the bottom of its row. A class this generic cannot be shared. */
.nav-menu .nav-more,.nav-sub .nav-more{
  margin:0 2px 0 auto; flex:0 0 auto; width:6px; height:6px; align-self:center;
  border-right:1.5px solid currentColor; border-top:1.5px solid currentColor;
  transform:rotate(45deg); opacity:.5;
}
.nav-menu li:hover > a .nav-more,.nav-sub li:hover > a .nav-more{opacity:.9}
.nav-all a{color:var(--gold); border-top:1px solid var(--line); margin-top:.35rem; padding-top:.7rem; border-radius:0}

/* second level: a flyout beside its row, overlapping by the panel padding so
   the pointer never crosses a gap on its way over */
.nav-sub{
  position:absolute; top:-.4rem; left:100%; margin-left:-.25rem; display:none; z-index:61;
  list-style:none; padding:.4rem; min-width:220px;
  background:var(--ink); border:1px solid var(--line-2); border-radius:var(--rs);
  box-shadow:0 30px 70px -32px rgba(0,0,0,.9);
}
/* SCROLLING ONLY WHERE NOTHING OPENS OUT OF IT.
   overflow-y:auto makes a clipping box, and an absolutely positioned child is
   clipped to it - so a panel that scrolls cannot have a flyout of its own: the
   third level was drawn INSIDE the second and all that showed was the sliver
   that overlapped it. Panels that branch are marked in the markup rather than
   with :has(), which is about as old as the color-mix() we just removed. */
/* overflow-y:auto computes overflow-x to auto as well, so a capped panel got a
   HORIZONTAL scrollbar and cut its longest names. The width comes from the
   content instead - max-content, no cap - and there is then nothing to scroll
   sideways. Which side it opens on, and how tall it is, is decided at hover
   time by the script in partials/header.php, because only then is it known how
   much room there is. */
.nav-sub{width:max-content}
.nav-sub--leaf{max-height:min(84vh,720px); overflow-y:auto; overflow-x:hidden}
.has-sub:hover > .nav-sub,.has-sub:focus-within > .nav-sub{display:block}
/* The third level opens from a row of the second exactly as the second opens
   from a row of the first, and sits above it. */
.nav-sub .nav-sub{top:-.4rem; z-index:62}
/* Which side a flyout opens on is decided at hover time (partials/header.php),
   not by a width breakpoint: a blanket "flip below 1180px" put every panel off
   the LEFT edge instead, which is the same bug facing the other way. */

/* ---------------- footer: full-width rules ---------------- */
.foot-band{border-bottom:1px solid var(--line)}
.foot-band--last{border-bottom:0}
.foot-news{display:flex; align-items:center; gap:1.5rem; flex-wrap:wrap; padding-block:2rem; border-bottom:0}
.foot-cols{padding-block:3rem}
.foot-base{padding-block:1.5rem; border-top:0}

/* ---------------- "more about this": animated, full-row ---------------- */
.more{margin-top:1.6rem}
.more-toggle{
  font:inherit; font-size:var(--s--1); font-weight:600; letter-spacing:.04em;
  color:var(--gold); background:transparent; border:1px solid var(--gold);
  border-radius:999px; padding:.5rem 1.1rem; cursor:pointer;
  display:inline-flex; align-items:center; gap:.6rem; transition:background .15s;
}
.more-toggle::after{content:"+"; font-weight:400; line-height:1}
.more-toggle[aria-expanded="true"]::after{content:"\2212"}
.more-toggle:hover{background:rgba(217,154,68,.12)}
/* grid-rows 0fr -> 1fr is the one way to transition to auto height */
.more-panel{display:grid; grid-template-rows:0fr; transition:grid-template-rows .42s cubic-bezier(.22,.7,.24,1)}
.more-panel[hidden]{display:none}
.more-panel.open{grid-template-rows:1fr}
.more-panel > .more-body{overflow:hidden; min-height:0; padding-top:0; transition:padding-top .42s}
.more-panel.open > .more-body{padding-top:1.35rem}
@media (prefers-reduced-motion:reduce){.more-panel{transition:none}}

/* ============================ STICKY TRANSPORT ============================
   Appears the moment anything on the page starts playing and mirrors it, so
   the controls stay reachable once the listener has scrolled past the player.
   Sits ABOVE the consent bar via --as-dock (partials/bottom_dock measures it)
   and carries .as-dock-over so back-to-top clears it in turn. */
.dock{
  position:fixed; left:0; right:0; bottom:var(--as-dock, 0px); z-index:80;
  background:var(--ink); border-top:1px solid var(--line-2);
  transform:translateY(110%); transition:transform .3s cubic-bezier(.2,.8,.2,1);
}
.dock.up{transform:none}
.dock .wrap{display:flex; align-items:center; gap:clamp(.7rem,1.5vw,1.25rem); min-height:66px; padding-block:.6rem}
/* The dock sits outside any .player, so it takes the product accent directly. */
.dk-play{--sz:38px; --pl-acc:var(--gold); --pl-acc-ink:var(--gold-ink)}
.dk-id{min-width:0; display:flex; flex-direction:column; flex-shrink:0; max-width:clamp(110px,18vw,240px)}
.dk-id b{font-family:Poppins,sans-serif; font-weight:600; font-size:var(--s--1); white-space:nowrap; overflow:hidden; text-overflow:ellipsis}
.dk-id span{font-size:.6875rem; color:var(--muted); white-space:nowrap; overflow:hidden; text-overflow:ellipsis; letter-spacing:.08em; text-transform:uppercase; font-family:"Plex Mono",monospace}
.dk-wave{flex:1; min-width:60px; display:flex; align-items:center; gap:1px; height:32px; cursor:pointer}
.dk-wave i{flex:1; background:var(--line-2); display:block; transition:background .1s}
.dk-wave i.lit{background:var(--dk-acc, var(--gold))}
.dk-time{font-family:"Plex Mono",monospace; font-size:.6875rem; color:var(--muted); flex-shrink:0}
.dk-close{background:none; border:0; color:var(--muted); cursor:pointer; font-size:1.35rem; line-height:1; padding:.25rem .5rem; flex-shrink:0}
.dk-close:hover{color:var(--text)}
@media (max-width:700px){ .dk-id{display:none} }

/* ---------------- per-section layout (admin: Sections > Layout) ----------------
   Four independent choices - where the image goes, and how the title, the copy
   and the "more" panel are aligned - so a section is arranged by whoever wrote
   it rather than by a rule in this file. */
.ta-left{text-align:left} .ta-center{text-align:center; margin-inline:auto} .ta-right{text-align:right; margin-inline:0 0}
/* feature-title carries max-width:16ch, which would strand a centred or
   right-set title against the left edge */
h2.ta-center{max-width:22ch; margin-inline:auto}
/* A centred title over a FULL-WIDTH body is centred on the body, not squeezed
   into 22ch and left sitting off to one side of it - which is what a long title
   looked like: shoved to the right. */
.sec-body.col-full h2.ta-center{max-width:none}
h2.ta-right{max-width:22ch; margin-left:auto}
.sec-body{min-width:0}
.col-full{max-width:none}
.col-left{max-width:min(58ch,60%); margin-right:auto}
.col-right{max-width:min(58ch,60%); margin-left:auto}
/* THE SECTION'S OWN CHOICE BEATS THE BAND'S DEFAULT MEASURE.
   .feat-bg caps its paragraphs at 50ch, which is right for copy set beside a
   picture and wrong the moment a section asks for full width - and it only
   started biting these sections when they gained a backdrop and became feat-bg.
   The column decides the width here; the paragraphs fill it. */
.sec-body.col-full p,.sec-body.col-full li,
.sec-body.col-left p,.sec-body.col-left li,
.sec-body.col-right p,.sec-body.col-right li{max-width:none}
/* Same for the "more" panel: it is the long-form half of a section and was
   never meant to be held to the beside-a-picture measure. */
.more-body p,.more-body li,.feat-bg .more-body p,.feat-bg .more-body li{max-width:none}
.tx-right{margin-left:auto; text-align:right}
.tx-right p{margin-left:auto}
.ma-center{text-align:center}
.ma-right{text-align:right}
.ma-center .more-body,.ma-right .more-body{margin-inline:auto; text-align:left}
@media (max-width:800px){ .col-left,.col-right{max-width:none} }

/* ---------------- category composite banner ----------------
   Built offline in the admin from the products' own banners. Faded out at the
   bottom so the page head reads as part of the same block rather than sitting
   on a hard edge - the same treatment the product hero gets. */
/* THE SAME BLOCK AS THE PRODUCT AND HOME HERO, with the composite as its
   picture: the same height, the same scrim under the copy, the copy resting on
   the bottom of it. A category is introduced the way an instrument is, and the
   three heroes on the site are then one object with one set of rules.
   The scrim darkens the BOTTOM, where the words are - it is not the old fade to
   --ink across the whole strip, which cut a hard edge through the artwork. */
.cathero{
  position:relative; isolation:isolate; overflow:hidden; background:var(--well);
  min-height:clamp(460px,66vh,760px);
  display:flex; align-items:flex-end;
}
.cathero::before{
  content:""; position:absolute; inset:0; z-index:-2;
  background:var(--cat-bg) center/cover no-repeat;
}
.cathero::after{
  content:""; position:absolute; inset:0; z-index:-1; pointer-events:none;
  /* Full width, and weighted to the bottom where the words are. It has to be
     strong enough to carry the type over a BRIGHT slice - the Petit Piano and
     the horn section are both nearly white where the title falls - and it
     starts well above the copy so it arrives as shading on the picture rather
     than as an edge across it. */
  background:linear-gradient(to bottom,
    transparent 20%,
    rgba(var(--art-scrim),.42) 46%,
    rgba(var(--art-scrim),.82) 72%,
    rgba(var(--art-scrim),.97) 100%);
}
.cathero > .wrap{width:100%}
/* The copy is over artwork, so it takes the over-artwork colours. */
.cathero .crumb{color:var(--art-text); opacity:.75}
.cathero .crumb a{color:var(--art-text)}
.cathero .lede{color:var(--art-text); opacity:.92}
.cathero .toolbar .mono{color:var(--art-text) !important; opacity:.72}

/* ---------------- video overlay ----------------
   A scrim and a 16:9 frame, nothing else. No chrome box, no border - the
   player is the content and the page behind it is the frame. */
/* THE hidden ATTRIBUTE HAS TO WIN. `display:grid` here is an AUTHOR rule and
   [hidden]{display:none} is only a USER-AGENT one, so the attribute lost and a
   full-screen fixed overlay sat over the page at opacity 0, swallowing every
   click on the site. Any rule that gives a hideable element a display value
   needs its own [hidden] beside it. */
.vmodal[hidden]{display:none}
.vmodal{
  position:fixed; inset:0; z-index:200; display:grid; place-items:center;
  padding:clamp(1rem,4vw,3rem);
  background:rgba(6,6,9,.86); opacity:0; transition:opacity .22s ease;
  -webkit-backdrop-filter:blur(3px); backdrop-filter:blur(3px);
}
.vmodal.on{opacity:1}
.vm-frame{width:min(1600px, 100%); display:grid; place-items:center}
.vm-box{
  width:100%; aspect-ratio:16/9; border-radius:var(--r); overflow:hidden; background:#000;
  box-shadow:0 40px 120px -30px rgba(0,0,0,.95), 0 0 0 1px rgba(255,255,255,.06);
  transform:scale(.97); transition:transform .26s cubic-bezier(.2,.8,.2,1);
}
.vmodal.on .vm-box{transform:none}
.vm-box > div,.vm-box iframe{width:100%; height:100%; border:0; display:block}
.vm-close{
  position:absolute; top:clamp(.6rem,2vw,1.4rem); right:clamp(.6rem,2vw,1.4rem);
  width:44px; height:44px; border:0; border-radius:50%; cursor:pointer;
  background:rgba(255,255,255,.1); color:#fff; font-size:1.6rem; line-height:1;
  display:grid; place-items:center; transition:background .18s;
}
.vm-close:hover{background:rgba(255,255,255,.2)}
/* The overlays hand focus to their dialog so Escape and the tab order work.
   That container is not a control and must not be drawn as one - a focus ring
   on it reads as a warning round whatever it happens to enclose. Keyboard
   focus on the controls inside it is untouched. */
.vm-frame:focus,.im-frame:focus{outline:none}
body.vm-open{overflow:hidden}

/* ---------------- image lightbox ----------------
   The video overlay's sibling, and deliberately not the same object: that one
   is a fixed 16:9 frame around an iframe, and a screenshot is whatever shape it
   was taken at. Same scrim, same close button, same escape hatches. */
.imodal[hidden]{display:none}
.imodal{
  position:fixed; inset:0; z-index:200; display:grid; place-items:center;
  padding:clamp(1rem,4vw,3rem);
  background:rgba(6,6,9,.9); opacity:0; transition:opacity .22s ease;
  -webkit-backdrop-filter:blur(3px); backdrop-filter:blur(3px);
}
.imodal.on{opacity:1}
.im-frame{
  margin:0; display:flex; flex-direction:column; align-items:center; gap:.9rem;
  max-width:100%; max-height:100%;
  transform:scale(.98); transition:transform .26s cubic-bezier(.2,.8,.2,1);
}
.imodal.on .im-frame{transform:none}
/* The picture is sized by the VIEWPORT, not by its own pixels: an interface
   shot is often wider than any screen, and left at its natural size it would
   push the caption off the bottom and the scrim would stop closing anything. */
.im-frame img{
  display:block; max-width:min(1600px,94vw); max-height:82vh; width:auto; height:auto;
  object-fit:contain; border-radius:var(--rs); background:var(--well);
  box-shadow:0 40px 120px -30px rgba(0,0,0,.95), 0 0 0 1px rgba(255,255,255,.06);
}
.im-frame figcaption{
  font-family:"Plex Mono",monospace; font-size:var(--s--1); letter-spacing:.04em;
  color:var(--art-text,#fff); opacity:.75; text-align:center; max-width:70ch;
}
.im-frame figcaption[hidden]{display:none}
/* Same disc as the carousel's own arrows, so stepping through the pictures
   full-size feels like the same control as stepping through them small. */
.im-nav{
  position:absolute; top:50%; transform:translateY(-50%); z-index:2;
  width:46px; height:46px; border-radius:50%; cursor:pointer;
  background:rgba(255,255,255,.1); border:1px solid rgba(255,255,255,.16); color:#fff;
  display:grid; place-items:center; transition:background .18s, border-color .18s;
}
.im-nav[hidden]{display:none}
.im-nav:hover{background:var(--gold); border-color:var(--gold); color:var(--gold-ink)}
.im-nav::before{
  content:""; width:9px; height:9px;
  border-right:2px solid currentColor; border-top:2px solid currentColor;
}
.im-nav.next::before{transform:rotate(45deg) translate(-2px,2px)}
.im-nav.prev::before{transform:rotate(-135deg) translate(-2px,2px)}
.im-nav.prev{left:clamp(.5rem,2vw,1.5rem)} .im-nav.next{right:clamp(.5rem,2vw,1.5rem)}
@media (prefers-reduced-motion:reduce){ .imodal,.im-frame{transition:none} }
/* The centred slide says it can be opened; the ones beside it say they can be
   brought over. Two different jobs, two different cursors. */
.cv-item{cursor:pointer}
.cv-item.on{cursor:zoom-in}
@media (max-width:760px){
  .im-frame img{max-height:74vh}
  .im-nav{width:40px; height:40px}
}
/* A DOCUMENT in the same shell - the terms at checkout. The video box is a 16:9
   frame; this one is a column that scrolls its middle and keeps its head and
   foot in place, because the button that accepts has to stay reachable however
   long the terms are. */
.vmodal-box{
  width:min(720px,100%); max-height:min(80vh,760px); display:flex; flex-direction:column;
  background:var(--lift-2); border:1px solid var(--line-2); border-radius:var(--r); overflow:hidden;
  box-shadow:0 40px 120px -30px rgba(0,0,0,.95);
  transform:scale(.97); transition:transform .26s cubic-bezier(.2,.8,.2,1);
}
.vmodal.on .vmodal-box{transform:none}
.vmodal-head{display:flex; align-items:center; justify-content:space-between; gap:1rem;
  padding:1rem 1.25rem; border-bottom:1px solid var(--line)}
.vmodal-head b{font-family:Poppins,sans-serif; font-weight:600; font-size:var(--s-1)}
.vmodal-x{background:none; border:0; color:var(--muted); font-size:1.7rem; line-height:1; cursor:pointer; padding:0 .25rem}
.vmodal-x:hover{color:var(--text)}
.vmodal-body{overflow:auto; padding:1.25rem; font-size:var(--s--1); line-height:1.7; color:var(--text-2)}
.vmodal-foot{padding:.9rem 1.25rem; border-top:1px solid var(--line); text-align:right}

/* ---------------- checkout ---------------- */
.co-err{border:1px solid #7A3A3A; background:rgba(122,58,58,.18); border-radius:var(--rs);
  padding:.85rem 1rem; margin:0 0 1.5rem; color:#F0C4C4}
.co-known{border:1px solid var(--line); border-radius:var(--rs); padding:1rem 1.15rem; margin-bottom:1.5rem}
.co-known p{margin:0 0 .6rem; line-height:1.6; color:var(--text-2)}
.co-known b{color:var(--text)}
.co-known a{font-size:var(--s--1)}
/* [hidden] FIRST, and it is not decoration: an author `display` beats the UA
   rule, so without this the collapsed billing block stays on screen next to the
   summary of the same details. Same trap as .vmodal above. */
.billing-grid[hidden],.co-known[hidden]{display:none}
.billing-grid{display:grid; grid-template-columns:150px minmax(0,1fr); gap:.7rem 1rem; align-items:center; max-width:560px}
.billing-grid label{font-size:var(--s--1); color:var(--text-2)}
.billing-grid input,.billing-grid select{width:100%; box-sizing:border-box}
.billing-grid input[type=email],.billing-grid input[type=text]{
  font:inherit; font-size:var(--s--1); background:transparent; border:1px solid var(--line-2);
  color:var(--text); padding:.6rem .9rem; border-radius:var(--rs);
}
.billing-grid input:focus,.billing-grid select:focus{outline:none; border-color:var(--text-2)}
.co-terms{display:flex; align-items:flex-start; gap:.6rem; font-size:var(--s--1); color:var(--text-2); margin:0 0 1.25rem}
.co-terms input{margin-top:.2rem; accent-color:var(--gold)}
.co-pay{display:flex; flex-direction:column; gap:.8rem; max-width:380px}
/* THE PAYMENT PARTIALS ARE CLASSIC'S, deliberately: they carry the Stripe and
   PayPal integrations, and forking them to restyle a button would fork the code
   that takes the money. They emit `productlistbutton`, which this template only
   styles inside a legacy view - so on the ported checkout those buttons would
   come out unstyled. Map the class instead, scoped to a modern view so the
   legacy pages are untouched. */
.modern-view .productlistbutton{
  font-family:"Plex Sans",sans-serif; font-size:var(--s-0); font-weight:600;
  padding:.75rem 1.6rem; cursor:pointer; white-space:nowrap; border-radius:var(--rs);
  border:1px solid var(--line-2); background:transparent; color:var(--text);
  display:inline-flex; align-items:center; justify-content:center; gap:.6rem;
  transition:background .15s, border-color .15s, color .15s, opacity .15s;
}
.modern-view .productlistbutton:hover{border-color:var(--text-2)}
/* The one that starts the card flow is the primary action on the page. */
.modern-view #card-btn,.modern-view #card-pay{
  background:var(--gold); border-color:var(--gold); color:var(--gold-ink);
}
.modern-view #card-btn:hover,.modern-view #card-pay:hover{opacity:.87}
.modern-view .co-pay .productlistbutton{width:100%}
.btn-revolut{background:#0666EB; border-color:#0666EB; color:#fff}
.btn-revolut:hover{opacity:.88; color:#fff; border-color:#0666EB}
.co-warn{border:1px solid #6E6130; background:rgba(110,97,48,.2); border-radius:var(--rs);
  padding:.85rem 1rem; margin-bottom:1.25rem; font-size:var(--s--1); color:var(--text-2)}
.co-warn b{color:var(--text); display:block; margin-bottom:.35rem}
.co-warn ul{margin:.3rem 0 .5rem 1.1rem; padding:0}
.sum-h{font-family:Poppins,sans-serif; font-weight:600; font-size:var(--s-1); margin:0 0 1rem}

/* ---------------- order confirmation ---------------- */
.oc-wrap{max-width:640px}
.oc-next{border:1px solid var(--line); border-radius:var(--r); padding:1.5rem 1.6rem; margin:2rem 0 1.5rem; background:var(--lift-1)}
.oc-next h2{font-family:Poppins,sans-serif; font-weight:600; font-size:var(--s-1); margin:0 0 .6rem}
.oc-next p{margin:0 0 1.1rem; color:var(--text-2); font-size:var(--s--1); line-height:1.7}
.oc-next + .oc-next{margin-top:0}
.oc-body{color:var(--text-2); font-size:var(--s--1); line-height:1.7}
.oc-body > :last-child{margin-bottom:1.1rem}
.oc-acts{display:flex; gap:.7rem; flex-wrap:wrap; justify-content:center; margin:0 !important}
.oc-serials{margin:2rem 0 0}
.oc-serials h2{font-family:Poppins,sans-serif; font-weight:600; font-size:var(--s-1); margin:0 0 .6rem}
.oc-serials table{width:100%; border-collapse:collapse; font-size:var(--s--1)}
.oc-serials td{padding:.6rem 0; border-bottom:1px solid var(--line); vertical-align:middle}
.oc-serials td.oc-serial{text-align:right; padding-left:1rem}
.oc-serials code{font-family:"Plex Mono",ui-monospace,monospace; color:var(--text); user-select:all}
.oc-plat{font-size:.8em; color:var(--text-2); margin-left:.4rem}
.oc-serials .oc-note{margin:.8rem 0 0}
.oc-note{font-size:var(--s--1); color:var(--text-2)}
.oc-opt{border-top:1px solid var(--line); margin-top:1.5rem; padding-top:1.25rem}
.oc-opt p{font-size:var(--s--1); color:var(--text-2); margin:0 0 .8rem}

/* ---------------- content pages ----------------
   A CMS page, a news article, the guide: one column at the reading measure,
   because the body IS the page. Anything wider stops being readable prose. */
/* WIDER THAN THE READING MEASURE, deliberately. 64ch is right for an article
   someone reads start to finish; these are reference documents - terms, an
   installation page, a policy - that are scanned for the paragraph that
   answers something, and at 604px in a 1200px band they read as a column
   stranded at the left of a page built for full-width bands.
   96ch is also wide enough for the pictures the old content carries: the UVI
   Workstation page has an <img width="900"> that the narrow column was
   shrinking. */
.doc{max-width:min(96ch,100%); font-size:var(--s-0)}
.doc h2{font-family:Poppins,sans-serif; font-weight:600; font-size:var(--s-2); margin:2.5rem 0 1rem; color:var(--text)}
.doc h3{font-family:Poppins,sans-serif; font-weight:600; font-size:var(--s-1); margin:2rem 0 .75rem; color:var(--text)}
.newslist{border-top:1px solid var(--line)}
.newsrow{display:grid; grid-template-columns:120px minmax(0,1fr); gap:1.5rem; padding:1.6rem 0; border-bottom:1px solid var(--line)}
.newsrow time{color:var(--muted); padding-top:.35rem}
.newsrow h2{font-family:Poppins,sans-serif; font-weight:600; font-size:var(--s-1); margin:0 0 .4rem}
.newsrow h2 a{color:var(--text)}
.newsrow h2 a:hover{color:var(--gold)}
.newsrow p{margin:0; color:var(--text-2); font-size:var(--s--1); line-height:1.7; max-width:var(--measure)}
@media (max-width:640px){ .newsrow{grid-template-columns:minmax(0,1fr); gap:.4rem} }

/* payment pages */
.pay-test{border:1px solid #6E6130; background:rgba(110,97,48,.2); border-radius:var(--rs);
  padding:.7rem 1rem; margin:0 0 1.5rem; font-size:var(--s--1); color:var(--text-2)}
.pay-form{max-width:480px}
.pay-err{margin-top:.9rem; font-size:var(--s--1); color:#E88}
.nf-code{font-family:"Plex Mono",monospace; font-size:.6875rem; letter-spacing:.3em; color:var(--gold); margin:0 0 1rem}

/* user demos */
.demolist{border-top:1px solid var(--line)}
.demorow{padding:1.6rem 0; border-bottom:1px solid var(--line)}
.demorow .media{margin-bottom:.9rem; border-radius:var(--rs); overflow:hidden}
.demorow .media iframe{display:block; max-width:100%; border:0; border-radius:var(--rs)}
.demorow .cap{color:var(--text-2); font-size:var(--s--1); line-height:1.7}
.demogrid{display:grid; grid-template-columns:repeat(auto-fill,minmax(320px,1fr)); gap:1.5rem 2rem}

/* contact */
.form-note{border:1px solid #6E6130; background:rgba(110,97,48,.2); border-radius:var(--rs);
  padding:1rem 1.15rem; margin:0 0 1.75rem; font-size:var(--s--1); color:var(--text-2)}
.form-note p{margin:0 0 .5rem}
.form-note p:last-child{margin:0}
.frm{max-width:620px}
.frm p{margin:0 0 1rem}
.frm label{display:block; font-size:var(--s--1); color:var(--text-2); margin-bottom:.4rem}
/* ---- the support form ----
   Two columns for the short answers, one for the long ones. Nine fields in a
   single column is what makes a form look like a tax return; paired by subject
   it is four short rows and a box. */
.tkform .tkgrid{display:grid; grid-template-columns:1fr 1fr; gap:0 1.25rem}
@media (max-width:640px){ .tkform .tkgrid{grid-template-columns:1fr} }
.tkform input[type=text],.tkform select,.tkform textarea{
  width:100%; box-sizing:border-box; font:inherit; font-size:var(--s-0);
  background:var(--lift-2); border:1px solid var(--line-2); border-radius:var(--rs);
  padding:.6rem .75rem; color:var(--text);
}
.tkform textarea{resize:vertical; line-height:1.6}
.tkform input:focus,.tkform select:focus,.tkform textarea:focus{outline:none; border-color:var(--gold)}
/* Said on the field it applies to, in the label, where it is read with the
   question rather than after the answer. */
.tkform .req{font-family:"Plex Mono",monospace; font-size:.625rem; letter-spacing:.1em;
  text-transform:uppercase; color:var(--gold); margin-left:.5rem}
.tkform .opt{color:var(--muted); font-weight:400}
/* The one section break: everything under it is optional, said once. */
.tkform .tksec{
  margin:1.75rem 0 .75rem; padding-top:1.25rem; border-top:1px solid var(--line);
  font-family:Poppins,sans-serif; font-weight:600; color:var(--text); font-size:var(--s-0);
}
.frm input[type=text],.frm input[type=email],.frm input[type=password],.frm textarea,.frm select{
  width:100%; box-sizing:border-box; font:inherit; font-size:var(--s--1);
  background:transparent; border:1px solid var(--line-2); color:var(--text);
  padding:.65rem .9rem; border-radius:var(--rs);
}
.frm textarea{line-height:1.6; resize:vertical}
.frm input:focus,.frm textarea:focus,.frm select:focus{outline:none; border-color:var(--text-2)}
.frm .err{border:1px solid #7A3A3A; background:rgba(122,58,58,.18); border-radius:var(--rs);
  padding:.85rem 1rem; margin:0 0 1.5rem; color:#F0C4C4}

/* ---------------- install guide / FAQ ---------------- */
/* Centred, not left. These are picture-sized blocks inside a much wider band,
   and pinned left they read as a page that has lost its right-hand half - which
   is exactly what the install guide looked like. */
.dl-two{display:grid; grid-template-columns:repeat(auto-fit,minmax(240px,1fr)); gap:1.25rem;
  max-width:640px; margin-inline:auto}
.dl-card{
  display:flex; flex-direction:column; align-items:center; gap:.35rem; text-align:center;
  border:1px solid var(--line-2); border-radius:var(--r); padding:1.75rem 1.25rem;
  color:var(--text); transition:border-color .18s, background .18s;
}
.dl-card:hover{border-color:var(--gold); background:var(--veil-2); color:var(--text)}
.dl-card b{font-family:Poppins,sans-serif; font-weight:600; font-size:var(--s-1)}
.dl-ico{width:34px; height:34px; color:var(--gold); margin-bottom:.4rem}
.dl-ico svg{width:100%; height:100%; display:block}
/* THE REAL ICON, for the version that comes out of the page editor. The card
   markup is admin-editable content now and the sanitiser removes <svg>
   outright - correctly: an editable field is not a place to accept arbitrary
   vector markup. So the icon is carried by the STYLESHEET instead, as a mask
   rather than a background, which is what lets it keep currentColor and turn
   dark with the rest of the card on hover. Same glyph the template used to
   emit inline: a stem, an arrowhead, and the tray it lands in.
   An attempt to draw this from element borders is what "awful" referred to,
   and it deserved it - two rotated boxes cannot be an arrow. */
.dl-ico:empty{
  background:currentColor;
  -webkit-mask:var(--ico-download) center/contain no-repeat;
          mask:var(--ico-download) center/contain no-repeat;
}
:root{
  --ico-download:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='1.7' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M12 3.5v11'/%3E%3Cpath d='M7.5 10l4.5 4.5 4.5-4.5'/%3E%3Cpath d='M4 20.5h16'/%3E%3C/svg%3E");
}
.guide-vid{margin:2.5rem auto; max-width:900px; aspect-ratio:16/9; border-radius:var(--r); overflow:hidden; background:#000}
.guide-vid iframe{width:100%; height:100%; border:0; display:block}
.faqs{max-width:min(96ch,100%); margin-inline:auto}   /* the same reasoning as .doc above */
.faq-cat{font-family:Poppins,sans-serif; font-weight:600; font-size:var(--s-1); color:var(--gold);
  margin:2.5rem 0 .5rem; letter-spacing:.01em}
.faq{border-bottom:1px solid var(--line)}
.topictitle{
  position:relative; cursor:pointer; padding:1.05rem 2.5rem 1.05rem 0;
  font-size:var(--s-0); color:var(--text); transition:color .15s;
}
.topictitle:hover{color:var(--gold)}
/* The marker is drawn, so it does not depend on FontAwesome - which the layout
   loads for INHERITED views only, and this view is the template's own. */
.topictitle::after{
  content:""; position:absolute; right:.55rem; top:50%; width:8px; height:8px; box-sizing:border-box;
  border-right:2px solid currentColor; border-bottom:2px solid currentColor;
  transform:translateY(-70%) rotate(-45deg); transition:transform .18s;
}
.faq.open .topictitle::after{transform:translateY(-30%) rotate(45deg)}
.topiccontent{padding:0 0 1.25rem; color:var(--text-2); font-size:var(--s--1); line-height:1.75}

/* ============================ ACCOUNT ============================
   A sidebar and a column of panels. The panels are the same object as the cart
   summary - a hairline box on the page ground - so the account does not read as
   a different product from the shop. */
/* ACCOUNT PAGES RUN ONE STEP LARGER THAN THE REST OF THE SITE.
   Nearly everything in here was set in --s--1 (13px) - tables, hints, notes,
   the side nav, the ticket thread - while the pages people browse are 15px.
   That is backwards: a catalogue page is looked at, an order, a serial and a
   support reply are READ, often by someone checking a number against a screen
   somewhere else.
   Redefining the token on the container rather than editing thirty
   declarations means anything added here later gets the same treatment, and
   nothing outside the account area moves. */
.acct,.authbox{--s--1:.875rem}
.acct{display:grid; grid-template-columns:230px minmax(0,1fr); gap:clamp(1.5rem,4vw,3rem); align-items:start}
.acct-side{display:flex; flex-direction:column; gap:.15rem; position:sticky; top:calc(var(--head-h) + 1.5rem)}
.acct-side .who{padding:0 .9rem 1rem; display:flex; flex-direction:column; gap:.2rem; border-bottom:1px solid var(--line); margin-bottom:.75rem}
.acct-side .who b{font-family:Poppins,sans-serif; font-weight:600; font-size:var(--s-0); color:var(--text)}
.acct-side .who span{font-family:"Plex Mono",monospace; font-size:.75rem; color:var(--muted); overflow:hidden; text-overflow:ellipsis}
.acct-side a{
  display:flex; align-items:center; gap:.7rem; padding:.6rem .9rem; border-radius:var(--rs);
  font-size:var(--s-0); color:var(--text-1); transition:background .15s, color .15s;
}
.acct-side a svg{width:17px; height:17px; flex-shrink:0; opacity:.85}
.acct-side a:hover{background:var(--veil-2); color:var(--text)}
.acct-side a.on{background:var(--lift-2); color:var(--text)}
.acct-side a.on svg{color:var(--gold); opacity:1}
.acct-side .out{margin-top:.75rem; padding-top:1rem; border-top:1px solid var(--line); border-radius:0; color:var(--muted)}
.acct-side .out:hover{background:none; color:var(--text-2)}

.acct-panel{border:1px solid var(--line); border-radius:var(--r); padding:clamp(1.25rem,3vw,1.75rem); margin-bottom:1.5rem}
.acct-panel > h1,.acct-panel > h2{font-family:Poppins,sans-serif; font-weight:600; margin:0 0 1.25rem; color:var(--text)}
.acct-panel > h1{font-size:var(--s-2)}
.acct-panel > h2{font-size:var(--s-1)}
.acct-panel .hint{margin:-.75rem 0 1.25rem; color:var(--text-2); font-size:var(--s--1); line-height:1.7}
.acct-head{display:flex; justify-content:space-between; align-items:center; gap:1rem; flex-wrap:wrap; margin-bottom:1rem}
.acct-head h1{margin:0 !important}
/* What is left of a panel heading once the title moved to the band above: the
   one fact that identifies this row - an order's date, a ticket's subject -
   beside whatever control sat next to the heading. */
.acct-sub{font-family:Poppins,sans-serif; font-weight:600; font-size:var(--s-1); color:var(--text)}

/* Three numbers, and each one is a link to the list behind it. */
.acct-stats{display:grid; grid-template-columns:repeat(auto-fit,minmax(150px,1fr)); gap:1rem}
.acct-stats a{border:1px solid var(--line); border-radius:var(--rs); padding:1.25rem 1rem; text-align:center; transition:border-color .18s, background .18s}
.acct-stats a:hover{border-color:var(--line-2); background:var(--veil)}
.acct-stats .n{font-family:Poppins,sans-serif; font-weight:300; font-size:var(--s-4); line-height:1; color:var(--text)}
.acct-stats .l{font-family:"Plex Mono",monospace; font-size:.75rem; letter-spacing:.1em; text-transform:uppercase; color:var(--muted); margin-top:.5rem}

.acct-table{width:100%; border-collapse:collapse; font-size:var(--s--1)}
.acct-table th{
  text-align:left; font-family:"Plex Mono",monospace; font-weight:500; font-size:.75rem;
  letter-spacing:.1em; text-transform:uppercase; color:var(--muted);
  padding:0 .75rem .7rem 0; border-bottom:1px solid var(--line); white-space:nowrap;
}
.acct-table td{padding:.85rem .75rem .85rem 0; border-bottom:1px solid var(--line); color:var(--text-2); vertical-align:middle}
.acct-table tr:last-child td{border-bottom:0}
.acct-table td b,.acct-table td a{color:var(--text)}
.acct-table td a:hover{color:var(--gold)}
.acct-table .right{text-align:right; padding-right:0}
.acct-table .mono{font-family:"Plex Mono",monospace; font-variant-numeric:tabular-nums; color:var(--text)}
.acct-table code{font-family:"Plex Mono",monospace; font-size:.875rem; color:var(--text); letter-spacing:.04em}
/* An iLok id is a username, not a serial: it wraps where a serial must not, and
   it is quieter, because it identifies the account rather than the licence. */
.acct-table code.ilok{color:var(--text-2); letter-spacing:0; word-break:break-word}
.acct-meta{display:block; margin-top:.25rem; font-family:"Plex Mono",monospace; font-size:.75rem; letter-spacing:.04em; color:var(--muted)}

/* Status pills. Every state the order machine can be in gets a colour, and
   anything unlisted falls back to the neutral one rather than to nothing. */
.pill{
  display:inline-block; font-family:"Plex Mono",monospace; font-size:.6875rem; letter-spacing:.08em;
  text-transform:uppercase; padding:.25rem .6rem; border-radius:999px;
  background:var(--lift-3); color:var(--text-2); border:1px solid var(--line-2); white-space:nowrap;
}
.pill.complete,.pill.registered{background:rgba(89,201,138,.14); color:#7FD9A6; border-color:rgba(89,201,138,.3)}
.pill.pending,.pill.processing,.pill.open{background:rgba(217,154,68,.14); color:var(--gold); border-color:rgba(217,154,68,.3)}
.pill.failed,.pill.canceled,.pill.cancelled{background:rgba(200,90,90,.14); color:#E09090; border-color:rgba(200,90,90,.3)}

.acct-empty{color:var(--text-2); font-size:var(--s--1)}
.acct-acts{display:flex; gap:.5rem; justify-content:flex-end; flex-wrap:wrap}
.acct-note{font-size:var(--s--1); color:var(--text-2); line-height:1.7; margin:1.25rem 0 0}
.acct-note + .acct-note{margin-top:.6rem}
.acct-tsub{display:inline-block; margin-top:.3rem; font-size:.8125rem; color:var(--text-2)}
.acct-table a.acct-guide{display:inline-block; margin-top:.35rem; font-size:.8125rem; color:var(--gold); white-space:nowrap}
/* My serials: a serial (AS ones are 31 characters) is never broken over lines. */
.acct-serials code{white-space:nowrap}
/* The "?" holding order and date. The tip opens to the LEFT: the table sits in a
   scrolling panel on phones, which would clip a tip opening upwards on the
   first row. The ::after bridges the gap so the pointer can reach the link. */
.acct-info{position:relative; display:inline-flex; align-items:center; justify-content:center;
  width:1.4rem; height:1.4rem; border:1px solid var(--line-2); border-radius:50%;
  font-family:Poppins,sans-serif; font-weight:600; font-size:.75rem; color:var(--text-2); cursor:help}
.acct-info:hover,.acct-info:focus,.acct-info:focus-within{color:var(--text); border-color:var(--text-2); outline:none}
.acct-tip{display:none; position:absolute; right:calc(100% + .5rem); top:50%; transform:translateY(-50%); z-index:5;
  white-space:nowrap; text-align:left; background:var(--lift-3); border:1px solid var(--line-2); border-radius:8px;
  padding:.5rem .75rem; font-family:inherit; font-weight:400; font-size:.8125rem; line-height:1.55;
  color:var(--text-2); box-shadow:0 8px 24px rgba(0,0,0,.45); cursor:default}
.acct-tip::after{content:''; position:absolute; top:0; bottom:0; left:100%; width:.6rem}
.acct-info:hover .acct-tip,.acct-info:focus .acct-tip,.acct-info:focus-within .acct-tip{display:block}
.acct-table .acct-tip a{color:var(--text)}
.acct-table a.acct-guide:hover{color:var(--text)}

/* A signed-out form - log in, register, reset - is one card in the middle of
   the page, because there is nothing else on it to look at. */
.authbox{max-width:420px; margin-inline:auto; border:1px solid var(--line); border-radius:var(--r); padding:clamp(1.5rem,4vw,2.25rem)}
.authbox.wide{max-width:640px}
.authbox h1{font-family:Poppins,sans-serif; font-weight:600; font-size:var(--s-2); margin:0 0 1.5rem}
/* Passkeys: the "or" rule under the login form, status lines, the offer box. */
.pk-signin{margin:1.25rem 0 0}
.pk-or{display:flex; align-items:center; gap:.75rem; margin:0 0 1rem; color:var(--muted); font-size:var(--s--1)}
.pk-or:before,.pk-or:after{content:""; flex:1; height:1px; background:currentColor; opacity:.35}
.pk-msg{display:inline-block; margin:.35rem 0 0; font-size:var(--s--1); color:var(--text-2); min-height:1.2em}
.pk-msg.is-err{color:#E09090}
.pk-add p:last-child .pk-msg{margin-left:.75rem}
.pk-remove{display:inline}
.pk-actions{display:flex; flex-wrap:wrap; align-items:center; gap:.5rem .75rem; margin:0}
.pk-actions .pk-msg{margin:0}
/* Same rule as .vmodal etc. above: these boxes carry author display rules
   (.frm, .acct-panel, .msg-ok), so the user-agent [hidden] would lose. */
.pk-signin[hidden],.pk-nudge[hidden],.pk-add[hidden],#pk-nudge-done[hidden],#pk-unsupported[hidden]{display:none}
.authbox .links{margin:1.25rem 0 0; font-size:var(--s--1); color:var(--muted)}
.msg-info{border:1px solid #6E6130; background:rgba(110,97,48,.2); border-radius:var(--rs);
  padding:.8rem 1rem; margin:0 0 1.25rem; font-size:var(--s--1); color:var(--text-2)}
@media (max-width:820px){
  .acct{grid-template-columns:minmax(0,1fr)}
  .acct-side{position:static; flex-direction:row; flex-wrap:wrap; gap:.35rem}
  .acct-side .who{width:100%; border-bottom:0; margin-bottom:0}
  .acct-side .out{margin-top:0; padding-top:.6rem; border-top:0}
}

/* Form bits shared by the account, auth and entry forms. */
.frm .opt{color:var(--muted); font-weight:400}
.frm .chk{display:flex; align-items:flex-start; gap:.6rem; color:var(--text-2); font-size:var(--s--1)}
.frm .chk input{margin-top:.2rem; accent-color:var(--gold)}
.frm .chk span{line-height:1.6}
.frm-two{display:grid; grid-template-columns:1fr 1fr; gap:0 1rem}
.frm input[type=file]{
  width:100%; max-width:460px; box-sizing:border-box; font:inherit; font-size:var(--s--1);
  background:var(--lift-2); border:1px solid var(--line-2); border-radius:var(--rs);
  padding:.55rem .7rem; color:var(--text-2);
}
.msg-ok{border:1px solid rgba(89,201,138,.35); background:rgba(89,201,138,.12); border-radius:var(--rs);
  padding:.8rem 1rem; margin:0 0 1.25rem; font-size:var(--s--1); color:#7FD9A6}
/* Its error twin, the same box as `.frm .err`. That rule only matches an error
   INSIDE a .frm, so the redeem box's "not recognised", My details' password and
   passkey errors, and every `<p class="frm err">` (login, forgotten, contact,
   competition, share - both classes on one element) read as ordinary text. */
.err{border:1px solid #7A3A3A; background:rgba(122,58,58,.18); border-radius:var(--rs);
  padding:.85rem 1rem; margin:0 0 1.25rem; font-size:var(--s--1); color:#F0C4C4}
.news-opt{display:flex; align-items:center; gap:1rem; flex-wrap:wrap; margin:1.5rem 0}
.credit-bal{display:flex; gap:1.5rem; flex-wrap:wrap}
.credit-bal .price{font-size:var(--s-3); color:var(--gold)}
.acct-table .plus{color:#7FD9A6}
@media (max-width:560px){ .frm-two{grid-template-columns:minmax(0,1fr)} }

/* ---------------- support thread ---------------- */
.tk-msg{border-radius:var(--rs); padding:1rem 1.15rem; margin-bottom:1rem; background:var(--lift-2); border-left:3px solid var(--line-2)}
.tk-msg.staff{border-left-color:var(--gold)}
.tk-head{margin-bottom:.6rem; font-size:var(--s--1); color:var(--text)}
.tk-head .tk-date{color:var(--muted); font-family:"Plex Mono",monospace; font-size:.75rem; margin-left:.7rem}
.tk-body{line-height:1.7; overflow-wrap:break-word; color:var(--text-2); font-size:var(--s--1)}
.tk-body img{max-width:100%; height:auto}
.tk-body blockquote{border-left:2px solid var(--line-2); margin:.6rem 0; padding-left:.9rem; color:var(--muted)}
/* Long replies are collapsed rather than scrolled inside their own box: a
   scrollbar within a scrolling page is what makes a long thread unreadable. The
   cap applies only to messages that actually overflow - the view's JS adds
   .tk-clip after measuring - so a short reply is untouched and a browser
   without JS shows everything. */
.tk-msg.tk-clip .tk-body{max-height:320px; overflow:hidden; position:relative}
.tk-msg.tk-clip .tk-body::after{
  content:''; position:absolute; left:0; right:0; bottom:0; height:70px;
  background:linear-gradient(to bottom, rgba(22,22,29,0), var(--lift-2)); pointer-events:none;
}
.tk-msg.tk-open .tk-body{max-height:none; overflow:visible}
.tk-msg.tk-open .tk-body::after{display:none}
.tk-more{display:inline-block; margin-top:.6rem; background:none; border:0; padding:0;
  color:var(--gold); font:inherit; font-size:var(--s--1); cursor:pointer}
.tk-more:hover{text-decoration:underline}
.tk-atts{margin-top:.7rem; font-size:var(--s--1); display:flex; gap:1rem; flex-wrap:wrap}
.tk-atts a{color:var(--text-2)}
.tk-atts a:hover{color:var(--gold)}

/* ---------------- competitions ---------------- */
.cmp-grid{display:grid; grid-template-columns:repeat(auto-fill,minmax(300px,1fr)); gap:1.25rem}
.cmp-card{display:flex; flex-direction:column; align-items:flex-start; gap:.7rem;
  border:1px solid var(--line-2); border-radius:var(--r); padding:1.25rem 1.4rem; color:var(--text);
  transition:border-color .18s, background .18s}
.cmp-card:hover{border-color:var(--gold); background:var(--veil-2); color:var(--text)}
.cmp-card b{font-family:Poppins,sans-serif; font-weight:600; font-size:var(--s-1)}
.cmp-entries{display:grid; grid-template-columns:repeat(auto-fill,minmax(330px,1fr)); gap:1.5rem}
.cmp-entry{border:1px solid var(--line); border-radius:var(--r); padding:1.25rem}
.cmp-top{display:flex; align-items:center; gap:.6rem; flex-wrap:wrap}
.cmp-top b{font-family:Poppins,sans-serif; font-weight:600; font-size:var(--s-0); color:var(--text)}
.cmp-title{margin:.3rem 0 0; font-size:var(--s--1); color:var(--text-2)}
.cmp-media{margin:.9rem 0}
.cmp-vid{position:relative; padding-top:56.25%; border-radius:var(--rs); overflow:hidden; background:#000}
.cmp-vid iframe{position:absolute; inset:0; width:100%; height:100%; border:0}
.cmp-desc{font-size:var(--s--1); color:var(--text-2); line-height:1.7; margin:0}
.cmp-foot{display:flex; justify-content:space-between; align-items:center; gap:1rem; margin-top:1rem}
.cmp-foot .votes{font-size:var(--s--1); color:var(--text-2)}
.cmp-foot .votes b{color:var(--text)}

/* ---------------- share & save ---------------- */
.steps{line-height:1.9; color:var(--text-2); font-size:var(--s--1); padding-left:1.2rem; margin:1rem 0}
.steps b{color:var(--text)}
.fineprint{font-size:var(--s--1); color:var(--muted); line-height:1.7}
.claim-box{border:1px solid var(--line-2); border-radius:var(--rs); padding:1rem 1.15rem; margin:1.5rem 0}
.claim-box p{margin:0; color:var(--text-2); font-size:var(--s--1); line-height:1.7}
.claim-box b{color:var(--text)}
@media (max-width:620px){ .billing-grid{grid-template-columns:minmax(0,1fr); gap:.35rem} }
@media (prefers-reduced-motion:reduce){ .vmodal,.vm-box{transition:none} }

/* ============================ PHONES AND TABLETS ============================
   The rules above already collapse the big grids; this block is what a real
   phone found that a narrow desktop window did not. Ordered widest first, so
   each one narrows the last.

   Two principles behind the choices:
   - Nothing that navigates is deleted. A control that does not fit becomes a
     scroller or a menu; it does not disappear, because the page it leads to
     has no other way in.
   - Nothing scrolls sideways except things that are meant to. The page itself
     must never move horizontally, so anything wider than the screen is either
     wrapped or given its own overflow box. */

/* ---- tablets ---- */
@media (max-width:1000px){
  /* THE SECTION BAR SCROLLS, IT DOES NOT VANISH. It was display:none here,
     which on the longest page on the site removed the only way to reach Watch,
     Listen, Interface or Specs without scrolling past everything. */
  .anchors{
    display:flex; gap:1.5rem; margin-right:0; width:100%; order:-1;
    overflow-x:auto; overscroll-behavior-x:contain; -webkit-overflow-scrolling:touch;
    scrollbar-width:none; padding-block:.15rem;
    /* No fade at the edges. A mask that dims the first 12px also dims the
       first letter of "Overview" at a width where the row fits and does not
       scroll at all - and a row cut off mid-word is already the clearest
       possible sign that there is more of it. */
  }
  .anchors::-webkit-scrollbar{display:none}
  .anchors a{white-space:nowrap; flex:0 0 auto}
  .probar .wrap{gap:.75rem; padding-block:.5rem}
  .probar .buy{width:100%; justify-content:flex-start}
}

/* ---- phones ---- */
@media (max-width:760px){
  /* EUR / USD wrapped onto two lines and pushed the header to 44px of chrome.
     It is three words; it stays on one. */
  .tools .cur{white-space:nowrap; font-size:.75rem; letter-spacing:.04em}
  .tools{gap:.6rem}
  .site-head .wrap{gap:.6rem}

  /* The category banner is a 2.6:1 composite. In a 460px band on a 375px
     screen `cover` shows about a sixth of it - one enormous letter - so the
     band gets shorter and the picture gets wider before it is cropped. */
  .cathero{min-height:clamp(240px,42vh,340px)}
  .pagehead{padding-block:clamp(1.75rem,7vw,2.5rem)}

  /* Three lines of blurb reserved at desktop sizes is a third of a phone
     screen before anything has been said. */
  .bhero-txt p{min-height:0; -webkit-line-clamp:3}
  /* The copy covers a much bigger share of the picture on a phone, and it
     lands on the artwork's own wordmark - which is bright on most of these
     banners. The scrim starts higher and finishes darker to carry it. */
  .bhero::after{
    background:linear-gradient(to bottom, transparent 22%,
      rgba(var(--art-scrim),calc(var(--bhero-veil) * .64)) 50%,
      rgba(var(--art-scrim),calc(var(--bhero-veil) + .04)) 100%);
  }

  /* A thumbnail strip is a swipe, and it should say so: each one snaps and the
     row does not drag the page sideways with it. */
  .strip{scroll-snap-type:x mandatory; overscroll-behavior-x:contain}
  .strip button{flex:0 0 42%; scroll-snap-align:start}

  /* Account: the sidebar is a row of chips above the panels. It already wraps;
     these keep it from looking like a stack of half-empty rows. */
  .acct-side a{padding:.5rem .7rem; font-size:.875rem}
  .acct-side .who{padding-bottom:.6rem}

  /* Any table that cannot narrow gets its own scroller rather than widening
     the page. The account tables are the ones with five columns.
     The values that must not be broken up say so: a serial wrapped over four
     lines is unreadable and uncopyable, and a status pill split in half reads
     as two words. The row stays one line and the panel is swiped instead -
     every column survives, which hiding them would not do. */
  .acct-panel{overflow-x:auto}
  .acct-table code,.acct-table .pill,.acct-table .mono,.acct-table .right,
  .acct-table th{white-space:nowrap}

  .vmodal{padding:.75rem}
  .vmodal-box{max-height:88vh}
  .oc-acts .btn{flex:1 1 auto; justify-content:center}
}

/* ---- small phones ---- */
@media (max-width:420px){
  /* Hidden here, and moved into the menu (see partials/header.php) - not
     dropped: this is the only currency switch on the site. */
  .tools .cur{display:none}
  .tiles{gap:1.5rem}
  .btn{padding:.7rem 1.2rem}
  .btn.btn-buy{padding-inline:1.1rem .35rem}
  .btn-buy .pz{margin-left:.8rem; padding-inline:.8rem}
  .strip button{flex:0 0 55%}
}

/* ---- touch, at any width ----
   A finger is not a pointer: 38px is a comfortable mouse target and a small
   touch one, and these three sit close enough together to be mis-hit. Hover
   states are also useless here, so the actions stop depending on one. */
/* The menu's own currency row. */
.mnav-cur{display:flex; gap:1.25rem; padding:.35rem 0 .9rem}
.mnav-cur a{font-family:"Plex Mono",monospace; font-size:.8125rem; letter-spacing:.06em; color:var(--muted)}
.mnav-cur a.on{color:var(--gold)}

@media (hover:none){
  .tact{width:44px; height:44px}
  .foot-social a{width:44px; height:44px}
  .tact svg{width:21px; height:21px}
  .tile-acts{gap:.55rem; padding:.55rem}
  .dk-skip{width:40px; height:40px}
}

/* ---------------- live search suggestions ---------------- */
.hs-drop{
  position:absolute; top:calc(100% + .5rem); left:0; right:0; z-index:70; min-width:290px;
  background:var(--ink); border:1px solid var(--line-2); border-radius:var(--rs);
  padding:.35rem; box-shadow:0 30px 70px -32px rgba(0,0,0,.9);
  max-height:min(70vh,460px); overflow-y:auto;
}
.hs-row{
  display:flex; align-items:center; gap:.65rem; padding:.45rem .55rem; border-radius:6px;
  font-size:var(--s--1); color:var(--text-2);
}
.hs-row img,.hs-noimg{width:39px; height:26px; flex:0 0 39px; border-radius:3px; object-fit:cover; background:var(--well)}
.hs-noimg{border:1px solid var(--line)}
.hs-name{min-width:0; flex:1; white-space:nowrap; overflow:hidden; text-overflow:ellipsis}
.hs-price{font-family:"Plex Mono",monospace; font-size:.6875rem; color:var(--muted); flex-shrink:0}
.hs-row:hover,.hs-row.on{background:var(--veil-2); color:var(--text)}
.hs-row.on .hs-price,.hs-row:hover .hs-price{color:var(--text-2)}

/* The search page's own box: the header's control at page scale. */
.searchbar.hsearch{
  display:flex; align-items:center; gap:.7rem; position:relative;
  margin-top:1.5rem; max-width:640px; min-width:0; padding:.55rem .55rem .55rem .9rem;
  border:1px solid var(--line-2); border-radius:999px; background:var(--veil);
}
.searchbar.hsearch:focus-within{border-color:var(--text-2)}
.searchbar.hsearch svg{width:17px; height:17px; flex-shrink:0; opacity:.55}
.searchbar.hsearch input{
  flex:1; min-width:0; background:none; border:0; outline:none; color:var(--text);
  font:inherit; font-size:var(--s-0);
}
.searchbar.hsearch input::placeholder{color:var(--text-2)}

/* ---------------- SCENE BANDS ----------------
   A room behind a section: an auditorium behind the video, a listening room
   behind the players. The same room on every product, so these are template
   assets in the stylesheet rather than admin fields - a section only names its
   picture.

   The wash over it is rgba(var(--art-scrim)) - the PAGE's own colour - so it
   deepens the room on a dark page and lifts it toward the paper on a bright
   one, instead of leaving a dark hole punched in the layout. --art-veil is how
   far, and Palette raises it when the ground is light. Heavy at the very top
   and bottom so the band meets its neighbours without a seam, and barely there
   across the middle: the room IS the point. */
.scene{position:relative; isolation:isolate; overflow:hidden; background:var(--ink)}
.scene::before{
  content:""; position:absolute; inset:0; z-index:-2;
  background:var(--scene) center/cover no-repeat;
}
.scene::after{
  content:""; position:absolute; inset:0; z-index:-1;
  background:linear-gradient(to bottom,
    rgba(var(--art-scrim),.94) 0%,
    rgba(var(--art-scrim),var(--art-veil)) 20%,
    rgba(var(--art-scrim),var(--art-veil)) 76%,
    rgba(var(--art-scrim),.96) 100%);
}
/* The picture itself comes from the section's own style attribute, because it
   is a per-product setting now. This is only the fallback for a band that
   somehow has none. */
.scene{--scene:none}

/* The copy sits ON the room now, so it takes the over-artwork colour. */
.scene h2.sec,.scene .th-cap b{color:var(--art-text)}
.scene .sec-sub{color:var(--art-text); opacity:.72}
.scene .th-thumb small{color:var(--art-text); opacity:.62}
.scene .th-thumb[aria-selected="true"] small{opacity:1}
/* The players are panels standing IN the room, so they need a ground of their
   own - the near-transparent surface wash disappears against a photograph. */
.scene .player{background:rgba(var(--art-scrim),.66); backdrop-filter:blur(6px); border-color:var(--line-2)}

/* ============================ PLATFORMS ============================
   AS Engine / UVI player. The picker sits beside the buy button in the sticky
   bar, in the same outlined-pill language; hovering (or focusing, or tapping)
   it opens a box that says what each platform is. Texts come from
   Catalog > Platforms. */
.probar .buy{position:relative}
.plat{position:relative; display:inline-flex; align-items:center}
.pseg{display:inline-flex; border:1px solid var(--line-2); border-radius:999px; padding:3px; gap:2px}
.pseg button{font:inherit; font-size:var(--s--1); border:0; background:transparent; color:var(--text-2);
  padding:.42rem .95rem; border-radius:999px; cursor:pointer; white-space:nowrap}
.pseg button.on{color:var(--gold); box-shadow:inset 0 0 0 1px var(--gold); background:rgba(217,154,68,.10)}
.pseg button:focus-visible{outline:2px solid var(--gold); outline-offset:2px}
.pseg.single button{cursor:help}
.pseg.single.uvi button.on{color:var(--text); box-shadow:inset 0 0 0 1px var(--line-2); background:transparent}
.pseg .short{display:none}
.plat-pop{position:absolute; right:0; top:calc(100% + 12px); width:min(380px,calc(100vw - 32px)); z-index:60;
  background:var(--ink); border:1px solid var(--line-2); border-radius:var(--r); padding:1.15rem 1.25rem 1.1rem;
  box-shadow:0 18px 40px -16px rgba(0,0,0,.7); display:grid; gap:.75rem; text-align:left;
  opacity:0; visibility:hidden; transform:translateY(-4px); transition:opacity .15s,transform .15s,visibility .15s}
.plat-pop::before{content:""; position:absolute; top:-6px; right:42px; width:10px; height:10px; background:var(--ink);
  border-left:1px solid var(--line-2); border-top:1px solid var(--line-2); transform:rotate(45deg)}
/* THE GAP STAYS, THE HOVER DOES NOT BREAK. The box sits 12px below the
   picker, and crossing that space left both elements, so the box closed before
   the pointer could reach its link. An invisible strip, part of the box, fills
   the gap: the pointer never leaves .plat on its way down. It only exists while
   the box is visible (visibility:hidden takes it out of hit-testing). A short
   delay on closing forgives a diagonal move that clips the corner. */
.plat-pop::after{content:""; position:absolute; left:0; right:0; top:-16px; height:16px}
.plat .plat-pop{transition:opacity .15s .12s, transform .15s .12s, visibility 0s .27s}
.plat:hover .plat-pop,.plat:focus-within .plat-pop,.plat.open .plat-pop{opacity:1; visibility:visible; transform:none;
  transition:opacity .15s, transform .15s, visibility 0s}
.plat-pop .pp{display:grid; gap:.15rem}
.plat-pop .pp b{font-family:Poppins,sans-serif; font-weight:600; color:var(--text); font-size:var(--s-0)}
.plat-pop .pp b em{font-style:normal; font-family:"Plex Mono",monospace; font-size:.68em; letter-spacing:.08em;
  text-transform:uppercase; color:var(--gold); margin-left:.5rem}
.plat-pop .pp span{color:var(--text-2); font-size:var(--s--1); line-height:1.55}
.plat-pop .pp + .pp,.plat-pop .pp-link + .pp{border-top:1px solid var(--line); padding-top:.75rem}
.plat-pop .both{border-top:1px solid var(--line); padding-top:.75rem; color:var(--text); font-size:var(--s--1); line-height:1.55}
.plat-pop .both b{color:var(--gold); font-weight:600}
.plat-pop a{color:var(--gold); font-size:var(--s--1)}
.plat-pop .pp-link{margin-top:-.35rem}
.owned{font-size:var(--s--1); color:var(--text-2); white-space:nowrap}
.owned b{color:var(--text); font-weight:600}
.plat.owner{cursor:help}
@media (max-width:700px){
  .probar .buy{gap:.6rem; flex-wrap:nowrap}
  .probar .buy .btn-buy{margin-left:auto}
  .plat{position:static}
  .pseg button{padding:.42rem .75rem}
  .pseg .long{display:none}
  .pseg .short{display:inline}
  .plat-pop{left:0; right:0; width:auto; top:calc(100% + 10px)}
  .plat-pop::before{left:70px; right:auto}
  .owned{white-space:normal}
}
/* The Tech specs blocks, one set per platform, switched in step with the bar. */
.spec-plat{grid-column:1/-1; display:flex; align-items:center; gap:.9rem; flex-wrap:wrap; margin-top:2.4rem}
.spec-plat .lbl{font-family:"Plex Mono",monospace; font-size:.6875rem; letter-spacing:.1em; text-transform:uppercase; color:var(--muted)}
.spec-plat + .specs-blocks{margin-top:1.2rem}
.specs-blocks[hidden]{display:none}
/* Cart: the platform of each line, the switch, and the add-on. */
.line .platline{display:flex; flex-wrap:wrap; align-items:center; gap:.5rem .7rem; margin-top:.55rem; font-size:var(--s--1); color:var(--text-2)}
.chip-plat{font-family:"Plex Mono",monospace; font-size:.6875rem; letter-spacing:.08em; text-transform:uppercase;
  border:1px solid var(--gold); color:var(--gold); border-radius:999px; padding:.18rem .6rem; white-space:nowrap}
.chip-plat.uvi{border-color:var(--line-2); color:var(--text)}
.platline a{color:var(--text-2); text-decoration:underline; text-underline-offset:3px}
.addon{display:flex; align-items:center; gap:.6rem; margin-top:.6rem; padding:.55rem .75rem; border:1px dashed var(--line-2);
  border-radius:var(--rs); font-size:var(--s--1); color:var(--text-2); max-width:460px; text-decoration:none}
.addon:hover{color:var(--text); border-color:var(--text-2)}
.addon.on{border-style:solid; border-color:var(--gold); color:var(--text)}
.addon .box{width:14px; height:14px; border:1px solid var(--line-2); border-radius:3px; flex:none; display:grid; place-items:center;
  font-size:11px; line-height:1; color:var(--gold-ink)}
.addon.on .box{background:var(--gold); border-color:var(--gold)}
.addon .ap{margin-left:auto; font-family:"Plex Mono",monospace; color:var(--gold)}
.upline-note{font-size:var(--s--1); color:var(--text-2); margin-top:.35rem}
