/* ===========================================================================
   Online Firework Shop — shared components
   Deploy 004. Section bands, headings, product cards, rails, chips, badges,
   the noise meter, category tiles and pills.

   Extracted from the approved concepts. Used on the homepage, category,
   product and basket, so it loads on every page rather than per module.
   =========================================================================== */

.band{--band-pad:48px; padding-block:var(--band-pad)}
@media(min-width:768px){
  .band{--band-pad:64px}
}
@media(min-width:1024px){
  .band{--band-pad:96px}
}
.band--surface{background:var(--ink-band)}

/* ---------- vertical rhythm when sections are modules ---------------------
   In the concepts a section is one .band: heading and content together, 96px
   top and bottom, 32px between the heading block and the grid.

   Built from modules, each module renders its own .band, so a heading module
   above a product module produced 96px + 96px = 192px between a heading and
   the products it introduces.

   Two rules fix it without the modules needing to know about each other:
     - consecutive bands share one gap rather than each contributing a full one
     - an HTML Content band that is followed by another band is an intro, so it
       hugs what comes next
   Browsers without :has() simply keep the full gap, which is the safe way to
   be wrong. */
.band + .band{padding-top:0}
/* ...except a surface band, which carries its own background. Losing the top
   padding there puts the content flush against the colour change instead of
   inside it, which reads as a mistake rather than as tighter spacing the way it
   does between two bands that share the page's ground. */
.band + .band--surface{padding-top:var(--band-pad)}
.band--html:has(+ .band){padding-bottom:var(--s-6)}

/* A module that renders its own heading is a complete section, not a
   continuation of the one above, so it keeps the full top padding the rule
   above would otherwise take away. Without this a surface band would start
   flush against its own heading. */
.band + .band--sec,
.band + .band--html{padding-top:var(--band-pad)}
.dot{width:7px; height:7px; border-radius:var(--r-pill); flex:none}
.dot--open{background:var(--in-stock); box-shadow:0 0 0 3px rgba(60,180,252,.18)}
.dot--shut{background:var(--out-of-stock)}
/* ---------- section heads ---------- */
.prose{color:var(--text-secondary); max-width:64ch}
.prose + .prose{margin-top:var(--s-4)}
.prose p + p{margin-top:var(--s-4)}
.shead{margin-bottom:var(--s-6)}
.shead p{margin-top:var(--s-4)}
.shead--row{display:grid; gap:var(--s-4)}
/* A rail heading whose band follows another band has had its top padding taken
   by `.band + .band`. The heading starts a new section rather than continuing
   the one above, so it needs that gap back. Scoped to that situation: a rail
   heading in a band that already keeps its padding — the news module on the
   home page, which is a --sec — must not be given it twice. */
.band + .band .shead--row{padding-top:var(--s-6)}
@media(min-width:900px){
  .shead--row{grid-template-columns:minmax(0,1fr) auto; align-items:end; column-gap:var(--s-7)}
  .shead--row p{margin-top:var(--s-4)}
  .shead__link{align-self:end; padding-bottom:4px; white-space:nowrap}
}
/* ---------- entry routes (mosaic) ---------- */
.mosaic{display:grid; gap:var(--s-4); grid-template-columns:repeat(2,minmax(0,1fr))}
@media(min-width:900px){
  .mosaic{grid-template-columns:repeat(4,minmax(0,1fr)); grid-auto-rows:minmax(150px,auto)}
}
.tile{position:relative; display:flex; flex-direction:column; justify-content:flex-end;
  padding:var(--s-5); background:var(--ink-800); border:1px solid var(--ink-700);
  border-radius:var(--r-tile); color:var(--text-primary); min-height:150px; overflow:hidden;
  transition:border-color var(--t-micro) ease-out, background var(--t-micro) ease-out}
.tile:hover{text-decoration:none; color:var(--text-primary); border-color:var(--spark-cyan); background:var(--ink-600)}
.tile__name{font-family:var(--font-display); font-weight:700; text-transform:uppercase;
  letter-spacing:.02em; font-size:1.25rem; line-height:1.05}
/* text-secondary rather than tertiary. On a plain tile either reads; over a
   photograph the tertiary grey is what forces the scrim to stay heavy. Lifting
   it one step takes every tile to 0% of the supporting line under 4.5:1 at 70%
   darkness, where the tertiary grey put the lead tile at 15.6%. */
.tile__meta{font-size:.8125rem; color:var(--text-secondary); margin-top:4px}
@media(min-width:900px){
  .tile--lead{grid-column:span 2; grid-row:span 2}
  .tile--lead .tile__name{font-size:2.25rem}
  .tile--lead .tile__meta{font-size:.9375rem; max-width:34ch}
}
.tile--lead{background:linear-gradient(160deg,var(--ink-600) 0%,var(--ink-800) 55%)}
/* The lead tile spans two rows, which leaves the right of row three empty.
   The offer block fills it. It links to the same BOGOF category the gold band
   further down the page introduces, so it takes the same treatment: the gold,
   the near-black for the name and the blue for the line under it. The hover is
   the same lighter gold a primary button uses, so the one interactive gold fill
   on the page behaves consistently wherever it appears. */
.tile--offer{background:var(--fuse-gold); border-color:var(--fuse-gold); grid-column:span 2}
.tile--offer:hover{background:#FFD34A; border-color:#FFD34A; color:var(--text-on-accent)}
.tile--offer .tile__name{color:var(--text-on-accent)}
.tile--offer .tile__meta{color:var(--ink-800); max-width:46ch}
.tile--offer .tile__flag{align-self:flex-start; margin-bottom:auto;
  background:rgba(5,3,26,.12);
  border:1.5px solid var(--text-on-accent); border-radius:var(--r-chip);
  font-size:.6875rem; font-weight:700; text-transform:uppercase; letter-spacing:.08em;
  padding:4px 8px; color:var(--text-on-accent)}
@media(min-width:900px){
  .tile--offer .tile__name{font-size:1.75rem}
}
.pills{display:flex; flex-wrap:wrap; gap:var(--s-2); margin-top:var(--s-5);
  align-items:center}
.pills__label{font-size:.875rem; color:var(--text-tertiary); margin-right:var(--s-2)}
.pill{display:inline-flex; align-items:center; height:44px; padding:0 18px;
  border:1px solid var(--ink-700); border-radius:var(--r-pill); color:var(--text-secondary);
  font-size:.875rem; font-weight:500; transition:all var(--t-micro) ease-out}
.pill:hover{text-decoration:none; border-color:var(--spark-cyan); color:var(--spark-cyan)}
.shop__row .dot{position:relative; top:-2px; display:inline-block; margin-right:7px}
/* ---------- product cards ---------- */
.card{background:var(--ink-800); border:1px solid var(--ink-700); border-radius:var(--r-card);
  overflow:hidden; display:flex; flex-direction:column;
  transition:border-color var(--t-micro) ease-out, transform var(--t-micro) ease-out}
.card:hover{border-color:rgba(60,180,252,.4); transform:translateY(-2px)}
/* Product shots are supplied on white grounds, so the media tile is deliberately light.
   If photography is standardised onto a black ground later, flip this to var(--ink-900). */
.card__media{position:relative; background:var(--paper); aspect-ratio:1; display:grid; place-items:center}
.card__media img{width:100%; height:100%; object-fit:contain; padding:14px}
.card__body{padding:var(--s-4); display:flex; flex-direction:column; gap:10px; flex:1}
.card__brand{font-size:.75rem; text-transform:uppercase; letter-spacing:.06em; color:var(--text-tertiary)}
/* Product names must not inherit the display face from the global h1,h2,h3 rule
   (brand-style-guide.md 4.3: product names are always Chivo, sentence case). */
.card__name{font-family:var(--font-body); text-transform:none; letter-spacing:0;
  font-size:1.0625rem; font-weight:700; line-height:1.3; color:var(--text-primary)}
.card__name a{color:inherit}
.chips{display:flex; flex-wrap:wrap; gap:6px}
.chip{font-size:.75rem; font-weight:700; text-transform:uppercase; letter-spacing:.06em;
  border:1px solid var(--violet); color:var(--violet); border-radius:var(--r-chip); padding:4px 7px}
.card__foot{margin-top:auto; display:flex; align-items:center; justify-content:space-between; gap:var(--s-3)}
.price{font-size:1.25rem; font-weight:700; line-height:1.2}
.badge{position:absolute; top:10px; left:10px; z-index:2;
  font-size:.75rem; font-weight:700; text-transform:uppercase; letter-spacing:.06em;
  border-radius:var(--r-chip); padding:5px 8px}
.badge--offer{background:var(--magenta); color:var(--text-on-accent)}
.badge--new{background:var(--ink-900); color:var(--spark-cyan); border:1px solid var(--spark-cyan)}
/* ---------- rail ---------- */
.rail{display:grid; grid-auto-flow:column; grid-auto-columns:minmax(230px,255px);
  gap:var(--s-4); overflow-x:auto; scroll-snap-type:x mandatory;
  padding-bottom:var(--s-4); margin-inline:-20px; padding-inline:20px;
  scrollbar-width:thin; scrollbar-color:var(--ink-600) transparent}
@media(min-width:768px){
  .rail{margin-inline:0; padding-inline:0}
}
/* Five cards fill the row exactly at desktop; the rest are reached by scrolling.
   The scrollbar is the only affordance saying so, which is why it is styled
   visible above rather than left to the browser to hide. Widening the divisor
   to 5.4 would peek the sixth card instead, if that reads better in use. */
@media(min-width:1024px){
  .rail{grid-auto-columns:calc((100% - 4 * var(--s-4)) / 5)}
}
.rail > *{scroll-snap-align:start}
.rail::-webkit-scrollbar{height:6px}
.rail::-webkit-scrollbar-thumb{background:var(--ink-600); border-radius:var(--r-pill)}
.rail__end{display:grid; place-items:center; border:1px dashed var(--ink-600);
  border-radius:var(--r-card); color:var(--spark-cyan); font-weight:700; text-align:center; padding:var(--s-5)}
/* The colour is restated on hover on purpose: Bootstrap ships a:hover at
   specificity 101 and the base .rail__end rule is 100, so without this the
   text turns navy while the border turns cyan. The class sat in this sheet
   unused until the shop page put it on a link. */
.rail__end:hover,.rail__end:focus-visible{text-decoration:none; color:var(--spark-cyan);
  border-color:var(--spark-cyan); background:rgba(60,180,252,.06)}
/* ---------- grid of cards ---------- */
.grid4{display:grid; gap:var(--s-4); grid-template-columns:repeat(2,minmax(0,1fr))}
@media(min-width:900px){
  .grid4{grid-template-columns:repeat(4,minmax(0,1fr))}
}
/* noise meter: shared component, used on product cards, the PDP spec panel and
   the category filters. Defined here because every page includes this stylesheet. */
.meter{display:inline-flex; gap:5px; align-items:center; vertical-align:middle}
.meter span{flex:none; width:26px; height:9px; border-radius:2px; background:var(--ink-600)}
.meter span[data-on]{background:var(--violet)}
.meter--lg span{width:38px; height:12px}
.price--now{font-size:2.5rem; font-weight:700; line-height:1; font-family:var(--font-display);
  letter-spacing:.01em}
.price--was{font-size:1.125rem; color:var(--text-tertiary); text-decoration:line-through}
.price--save{font-size:.8125rem; font-weight:700; text-transform:uppercase; letter-spacing:.06em;
  color:var(--text-on-accent); background:var(--magenta); border-radius:var(--r-chip); padding:5px 9px}
/* ---------- brands ---------- */
.brands{display:flex; flex-wrap:wrap; gap:var(--s-2)}
.brand{font-family:var(--font-display); font-weight:600; text-transform:uppercase;
  letter-spacing:.03em; font-size:1.125rem; color:var(--text-tertiary);
  border:1px solid var(--ink-700); border-radius:var(--r-control); padding:9px 14px;
  transition:all var(--t-micro) ease-out}
.brand:hover{text-decoration:none; color:var(--spark-cyan); border-color:var(--spark-cyan)}
/* ---------- reassurance ---------- */
.cols3{display:grid; gap:var(--s-6)}
@media(min-width:900px){
  .cols3{grid-template-columns:repeat(3,minmax(0,1fr)); gap:var(--s-7)}
  .cols3 > * + *{border-left:1px solid var(--ink-700); padding-left:var(--s-7)}
}
.cols3 h3{margin-bottom:var(--s-3)}

/* ---------- HTML Content module ------------------------------------------
   The client writes plain paragraphs and headings in the WYSIWYG; these give
   that output the same rhythm as designed sections without them needing to
   know any class names. */
.ofs-html > h2{font-size:clamp(1.875rem,1.2rem + 2.6vw,2.75rem); line-height:1; margin-bottom:var(--s-4)}
.ofs-html > h3{margin-bottom:var(--s-3)}
.ofs-html > p{color:var(--text-secondary); max-width:64ch}
.ofs-html > p + p{margin-top:var(--s-4)}
.ofs-html > ul{color:var(--text-secondary); max-width:64ch; padding-left:1.15em}
.ofs-html > ul li + li{margin-top:8px}
.ofs-html > ul li::marker{color:var(--violet)}
.ofs-html > *:last-child{margin-bottom:0}

/* ---------- the offers section carries the gold ----------
   The gold matches the Buy One Get One Free labels — the same `--fuse-gold`
   over `--text-on-accent` — so the offer reads as one idea from the band that
   explains it through to the label on every card below.

   Scoped to the two modules that make up that section, not to `.band--html`:
   three HTML Content bands and two Featured rails run on this page off two
   shared templates, and the other three are ordinary ink.

     .band--html-37     "Offers intro"
     .band--featured-28 "Buy one get one free"

   Keyed on the module id rather than the admin name so that renaming either
   module cannot silently drop the colour. The pair is adjacent in the layout,
   so the two fills meet and read as one block.

   Everything inside the intro was written for the ink ground and has to be
   inverted, not just backed with a new colour. Left alone on gold the
   supporting copy measures 1.1:1, a link 1.4:1 and a list marker 2.2:1 — the
   copy would be all but invisible. The client edits this content in the admin,
   so links and lists are covered even though today's copy has neither. */
.band--html-37,
.band--featured-28{background:var(--fuse-gold)}
.band--html-37{color:var(--text-on-accent)}
.band--html-37 .ofs-html > h2,
.band--html-37 .ofs-html > h3{color:var(--text-on-accent)}
/* The body copy takes the blue rather than the near-black, which keeps a step
   between the heading and the copy now that both are dark on one ground. */
.band--html-37 .ofs-html > p,
.band--html-37 .ofs-html > ul{color:var(--ink-800)}
.band--html-37 .ofs-html > ul li::marker{color:var(--ink-800)}
.band--html-37 a{color:var(--text-on-accent); text-decoration:underline;
  text-underline-offset:3px}
.band--html-37 a:hover{color:var(--ink-800)}
/* The focus ring is cyan everywhere else and all but vanishes on gold. */
.band--html-37 a:focus-visible{outline:2px solid var(--text-on-accent); outline-offset:2px}
/* The rail's scroll thumb is the only affordance saying there are more cards,
   and its track is transparent, so it is checked against the gold too. */
.band--featured-28 .rail{scrollbar-color:var(--ink-700) transparent}
.band--featured-28 .rail::-webkit-scrollbar-thumb{background:var(--ink-700)}

/* ---------------------------------------------------------------------------
   Category listing — deploy 014.

   These live in the shared sheet rather than a category stylesheet because the
   category controller is core: this install cannot OCMOD a catalog controller,
   so there is nowhere to call addStyle() from. A <link> in the body would work
   but costs an extra request on every category page, which is worse than the
   ~2.5KB these add to a file every page already loads.
   --------------------------------------------------------------------------- */

.cathead{padding-bottom:0}
.cathead .wrap{padding-block:40px}
.cathead h1{margin-bottom:var(--s-4)}
.lede{font-size:1.125rem; line-height:1.55; color:var(--text-secondary); max-width:56ch}
.cathead__meta{display:flex; flex-wrap:wrap; gap:var(--s-3) var(--s-5); margin-top:var(--s-5);
  font-size:.875rem; color:var(--text-tertiary)}
.cathead__meta span{display:inline-flex; align-items:center; gap:8px}
.cathead__meta i{color:var(--spark-cyan)}

/* Sidebar and grid. One column until there is room for both. */
.catlayout{display:grid; gap:var(--s-6)}
@media(min-width:900px){
  .catlayout--aside{grid-template-columns:262px minmax(0,1fr); gap:var(--s-7); align-items:start}
}

/* ---------- subcategory pills ---------- */
.pills{display:flex; flex-wrap:wrap; gap:var(--s-2); margin-top:var(--s-5); align-items:center}
.pills__label{font-size:.875rem; color:var(--text-tertiary); margin-right:var(--s-2)}
.pill{display:inline-flex; align-items:center; height:44px; padding:0 18px;
  border:1px solid var(--ink-700); border-radius:var(--r-pill); background:var(--ink-800);
  color:var(--text-secondary); font-size:.875rem; white-space:nowrap}
.pill:hover{text-decoration:none; border-color:var(--spark-cyan); color:var(--spark-cyan)}

/* ---------- toolbar ---------- */
.toolbar{display:flex; flex-wrap:wrap; gap:var(--s-3); align-items:center;
  padding-bottom:var(--s-5); border-bottom:1px solid var(--ink-700); margin-bottom:var(--s-5)}
.toolbar__count{font-size:.9375rem; color:var(--text-secondary); margin-right:auto}
.toolbar__count b{color:var(--text-primary)}
.selectwrap{position:relative; display:inline-flex; align-items:center}
.selectwrap select{appearance:none; -webkit-appearance:none; height:44px; padding:0 40px 0 14px;
  background:var(--ink-800); border:1px solid var(--ink-700); border-radius:var(--r-control);
  color:var(--text-primary); font:inherit; font-size:.9375rem; cursor:pointer}
.selectwrap select:focus-visible{outline:2px solid var(--spark-cyan); outline-offset:1px}
.selectwrap i{position:absolute; right:14px; pointer-events:none; font-size:.75rem;
  color:var(--text-tertiary)}

/* ---------- the product grid ---------- */
/* Three across at most. Four fitted, but a firework card carries an image, a
   brand, a name and a price, and at four they were 216px wide. */
.pgrid{display:grid; gap:var(--s-4); grid-template-columns:repeat(2,minmax(0,1fr))}
@media(min-width:768px){.pgrid{grid-template-columns:repeat(3,minmax(0,1fr))}}

/* ---------- pagination ---------- */
/* The concept used a load-more button with a progress bar. Paging has to stay
   crawlable, so the bar and the count are kept and the button becomes the page
   links. */
.pager{display:grid; place-items:center; gap:var(--s-4); margin-top:var(--s-7);
  padding-top:var(--s-6); border-top:1px solid var(--ink-700); text-align:center}
.pager__bar{width:100%; max-width:280px; height:4px; border-radius:2px;
  background:var(--ink-700); overflow:hidden}
.pager__bar span{display:block; height:100%; background:var(--spark-cyan);
  border-radius:2px}
.pager__r{font-size:.875rem; color:var(--text-tertiary)}
.pager .pagination{display:flex; flex-wrap:wrap; gap:6px; list-style:none; padding:0; margin:0}
.pager .pagination li a,
.pager .pagination li span{display:inline-flex; align-items:center; justify-content:center;
  min-width:44px; height:44px; padding:0 12px; border:1px solid var(--ink-700);
  border-radius:var(--r-control); background:var(--ink-800); color:var(--text-secondary);
  font-size:.9375rem}
.pager .pagination li a:hover{text-decoration:none; border-color:var(--spark-cyan);
  color:var(--spark-cyan)}
.pager .pagination li.active span{background:var(--spark-cyan); border-color:var(--spark-cyan);
  color:var(--text-on-accent); font-weight:700}

/* ---------- empty ---------- */
.emptycat{padding:var(--s-7) 0}

/* ---------- refine panel (the category module in the left column) ---------- */
/* The two panels are separated with a flex gap, not `.filters + .filters`: the
   filter module emits a <script> between them, so they are not adjacent
   siblings. A script is display:none and never becomes a flex item, so the gap
   lands where it should. */
.col-left{min-width:0; display:flex; flex-direction:column; gap:var(--s-5)}

/* The whole column is pinned, and moves as one piece.

   Pinning only the filter left the category tree sliding underneath it, so it
   is all or nothing. All of it needs the column to fit the window: open, the
   browse panel made the column 1286px against roughly 750px of usable height.
   Collapsing the browse groups by default brings it to about 700px, which fits.

   The header measures 135px while scrolled, so the offset is 143px.

   No height gate and no max-height. An earlier version only pinned above 930px
   of window so the column would always fit whole — but a normal browser window
   is 750 to 800px tall, so it never pinned for anyone. Making the behaviour
   correct in a case nobody has is worse than making it useful in the case
   everybody has.

   The column is 776px. On a window shorter than about 920px the last of the
   browse panel therefore sits below the fold while pinned. The filter — the part
   that matters here, and the part the concept pins — is 527px and stays fully
   visible down to a 670px window. Browse is three collapsed rows, so what is
   lost is at most a row or two, and it is all still there at the top of the
   page before pinning engages. */
@media(min-width:900px){
  .col-left{position:sticky; top:143px}
}

/* Which branch you are inside, since it is no longer opened to tell you. */
.fgroup--here > summary{color:var(--spark-cyan)}
.fgroup--here > summary::after{content:""; width:6px; height:6px; border-radius:50%;
  background:var(--spark-cyan); flex:none; margin-left:-2px; order:-1; margin-right:8px}
.filters{border:1px solid var(--ink-700); border-radius:var(--r-card); background:var(--ink-800)}
.filters__head{display:flex; align-items:center; justify-content:space-between;
  padding:var(--s-4) var(--s-5); border-bottom:1px solid var(--ink-700)}
.filters__head h2{font-size:1.125rem}
.fgroup{border-bottom:1px solid var(--ink-700)}
.fgroup:last-child{border-bottom:0}
.fgroup summary{list-style:none; cursor:pointer; padding:var(--s-4) var(--s-5);
  display:flex; align-items:center; justify-content:space-between; gap:var(--s-3);
  font-family:var(--font-display); font-weight:700; text-transform:uppercase;
  font-size:1.0625rem; letter-spacing:.02em; color:var(--text-primary)}
.fgroup summary::-webkit-details-marker{display:none}
.fgroup summary:hover{color:var(--spark-cyan)}
.fgroup summary i{font-size:.75rem; color:var(--text-tertiary);
  transition:transform var(--t-micro) ease-out}
.fgroup[open] summary i{transform:rotate(180deg)}
.fgroup__body{padding:0 var(--s-5) var(--s-4)}
.fgroup--flat{padding:var(--s-3) var(--s-5)}
.fnav{display:block; padding:7px 0; font-size:.9375rem; color:var(--text-secondary);
  line-height:1.35}
.fnav:hover{color:var(--spark-cyan); text-decoration:none}
/* Both states spelled out. The element also carries .fnav, whose :hover rule
   already outranks Bootstrap, but check-link-hover.py reads a class on its own
   and cannot see that — and being explicit is cheaper than loosening the guard. */
.fnav--on,
.fnav--on:hover{color:var(--spark-cyan); font-weight:700}

/* ---------- breadcrumbs ---------- */
/* Breadcrumb. A full-width band with its own ground and a rule under it, as in
   the concepts — not an inline row inside the first band. The separator is a
   ::before on each li after the first, so it is never read out as content, and
   the current page is a span rather than a link to itself. */
.crumb{border-bottom:1px solid var(--ink-700); background:var(--ink-900)}
.crumb ol{list-style:none; margin:0; padding:14px 0; display:flex; flex-wrap:wrap;
  gap:6px 10px; font-size:.8125rem; color:var(--text-tertiary)}
.crumb li{display:flex; align-items:center; gap:10px}
.crumb li + li::before{content:"/"; color:var(--ink-600)}
.crumb a{color:var(--text-tertiary)}
.crumb a:hover{color:var(--spark-cyan)}
.crumb [aria-current]{color:var(--text-secondary)}

/* ---------- filter options ---------- */
.filters + .filters{margin-top:var(--s-5)}
.filters__clear{font-size:.8125rem; font-weight:700}
.fopt{display:flex; align-items:center; gap:11px; padding:7px 0; min-height:36px;
  cursor:pointer; font-size:.9375rem; color:var(--text-secondary); position:relative}
.fopt:hover{color:var(--text-primary)}
.fopt input{position:absolute; opacity:0; width:0; height:0}
.fbox{width:19px; height:19px; border:1.5px solid var(--ink-600); border-radius:3px; flex:none;
  display:grid; place-items:center; font-size:.625rem; color:var(--text-on-accent);
  transition:background var(--t-micro) ease-out, border-color var(--t-micro) ease-out}
.fopt input:checked + .fbox{background:var(--spark-cyan); border-color:var(--spark-cyan)}
.fopt input:checked + .fbox::before{content:"\f00c"; font-family:"Font Awesome 6 Free"; font-weight:900}
.fopt input:focus-visible + .fbox{outline:2px solid var(--spark-cyan); outline-offset:2px}
.fopt input:checked ~ .fopt__t{color:var(--text-primary); font-weight:700}
.fopt__t{min-width:0}

/* ---------- noise meter, in the filter panel and on a card ---------- */
.meter{display:inline-flex; gap:2px; flex:none}
.meter span{width:13px; height:8px; border-radius:1px; background:var(--ink-700); flex:none}
.meter span[data-on]{background:var(--violet)}
.fopt__t{margin-right:auto}
.fopt__n{color:var(--text-tertiary); font-size:.8125rem}
.fopt .meter + .fopt__n{margin-left:8px}

/* ---------- brand line above a product name ---------- */
.card__brand{font-size:.75rem; text-transform:uppercase; letter-spacing:.06em;
  color:var(--text-tertiary); margin-bottom:4px}

/* ---------------------------------------------------------------------------
   Product page — deploy 023. In the shared sheet for the same reason as the
   category rules: the product controller is core and cannot call addStyle here.
   --------------------------------------------------------------------------- */

.pdp{display:grid; gap:var(--s-6)}
@media(min-width:1024px){
  .pdp{grid-template-columns:minmax(0,1.05fr) minmax(0,.95fr); gap:var(--s-8); align-items:start}
}
.gal__main{background:var(--paper); border:1px solid var(--ink-700);
  border-radius:var(--r-tile); overflow:hidden; position:relative}
.gal__main img{width:100%; aspect-ratio:1; object-fit:contain; padding:var(--s-5)}
.gal__flag{position:absolute; top:14px; left:14px; z-index:2; background:var(--magenta);
  color:var(--text-on-accent); border-radius:var(--r-chip); font-size:.75rem; font-weight:700;
  text-transform:uppercase; letter-spacing:.06em; padding:6px 10px}
.gal__thumbs{display:flex; gap:10px; margin-top:12px; flex-wrap:wrap}
.gal__thumb{width:74px; height:74px; border:1px solid var(--ink-700); border-radius:var(--r-control);
  background:var(--paper); display:grid; place-items:center; cursor:pointer; overflow:hidden; padding:0}
.gal__thumb[aria-current="true"]{border-color:var(--spark-cyan)}
.gal__thumb img{width:100%; height:100%; object-fit:contain; padding:6px}

.buy__brand{display:flex; align-items:center; gap:12px; margin-bottom:var(--s-4)}
.buy__brand img{width:44px; height:44px; object-fit:contain; background:var(--paper);
  border-radius:var(--r-control); padding:4px; flex:none}
.buy__brand a{font-size:.875rem; text-transform:uppercase; letter-spacing:.06em; font-weight:700}
.pdp h1{font-family:var(--font-display); font-weight:700; text-transform:uppercase;
  font-size:clamp(1.875rem,1.2rem + 2.2vw,2.75rem); line-height:1; letter-spacing:.01em;
  margin-bottom:var(--s-4)}
.buy__code{font-size:.8125rem; color:var(--text-tertiary); margin-bottom:var(--s-5)}
.buy__code span + span{margin-left:var(--s-4)}

.pricebox{border:1px solid var(--ink-700); border-radius:var(--r-card);
  background:var(--ink-800); padding:var(--s-5); margin-bottom:var(--s-5)}
.pricerow{display:flex; align-items:baseline; gap:var(--s-3); flex-wrap:wrap}
.price--now{font-size:2.5rem; font-weight:700; line-height:1; font-family:var(--font-display);
  color:var(--text-primary)}
.price--save{font-size:.8125rem; font-weight:700; text-transform:uppercase; letter-spacing:.06em;
  color:var(--text-on-accent); background:var(--fuse-gold); border-radius:var(--r-chip);
  padding:5px 9px}
.tier{margin-top:var(--s-4); padding-top:var(--s-4); border-top:1px solid var(--ink-700);
  display:flex; gap:var(--s-3); align-items:flex-start; font-size:.9375rem}
.tier i{color:var(--fuse-gold); margin-top:3px}
.tier b{color:var(--fuse-gold)}
.tier ul{margin:0; padding-left:1.1em}

.stockline{display:flex; align-items:center; gap:9px; font-weight:700; font-size:.9375rem;
  margin-bottom:var(--s-5)}
.qtyrow{display:flex; gap:var(--s-3); align-items:stretch; margin-bottom:var(--s-3)}
.qtyrow .btn{flex:1}
.qty{display:inline-flex; align-items:center; border:1px solid var(--ink-700);
  border-radius:var(--r-control); background:var(--ink-800); flex:none}
.qty button{width:46px; height:52px; flex:none; background:none; border:0; padding:0;
  color:var(--text-secondary); cursor:pointer}
.qty button:hover{background:var(--ink-800); color:var(--spark-cyan)}
.qty button:focus-visible{outline:2px solid var(--spark-cyan); outline-offset:-2px}
.qty input{width:50px; height:52px; flex:none; background:none; border:0; color:var(--text-primary);
  font:inherit; font-weight:700; text-align:center; -moz-appearance:textfield}
.qty input:focus-visible{outline:2px solid var(--spark-cyan); outline-offset:-2px}
.qty input::-webkit-outer-spin-button,
.qty input::-webkit-inner-spin-button{-webkit-appearance:none; margin:0}

.getit{border:1px solid var(--ink-700); border-radius:var(--r-card); overflow:hidden;
  margin-top:var(--s-5)}
.getit__row{display:flex; gap:var(--s-4); padding:var(--s-4) var(--s-5); align-items:flex-start}
.getit__row + .getit__row{border-top:1px solid var(--ink-700)}
.getit__row i{color:var(--spark-cyan); width:20px; text-align:center; margin-top:3px; flex:none}
.getit__t,.getit__s{display:block}
.getit__t{font-weight:700; font-size:.9375rem}
.getit__s{font-size:.8125rem; color:var(--text-secondary); margin-top:2px}
.getit__s strong{color:var(--fuse-gold)}

.specgrid{display:grid; gap:1px; background:var(--ink-700); border:1px solid var(--ink-700);
  border-radius:var(--r-card); overflow:hidden}
@media(min-width:640px){.specgrid{grid-template-columns:repeat(2,1fr)}}
/* One column per spec, rather than a fixed four.
   auto-fit gives as many equal columns as will fit and then collapses the
   tracks it does not need, so a product with five specs gets five columns and
   one with six gets six — no count has to be passed in from the controller.

   The floor is 185px. The widest label in the catalogue, "Classification", needs
   198px to sit on one line inside the cell's icon and padding, and the container
   never exceeds 1224px, so a 198px floor would cap the grid at six columns only
   above 1400px. 185px brings six columns down to a 1280px screen, and
   overflow-wrap on the label stops that one long word pushing the track open. */
@media(min-width:1024px){.specgrid{grid-template-columns:repeat(auto-fit,minmax(185px,1fr))}}
.spec{background:var(--ink-900); padding:var(--s-5); display:flex; gap:var(--s-4);
  align-items:flex-start}
.spec i{color:var(--spark-cyan); font-size:1.25rem; width:26px; text-align:center; flex:none;
  margin-top:2px}
.spec__k,.spec__v,.spec__s{display:block}
.spec__k{font-size:.75rem; text-transform:uppercase; letter-spacing:.06em; color:var(--text-tertiary);
  overflow-wrap:anywhere}
.spec__v{font-weight:700; font-size:1.125rem; margin-top:4px; line-height:1.2}
.spec__s{font-size:.8125rem; color:var(--text-secondary); margin-top:4px; font-weight:400}

.warn{display:flex; gap:var(--s-4); align-items:flex-start; border:1px solid var(--ink-700);
  border-radius:var(--r-card); background:var(--ink-800); padding:var(--s-5);
  /* it was sitting flush against whatever followed it */
  margin-bottom:var(--s-3)}
.warn i{color:var(--fuse-gold); margin-top:3px}
/* The paragraph was inheriting the 10px bottom margin every p carries, which
   padded the box out below the text and pushed the icon off centre. */
.warn p{margin:0; font-size:.9375rem; color:var(--text-secondary)}
.warn p + p{margin-top:var(--s-3)}

.split2{display:grid; gap:var(--s-7)}
@media(min-width:900px){.split2{grid-template-columns:minmax(0,1fr) minmax(0,1fr); gap:var(--s-8)}}

.avail{display:grid; gap:1px; background:var(--ink-700); border:1px solid var(--ink-700);
  border-radius:var(--r-card); overflow:hidden}
@media(min-width:768px){.avail{grid-template-columns:repeat(3,1fr)}}
.av{background:var(--ink-900); padding:var(--s-5); display:flex; gap:var(--s-4); align-items:flex-start}
.av i{font-size:1.125rem; width:22px; text-align:center; flex:none; margin-top:2px}
.av--yes i{color:var(--in-stock)}
.av--no i{color:var(--out-of-stock)}
.av__t,.av__s{display:block}
.av__t{font-weight:700}
.av__s{font-size:.8125rem; color:var(--text-tertiary); margin-top:3px}

/* Click to load. The embed is not requested until the button is pressed, so no
   third-party cookie is set and nothing is fetched from YouTube on page load. */
.vidwrap{position:relative; border:1px solid var(--ink-700); border-radius:var(--r-tile);
  overflow:hidden; background:var(--ink-800); aspect-ratio:16/9; width:100%;
  display:grid; place-items:center; text-align:center; padding:var(--s-6);
  color:var(--text-primary); font:inherit; cursor:pointer}
.vidwrap:hover{border-color:var(--spark-cyan)}
.vidwrap i{font-size:3rem; color:var(--spark-cyan); margin-bottom:var(--s-3)}
.vidwrap p{color:var(--text-tertiary); font-size:.875rem; max-width:42ch; margin-top:var(--s-2)}
.vidwrap iframe{position:absolute; inset:0; width:100%; height:100%; border:0}

/* ---------------------------------------------------------------------------
   Basket — deploy 024.
   --------------------------------------------------------------------------- */

.cartgrid{display:grid; gap:var(--s-6)}
@media(min-width:1024px){
  .cartgrid{grid-template-columns:minmax(0,1fr) 380px; gap:var(--s-7); align-items:start}
}
.lines{border:1px solid var(--ink-700); border-radius:var(--r-card); overflow:hidden}
.line{display:grid; grid-template-columns:88px minmax(0,1fr); gap:var(--s-4);
  padding:var(--s-5); background:var(--ink-900)}
@media(min-width:640px){.line{grid-template-columns:104px minmax(0,1fr) auto; gap:var(--s-5)}}
.line + .line{border-top:1px solid var(--ink-700)}
.line__img{background:var(--paper); border-radius:var(--r-control); overflow:hidden; height:88px}
@media(min-width:640px){.line__img{height:104px}}
.line__img img{width:100%; height:100%; object-fit:contain; padding:8px}
.line__name{font-family:var(--font-body); text-transform:none; letter-spacing:normal;
  font-weight:700; font-size:1.0625rem; line-height:1.3; margin:0 0 8px; color:var(--text-primary)}
.line__name a{color:inherit}
.line__name a:hover{color:var(--spark-cyan); text-decoration:none}
.line__unit{font-size:.8125rem; color:var(--text-secondary)}
.line__opt{font-size:.8125rem; color:var(--text-tertiary); margin-top:4px}
.line__free{display:inline-block; margin-top:6px; font-size:.75rem; font-weight:700;
  text-transform:uppercase; letter-spacing:.06em; color:var(--text-on-accent);
  background:var(--fuse-gold); border-radius:var(--r-chip); padding:4px 9px}
.line__right{display:flex; align-items:center; justify-content:space-between; gap:var(--s-4);
  margin-top:var(--s-4)}
@media(min-width:640px){
  .line__right{flex-direction:column; align-items:flex-end; justify-content:flex-start; margin-top:0}
}
.line__total{font-weight:700; font-size:1.25rem; white-space:nowrap}
.line__rm{background:none; border:0; color:var(--text-tertiary); cursor:pointer;
  font-size:.8125rem; padding:8px 0; min-height:44px}
.line__rm:hover{color:var(--out-of-stock); text-decoration:underline}
.qty--sm button{width:40px; height:44px}
.qty--sm input{width:44px; height:44px}

/* The order summary's box, shared with the basket modules (deploy 097) so the
   coupon, voucher, reward and shipping blocks are the same object as the
   summary beside them rather than a lookalike that drifts. .summary keeps the
   sticky behaviour to itself. */
.summary,.cartmod{border:1px solid var(--ink-700); border-radius:var(--r-card);
  background:var(--ink-800); overflow:hidden}
@media(min-width:1024px){.summary{position:sticky; top:143px}}
.summary__head,.cartmod__head{padding:var(--s-4) var(--s-5); border-bottom:1px solid var(--ink-700)}
.summary__head h2,.cartmod__head h2{font-size:1.25rem}
.summary__body,.cartmod__body{padding:var(--s-5)}
.totrow{display:flex; justify-content:space-between; gap:var(--s-4); font-size:.9375rem;
  padding:7px 0; color:var(--text-secondary)}
.totrow--grand{border-top:1px solid var(--ink-700); margin-top:var(--s-3); padding-top:var(--s-4);
  font-size:1.25rem; font-weight:700; color:var(--text-primary)}
.summary .btn{margin-top:var(--s-4)}
.summary__note{font-size:.8125rem; color:var(--text-tertiary); margin-top:var(--s-4)}

.emptycart{border:1px solid var(--ink-700); border-radius:var(--r-card);
  background:var(--ink-800); padding:var(--s-8) var(--s-6); text-align:center}
.emptycart i{font-size:2.5rem; color:var(--ink-600); margin-bottom:var(--s-4)}
.emptycart h2{margin-bottom:var(--s-3)}

/* Coupon, voucher, reward and shipping-estimate blocks on the basket. Still
   stock Bootstrap inside, so they are contained rather than restyled: no
   escaping the column, and inputs that behave on a phone. */
.cartmods{margin-top:var(--s-5); max-width:100%; overflow-x:hidden}
.cartmods .panel{border:1px solid var(--ink-700); border-radius:var(--r-card);
  background:var(--ink-800); margin-bottom:var(--s-4)}
.cartmods .panel-heading{padding:var(--s-4) var(--s-5); border-bottom:1px solid var(--ink-700)}
.cartmods .panel-title{font-size:1rem; margin:0}
.cartmods .panel-title a{color:var(--text-primary); font-weight:700}
.cartmods .panel-body{padding:var(--s-5)}
.cartmods .form-control{width:100%; max-width:100%; height:46px; background:var(--ink-900);
  border:1px solid var(--ink-700); border-radius:var(--r-control); color:var(--text-primary);
  font:inherit; padding:0 12px}
.cartmods select.form-control{height:46px}
.cartmods .control-label{display:block; font-size:.875rem; color:var(--text-secondary);
  margin-bottom:6px; width:auto; text-align:left; padding:0}
.cartmods .form-group{margin-bottom:var(--s-4)}
.cartmods .col-sm-2,.cartmods .col-sm-10,.cartmods .col-sm-4{width:100%; float:none; padding:0}
.cartmods .btn{display:inline-flex; align-items:center; justify-content:center; height:46px;
  padding:0 18px; border-radius:var(--r-control); background:var(--fuse-gold);
  color:var(--text-on-accent); border:0; font-weight:700}
.cartmods .btn:hover{background:#FFD34A; color:var(--text-on-accent)}

/* ---------------------------------------------------------------------------
   Checkout — deploy 025.

   Three steps wrapping OpenCart's six panels. The panels keep their ids and
   classes because the orchestration script targets them, so the Bootstrap
   markup inside is restyled here rather than rewritten.
   --------------------------------------------------------------------------- */

.costep{border:1px solid var(--ink-700); border-radius:var(--r-card); overflow:hidden}
.costep + .costep{margin-top:var(--s-4)}
.costep__head{display:flex; align-items:center; gap:var(--s-4); padding:var(--s-5);
  background:var(--ink-800)}
.costep__n{width:34px; height:34px; border-radius:var(--r-pill); flex:none; display:grid;
  place-items:center; font-family:var(--font-display); font-weight:700; font-size:1.0625rem;
  border:2px solid var(--ink-600); color:var(--text-tertiary)}
/* Lit when any panel inside this step is open. :has() does the work the concept
   did with a data-state attribute, so no script is needed to keep it in step. */
.costep:has(.panel-collapse.in) .costep__n,
.costep:has(.panel-collapse.collapsing) .costep__n{border-color:var(--spark-cyan);
  color:var(--spark-cyan)}
.costep__t,.costep__v{display:block}
.costep__t{font-family:var(--font-display); font-weight:700; text-transform:uppercase;
  font-size:1.375rem; letter-spacing:.02em; line-height:1; color:var(--text-primary)}
.costep__v{font-size:.875rem; color:var(--text-secondary); margin-top:5px}
.costep__body{border-top:1px solid var(--ink-700)}

/* ---- OpenCart's panels, inside a step ---- */
.costep .panel{border:0; margin:0; background:none; box-shadow:none}
.costep .panel + .panel{border-top:1px solid var(--ink-700)}
.costep .panel-heading{padding:0; background:none; border:0}
.costep .panel-title{margin:0; font-size:1rem}
.costep .panel-title,
.costep .panel-title a{display:block; padding:var(--s-4) var(--s-5); font-weight:700;
  color:var(--text-primary)}
.costep .panel-title a:hover{color:var(--spark-cyan); text-decoration:none}
.costep .panel-title a .fa-caret-down{float:right; color:var(--text-tertiary)}
.costep .panel-body{padding:0 var(--s-5) var(--s-5)}
.costep .panel-body:empty{padding:0}

/* ---- the Bootstrap forms the sub-templates render ---- */
.costep .form-group{margin-bottom:var(--s-4)}
.costep .control-label,
.costep legend{display:block; width:auto; float:none; text-align:left; padding:0;
  font-size:.875rem; color:var(--text-secondary); margin-bottom:6px; border:0}
.costep .col-sm-2,.costep .col-sm-3,.costep .col-sm-4,
.costep .col-sm-9,.costep .col-sm-10{width:100%; float:none; padding:0}
.costep .form-control{width:100%; max-width:100%; height:48px; background:var(--ink-900);
  border:1px solid var(--ink-700); border-radius:var(--r-control); color:var(--text-primary);
  font:inherit; padding:0 14px}
.costep textarea.form-control{height:auto; padding:12px 14px}
.costep .form-control:focus{outline:2px solid var(--spark-cyan); outline-offset:1px}
.costep .radio,.costep .checkbox{margin:0 0 10px}
.costep .radio label,.costep .checkbox label{display:flex; gap:10px; align-items:flex-start;
  font-size:.9375rem; cursor:pointer}
.costep input[type=radio],.costep input[type=checkbox]{width:20px; height:20px; flex:none;
  accent-color:var(--spark-cyan); margin:0}
.costep .btn{display:inline-flex; align-items:center; justify-content:center; height:48px;
  padding:0 22px; border:0; border-radius:var(--r-control); font-weight:700;
  background:var(--fuse-gold); color:var(--text-on-accent)}
.costep .btn:hover{background:#FFD34A; color:var(--text-on-accent)}
.costep .btn-default{background:transparent; color:var(--spark-cyan);
  border:1px solid var(--spark-cyan)}
.costep .btn-default:hover{background:rgba(60,180,252,.1); color:var(--spark-cyan)}
.costep .buttons{display:flex; gap:var(--s-3); flex-wrap:wrap; margin-top:var(--s-5)}
.costep .pull-right,.costep .pull-left{float:none}
.costep .text-danger{color:var(--fuse-gold); font-size:.8125rem; margin-top:6px; display:block}
.costep .alert{border:1px solid var(--ink-700); background:var(--ink-800);
  border-radius:var(--r-card); padding:var(--s-4) var(--s-5); margin-bottom:var(--s-4)}
.costep .alert-danger{border-color:var(--fuse-gold)}
.costep table{width:100%; border-collapse:collapse; font-size:.9375rem}
.costep table td,.costep table th{padding:10px 12px; border-bottom:1px solid var(--ink-700);
  text-align:left}
.costep table tfoot td{font-weight:700; color:var(--text-primary)}
.costep .table-responsive{overflow-x:auto}

/* Bootstrap pulls .radio/.checkbox inputs out of the flow with a negative
   margin, which collided with the flex label here and clipped the text. */
.costep .radio input[type=radio],
.costep .checkbox input[type=checkbox]{position:static; margin:0}
.costep .radio,.costep .checkbox{padding-left:0}

/* The sub-templates use h1/h2/h3 for panel-level headings, which the global
   display sizing blew up to page-title scale inside a panel. */
.costep h1,.costep h2,.costep h3,.costep h4,.costep legend{font-family:var(--font-body);
  text-transform:none; letter-spacing:normal; font-size:1rem; font-weight:700;
  line-height:1.3; color:var(--text-primary); margin:0 0 var(--s-3)}
.costep .panel-body > p{color:var(--text-secondary); font-size:.9375rem;
  margin-bottom:var(--s-4)}
.costep .well{border:1px solid var(--ink-700); border-radius:var(--r-card);
  background:var(--ink-900); padding:var(--s-5); margin-bottom:var(--s-4)}

/* ---------------------------------------------------------------------------
   Order confirmation — deploy 027.
   --------------------------------------------------------------------------- */

.done{text-align:center; padding-bottom:var(--s-6)}
.done__tick{display:grid; place-items:center; width:64px; height:64px; margin:0 auto var(--s-5);
  border-radius:var(--r-pill); background:var(--in-stock); color:var(--text-on-accent);
  font-size:1.75rem}
.done h1{margin-bottom:var(--s-4)}
.done .lede{margin:0 auto; text-align:center}

.panel-facts{display:grid; gap:1px; background:var(--ink-700); border:1px solid var(--ink-700);
  border-radius:var(--r-card); overflow:hidden; margin-top:var(--s-6)}
@media(min-width:640px){.panel-facts{grid-template-columns:repeat(3,1fr)}}
.fact{background:var(--ink-900); padding:var(--s-5)}
.fact__k,.fact__v{display:block}
.fact__k{font-size:.75rem; text-transform:uppercase; letter-spacing:.06em;
  color:var(--text-tertiary)}
.fact__v{font-weight:700; font-size:1.125rem; margin-top:5px; word-break:break-word}

/* The steps. A line runs down the markers, and the one already done is filled. */
.tl{list-style:none; margin:0; padding:0}
.tl__s{position:relative; padding:0 0 var(--s-6) var(--s-7)}
.tl__s::before{content:""; position:absolute; left:11px; top:22px; bottom:0; width:2px;
  background:var(--ink-700)}
.tl__s:last-child{padding-bottom:0}
.tl__s:last-child::before{display:none}
.tl__s::after{content:""; position:absolute; left:4px; top:5px; width:16px; height:16px;
  border-radius:var(--r-pill); border:2px solid var(--ink-600); background:var(--ink-900)}
.tl__s--done::after{background:var(--in-stock); border-color:var(--in-stock)}
.tl__t,.tl__d{display:block}
.tl__t{font-weight:700}
.tl__d{font-size:.875rem; color:var(--text-secondary); margin-top:4px}

.nextsteps{display:grid; gap:1px; background:var(--ink-700); border:1px solid var(--ink-700);
  border-radius:var(--r-card); overflow:hidden}
.nextstep{background:var(--ink-900); padding:var(--s-5); display:flex; gap:var(--s-4);
  align-items:flex-start}
.nextstep i{color:var(--spark-cyan); font-size:1.125rem; width:24px; text-align:center;
  flex:none; margin-top:3px}

/* The confirmation lists what was ordered without images, so the basket's
   three-column line collapses to two. */
.line--plain{grid-template-columns:minmax(0,1fr) auto}
@media(min-width:640px){.line--plain{grid-template-columns:minmax(0,1fr) auto}}

/* ---------------------------------------------------------------------------
   Account — deploy 028.
   --------------------------------------------------------------------------- */

.acct{display:grid; gap:var(--s-6)}
@media(min-width:900px){
  .acct--nav{grid-template-columns:262px minmax(0,1fr); gap:var(--s-7); align-items:start}
}
.anav{border:1px solid var(--ink-700); border-radius:var(--r-card); overflow:hidden;
  background:var(--ink-800)}
.anav__who{padding:var(--s-5); border-bottom:1px solid var(--ink-700)}
.anav__n{font-family:var(--font-display); font-weight:700; text-transform:uppercase;
  font-size:1.125rem; letter-spacing:.02em; color:var(--text-primary)}
.anav__e{font-size:.8125rem; color:var(--text-tertiary); margin-top:5px; word-break:break-all}
/* The nearest shop's logo on an area page, between "Nearest shop" and the
   town. A block so the link is exactly the size of the picture and no wider,
   and a focus ring of its own because an image link has no underline to show
   that it has been reached by keyboard. */
.anav__logo{display:block; width:max-content; margin-top:var(--s-3); border-radius:var(--r-chip)}
.anav__logo img{display:block; height:auto}
.anav__logo:hover img{opacity:.85}
.anav__logo:focus-visible{outline:2px solid var(--spark-cyan); outline-offset:4px}
.anav__logo + .anav__e{margin-top:var(--s-3)}
.anav__links a{display:flex; align-items:center; gap:13px; padding:14px var(--s-5);
  color:var(--text-secondary); font-size:.9375rem}
.anav__links a + a{border-top:1px solid var(--ink-700)}
.anav__links a:hover{text-decoration:none; color:var(--text-primary); background:var(--ink-600)}
.anav__links a i{width:20px; text-align:center; color:var(--text-tertiary); flex:none}
.anav__links a[aria-current]{color:var(--spark-cyan); background:var(--ink-900); font-weight:700}
.anav__links a[aria-current] i{color:var(--spark-cyan)}
.anav__out,.anav__out:hover{color:var(--text-tertiary)}

.tiles{display:grid; gap:var(--s-4); grid-template-columns:repeat(2,minmax(0,1fr))}
@media(min-width:900px){.tiles{grid-template-columns:repeat(3,minmax(0,1fr))}}
.atile{display:block; border:1px solid var(--ink-700); border-radius:var(--r-card);
  background:var(--ink-800); padding:var(--s-5); color:var(--text-primary)}
.atile:hover{text-decoration:none; border-color:var(--spark-cyan); color:var(--text-primary)}
.atile i{display:block; font-size:1.25rem; color:var(--spark-cyan); margin-bottom:var(--s-3)}
.atile__t,.atile__s{display:block}
.atile__t{font-weight:700}
.atile__s{font-size:.8125rem; color:var(--text-secondary); margin-top:5px}

.orders{display:grid; gap:var(--s-4)}
.order{border:1px solid var(--ink-700); border-radius:var(--r-card); overflow:hidden;
  background:var(--ink-900)}
.order__head{display:flex; flex-wrap:wrap; gap:var(--s-3) var(--s-5); align-items:center;
  padding:var(--s-4) var(--s-5); background:var(--ink-800);
  border-bottom:1px solid var(--ink-700)}
.order__no{font-family:var(--font-display); font-weight:700; text-transform:uppercase;
  font-size:1.125rem; letter-spacing:.02em}
.order__meta{font-size:.8125rem; color:var(--text-tertiary)}
.order__meta b{color:var(--text-secondary); font-weight:700}
.order__stat{margin-left:auto}
.order__body{display:grid; gap:var(--s-4); padding:var(--s-5)}
.order__acts{display:flex; flex-wrap:wrap; gap:var(--s-3); align-items:center}
.order__tot{font-family:var(--font-display); font-weight:700; font-size:1.5rem;
  margin-right:auto}

.cols2{display:grid; gap:var(--s-4)}
@media(min-width:640px){.cols2{grid-template-columns:repeat(2,minmax(0,1fr))}}
.addrbox{border:1px solid var(--ink-700); border-radius:var(--r-card); padding:var(--s-5);
  background:var(--ink-900)}
.addrbox address{font-style:normal; margin-top:var(--s-3); color:var(--text-secondary);
  line-height:1.6}

.empty{text-align:center; padding:var(--s-8) var(--s-5); border:1px dashed var(--ink-600);
  border-radius:var(--r-card)}
.empty i{font-size:2.5rem; color:var(--ink-600); margin-bottom:var(--s-4)}
.empty h2{margin-bottom:var(--s-3)}
.empty p{color:var(--text-secondary); max-width:44ch; margin:0 auto var(--s-5)}

/* ---------------------------------------------------------------------------
   Contact — deploy 029.
   --------------------------------------------------------------------------- */

.fastrow{display:grid; gap:var(--s-4); margin-top:var(--s-6)}
@media(min-width:768px){.fastrow{grid-template-columns:repeat(3,minmax(0,1fr))}}
.fast{display:flex; gap:var(--s-4); align-items:flex-start; padding:var(--s-5);
  border:1px solid var(--ink-700); border-radius:var(--r-card); background:var(--ink-800);
  color:var(--text-primary)}
.fast:hover{text-decoration:none; border-color:var(--spark-cyan); color:var(--text-primary)}
.fast--static,.fast--static:hover{border-color:var(--ink-700); cursor:default}
.fast i{color:var(--spark-cyan); font-size:1.125rem; width:24px; text-align:center; flex:none;
  margin-top:3px}
.fast__t,.fast__s{display:block}
.fast__t{font-weight:700; font-size:1.0625rem; line-height:1.3}
.fast__s{font-size:.8125rem; color:var(--text-secondary); margin-top:5px}

/* ---- form fields ---- */
.fieldrow{display:grid; gap:var(--s-4)}
.field__l{display:block; font-size:.875rem; color:var(--text-secondary); margin-bottom:6px}
.field__i{width:100%; background:var(--ink-900); border:1px solid var(--ink-700);
  border-radius:var(--r-control); color:var(--text-primary); font:inherit; font-size:.9375rem;
  padding:13px 14px}
textarea.field__i{line-height:1.55; resize:vertical}
.field__i:focus{outline:2px solid var(--spark-cyan); outline-offset:1px}
.field__e{display:block; font-size:.8125rem; color:var(--fuse-gold); margin-top:6px}

/* ---- questions ---- */
.faq{border:1px solid var(--ink-700); border-radius:var(--r-card); overflow:hidden;
  background:var(--ink-800)}
.faq__i + .faq__i{border-top:1px solid var(--ink-700)}
.faq__i summary{list-style:none; cursor:pointer; padding:var(--s-4) var(--s-5);
  display:flex; align-items:center; justify-content:space-between; gap:var(--s-3);
  font-weight:700; font-size:.9375rem; color:var(--text-primary)}
.faq__i summary::-webkit-details-marker{display:none}
.faq__i summary:hover{color:var(--spark-cyan)}
.faq__i summary i{font-size:.75rem; color:var(--text-tertiary);
  transition:transform var(--t-micro) ease-out}
.faq__i[open] summary i{transform:rotate(180deg)}
.faq__a{padding:0 var(--s-5) var(--s-5); color:var(--text-secondary); font-size:.9375rem;
  line-height:1.6}
.faq__a p + p{margin-top:var(--s-3)}
.fast__s--lines{white-space:pre-line}

/* ---------------------------------------------------------------------------
   Shops — deploy 031.
   --------------------------------------------------------------------------- */

.lochero{display:grid; gap:var(--s-6)}
@media(min-width:900px){
  .lochero{grid-template-columns:minmax(0,1.15fr) minmax(0,.85fr); gap:var(--s-8); align-items:start}
}
.locaddr{font-style:normal; margin-top:var(--s-5); color:var(--text-secondary); line-height:1.7}
.locacts{display:flex; flex-wrap:wrap; gap:var(--s-3); margin-top:var(--s-5)}
.locacts .btn i{margin-right:8px}

.hours{border:1px solid var(--ink-700); border-radius:var(--r-card); background:var(--ink-800);
  overflow:hidden}
.hours__h{padding:var(--s-4) var(--s-5); border-bottom:1px solid var(--ink-700)}
.hours__h h2{font-size:1.125rem}
.hours__b{padding:var(--s-4) var(--s-5)}
.hours__n{padding:0 var(--s-5) var(--s-5); font-size:.8125rem; color:var(--text-tertiary)}

.steps{list-style:none; margin:0; padding:0; display:grid; gap:1px;
  background:var(--ink-700); border:1px solid var(--ink-700); border-radius:var(--r-card);
  overflow:hidden}
@media(min-width:900px){.steps{grid-template-columns:repeat(4,1fr)}}
.step{background:var(--ink-900); padding:var(--s-5); display:flex; gap:var(--s-4);
  align-items:flex-start}
.step__n{width:30px; height:30px; border-radius:var(--r-pill); flex:none; display:grid;
  place-items:center; font-family:var(--font-display); font-weight:700;
  border:2px solid var(--ink-600); color:var(--text-tertiary)}
.step__t,.step__s{display:block}
.step__t{font-weight:700}
.step__s{font-size:.8125rem; color:var(--text-secondary); margin-top:5px}

.shopmini{display:grid; gap:var(--s-4)}
@media(min-width:768px){.shopmini{grid-template-columns:repeat(3,minmax(0,1fr))}}
.mini{display:block; border:1px solid var(--ink-700); border-radius:var(--r-card);
  background:var(--ink-800); padding:var(--s-5); color:var(--text-primary)}
.mini:hover{text-decoration:none; border-color:var(--spark-cyan); color:var(--text-primary)}
.mini__t,.mini__a,.mini__s{display:block}
.mini__t{font-family:var(--font-display); font-weight:700; text-transform:uppercase;
  font-size:1.25rem; letter-spacing:.02em}
.mini__a{font-size:.8125rem; color:var(--text-tertiary); margin-top:6px}
.mini__s{display:flex; align-items:center; gap:8px; font-size:.875rem;
  color:var(--text-secondary); margin-top:var(--s-3)}

/* ==========================================================================
   Site map (information/sitemap)
   Counts sit in a pill to the right of each link, so the names stay in one
   readable column and the eye can run down the numbers separately.
   ========================================================================== */
.mapglance{display:flex;flex-wrap:wrap;gap:8px;margin-top:32px}
.mapglance span{
  display:inline-flex;align-items:baseline;gap:6px;padding:8px 14px;
  background:var(--ink-700);border:1px solid rgba(255,255,255,.08);border-radius:999px;
  font-size:14px;color:var(--text-secondary)}
.mapglance b{
  font-family:var(--font-display);font-size:19px;font-weight:700;
  color:var(--fuse-gold);letter-spacing:.01em}

.mapgrid{display:grid;gap:32px;margin-top:32px}
@media(min-width:760px){.mapgrid{grid-template-columns:repeat(2,1fr);gap:48px}}
@media(min-width:1100px){.mapgrid--3{grid-template-columns:repeat(3,1fr)}}

.mapcol h3{
  font-family:var(--font-display);font-weight:700;font-size:15px;letter-spacing:.09em;
  text-transform:uppercase;color:var(--text-tertiary);
  margin:0 0 12px;padding-bottom:8px;border-bottom:1px solid rgba(255,255,255,.10)}
.mapcol h3 a{color:var(--text-tertiary);text-decoration:none}
.mapcol h3 a:hover,.mapcol h3 a:focus-visible{color:var(--spark-cyan);text-decoration:none}
.maplist{list-style:none;margin:0;padding:0}
.maplist li{border-bottom:1px solid rgba(255,255,255,.05)}
.maplist li:last-child{border-bottom:0}
.maplist a{
  display:flex;align-items:baseline;gap:12px;padding:9px 0;
  color:var(--text-primary);text-decoration:none;line-height:1.35}
.maplist a:hover,.maplist a:focus-visible{color:var(--spark-cyan);text-decoration:none}
.maplist a:hover .mapn,.maplist a:focus-visible .mapn{color:var(--spark-cyan);border-color:currentColor}
.mapname{flex:1 1 auto;min-width:0}
.mapn{
  flex:0 0 auto;font-size:12px;font-variant-numeric:tabular-nums;color:var(--text-tertiary);
  border:1px solid rgba(255,255,255,.14);border-radius:999px;padding:1px 8px;
  transition:color 150ms ease-out,border-color 150ms ease-out}
.maphub a{font-family:var(--font-display);font-size:20px;font-weight:700;letter-spacing:.01em;color:var(--spark-cyan)}
.maphub a:hover{color:var(--text-primary)}
.mapsub{color:var(--text-tertiary);font-size:13px;margin:2px 0 8px}
.mapsub a{color:var(--text-tertiary)}
.mapsub a:hover{color:var(--spark-cyan)}

.townindex{margin-top:48px;padding-top:24px;border-top:1px solid rgba(255,255,255,.10)}
.townindex h3{
  font-family:var(--font-display);font-weight:700;font-size:15px;letter-spacing:.09em;
  text-transform:uppercase;color:var(--text-tertiary);margin:0 0 8px}
.towncols{list-style:none;margin:16px 0 0;padding:0;columns:2;column-gap:32px}
@media(min-width:620px){.towncols{columns:3}}
@media(min-width:980px){.towncols{columns:4}}
@media(min-width:1240px){.towncols{columns:5}}
.towncols li{break-inside:avoid;margin-bottom:2px}
.towncols a{
  display:block;padding:5px 0;font-size:14px;color:var(--text-secondary);
  text-decoration:none;line-height:1.35}
.towncols a:hover,.towncols a:focus-visible{color:var(--spark-cyan);text-decoration:none}

/* ==========================================================================
   Sign in (account/login)
   ========================================================================== */
.signwrap{max-width:1000px;margin-inline:auto}
.signgrid{display:grid;gap:32px}
@media(min-width:900px){.signgrid{grid-template-columns:minmax(0,460px) minmax(0,1fr);gap:64px;align-items:start}}

.card-sign{
  background:var(--ink-800);border:1px solid rgba(255,255,255,.09);
  border-radius:var(--r-card);padding:32px}
@media(min-width:600px){.card-sign{padding:48px}}

.f-row{margin-bottom:24px}
.f-row label{
  display:block;font-family:var(--font-display);font-weight:700;font-size:14px;
  letter-spacing:.06em;text-transform:uppercase;color:var(--text-secondary);margin-bottom:6px}
.f-in{
  width:100%;padding:13px 14px;font-family:var(--font-body);
  /* 16px: anything smaller makes iOS zoom the page on focus */
  font-size:16px;
  color:var(--text-primary);background:var(--ink-900);
  border:1px solid rgba(255,255,255,.16);border-radius:var(--r-control);
  transition:border-color 150ms ease-out}
.f-in:hover{border-color:rgba(255,255,255,.28)}
.f-in:focus{outline:2px solid var(--spark-cyan);outline-offset:1px;border-color:transparent}
.f-in::placeholder{color:var(--text-tertiary)}
.f-in[aria-invalid="true"]{border-color:var(--error)}

.f-pw{position:relative}
.f-pw .f-in{padding-right:78px}
.f-reveal{
  position:absolute;right:4px;top:50%;transform:translateY(-50%);
  display:inline-flex;align-items:center;justify-content:center;
  /* 44px so it is a real touch target, not a 32px one inside the field */
  min-height:44px;min-width:56px;
  background:transparent;border:0;cursor:pointer;padding:0 10px;
  font-family:var(--font-body);font-size:13px;color:var(--text-tertiary);border-radius:4px}
.f-reveal:hover{color:var(--spark-cyan)}
.f-reveal:focus-visible{outline:2px solid var(--spark-cyan);outline-offset:1px}

.f-foot{display:flex;flex-wrap:wrap;align-items:center;gap:16px;margin-top:24px}
.f-forgot{font-size:14px;color:var(--spark-cyan)}
.f-forgot:hover{color:var(--text-primary)}

.alertbox{
  display:flex;gap:12px;align-items:flex-start;
  padding:14px 16px;border-radius:var(--r-control);margin-bottom:24px;
  background:rgba(255,107,107,.10);border:1px solid rgba(255,107,107,.42);
  color:var(--text-primary);font-size:15px;line-height:1.5}
.alertbox i{color:var(--error);margin-top:3px}
.alertbox a{color:var(--spark-cyan)}
.alertbox a:hover{color:var(--text-primary)}
.alertbox--ok{background:rgba(60,180,252,.10);border-color:rgba(60,180,252,.42)}
.alertbox--ok i{color:var(--spark-cyan)}

.sideblock{
  border:1px solid rgba(255,255,255,.09);border-radius:var(--r-card);
  padding:24px;margin-bottom:16px}
.sideblock--lead{background:var(--ink-band);border-color:rgba(240,192,36,.30)}
.sideblock h3{
  font-family:var(--font-display);font-weight:700;font-size:20px;letter-spacing:.01em;
  margin:0 0 6px;color:var(--text-primary)}
.sideblock p{margin:0 0 16px;color:var(--text-secondary);font-size:15px;line-height:1.55}
.sideblock p:last-child{margin-bottom:0}
.sideblock a:not(.btn){color:var(--spark-cyan)}
.sideblock a:not(.btn):hover{color:var(--text-primary)}
.whatfor{list-style:none;margin:0 0 16px;padding:0}
.whatfor li{
  display:flex;gap:10px;align-items:flex-start;
  font-size:15px;color:var(--text-secondary);padding:4px 0}
.whatfor i{color:var(--spark-cyan);margin-top:4px;font-size:13px}

/* the basket's sign-in offer: a sentence, not a competing call to action */
.cart-signin{
  margin:0 0 24px;font-size:15px;color:var(--text-secondary);line-height:1.55}
.cart-signin a{color:var(--spark-cyan)}
.cart-signin a:hover{color:var(--text-primary)}

/* register and forgotten reuse the sign-in card; these are the additions */
.f-two{display:grid;gap:0 24px}
@media(min-width:560px){.f-two{grid-template-columns:1fr 1fr}}
.f-err{
  display:flex;gap:8px;align-items:flex-start;margin:6px 0 0;
  font-size:14px;color:var(--error);line-height:1.45}
.f-err i{margin-top:3px}
.f-note{margin:6px 0 0;font-size:13px;color:var(--text-tertiary);line-height:1.45}
.f-set{border:0;padding:0;margin-inline:0}
.f-set legend{
  font-family:var(--font-display);font-weight:700;font-size:14px;letter-spacing:.06em;
  text-transform:uppercase;color:var(--text-secondary);padding:0;margin-bottom:6px}
.f-check{
  display:flex;gap:10px;align-items:flex-start;
  font-size:15px;color:var(--text-secondary);line-height:1.5;cursor:pointer}
.f-check input{
  flex:0 0 auto;width:20px;height:20px;margin:1px 0 0;
  accent-color:var(--spark-cyan);cursor:pointer}
.f-check + .f-check{margin-top:10px}
.f-check a{color:var(--spark-cyan)}
.f-check a:hover{color:var(--text-primary)}
select.f-in{appearance:none;background-image:none}
textarea.f-in{resize:vertical}

/* ---------- address book ---------- */
.addrgrid{list-style:none;margin:0 0 32px;padding:0;display:grid;gap:16px}
@media(min-width:700px){.addrgrid{grid-template-columns:repeat(2,1fr)}}
@media(min-width:1100px){.addrgrid{grid-template-columns:repeat(3,1fr)}}
.addrcard{
  display:flex;flex-direction:column;gap:12px;
  border:1px solid rgba(255,255,255,.09);border-radius:var(--r-card);
  padding:20px;background:var(--ink-800)}
.addrcard--default{border-color:rgba(240,192,36,.42);background:var(--ink-band)}
.addrcard__tag{
  display:flex;align-items:center;gap:8px;margin:0;
  font-family:var(--font-display);font-size:13px;letter-spacing:.08em;
  text-transform:uppercase;color:var(--fuse-gold)}
.addrcard__body{
  margin:0;font-style:normal;color:var(--text-secondary);font-size:15px;line-height:1.6;flex:1 1 auto}
.addrcard__acts{display:flex;align-items:center;gap:16px;margin-top:auto}
.addrcard__del{font-size:14px;color:var(--text-tertiary)}
.addrcard__del:hover,.addrcard__del:focus-visible{color:var(--error)}
.f-opt{
  font-family:var(--font-body);font-weight:400;font-size:12px;letter-spacing:0;
  text-transform:none;color:var(--text-tertiary);margin-left:6px}
.addrcard__note{font-size:13px;color:var(--text-tertiary);line-height:1.4}

/* ---------- returns ---------- */
.retsec{
  font-family:var(--font-display);font-weight:700;font-size:15px;letter-spacing:.09em;
  text-transform:uppercase;color:var(--text-tertiary);
  margin:32px 0 16px;padding-bottom:8px;border-bottom:1px solid rgba(255,255,255,.10)}
.card-sign > .retsec:first-child,.retsec:first-child{margin-top:0}
.retlist{list-style:none;margin:0 0 24px;padding:0}
.retrow{
  display:flex;flex-wrap:wrap;align-items:center;gap:12px 20px;
  padding:16px 0;border-bottom:1px solid rgba(255,255,255,.08)}
.retrow__main{flex:1 1 240px;min-width:0}
.retrow__id{margin:0;font-family:var(--font-display);font-size:20px;font-weight:700}
.retrow__id a{color:var(--text-primary)}
.retrow__id a:hover{color:var(--spark-cyan)}
.retrow__meta{margin:2px 0 0;font-size:14px;color:var(--text-tertiary)}
.retrow__status{
  margin:0;font-size:13px;letter-spacing:.04em;text-transform:uppercase;
  color:var(--fuse-gold);white-space:nowrap}
.retfacts{display:grid;grid-template-columns:auto 1fr;gap:8px 24px;margin:0}
.retfacts dt{font-size:13px;letter-spacing:.05em;text-transform:uppercase;color:var(--text-tertiary)}
.retfacts dd{margin:0;color:var(--text-primary)}
.retcomment{color:var(--text-secondary);line-height:1.6;white-space:pre-line}
.rethist{list-style:none;margin:0;padding:0}
.rethist li{padding:12px 0;border-bottom:1px solid rgba(255,255,255,.07)}
.rethist li:last-child{border-bottom:0}
.rethist__s{margin:0;font-family:var(--font-display);font-size:16px;font-weight:700;color:var(--text-primary)}
.rethist__d{margin:2px 0 0;font-size:13px;color:var(--text-tertiary)}
.rethist__c{margin:6px 0 0;font-size:14px;color:var(--text-secondary);line-height:1.5}

/* a radio with an explanation under it, rather than a bare label */
.f-choice{align-items:flex-start;padding:12px 0}
.f-choice + .f-choice{margin-top:0;border-top:1px solid rgba(255,255,255,.07)}
.f-choice__t{display:block;font-family:var(--font-display);font-size:18px;font-weight:700;
  color:var(--text-primary);letter-spacing:.01em}
.f-choice__d{display:block;margin-top:2px;font-size:14px;color:var(--text-tertiary);line-height:1.5}

/* feedback under the add-to-basket button */
.buynote{
  margin:12px 0 0;font-size:15px;line-height:1.5;
  color:var(--in-stock)}
.buynote--bad{color:var(--error)}

/* ---------- saved for later ---------- */
.savelink{margin:14px 0 0}
.savelink button{
  display:inline-flex;align-items:center;gap:8px;min-height:44px;
  background:none;border:0;padding:0;cursor:pointer;
  font-family:var(--font-body);font-size:15px;color:var(--text-tertiary)}
.savelink button:hover{color:var(--spark-cyan)}
.savelink button:focus-visible{outline:2px solid var(--spark-cyan);outline-offset:2px;border-radius:4px}

.savedgrid{list-style:none;margin:0 0 32px;padding:0;display:grid;gap:16px}
@media(min-width:760px){.savedgrid{grid-template-columns:repeat(2,1fr)}}
.saved{
  display:flex;gap:16px;align-items:flex-start;
  border:1px solid rgba(255,255,255,.09);border-radius:var(--r-card);
  padding:16px;background:var(--ink-800)}
.saved__img{flex:0 0 88px}
.saved__img img{width:88px;height:auto;border-radius:var(--r-control)}
.saved__body{flex:1 1 auto;min-width:0}
.saved__name{margin:0;font-family:var(--font-display);font-size:19px;font-weight:700;line-height:1.2}
.saved__name a{color:var(--text-primary)}
.saved__name a:hover{color:var(--spark-cyan)}
.saved__code{margin:2px 0 0;font-size:13px;color:var(--text-tertiary)}
.saved__stock{margin:6px 0 0;font-size:13px;color:var(--text-secondary)}
.saved__price{margin:6px 0 0;display:flex;gap:8px;align-items:baseline}
.saved__acts{display:flex;flex-direction:column;gap:10px;align-items:flex-end;flex:0 0 auto}
.buynote a{color:var(--spark-cyan);text-decoration:underline}
.buynote a:hover{color:var(--text-primary)}

/* ---------- search refine form ---------- */
.srch{margin:0 0 32px}
.srch__row{display:grid;gap:16px;align-items:end}
@media(min-width:760px){.srch__row{grid-template-columns:2fr 1.4fr auto}}
.srch__row .f-row{margin-bottom:0}
.srch__go .btn{width:100%;min-height:48px}
.srch__opts{display:flex;flex-wrap:wrap;gap:12px 32px;margin-top:16px}
.srch__opts .f-check + .f-check{margin-top:0}

.listbar{display:flex;flex-wrap:wrap;gap:12px;justify-content:flex-end;margin-bottom:24px}

/* ---------- compare table ---------- */
/* Four columns of specs will not fit a phone, so the table scrolls inside its
   own box rather than the page scrolling sideways. */
.cmpwrap{overflow-x:auto;-webkit-overflow-scrolling:touch;margin-bottom:32px}
.cmp{border-collapse:collapse;width:100%;min-width:640px}
.cmp th,.cmp td{
  padding:14px 16px;text-align:left;vertical-align:top;
  border-bottom:1px solid rgba(255,255,255,.08);font-size:15px}
.cmp thead th{vertical-align:bottom;background:var(--ink-800)}
.cmp thead img{width:110px;height:auto;border-radius:var(--r-control);margin-bottom:10px}
.cmp__name{
  display:block;font-family:var(--font-display);font-size:18px;font-weight:700;
  line-height:1.2;color:var(--text-primary)}
.cmp__name:hover{color:var(--spark-cyan)}
.cmp tbody th[scope="row"]{
  font-family:var(--font-display);font-size:13px;letter-spacing:.06em;text-transform:uppercase;
  color:var(--text-tertiary);white-space:nowrap;background:var(--ink-900);
  position:sticky;left:0}
.cmp__grp th{
  font-family:var(--font-display);font-size:14px;letter-spacing:.08em;text-transform:uppercase;
  color:var(--fuse-gold);background:var(--ink-band);white-space:normal}
.cmp__acts{display:flex;flex-direction:column;gap:10px;align-items:flex-start}

/* ---------- compare control ---------- */
/* On a card it sits in the free top-right corner of the image. The badge owns
   the top-left, and the price row has no room for a third control. */
.card__cmp{
  position:absolute; top:6px; right:6px; z-index:2;
  display:inline-flex; align-items:center; justify-content:center;
  /* 44px, the same touch target used for the product page's Show/Hide */
  width:44px; height:44px; padding:0; cursor:pointer;
  background:rgba(5,3,26,.62); border:1px solid rgba(255,255,255,.20);
  border-radius:var(--r-pill); color:var(--text-secondary); font-size:14px;
  transition:background var(--t-micro) ease-out, color var(--t-micro) ease-out,
             border-color var(--t-micro) ease-out}
.card__cmp:hover{background:var(--ink-900); color:var(--spark-cyan); border-color:var(--spark-cyan)}
.card__cmp:focus-visible{outline:2px solid var(--spark-cyan); outline-offset:2px}
.card__cmp[aria-pressed="true"]{
  background:var(--fuse-gold); border-color:var(--fuse-gold); color:var(--text-on-accent)}

/* the product page keeps its two quiet links side by side */
.savelink{display:flex; flex-wrap:wrap; gap:8px 24px; align-items:center}
.savelink button[data-compare][aria-pressed="true"]{color:var(--fuse-gold)}

/* ---------- tile backgrounds ----------
   A tile with a picture of its own shows it behind the name; tiles without one
   are untouched, so nothing changes until an image is chosen in the admin. */
.tile--bg{isolation:isolate; overflow:hidden}
.tile--bg .tile__bg{
  position:absolute; inset:0; width:100%; height:100%;
  object-fit:cover; z-index:-2;
  transition:transform var(--t-panel) var(--ease-out)}
/* The tile pictures are supplied already faded to black across their left
   third, so the scrim no longer has to darken the whole tile to carry the type.
   It runs left to right instead of top to bottom: full weight across the band
   the words actually occupy, then away to nothing, so the right of the picture
   keeps its own colour and the top of the tile is not veiled at all.

   Where it clears follows the mosaic's own breakpoint, because the text
   footprint changes with it. Two columns up to 900px puts the type across 83%
   of a 160px tile; four columns above it leaves the longest name — "Rockets &
   rocket packs" — ending at 72%.

   Measured over all seven pictures across the region the type really covers,
   both are better than the vertical gradient they replace, not worse:

     4 columns   name  9.9 -> 15.1   meta 3.3 -> 5.0
     2 columns   name  7.6 -> 16.2   meta 2.5 -> 5.4

   The meta had been under the 4.5:1 it needs at 15px, in about 2% of the area
   it covers. It now clears it everywhere. */
.tile--bg .tile__scrim{
  position:absolute; inset:0; z-index:-1; pointer-events:none;
  /* --tile-scrim is set per tile in the admin. Scaling the element's opacity
     rather than each stop's alpha keeps one number in charge and avoids calc()
     inside a colour function. */
  opacity:var(--tile-scrim, 1);
  background:linear-gradient(90deg,
    rgba(5,3,26,.92) 0%,
    rgba(5,3,26,.90) 70%,
    rgba(5,3,26,.85) 83%,
    rgba(5,3,26,.10) 92%,
    rgba(5,3,26,0)   97%)}
@media(min-width:900px){
  .tile--bg .tile__scrim{
    background:linear-gradient(90deg,
      rgba(5,3,26,.92) 0%,
      rgba(5,3,26,.90) 60%,
      rgba(5,3,26,.85) 72%,
      rgba(5,3,26,.10) 86%,
      rgba(5,3,26,0)   93%)}
}
/* .tile:hover swaps the background colour, which a picture hides. Give a tile
   with an image its own hover instead: the border still changes, and the
   picture lifts slightly. */
.tile--bg:hover .tile__bg{transform:scale(1.04)}
@media(prefers-reduced-motion:reduce){
  .tile--bg .tile__bg{transition:none}
  .tile--bg:hover .tile__bg{transform:none}
}

/* ---------------------------------------------------------------------------
   Brand pages (deploy 061)
   The single-brand page is the category page with a logo lockup and a stock
   state on each card; the index is a grid of brand tiles. Both live here for
   the same reason the category rules do: product/manufacturer.php is core, so
   there is nowhere to call addStyle() from.
   --------------------------------------------------------------------------- */

/* the brand mark beside the brand name */
.brandhead{display:flex; align-items:center; gap:var(--s-5); flex-wrap:wrap}
.brandhead h1{margin-bottom:0}
.brandhead__logo{width:96px; height:96px; flex:none; object-fit:contain;
  background:var(--paper); border-radius:var(--r-card); padding:10px}
.brandhead__count{display:block; margin-top:6px; font-size:.9375rem; color:var(--text-tertiary)}

/* stock state on a card. A brand's range is small enough that a sold-out line
   is a real part of the choice, rather than noise the way it would be on a
   150-product category page. */
.card__stock{display:flex; align-items:center; gap:6px; margin:6px 0 0;
  font-size:.8125rem; color:var(--text-tertiary)}
.card--out .card__media img{opacity:.55}
.card--out .price{color:var(--text-tertiary)}

/* the brands index */
.brandgrid{display:grid; gap:var(--s-4); list-style:none; margin:0; padding:0;
  grid-template-columns:repeat(2,minmax(0,1fr))}
@media(min-width:700px){.brandgrid{grid-template-columns:repeat(3,minmax(0,1fr))}}
@media(min-width:1000px){.brandgrid{grid-template-columns:repeat(4,minmax(0,1fr))}}

.brandcard{display:flex; flex-direction:column; align-items:center; text-align:center;
  gap:var(--s-3); padding:var(--s-5) var(--s-4); height:100%;
  background:var(--ink-800); border:1px solid var(--ink-700);
  border-radius:var(--r-card); color:var(--text-primary)}
.brandcard:hover{text-decoration:none; border-color:var(--spark-cyan); color:var(--text-primary)}
.brandcard:hover .brandcard__name{color:var(--spark-cyan)}

.brandcard__logo{width:96px; height:96px; flex:none; object-fit:contain;
  background:var(--paper); border-radius:var(--r-card); padding:10px}
.brandcard__logo--none{display:flex; align-items:center; justify-content:center;
  background:var(--ink-700); font-family:var(--font-display); font-size:34px;
  color:var(--text-tertiary)}
.brandcard__name{font-weight:600; line-height:1.3}
.brandcard__count{font-size:.8125rem; color:var(--text-tertiary)}

/* ---------------------------------------------------------------------------
   About Us (deploy 079)
   One page, reached by a route swap in event/ofs_infopages, so these rules only
   ever apply there. They live here rather than in a module stylesheet because
   information/information.php is core and has nowhere to call addStyle() from.
   --------------------------------------------------------------------------- */

/* the four shops */
.abshops{list-style:none; margin:0; padding:0; display:grid; gap:var(--s-4);
  grid-template-columns:repeat(2,minmax(0,1fr))}
@media(min-width:900px){.abshops{grid-template-columns:repeat(4,minmax(0,1fr))}}
.abshops li{display:flex; flex-direction:column; gap:6px; padding:var(--s-5);
  background:var(--ink-800); border:1px solid var(--ink-700); border-radius:var(--r-card)}
.abshops__town{font-family:var(--font-display); font-weight:700; font-size:1.75rem;
  text-transform:uppercase; letter-spacing:.02em; line-height:1; color:var(--fuse-gold)}
.abshops__name{font-size:.9375rem; color:var(--text-secondary)}
.abshops__link{margin-top:auto; padding-top:var(--s-3); font-size:.875rem; color:var(--spark-cyan)}
.abshops__link:hover{color:var(--text-primary); text-decoration:underline}

/* the counter, and the photograph of it */
.abwhy{display:grid; gap:var(--s-6)}
@media(min-width:900px){
  .abwhy{grid-template-columns:minmax(0,1.1fr) minmax(0,.9fr); gap:var(--s-8); align-items:center}
}
.abwhy__copy h2{margin-bottom:var(--s-4)}
.abwhy__copy .prose + .prose{margin-top:var(--s-4)}
.abwhy__shot{margin:0}
.abwhy__shot img{width:100%; height:auto; display:block; border-radius:var(--r-card);
  border:1px solid var(--ink-700)}
.abwhy__shot figcaption{margin-top:var(--s-3); font-size:.8125rem; color:var(--text-tertiary)}

/* displays, and the facts beside them */
.abdisplay{display:grid; gap:var(--s-6)}
@media(min-width:900px){
  .abdisplay{grid-template-columns:minmax(0,1.35fr) minmax(0,.65fr); gap:var(--s-8);
    align-items:start}
}
.abdisplay__eyebrow{display:block; font-size:.8125rem; text-transform:uppercase;
  letter-spacing:.08em; color:var(--text-tertiary); margin-bottom:var(--s-3)}
.abdisplay h2{margin-bottom:var(--s-4)}
.abdisplay .prose + .prose{margin-top:var(--s-4)}
.abfacts{margin:0; display:grid; gap:1px; background:var(--ink-700);
  border:1px solid var(--ink-700); border-radius:var(--r-card); overflow:hidden}
.abfacts div{background:var(--ink-900); padding:var(--s-4) var(--s-5);
  display:flex; justify-content:space-between; align-items:baseline; gap:var(--s-4)}
.abfacts dt{margin:0; font-size:.8125rem; text-transform:uppercase; letter-spacing:.06em;
  color:var(--text-tertiary)}
.abfacts dd{margin:0; font-family:var(--font-display); font-weight:700; font-size:1.5rem;
  line-height:1; color:var(--text-primary)}

/* ---------------------------------------------------------------------------
   Delivery (deploy 081)
   Reached by the route swap in event/ofs_infopages, so these only apply there.
   --------------------------------------------------------------------------- */

/* the two ways to get fireworks */
.delways{display:grid; gap:var(--s-4)}
@media(min-width:800px){.delways{grid-template-columns:repeat(2,minmax(0,1fr)); gap:var(--s-5)}}
.delway{display:flex; flex-direction:column; align-items:flex-start; gap:var(--s-3);
  padding:var(--s-6); background:var(--ink-800); border:1px solid var(--ink-700);
  border-radius:var(--r-card)}
/* Collection is the one we would rather people chose: it is free and it is next
   day, so it gets the gold edge and delivery does not. */
.delway--free{border-color:var(--fuse-gold)}
.delway__tag{font-size:.75rem; text-transform:uppercase; letter-spacing:.08em;
  color:var(--text-tertiary)}
.delway--free .delway__tag{color:var(--fuse-gold)}
.delway h2{margin:0}
.delway__price{margin:0; font-family:var(--font-display); font-weight:700; font-size:2.5rem;
  line-height:1; color:var(--text-primary)}
.delway__price span{display:block; margin-top:8px; font-family:var(--font-body);
  font-weight:400; font-size:.9375rem; color:var(--text-secondary)}
.delway .prose{margin:0}
.delway .btn{margin-top:auto}

/* coverage. The letters lead, because that is what a customer matches against
   their own postcode; the area name is a label under it. */
.delareas{list-style:none; margin:0 0 var(--s-5); padding:0; display:flex; flex-wrap:wrap;
  gap:var(--s-3)}
.delareas li{display:flex; flex-direction:column; align-items:center; gap:2px; min-width:80px;
  padding:var(--s-3) var(--s-4); background:var(--ink-900); border:1px solid var(--ink-700);
  border-radius:var(--r-card); font-family:var(--font-display); font-weight:700;
  font-size:1.375rem; line-height:1; color:var(--fuse-gold)}
.delareas li span{font-family:var(--font-body); font-weight:400; font-size:.75rem;
  color:var(--text-tertiary); white-space:nowrap}
.delareas__note{max-width:70ch}
.delareas__note + .delareas__note{margin-top:var(--s-4)}

/* what happens on the day */
.delrules{margin:0 0 var(--s-5); display:grid; gap:1px; background:var(--ink-700);
  border:1px solid var(--ink-700); border-radius:var(--r-card); overflow:hidden}
@media(min-width:900px){.delrules{grid-template-columns:repeat(2,minmax(0,1fr))}}
.delrules div{background:var(--ink-900); padding:var(--s-5)}
.delrules dt{margin:0 0 6px; font-family:var(--font-display); font-weight:700; font-size:1.25rem;
  letter-spacing:.01em; color:var(--text-primary)}
.delrules dd{margin:0; font-size:.9375rem; line-height:1.55; color:var(--text-secondary)}
.delrules a{color:var(--spark-cyan)}
.delrules a:hover{color:var(--text-primary)}
.delnote{max-width:70ch; font-size:.875rem; color:var(--text-tertiary)}

/* ---------------------------------------------------------------------------
   Document pages (deploy 082)
   Terms, and any other long piece whose copy stays in the admin field. The
   contents list is generated from the field's own headings, so these rules
   style whatever the merchant writes rather than a fixed set of sections.
   --------------------------------------------------------------------------- */
.doclayout{display:grid; gap:var(--s-6)}
@media(min-width:1000px){
  .doclayout{grid-template-columns:250px minmax(0,1fr); gap:var(--s-8); align-items:start}
}
/* Clears the sticky header, which is 134px plus the utility bar. */
.doctoc{padding:var(--s-5); background:var(--ink-800); border:1px solid var(--ink-700);
  border-radius:var(--r-card)}
@media(min-width:1000px){.doctoc{position:sticky; top:150px}}
.doctoc h2{font-size:1rem; margin:0 0 var(--s-3); color:var(--text-tertiary); letter-spacing:.06em}
.doctoc ol{list-style:none; margin:0; padding:0; display:grid; gap:2px}
.doctoc a{display:block; padding:6px 0; font-size:.875rem; line-height:1.35;
  color:var(--text-secondary)}
.doctoc a:hover{color:var(--spark-cyan); text-decoration:none}

.doc{max-width:68ch}
/* scroll-margin so a heading jumped to does not hide under the sticky header */
.doc h2{font-size:1.5rem; margin:var(--s-7) 0 var(--s-4); padding-top:var(--s-7);
  border-top:1px solid var(--ink-700); scroll-margin-top:160px}
.doc h2:first-child{margin-top:0; padding-top:0; border-top:0}
.doc p{margin:0 0 var(--s-4); color:var(--text-secondary); line-height:1.65}
.doc p:last-child{margin-bottom:0}
.doc strong,.doc b{color:var(--text-primary)}
.doc a{color:var(--spark-cyan)}
.doc a:hover{color:var(--text-primary)}
.doc ul,.doc ol{margin:0 0 var(--s-4) var(--s-5); color:var(--text-secondary); line-height:1.65}
.doc li{margin-bottom:6px}

/* ---------------------------------------------------------------------------
   FAQs (deploy 084)
   Native <details>, so it opens with no script and find-in-page still works.
   --------------------------------------------------------------------------- */
.faqintro{max-width:70ch}
.faqs{display:grid; gap:1px; background:var(--ink-700); border:1px solid var(--ink-700);
  border-radius:var(--r-card); overflow:hidden}
.faq{background:var(--ink-900)}
.faq summary{display:flex; align-items:flex-start; justify-content:space-between; gap:var(--s-4);
  padding:var(--s-5); cursor:pointer; list-style:none;
  font-family:var(--font-display); font-weight:700; font-size:1.25rem; line-height:1.25;
  color:var(--text-primary)}
/* Safari draws its own triangle unless this is turned off */
.faq summary::-webkit-details-marker{display:none}
.faq summary:hover{color:var(--fuse-gold)}
.faq summary:focus-visible{outline:2px solid var(--spark-cyan); outline-offset:-2px}
.faq summary i{flex:none; margin-top:4px; font-size:.75rem; color:var(--text-tertiary);
  transition:transform var(--t-micro) ease-out}
.faq[open] summary{color:var(--fuse-gold)}
.faq[open] summary i{transform:rotate(180deg)}
.faq__a{padding:0 var(--s-5) var(--s-5)}
.faq__a p{margin:0 0 var(--s-4); max-width:70ch; font-size:.9375rem; line-height:1.65;
  color:var(--text-secondary)}
.faq__a p:last-child{margin-bottom:0}
@media(prefers-reduced-motion:reduce){.faq summary i{transition:none}}

/* ---------------------------------------------------------------------------
   Customer testimonials (deploy 086)
   Quotes run from five words to sixty, so a row of equal cards would be mostly
   air. CSS columns let each one take the height it needs. A shop with too few
   quotes to fill the columns is set as a single pull quote instead.
   --------------------------------------------------------------------------- */
.voxintro{max-width:70ch}
.voxintro p{margin:0 0 var(--s-4)}
.voxintro p:last-child{margin-bottom:0}

/* .band + .band kills the top padding; these are four bands in a row and each
   needs its own breathing room, the same exception --sec and --html make. */
.band + .band--voices{padding-top:var(--band-pad)}

.voxjump{display:flex; flex-wrap:wrap; gap:var(--s-2); margin-top:var(--s-5)}
.voxjump a{display:inline-flex; align-items:baseline; gap:8px; padding:8px 14px;
  /* an ink-700 hairline alone all but disappears on the ink-900 ground, so the
     chip carries a fill as well */
  background:var(--ink-800); border:1px solid var(--ink-700); border-radius:var(--r-pill);
  font-family:var(--font-display); font-weight:600; font-size:1rem; letter-spacing:.01em;
  color:var(--text-secondary); text-decoration:none;
  transition:border-color var(--t-micro) ease-out, color var(--t-micro) ease-out}
.voxjump a b{font-weight:700; font-size:.8125rem; color:var(--text-tertiary)}
.voxjump a:hover{background:var(--ink-700); border-color:var(--fuse-gold);
  color:var(--fuse-gold); text-decoration:none}
.voxjump a:hover b{color:var(--fuse-gold)}

.vox__head{display:flex; flex-wrap:wrap; align-items:flex-end; justify-content:space-between;
  gap:var(--s-4); margin-bottom:var(--s-6); padding-bottom:var(--s-4);
  border-bottom:1px solid var(--ink-700)}
.vox__town{margin:0 0 2px; font-family:var(--font-display); font-weight:700;
  font-size:.8125rem; letter-spacing:.14em; text-transform:uppercase; color:var(--fuse-gold)}
.vox__head h2{margin:0; font-size:clamp(1.5rem, 3.4vw, 2.125rem); line-height:1.05}
.vox__to{flex:none; display:inline-flex; align-items:center; gap:8px;
  font-family:var(--font-display); font-weight:600; font-size:1rem; color:var(--text-secondary)}
.vox__to i{font-size:.8125rem; transition:transform var(--t-micro) ease-out}
.vox__to:hover{color:var(--fuse-gold); text-decoration:none}
.vox__to:hover i{transform:translateX(3px)}

.vox__set{columns:1; column-gap:var(--s-5)}
@media(min-width:700px){.vox__set{columns:2}}
@media(min-width:1100px){.vox__set{columns:3}}

.vox{break-inside:avoid; margin:0 0 var(--s-5); padding:var(--s-5);
  background:var(--ink-900); border:1px solid var(--ink-700); border-radius:var(--r-card);
  /* the fuse: a gold line down the left edge, burning out towards the bottom */
  border-left:2px solid transparent;
  background-image:linear-gradient(var(--ink-900), var(--ink-900)),
    linear-gradient(180deg, var(--fuse-gold) 0%, rgba(240,192,36,.25) 45%, rgba(240,192,36,0) 100%);
  background-origin:border-box;
  background-clip:padding-box, border-box}
.vox blockquote{margin:0; padding:0; border:0}
.vox blockquote p{margin:0 0 var(--s-3); font-size:.9375rem; line-height:1.65;
  color:var(--text-secondary)}
.vox blockquote p:last-child{margin-bottom:0}
.vox figcaption{display:flex; align-items:baseline; flex-wrap:wrap; gap:var(--s-3);
  margin-top:var(--s-4)}
.vox__who{font-family:var(--font-display); font-weight:700; font-size:1.25rem;
  line-height:1; letter-spacing:.01em; color:var(--text-primary)}
.vox__when{font-size:.75rem; letter-spacing:.06em; text-transform:uppercase;
  color:var(--text-tertiary)}

/* A shop with one or two quotes: no columns, and the words carry the section. */
.vox__set--solo{columns:auto}
.vox__set--solo .vox{max-width:44ch; padding:var(--s-6)}
.vox__set--solo .vox blockquote p{font-size:1.25rem; line-height:1.5;
  color:var(--text-primary)}

@media(prefers-reduced-motion:reduce){.vox__to i{transition:none}}

/* ---------------------------------------------------------------------------
   Category header backdrop (deploy 092)
   The artwork is bursts on a near-black sky, so it is composited with
   mix-blend-mode:screen rather than masked: screen keeps every bright pixel
   and drops the black to nothing, which means the sky dissolves into the ink
   ground exactly, with no edge to hide. The mask that remains is only there to
   stop the square ending in a corner.
   --------------------------------------------------------------------------- */
.cathead--lit{position:relative; overflow:hidden; background:var(--ink-900)}
.cathead--lit > .wrap{position:relative; z-index:1}

.cathead--lit::after{
  content:""; position:absolute; z-index:0;
  /* The box takes the section's own height rather than a square that gets
     clipped. A 620px square inside a 444px section had its downward fade cut
     off at 72%, where the mask was still at .6 — the artwork stopped in a hard
     horizontal line along the bottom of the header. */
  top:0; right:0; bottom:0;
  width:min(48%, 560px);
  background:url("../image/cathead-fireworks.jpg") no-repeat center right / cover;
  mix-blend-mode:screen;
  opacity:.62;
  /* Toward the heading, and away at top and bottom. Both now complete inside
     the box, because the box is what is actually on screen. */
  -webkit-mask-image:linear-gradient(270deg, #000 30%, rgba(0,0,0,.42) 60%, transparent 94%),
                     linear-gradient(180deg, rgba(0,0,0,.55) 0%, #000 30%, rgba(0,0,0,.35) 82%, transparent 100%);
          mask-image:linear-gradient(270deg, #000 30%, rgba(0,0,0,.42) 60%, transparent 94%),
                     linear-gradient(180deg, rgba(0,0,0,.55) 0%, #000 30%, rgba(0,0,0,.35) 82%, transparent 100%);
  -webkit-mask-composite:source-in;
          mask-composite:intersect;
  pointer-events:none;
}

@media(max-width:767px){ .cathead--lit::after{display:none} }
@media(min-width:768px) and (max-width:1023px){
  .cathead--lit::after{width:min(52%, 360px); opacity:.5}
}

/* No backdrop where a browser cannot drop the black, or the square would sit
   on the ink as a visible box. */
@supports not (mix-blend-mode: screen){ .cathead--lit::after{display:none} }

/* ---------------------------------------------------------------------------
   Add-to-basket confirmation (deploy 093)
   Replaces OpenCart's .alert-success, which common.js puts above #content and
   this theme never styled. Gold ground, ink text: at 13.6:1 it is the highest
   contrast pair the palette has, which is what a confirmation wants.
   --------------------------------------------------------------------------- */
.added{position:fixed; z-index:80; top:16px; left:50%; transform:translateX(-50%);
  width:min(calc(100% - 32px), 420px); pointer-events:none}
.added__box{position:relative; overflow:hidden; pointer-events:auto;
  background:var(--fuse-gold); color:var(--ink-900);
  border-radius:var(--r-card); padding:var(--s-5) var(--s-5) calc(var(--s-5) + 4px);
  box-shadow:0 18px 44px rgba(0,0,0,.55), 0 2px 0 rgba(255,255,255,.35) inset;
  opacity:0; transform:translateY(-14px);
  transition:opacity 180ms ease-out, transform 180ms ease-out}
.added--in .added__box{opacity:1; transform:none}

/* The failure state keeps the gold ground — it is the same event, reported the
   other way — and separates itself with the icon, the wording and a ring,
   rather than by introducing a colour the palette does not have. */
.added--bad .added__box{box-shadow:0 18px 44px rgba(0,0,0,.55),
  0 0 0 3px var(--ink-900) inset}
.added--bad .added__head i{color:var(--ink-900)}

.added__head{display:flex; align-items:center; gap:10px; margin:0 0 6px;
  font-family:var(--font-display); font-weight:700; font-size:1.375rem; line-height:1.1;
  letter-spacing:.01em}
.added__head i{font-size:1.0625rem}
.added__what{margin:0 0 var(--s-4); font-size:.9375rem; line-height:1.45;
  color:rgba(5,3,26,.78)}
.added__what[hidden]{display:none}

.added__do{display:flex; flex-wrap:wrap; gap:var(--s-2); margin:0}
.added__go,.added__shut{display:inline-flex; align-items:center; justify-content:center;
  min-height:40px; padding:0 var(--s-4); border-radius:var(--r-control);
  font-family:var(--font-display); font-weight:700; font-size:1rem; letter-spacing:.02em;
  cursor:pointer; border:2px solid var(--ink-900);
  transition:background-color var(--t-micro) ease-out, color var(--t-micro) ease-out}
.added__go{flex:1 1 auto; background:var(--ink-900); color:var(--fuse-gold); text-decoration:none}
.added__go:hover,.added__go:focus-visible{background:var(--ink-700); color:var(--fuse-gold);
  text-decoration:none}
.added__shut{background:transparent; color:var(--ink-900)}
.added__shut:hover{background:rgba(5,3,26,.1)}
.added__go:focus-visible,.added__shut:focus-visible{outline:3px solid var(--ink-900);
  outline-offset:2px}

/* The five seconds, made visible. An acknowledgement that removes itself
   without warning reads as a glitch; a bar that empties reads as a decision. */
.added__bar{position:absolute; left:0; right:0; bottom:0; height:4px;
  background:rgba(5,3,26,.16)}
.added__bar i{display:block; height:100%; background:var(--ink-900);
  transform:scaleX(1); transform-origin:left center}

@media(max-width:479px){
  .added{top:10px}
  .added__box{padding:var(--s-4)}
  .added__head{font-size:1.1875rem}
  .added__do{flex-direction:column}
  .added__go,.added__shut{width:100%}
}

/* The panel is a confirmation, not a performance. With motion turned down it
   appears and leaves outright, and the countdown bar stops moving — the timer
   still runs, so nothing about the behaviour changes. */
@media(prefers-reduced-motion:reduce){
  .added__box{transition:none; opacity:1; transform:none}
  .added__bar i{transition:none !important}
}

/* ---------------------------------------------------------------------------
   Basket stock states (deploy 095)
   The palette already carries a red, and already says what it is for: "Out of
   stock is grey, never red: red reads as an error." That rule is kept, not
   broken. A line that cannot be fulfilled is not a listing saying "out of
   stock" — it blocks checkout, so it is an error, and --error is what it takes.
   --------------------------------------------------------------------------- */
.warn--bad{background:rgba(255,107,107,.10); border-color:rgba(255,107,107,.55)}
.warn--bad i{color:var(--error)}
.warn--bad p{color:var(--text-primary)}

/* The per-line label, saying the number rather than pointing at a marker. */
.line__short{display:flex; align-items:flex-start; gap:8px; margin:6px 0 0;
  font-size:.8125rem; line-height:1.4; font-weight:600; color:var(--error)}
.line__short i{flex:none; margin-top:2px; font-size:.75rem}

/* ---------------------------------------------------------------------------
   Basket modules: coupon, voucher, reward, shipping estimate (deploy 097)
   Open, not accordions. The box itself is shared with .summary above.
   --------------------------------------------------------------------------- */
.cartmods{display:grid; gap:var(--s-4); margin-top:var(--s-5)}
.cartmod__intro{margin:0 0 var(--s-4); font-size:.9375rem; line-height:1.5;
  color:var(--text-secondary)}
.cartmod__label{display:block; margin-bottom:6px; font-size:.8125rem; font-weight:600;
  letter-spacing:.04em; text-transform:uppercase; color:var(--text-tertiary)}

.fld{width:100%; min-height:44px; padding:0 var(--s-3);
  background:var(--ink-900); color:var(--text-primary);
  border:1px solid var(--ink-600); border-radius:var(--r-control);
  font-family:inherit; font-size:.9375rem}
.fld:focus{outline:2px solid var(--spark-cyan); outline-offset:-1px; border-color:var(--spark-cyan)}
.fld[aria-invalid="true"]{border-color:var(--error)}
select.fld{appearance:none; padding-right:var(--s-6);
  background-image:linear-gradient(45deg,transparent 50%,var(--text-tertiary) 50%),
                   linear-gradient(135deg,var(--text-tertiary) 50%,transparent 50%);
  background-position:calc(100% - 18px) 19px, calc(100% - 12px) 19px;
  background-size:6px 6px, 6px 6px; background-repeat:no-repeat}

/* input and its button on one line, stacking when there is no room */
.cartmod__row{display:flex; flex-wrap:wrap; gap:var(--s-2)}
.cartmod__row .fld{flex:1 1 12rem}
.cartmod__row .btn{flex:0 0 auto}

.fldwrap{margin:0}
.cartmod__grid{display:grid; gap:var(--s-4); margin-bottom:var(--s-4)}
@media(min-width:620px){.cartmod__grid{grid-template-columns:repeat(2,minmax(0,1fr))}}

.cartmod__err{display:block; margin:6px 0 0; font-size:.8125rem; font-weight:600;
  color:var(--error)}
.cartmod__err[hidden]{display:none}

/* Quotes, inline where they were asked for rather than in a modal. */
.quotes{margin-top:var(--s-5); padding-top:var(--s-5);
  border-top:1px solid var(--ink-700)}
.quotes[hidden]{display:none}
.quotes__h{margin:0 0 var(--s-3); font-size:1rem}
.quotes__grp + .quotes__grp{margin-top:var(--s-4)}
.quotes__t{margin:0 0 6px; font-size:.8125rem; font-weight:600; letter-spacing:.04em;
  text-transform:uppercase; color:var(--text-tertiary)}
.quotes__row{display:flex; align-items:center; gap:10px; padding:9px var(--s-3);
  border:1px solid var(--ink-700); border-radius:var(--r-control);
  font-size:.9375rem; color:var(--text-secondary); cursor:pointer}
.quotes__row + .quotes__row{margin-top:6px}
.quotes__row:hover{border-color:var(--ink-600); color:var(--text-primary)}
.quotes__row:focus-within{border-color:var(--spark-cyan)}
.quotes__row input{accent-color:var(--fuse-gold); flex:none}
.quotes .btn{margin-top:var(--s-4)}

/* ---------------------------------------------------------------------------
   Buy One Get One Free (deploy 099)
   Gold, because the offer is the strongest thing on the card and gold is this
   palette's primary. Magenta already means "special offer"; a second magenta
   badge would read as the same thing said twice.
   --------------------------------------------------------------------------- */
.badge--bogof{background:var(--fuse-gold); color:var(--text-on-accent);
  /* the longest badge on the site, so it is allowed to wrap rather than run
     past the card edge */
  max-width:calc(100% - 20px); white-space:normal; line-height:1.2; text-align:left}
/* When a product carries a special as well, this sits under it. */
.badge--second{top:auto; margin-top:34px}

.gal__flag--bogof{background:var(--fuse-gold); color:var(--text-on-accent)}
.gal__flag--second{margin-top:38px}

/* The specials page is still stock Bootstrap and has no positioned image
   wrapper, so .badge — which is absolute — would otherwise hang off whatever
   ancestor happened to be positioned. One rule, so the label lands in the
   right place if that page ever shows one. (Deploy 100.) */
.specimg{position:relative; display:block}

/* ---------------------------------------------------------------------------
   Area pages: sidebar on the right, and the facts band (deploy 104)
   --------------------------------------------------------------------------- */
/* .acct--nav is shared with the account pages, so the side swap is a modifier
   rather than a change to the shared rule. Source order is unchanged, which
   keeps the shop card first when the columns stack on a phone. */
/* The hero sits directly above this grid inside the same wrap, so without a
   margin the shop card's border met the map card's with nothing between them.
   On the grid rather than on .anav alone: margin on the aside would drop it
   while the article stayed put, and the two columns would no longer start
   level. .acct--navright is the area pages' own modifier, so the account pages
   that share .acct--nav are untouched. */
.acct--navright{margin-top:var(--s-7)}

@media(min-width:900px){
  .acct--navright{grid-template-columns:minmax(0,1fr) 262px}
  .acct--navright > .anav{order:2}
}

.facts{display:grid; gap:1px; background:var(--ink-700);
  border:1px solid var(--ink-700); border-radius:var(--r-card); overflow:hidden}
@media(min-width:620px){.facts{grid-template-columns:repeat(2,minmax(0,1fr))}}
@media(min-width:1100px){.facts{grid-template-columns:repeat(4,minmax(0,1fr))}}

.fact{display:flex; align-items:flex-start; gap:var(--s-4);
  padding:var(--s-5); background:var(--ink-900)}
.fact > i{flex:none; margin-top:3px; font-size:1rem; color:var(--fuse-gold)}
.fact > span{display:flex; flex-direction:column; gap:4px; min-width:0}
.fact__k{font-size:.75rem; font-weight:700; letter-spacing:.08em;
  text-transform:uppercase; color:var(--text-tertiary)}
.fact__v{font-family:var(--font-display); font-weight:700; font-size:1.25rem;
  line-height:1.15; color:var(--text-primary)}
.fact__v a{color:var(--text-primary)}
.fact__v a:hover{color:var(--fuse-gold); text-decoration:none}
.fact__v small{display:block; margin-top:4px; font-family:var(--font-body);
  font-weight:400; font-size:.8125rem; line-height:1.4; color:var(--text-secondary)}
.fact__v .dot{margin-right:8px}

/* Postcode checker on the area pages (deploy 105). */
.pcheck{display:flex; flex-wrap:wrap; gap:var(--s-2); margin-top:var(--s-5)}
.pcheck input{flex:1 1 12rem; min-height:40px; padding:0 var(--s-3);
  background:var(--ink-900); color:var(--text-primary);
  border:1px solid var(--ink-600); border-radius:var(--r-control);
  font-family:inherit; font-size:.9375rem; text-transform:uppercase}
.pcheck input::placeholder{text-transform:none; color:var(--text-tertiary)}
.pcheck input:focus{outline:2px solid var(--spark-cyan); outline-offset:-1px;
  border-color:var(--spark-cyan)}
.pcheck .btn{flex:0 0 auto}
.pcheck__out{margin:var(--s-3) 0 0; font-size:.875rem; line-height:1.5;
  color:var(--text-secondary)}
.pcheck__out[hidden]{display:none}
.pcheck__out--yes{color:var(--in-stock)}
.pcheck__out--no{color:var(--error)}

/* The ordering and contact band on the area pages (deploy 107).
   --ink-band is the same ground .band--surface uses, which is what the concept
   asked for; it is a separate modifier because this band is not a --surface in
   the structural sense and should not pick up rules aimed at those. */
.band--ink{background:var(--ink-band)}
.band + .band--ink{padding-top:var(--band-pad)}
/* No font-size here. These were set to 1.5rem and came out at 24px against the
   38.6px of the rail heading beside them; dropping the override lets both take
   the same h2 clamp, so the two read as the same level of heading. */
.band--ink .prose h2{margin:0 0 var(--s-3)}
.band--ink .prose h2:not(:first-child){margin-top:var(--s-5)}
.band--ink .prose p{margin:0 0 var(--s-3); max-width:60ch}
.band--ink .prose p:last-child{margin-bottom:0}
.band--ink .prose a{color:var(--spark-cyan)}

/* ---------------------------------------------------------------------------
   Area hero: the trade line and the click-to-load map (deploy 112)
   .lochero and .locacts are the shop pages' own classes, reused rather than
   redefined — the concept gives .lochero a different meaning, and taking that
   name would have broken /shops/*.
   --------------------------------------------------------------------------- */
.loctrade{font-family:var(--font-display); font-weight:600; font-size:1.5rem;
  text-transform:uppercase; letter-spacing:.02em; color:var(--spark-cyan);
  margin:0 0 var(--s-5)}

.mapcard{position:relative; border:1px solid var(--ink-700); border-radius:var(--r-tile);
  background:var(--ink-800); overflow:hidden; min-height:320px; display:grid;
  place-items:center; text-align:center; padding:var(--s-6)}
@media(min-width:1024px){.mapcard{min-height:420px}}
.mapcard__grid{position:absolute; inset:0; opacity:.5;
  background-image:linear-gradient(var(--ink-700) 1px,transparent 1px),
    linear-gradient(90deg,var(--ink-700) 1px,transparent 1px);
  background-size:44px 44px}
.mapcard__road{position:absolute; inset:0; opacity:.65}
.mapcard__inner{position:relative; display:grid; justify-items:center; gap:var(--s-3)}
.mapcard__pin{font-size:2.5rem; color:var(--magenta)}
.mapcard__t{font-family:var(--font-display); font-weight:700; text-transform:uppercase;
  font-size:1.25rem; letter-spacing:.02em}
.mapcard__s{font-size:.875rem; line-height:1.5; color:var(--text-tertiary); max-width:34ch}
.mapcard__acts{display:flex; gap:var(--s-2); flex-wrap:wrap; justify-content:center;
  margin-top:var(--s-2)}

/* Once loaded the map fills the card and the placeholder gets out of the way. */
.mapcard--live{padding:0}
.mapcard--live .mapcard__inner,
.mapcard--live .mapcard__grid,
.mapcard--live .mapcard__road{display:none}
.mapcard__frame{width:100%; height:100%; min-height:320px; border:0; display:block}
@media(min-width:1024px){.mapcard__frame{min-height:420px}}

/* ---------------------------------------------------------------------------
   Video poster (deploy 114)
   The still is blurred rather than shown sharp: it is a hint at what is behind
   the button, not the thing itself, and a sharp frame reads as an image that
   failed to become a video.
   --------------------------------------------------------------------------- */
.vidwrap--poster{position:relative; overflow:hidden; isolation:isolate}
.vidwrap__poster{position:absolute; inset:0; width:100%; height:100%;
  object-fit:cover; z-index:0;
  filter:blur(10px) saturate(1.1) brightness(.55);
  /* the blur softens the edges, so the image is pushed past them */
  transform:scale(1.12);
  transition:filter var(--t-micro) ease-out, transform var(--t-micro) ease-out}
.vidwrap--poster > span{position:relative; z-index:1}

/* Sharpens a little on hover, so the control feels like it is about to open. */
.vidwrap--poster:hover .vidwrap__poster,
.vidwrap--poster:focus-visible .vidwrap__poster{filter:blur(7px) saturate(1.15) brightness(.62);
  transform:scale(1.15)}

@media(prefers-reduced-motion:reduce){
  .vidwrap__poster{transition:none}
  .vidwrap--poster:hover .vidwrap__poster{transform:scale(1.12)}
}
