/* BMC Auto Electrical - shared stylesheet.
   Generated by extract_assets.py from the approved single-page concept.
   That extraction was a ONE-TIME migration - this is now an AUTHORED file.
   Edit it directly. */
@font-face{
    font-family:'Oswald';
    src:url('oswald.woff2') format('woff2');
    font-weight:200 700; font-style:normal; font-display:swap;
  }
  :root{
    --accent:#08F1F7; --accent-bri:#6BF7FA; --ok:#3DD68C;
    --ink:#131519; --panel:#1A1D22; --panel2:#21252B; --line:#2C313A;
    --text:#EDF1F5; --muted:#98A2AD; --steel:#C2CAD4;
    /* Text sitting ON a cyan fill. There were three of these doing the same job -
       #111 on .btn-o, #0C1114 on an active chip, #062226 in the strip - which is
       why buttons did not quite match each other. One token now.
       --strip is a deliberately darker teal for the full-width call-to-action
       band, NOT the accent; it is tokenised so it stops looking like a mistake. */
    --on-accent:#0C1114; --strip:#0FA9B5; --on-strip:#062226;
    /* FULL lockup - wordmark, descriptor and bolt - with "AUTO ELECTRICAL"
       recoloured white, at the client's request 2026-07-22. It was #383838 in his
       supplied file, which is invisible on a dark header.
       logo-dark.png (the compact mark, no descriptor) is still in assets. If he
       decides the descriptor is too small to be worth the taller header, point
       --logo back at it and restore the sizes noted beside each rule below. */
    --logo:url('logo-full.png');
    --head:'Oswald','Arial Narrow',Arial,sans-serif;
    --body:-apple-system,'Segoe UI',Roboto,Arial,Helvetica,sans-serif;
  }
  *{box-sizing:border-box}
  body{margin:0;background:var(--ink);color:var(--text);font-family:var(--body);line-height:1.6;-webkit-font-smoothing:antialiased}
  h1,h2,h3,.h{font-family:var(--head);font-weight:600;line-height:1.05;margin:0;letter-spacing:.01em;text-transform:uppercase}
  a{color:inherit;text-decoration:none}
  .wrap{max-width:1160px;margin:0 auto;padding:0 22px}
  .accent{color:var(--accent)}
  .eyebrow{font-family:var(--head);text-transform:uppercase;letter-spacing:.22em;font-size:13px;color:var(--accent);font-weight:500}

  /* top bar */
  header{position:sticky;top:0;z-index:50;background:rgba(15,17,21,.9);backdrop-filter:blur(8px);border-bottom:1px solid var(--line)}
  /* 92px, was 70px with the compact mark. The full lockup is 2.59:1 and its
     descriptor band is only 15.5% of the artwork height, so the whole logo has to
     be about 61px tall before "AUTO ELECTRICAL" reaches 9.4px and becomes
     readable. At the old 36px it rendered at 5.6px, which reads as a fault rather
     than as type. Revert to 70px if the compact mark comes back. */
  .bar{display:flex;align-items:center;justify-content:space-between;height:92px}
  .logo{display:flex;align-items:center}
  .logo .mk{display:block;background:var(--logo) center/contain no-repeat;width:158px;height:61px}   /* compact was 116x36 */
  .foot .logo .mk{width:132px;height:51px}   /* compact was 103x32 */
  /* Stepped down twice rather than once: the descriptor has to stay above about
     7px or it stops being type again, so the logo cannot shrink as far as the
     compact mark could. */
  @media(max-width:820px){.bar{height:84px}.logo .mk{width:140px;height:54px}}
  @media(max-width:520px){.bar{height:78px}.logo .mk{width:124px;height:48px}}
    nav{display:flex;align-items:center;gap:28px}
  nav .lnk{font-family:var(--head);text-transform:uppercase;letter-spacing:.08em;font-size:14px;color:var(--steel)}
  nav .lnk:hover{color:var(--accent)}
  /* background and appearance are here for <button>, not for <a>. Every .btn on
     this site was an anchor until the parts page added a real button, and an
     anchor has no background of its own - so .btn-ghost, which sets a border and
     a near-white text colour and nothing else, rendered as the browser's grey
     buttonface with near-white text on it. An empty pale slab.
     appearance:none matters just as much on iOS, which restyles buttons with its
     own radius and gradient regardless of what the border-radius above says. */
  .btn{display:inline-flex;align-items:center;gap:9px;font-family:var(--head);text-transform:uppercase;letter-spacing:.06em;font-weight:600;font-size:15px;padding:12px 20px;border-radius:3px;cursor:pointer;border:2px solid transparent;background:transparent;-webkit-appearance:none;appearance:none;transition:.15s}
  .btn-o{background:var(--accent);color:var(--on-accent)}
  .btn-o:hover{background:var(--accent-bri)}
  .btn-ghost{border-color:var(--line);color:var(--text)}
  .btn-ghost:hover{border-color:var(--accent);color:var(--accent)}
  @media(max-width:820px){nav .lnk{display:none}}

  /* hero */
  .hero{position:relative;overflow:hidden;border-bottom:1px solid var(--line)}
  .hero .bg{position:absolute;inset:0;background:
    radial-gradient(1100px 640px at 84% -6%,rgba(8,241,247,.10),transparent 60%),
    #15181D}
  /* Hero photo. The #15181D above stays as the base colour - it is what shows while the
     image loads and if it ever 404s, so the white hero text is never on white.
     The ::after scrim is doing real work, not decoration: the photo has a bright sky on
     the right and the headline sits on the left, so without it the lead paragraph drops
     under 4.5:1 at the top-right corner. Darkest on the left, where the text is. */
  .hero .bg img{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;
    object-position:50% 42%;opacity:.5}
  .hero .bg::after{content:"";position:absolute;inset:0;background:
    linear-gradient(100deg,rgba(21,24,29,.94) 0%,rgba(21,24,29,.82) 38%,rgba(21,24,29,.55) 72%,rgba(21,24,29,.5) 100%),
    linear-gradient(to top,rgba(21,24,29,.85),transparent 45%)}
  @media(max-width:820px){
    /* On a phone the text stack is full-width and centred over the photo, so the
       left-weighted scrim above no longer lines up with it - go flat and heavier. */
    .hero .bg img{object-position:50% 46%;opacity:.4}
    .hero .bg::after{background:
      linear-gradient(to top,rgba(21,24,29,.92),rgba(21,24,29,.72) 55%,rgba(21,24,29,.8))}
  }
  /* ---------------------------------------------------------------------
     HERO WIRING LOOM. The lightning-bolt animation was reverted to this
     abstract circuit/loom on 2026-07-23 at the client's request - he preferred
     the original. Anchored right and dissolving before the headline so the copy
     side of the hero stays clean and the lead paragraph keeps its contrast.
     --------------------------------------------------------------------- */
  .hero .circuit{position:absolute;inset:0;opacity:.5;-webkit-mask-image:linear-gradient(100deg,transparent 34%,#000 68%);mask-image:linear-gradient(100deg,transparent 34%,#000 68%)}
  .hero .circuit .trace{stroke:var(--accent);stroke-width:1.4;fill:none;opacity:.28}
  .hero .circuit .tie{stroke:var(--accent);stroke-width:3.4;fill:none;opacity:.2;stroke-linecap:round}
  .hero .circuit .conn{stroke:var(--accent);stroke-width:1.5;fill:none;opacity:.34}
  .hero .circuit .surge{stroke:#7DF9FC;stroke-width:2.1;fill:none;stroke-linecap:round;stroke-dasharray:13 200;opacity:0}
  .hero .circuit .node{fill:var(--accent);opacity:.5}
  @keyframes bmc-surge{0%{stroke-dashoffset:13;opacity:0}7%{opacity:.85}86%{opacity:.85}100%{stroke-dashoffset:-100;opacity:0}}
  @keyframes bmc-node{0%,100%{opacity:.3}50%{opacity:.8}}
  @media(prefers-reduced-motion:no-preference){
    .hero .circuit .surge{animation:bmc-surge 17s linear infinite;animation-direction:reverse}
    .hero .circuit .surge:nth-of-type(2){animation-duration:24s;animation-delay:-6s}
    .hero .circuit .surge:nth-of-type(3){animation-duration:19.5s;animation-delay:-11s}
    .hero .circuit .surge:nth-of-type(4){animation-duration:21.5s;animation-delay:-3.5s}
    .hero .circuit .surge:nth-of-type(5){animation-duration:26s;animation-delay:-14s}
    .hero .circuit .node{animation:bmc-node 7.5s ease-in-out infinite}
    .hero .circuit .node:nth-of-type(2){animation-duration:9.1s;animation-delay:-1.9s}
    .hero .circuit .node:nth-of-type(3){animation-duration:8.3s;animation-delay:-3.4s}
    .hero .circuit .node:nth-of-type(4){animation-duration:10.2s;animation-delay:-.8s}
    .hero .circuit .node:nth-of-type(5){animation-duration:8.9s;animation-delay:-4.6s}
    .hero .circuit .node:nth-of-type(6){animation-duration:9.7s;animation-delay:-2.5s}
  }
  @media(hover:hover) and (pointer:fine){
    .hero .circuit{transition:opacity .7s ease}
    .hero .circuit .trace{transition:opacity .7s ease,stroke-width .7s ease,stroke .7s ease}
    .hero .circuit .surge{transition:stroke-width .7s ease,filter .7s ease}
    .hero .circuit .node{transition:opacity .7s ease,r .7s ease}
    .hero:has(.cta-row .btn:is(:hover,:focus-visible)) .circuit{opacity:.62;transition-duration:.35s}
    .hero:has(.cta-row .btn:is(:hover,:focus-visible)) .circuit .trace{opacity:.38;stroke-width:1.55;transition-duration:.35s}
    .hero:has(.cta-row .btn:is(:hover,:focus-visible)) .circuit .surge{stroke-width:2.4;transition-duration:.35s}
    .hero:has(.cta-row .btn:is(:hover,:focus-visible)) .circuit .node{opacity:.62;r:3.6;transition-duration:.35s}
  }
  /* This is a .wrap, so it must keep .wrap's box - 1160px and 22px of side
     padding - or the hero stops lining up with every other section on the page.
     Overriding max-width to 760px here left the copy centred in a narrower box
     while the rest of the page centred in the wider one, so on a wide monitor the
     hero visibly drifted right of everything above and below it. Overriding the
     padding to "92px 0 84px" also dropped the side padding, which put the hero
     text hard against the screen edge on a phone.
     The 760px measure belongs to the CONTENT, not to the container. */
  .hero-in{position:relative;padding:92px 22px 84px;max-width:1160px}
  .hero-in > *{max-width:760px}
  .hero h1{font-size:clamp(40px,6.6vw,78px);text-wrap:balance}
  .hero p.lead{font-size:19px;color:var(--steel);margin:22px 0 0;max-width:60ch}
  .cta-row{display:flex;flex-wrap:wrap;gap:14px;margin-top:34px}
  .tags{display:flex;flex-wrap:wrap;gap:10px 22px;margin-top:40px;padding-top:26px;border-top:1px solid var(--line)}
  .tags span{font-family:var(--head);text-transform:uppercase;letter-spacing:.1em;font-size:13.5px;color:var(--muted);display:flex;align-items:center;gap:8px}
  .tags span::before{content:"";width:8px;height:8px;background:var(--accent);border-radius:2px;transform:rotate(45deg)}

  /* 74px was inherited from the single-page concept, where six sections on one
     scrolling page needed the separation. Across five pages it was 888px of pure
     whitespace on the home page alone - 14% of its total height. */
  section{padding:64px 0}
  .sec-head{max-width:640px;margin-bottom:40px}
  .sec-head h2{font-size:clamp(28px,4vw,42px);margin-top:10px}
  .sec-head p{color:var(--muted);margin:14px 0 0;font-size:16px}

  /* sectors */
  .sectors{display:grid;grid-template-columns:repeat(3,1fr);gap:18px}
  .sector{background:var(--panel);border:1px solid var(--line);border-radius:2px;padding:26px 24px}
  .sector h3{font-size:23px;color:var(--accent)}
  .sector p{color:var(--muted);margin:12px 0 0;font-size:15px}
  @media(max-width:760px){.sectors{grid-template-columns:1fr}}

  /* services */
  .services{background:#0E1116;border-top:1px solid var(--line);border-bottom:1px solid var(--line)}
  .grid{display:grid;grid-template-columns:repeat(3,1fr);gap:18px}
  .card{background:var(--panel);border:1px solid var(--line);border-radius:2px;padding:26px 24px;transition:border-color .15s}
  .card:hover{border-color:var(--accent)}
  .ico{width:46px;height:46px;border-radius:3px;background:rgba(8,241,247,.10);display:grid;place-items:center;margin-bottom:16px}
  .ico svg{width:26px;height:26px;stroke:var(--accent);fill:none;stroke-width:1.7}
  .card h3{font-size:20px}
  .card p{color:var(--muted);margin:9px 0 0;font-size:14.5px}
  @media(max-width:860px){.grid{grid-template-columns:1fr 1fr}}
  @media(max-width:560px){.grid{grid-template-columns:1fr}}

  /* why */
  .why{display:grid;grid-template-columns:1.1fr 1fr;gap:50px;align-items:center}
  .why ul{list-style:none;padding:0;margin:24px 0 0;display:grid;gap:16px}
  .why li{display:flex;gap:14px;align-items:flex-start}
  .why li svg{width:24px;height:24px;flex:none;stroke:var(--accent);fill:none;stroke-width:2;margin-top:2px}
  .why li b{font-family:var(--head);text-transform:uppercase;letter-spacing:.03em;font-weight:500;font-size:17px;display:block}
  .why li span{color:var(--muted);font-size:14.5px}
  .why-panel{background:linear-gradient(150deg,rgba(8,241,247,.07),var(--panel));border:1px solid var(--line);border-radius:2px;padding:34px}
  .why-panel .big{font-family:var(--head);font-weight:700;font-size:60px;color:var(--accent);line-height:1;text-transform:uppercase}
  .why-panel .lbl{font-family:var(--head);text-transform:uppercase;letter-spacing:.14em;color:var(--steel);font-size:13px;margin-top:4px}
  .why-panel hr{border:0;border-top:1px solid var(--line);margin:24px 0}
  @media(max-width:820px){.why{grid-template-columns:1fr;gap:32px}}

  /* cta strip
     .strip is a <section>, so it was picking up the global section padding AND
     its own inner padding - 114px top and bottom, which read as a band floating
     in a gap rather than a band. The outer padding is zeroed and all the spacing
     is controlled here in one place. */
  .strip{background:var(--strip);color:var(--on-strip);padding:0}
  .strip .in{display:flex;flex-wrap:wrap;align-items:center;justify-content:space-between;gap:20px;padding:48px 0}
  .strip h2{font-size:clamp(26px,3.6vw,38px);color:var(--on-strip)}
  .strip p{margin:6px 0 0;font-weight:600}
  .strip .phone{font-family:var(--head);font-weight:700;font-size:clamp(30px,4vw,46px);color:var(--on-strip);white-space:nowrap}
  .strip .phone small{display:block;font-size:13px;letter-spacing:.14em;font-weight:500;text-align:right}

  /* contact */
  .contact{display:grid;grid-template-columns:1fr 1.05fr;gap:46px}
  .cinfo .row{display:flex;gap:14px;align-items:flex-start;padding:16px 0;border-bottom:1px solid var(--line)}
  .cinfo .row svg{width:22px;height:22px;stroke:var(--accent);fill:none;stroke-width:1.8;flex:none;margin-top:3px}
  .cinfo .row .lbl{font-family:var(--head);text-transform:uppercase;letter-spacing:.12em;font-size:12px;color:var(--muted)}
  .cinfo .row .val{font-size:17px}
  form{background:var(--panel);border:1px solid var(--line);border-radius:2px;padding:28px}
  form label{font-family:var(--head);text-transform:uppercase;letter-spacing:.08em;font-size:12px;color:var(--muted);display:block;margin:0 0 6px}
  form .f{margin-bottom:16px}
  form input,form textarea{width:100%;background:var(--ink);border:1px solid var(--line);border-radius:2px;color:var(--text);padding:12px 13px;font-family:var(--body);font-size:15px}
  form input:focus,form textarea:focus{outline:none;border-color:var(--accent)}
  /* Placeholders were never styled, so they fell back to the browser's own grey -
     #757575 in Chrome, which is not a colour on this site and sits at 3.97:1 on the
     input background, under the 4.5 minimum. It also differs per browser, so the
     form looked washed out in one and not another. --muted is 7:1 here.
     opacity:1 is required: Firefox dims placeholders by default and would undo it. */
  form input::placeholder,form textarea::placeholder{color:var(--muted);opacity:1}
  form .btn-o{width:100%;justify-content:center;border:0;font-size:16px;padding:14px}
  @media(max-width:820px){.contact{grid-template-columns:1fr;gap:30px}}

  footer{border-top:1px solid var(--line);background:#0E1116;padding:40px 0 30px}
  .foot{display:flex;flex-wrap:wrap;justify-content:space-between;gap:20px;align-items:center}
  .foot .muted{color:var(--muted);font-size:13.5px}
  .draft{background:var(--panel2);border:1px dashed var(--accent);border-radius:2px;padding:10px 16px;font-size:13px;color:var(--steel);text-align:center;margin-top:22px}
  .draft b{color:var(--accent);font-family:var(--head);letter-spacing:.06em}

/* ============================================================================
   NAVIGATION - desktop + mobile
   The single-page concept hid nav links entirely below 820px, which was fine
   when everything was one scrolling page and the phone button was all a mobile
   visitor needed. With five pages that would leave phone users - over 70% of
   trade traffic - with no way to reach anything.
   ========================================================================== */
.mobilebar{display:none;align-items:center;gap:10px}
.btn-call{padding:11px 13px}
/* color is set because a <button> otherwise inherits the UA's black buttontext.
   Only the visually-hidden label sits in here so nothing showed, but it is the
   same trap that made the Show more button a pale slab - left unset, it waits. */
.navtoggle{background:var(--panel);border:1px solid var(--line);border-radius:3px;
  color:var(--text);width:44px;height:44px;display:grid;place-items:center;
  cursor:pointer;padding:0}
.navtoggle:hover{border-color:var(--accent)}
.navtoggle:focus-visible{outline:2px solid var(--accent);outline-offset:2px}
.sr{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap}
.bars{position:relative;display:block;width:19px;height:2px;background:var(--text);border-radius:2px}
.bars::before,.bars::after{content:"";position:absolute;left:0;width:19px;height:2px;
  background:var(--text);border-radius:2px;transition:transform .2s ease}
.bars::before{top:-6px}
.bars::after{top:6px}
[aria-expanded="true"] .bars{background:transparent}
[aria-expanded="true"] .bars::before{transform:translateY(6px) rotate(45deg)}
[aria-expanded="true"] .bars::after{transform:translateY(-6px) rotate(-45deg)}

/* .mobilenav is a <nav>, and the base `nav{display:flex}` rule would otherwise
   beat the [hidden] attribute on specificity and leave the panel permanently
   open. This pair is load-bearing, not tidying. */
.mobilenav{display:block;border-top:1px solid var(--line);background:var(--panel)}
.mobilenav[hidden]{display:none}
.mobilenav .wrap{display:flex;flex-direction:column;padding-top:6px;padding-bottom:14px}
.mlnk{font-family:var(--head);text-transform:uppercase;letter-spacing:.08em;font-size:16px;
  color:var(--steel);padding:15px 2px;border-bottom:1px solid var(--line)}
.mlnk:last-child{border-bottom:0}
.mlnk[aria-current="page"]{color:var(--accent)}
.mcall{color:var(--accent)}
@media(max-width:820px){.desktopnav{display:none}.mobilebar{display:flex}}
@media(min-width:821px){.mobilenav{display:none}}
@media(prefers-reduced-motion:reduce){.bars::before,.bars::after{transition:none}}

/* ============================================================================
   PARTS CATALOGUE
   ========================================================================== */
.pagehead{padding:64px 0 26px}
.pagehead h1{font-size:clamp(34px,5vw,54px);margin-top:10px}
.pagehead p.lead{color:var(--steel);font-size:18px;margin:16px 0 0;max-width:62ch}
/* Trade login now lives in the header ("Trade Login" -> opens the dialog; when
   signed in the same button becomes "Log out"). The parts page keeps only the
   locked price chips and a one-line note, so the catalogue starts right up top. */
.tradeopen{background:none;border:0;cursor:pointer;font:inherit;padding:0}
.lnk.tradeopen{color:var(--steel)}
.lnk.tradeopen:hover,.mlnk.tradeopen:hover{color:var(--accent)}
/* Parts-page note pointing at the header login. */
.tradenote{margin:22px 0 0;padding:12px 16px;border:1px solid var(--line);
  border-left:3px solid var(--accent);background:var(--panel);border-radius:2px;
  color:var(--muted);font-size:14.5px;max-width:70ch}
.tradenote b{color:var(--text);font-weight:600}
/* Signed-in affordances, all driven by body.trade-in: the green header badge and
   the green "you're viewing trade pricing" note swap in for their logged-out
   counterparts. */
.tradenote-in{display:none;border-left-color:var(--ok)}
.tradenote-in b{color:var(--ok)}
body.trade-in .tradenote-out{display:none}
body.trade-in .tradenote-in{display:block}
.tradebadge{display:none;align-items:center;gap:6px;font-family:var(--head);text-transform:uppercase;
  letter-spacing:.07em;font-size:12px;font-weight:600;color:var(--on-accent);background:var(--ok);
  padding:6px 11px;border-radius:3px;white-space:nowrap}
body.trade-in .tradebadge{display:inline-flex}
.tradebadge svg{width:13px;height:13px;stroke:var(--on-accent);fill:none;stroke-width:3;
  stroke-linecap:round;stroke-linejoin:round}

/* Login dialog. Centred modal with its own backdrop. */
.tradedialog{position:relative;border:1px solid var(--line);border-top:3px solid var(--accent);
  border-radius:3px;background:var(--panel);color:var(--text);max-width:400px;
  width:calc(100% - 32px);padding:26px 24px 22px}
.tradedialog::backdrop{background:rgba(8,10,13,.66)}
.td-x{position:absolute;top:8px;right:10px;background:none;border:0;color:var(--muted);
  font-size:26px;line-height:1;cursor:pointer;padding:2px 8px}
.td-x:hover{color:var(--accent)}
.td-h{font-size:24px}
.td-copy{color:var(--muted);font-size:14.5px;margin:10px 0 18px}
/* Reset the global form box (panel/border/padding) - inside the dialog it would be a box-in-a-box. */
.tradedialog form{display:flex;flex-direction:column;gap:12px;background:none;border:0;padding:0}
.tradedialog input{width:100%;background:var(--ink);border:1px solid var(--line);border-radius:3px;
  color:var(--text);padding:12px 13px;font-family:var(--body);font-size:15px}
.tradedialog input:focus-visible{outline:2px solid var(--accent);outline-offset:1px}
.tradedialog input::placeholder{color:var(--muted);opacity:1}
.tradedialog .btn-o{justify-content:center;border:0;font-size:16px;padding:13px}
/* :empty keeps the slot from reserving a blank line until there is a message. */
.tb-msg{margin:12px 0 0;font-size:14px;color:#FFB39E}
.tb-msg:empty{display:none}
.td-hint{margin:14px 0 0;font-size:13px;color:var(--muted)}
.td-hint a{color:var(--accent)}
/* Preview-only password, inline in the dialog. build.py emits it on preview builds only. */
.tb-preview{display:block;margin-top:8px;color:var(--steel)}
.tb-preview code{font-family:ui-monospace,Menlo,Consolas,monospace;color:var(--accent);
  background:var(--ink);padding:1px 6px;border-radius:3px;font-size:12.5px}
/* Discreet owner/admin entry in the footer -> the CMS at /admin (GitHub-gated). */
.ownerlink{color:var(--muted);text-decoration:underline}
.ownerlink:hover{color:var(--accent)}

/* Builder credit. Deliberately quiet - same weight as the owner link, one line
   below the copyright so it reads as a byline and not as a second brand. */
.credit{color:var(--muted);text-decoration:underline}
.credit:hover{color:var(--accent)}

/* Locked price placeholder on a card. Reads as a chip, not a number, so a full
   grid of them looks deliberate rather than broken. Once JS swaps in the real
   price the .locked class comes off (and textContent drops the padlock), and the
   standard .price rule below takes over. */
.part .price.locked{font-family:var(--head);text-transform:uppercase;letter-spacing:.07em;
  font-size:12.5px;font-weight:600;color:var(--steel);display:inline-flex;align-items:center;
  gap:7px;padding:6px 10px;border:1px solid var(--line);border-radius:3px;background:var(--panel2)}
.part .price.locked .lock{width:13px;height:13px;flex:none;stroke:var(--accent);fill:none;
  stroke-width:1.8;stroke-linecap:round;stroke-linejoin:round}
.controls{display:flex;flex-wrap:wrap;gap:12px;align-items:center;
  padding:26px 0 30px;border-bottom:1px solid var(--line)}
.controls input[type=search]{flex:1 1 260px;min-width:0;background:var(--panel);
  border:1px solid var(--line);border-radius:3px;color:var(--text);
  padding:12px 14px;font-size:15px;font-family:var(--body)}
.controls input[type=search]:focus-visible{outline:2px solid var(--accent);outline-offset:1px}
.chips{display:flex;flex-wrap:wrap;gap:8px}
.chip{font-family:var(--head);text-transform:uppercase;letter-spacing:.06em;font-size:13px;
  padding:9px 14px;border-radius:3px;cursor:pointer;background:var(--panel);
  border:1px solid var(--line);color:var(--steel);transition:.15s}
.chip:hover{border-color:var(--accent);color:var(--accent)}
.chip[aria-pressed="true"]{background:var(--accent);border-color:var(--accent);color:var(--on-accent)}
.count{color:var(--muted);font-size:14px;margin:18px 0 0}
.parts{display:grid;grid-template-columns:repeat(3,1fr);gap:18px;padding:22px 0 10px}
@media(max-width:900px){.parts{grid-template-columns:repeat(2,1fr)}}
@media(max-width:600px){.parts{grid-template-columns:1fr}}
.part{background:var(--panel);border:1px solid var(--line);border-radius:2px;
  display:flex;flex-direction:column;overflow:hidden}
/* Same trap as .mobilenav above: `display:flex` on the class beats the browser's
   own [hidden]{display:none}, so the search and category filters set the attribute
   and nothing disappeared. The count updated while every card stayed on screen,
   which reads as "search is broken" rather than "a CSS rule won". */
.part[hidden]{display:none}
/* No-photo state. Flat grey behind one faint glyph read as a missing image; this
   reads as a category tile. The wash is angled off the top-left so twelve of them
   down a page have a consistent light source instead of looking like twelve holes,
   and the glyph is large and confident rather than apologetic. */
.part .shot{aspect-ratio:4/3;display:grid;place-items:center;
  border-bottom:1px solid var(--line);position:relative;
  background:
    radial-gradient(120% 100% at 18% 0%,rgba(8,241,247,.10),transparent 62%),
    linear-gradient(158deg,var(--panel2) 0%,var(--panel) 100%)}
/* Hairline grid, masked to fade out - gives the tile some surface without
   competing with the glyph. Pure CSS, no asset, no request. */
.part .shot::before{content:"";position:absolute;inset:0;opacity:.5;
  background-image:linear-gradient(rgba(255,255,255,.05) 1px,transparent 1px),
    linear-gradient(90deg,rgba(255,255,255,.05) 1px,transparent 1px);
  background-size:22px 22px;
  -webkit-mask-image:radial-gradient(85% 75% at 50% 45%,#000,transparent 78%);
  mask-image:radial-gradient(85% 75% at 50% 45%,#000,transparent 78%)}
.part .shot span{position:absolute;bottom:10px;font-family:var(--head);text-transform:uppercase;
  letter-spacing:.16em;font-size:11px;color:var(--muted);opacity:.9}
.part .shot svg{position:relative;width:62px;height:62px;stroke:var(--accent);fill:none;
  stroke-width:1.4;opacity:.55}
@media(prefers-reduced-motion:no-preference){
  .part .shot svg{transition:opacity .35s ease,transform .35s ease}
  .part:hover .shot svg{opacity:.8;transform:scale(1.06)}
}
/* Real photo. Parts get shot on a bench in whatever light the workshop has, so
   cover-crop rather than contain - a letterboxed phone photo on a dark panel looks
   broken, a crop just looks tight. */
.part .shot.has-img{display:block;background:#fff}
.part .shot.has-img img{width:100%;height:100%;object-fit:cover;display:block}
.part .body{padding:18px 18px 20px;display:flex;flex-direction:column;flex:1}
.part .meta{font-family:var(--head);text-transform:uppercase;letter-spacing:.12em;
  font-size:11px;color:var(--muted)}
.part h3{font-size:19px;margin:7px 0 0;line-height:1.15}
.part p{color:var(--muted);font-size:14.5px;margin:10px 0 0;flex:1}
.part .foot{display:flex;align-items:flex-end;justify-content:space-between;gap:12px;
  margin-top:16px;padding-top:14px;border-top:1px solid var(--line)}
.part .price{font-family:var(--head);font-weight:700;font-size:23px;color:var(--accent);line-height:1}
.part .price.poa{font-size:17px;letter-spacing:.04em}
.part .lead{font-family:var(--head);text-transform:uppercase;letter-spacing:.1em;
  font-size:11px;color:var(--muted);margin-top:5px}
.part .lead.stock{color:var(--ok)}
.part .btn{padding:10px 15px;font-size:13.5px;flex:none}
.empty{padding:50px 0 10px;color:var(--muted)}
.empty b{display:block;font-family:var(--head);text-transform:uppercase;font-size:20px;
  color:var(--text);margin-bottom:8px}
.empty a{color:var(--accent)}

.morewrap{display:flex;justify-content:center;padding:20px 0 4px}
/* .btn is display:inline-flex, which beats the browser's own [hidden]{display:none}.
   Written out rather than assumed - this is the third element on this site to hit
   that exact trap (.mobilenav and .part were the first two), and the failure is
   silent: the button just sits there offering more of a list that has no more. */
.showmore[hidden]{display:none}
.part:focus-visible{outline:2px solid var(--accent);outline-offset:2px}

/* ---------------------------------------------------------------------------
   PARTS ON A PHONE
   Stacked cards ran 486px each - 8.7 screens at twelve parts, and this page is
   sold on going to 500 without a rebuild. Below 600px the card turns on its side
   into the standard mobile list row: square thumbnail left, text right. That plus
   the "Show more" cap is what makes the page a fixed length at any catalogue size.
   ------------------------------------------------------------------------- */
@media(max-width:600px){
  .parts{gap:10px;padding:16px 0 6px}
  .part{flex-direction:row;align-items:stretch}
  /* aspect-ratio would fight a flex row, so the thumbnail is sized outright and
     told not to grow or shrink. The divider moves from the bottom to the side. */
  .part .shot{width:106px;flex:none;aspect-ratio:auto;border-bottom:0;
    border-right:1px solid var(--line)}
  .part .shot svg{width:38px;height:38px}
  /* The draft-only "photo to come" caption does not fit a 106px tile and would
     wrap into the glyph. It is a note to us, so on a phone it just goes. */
  .part .shot span{display:none}
  .part .body{padding:12px 14px 13px;min-width:0}
  .part h3{font-size:16px;margin-top:5px}
  /* Two lines of description, then ellipsis. Not hidden outright: it is often
     where the fitment detail lives, and search still matches the full text
     regardless of what is displayed. */
  .part p{font-size:13.5px;margin-top:6px;flex:0 1 auto;
    display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}
  /* Price and Enquire stay side by side - measured, they fit in the ~240px the
     body gets at 375px, and forcing them to stack cost 50px a card for nothing.
     They wrap instead of squeezing, which is what "Price on application" needs:
     it is nearly three times the width of "$128" and would otherwise crush the
     button. Wrapping is the rare case, so the common card stays short. */
  .part .foot{flex-wrap:wrap;gap:8px 12px;margin-top:12px;padding-top:11px}
  .part .price{font-size:20px}
  .part .price.poa{font-size:15px}
  .part .btn{padding:9px 14px;font-size:13px}
}

/* ============================================================================
   PAGE SPLIT (Phase 1) - furniture shared by Services / About / Contact
   ========================================================================== */

/* Placeholder blocks, deliberately loud. These mark the spots waiting on the
   client's own words - his bio, his tickets, his testimonials. A quiet
   placeholder is one that ships, and "Lorem ipsum" on a live trade site is a
   worse outcome than an obvious cyan-dashed box nobody can miss. */
.tbc{border:1px dashed var(--accent);background:rgba(8,241,247,.04);border-radius:2px;
  padding:20px 22px}
.tbc .lbl{font-family:var(--head);text-transform:uppercase;letter-spacing:.16em;
  font-size:11px;color:var(--accent);display:block;margin-bottom:9px}
.tbc p{margin:0;font-size:15px;color:var(--muted)}
.tbc p+p{margin-top:10px}

/* "Read the rest" link. Every home-page section is now a teaser, so each one
   needs an obvious way onward or the split just buries content. */
.more{display:inline-flex;align-items:center;gap:9px;font-family:var(--head);
  text-transform:uppercase;letter-spacing:.08em;font-size:14px;color:var(--accent);
  margin-top:26px}
.more::after{content:"\2192";transition:transform .15s}
.more:hover::after{transform:translateX(4px)}
a.card:hover h3{color:var(--accent)}

/* Services detail sections. Heading and intro on the left, the actual list of
   work on the right - so a customer scanning for one specific job finds it
   without reading the prose. */
.svc{display:grid;grid-template-columns:1fr 1fr;gap:46px;align-items:start;
  padding:46px 0;border-top:1px solid var(--line)}
.svc:first-of-type{border-top:0;padding-top:10px}
/* Icon sits beside the heading rather than stacked above it, so the top of the
   heading lines up with the top of the list in the right-hand column. Stacked,
   the first bullet floated above the heading it belonged to. */
.svc .svchead{display:flex;align-items:center;gap:15px}
.svc .svchead .ico{margin-bottom:0;flex:none}
.svc h2{font-size:clamp(24px,3.2vw,34px)}
.svc .lead{color:var(--steel);font-size:16.5px;margin:14px 0 0;max-width:52ch}
.svc ul{list-style:none;padding:0;margin:0;display:grid;gap:12px}
.svc li{display:flex;gap:12px;color:var(--muted);font-size:15px;line-height:1.5}
.svc li::before{content:"";width:8px;height:8px;margin-top:7px;flex:none;
  background:var(--accent);border-radius:2px;transform:rotate(45deg)}
/* A linked service heading scrolled to from the home page teaser would otherwise
   sit hard under the sticky 70px header. */
.svc[id]{scroll-margin-top:108px}   /* sticky header 92px + breathing room; was 86 for the 70px header */
/* Photo band under a service section. Spans both grid columns so it sits below the
   copy and the bullet list rather than squeezing either. aspect-ratio + width/height
   on the tag means the space is reserved before the image arrives - these are all
   lazy-loaded and below the fold, so without it the page reflows as you scroll it. */
.svc .svcshot{grid-column:1/-1;display:block;width:100%;height:auto;aspect-ratio:16/9;
  object-fit:cover;border:1px solid var(--line);border-radius:12px;margin-top:6px;
  background:#0f1216}
/* Same treatment, standalone rather than inside a .svc grid. */
.bandshot{display:block;width:100%;height:auto;aspect-ratio:16/9;object-fit:cover;
  border:1px solid var(--line);border-radius:12px;background:#0f1216}
@media(max-width:820px){.bandshot{aspect-ratio:3/2}}

/* Confirmed credentials. Deliberately not styled as a .tbc placeholder any more -
   these are claims Ben has put his name to, so they should read as fact. */
.creds{list-style:none;padding:0;margin:0;display:grid;gap:14px;
  grid-template-columns:repeat(auto-fit,minmax(260px,1fr))}
.creds li{border:1px solid var(--line);border-left:3px solid var(--accent);
  border-radius:10px;padding:16px 18px;background:rgba(255,255,255,.02)}
.creds .k{display:block;font-family:var(--head);letter-spacing:.04em;
  text-transform:uppercase;font-size:15px;color:var(--text)}
.creds .v{display:block;margin-top:7px;color:var(--muted);font-size:14.5px;line-height:1.55}
@media(max-width:820px){.svc{grid-template-columns:1fr;gap:24px;padding:34px 0}
  /* Portrait originals cropped to 16:9 get short on a narrow screen - let them take
     a taller slice back so the subject is still readable at 375px. */
  .svc .svcshot{aspect-ratio:3/2}}

/* Parts enquiry banner on the contact page. Fed by ?part= from the catalogue.
   Same [hidden] trap as .mobilenav and .part - this one is a plain <div> so the
   browser default would win, but the rule is written out rather than relied on:
   the two places it was NOT written out are both bugs this build already had. */
.enq{margin:0 0 32px;padding:16px 20px;border:1px solid var(--accent);
  border-left:3px solid var(--accent);background:rgba(8,241,247,.06);
  border-radius:2px;color:var(--steel);font-size:15px}
.enq[hidden]{display:none}
.enq b{color:var(--accent);font-weight:600}
.enq .chg{display:inline-block;margin-top:6px;font-size:13.5px;color:var(--muted);
  text-decoration:underline}
.enq .chg:hover{color:var(--accent)}

/* Contact details repeated as a compact block on the home page - the full
   two-column form lives on the contact page and is not duplicated here, so
   there is only ever one form to wire up to email at launch. */
.cta-panel{display:grid;grid-template-columns:1.15fr 1fr;gap:46px;align-items:center;
  background:var(--panel);border:1px solid var(--line);border-radius:2px;padding:38px}
@media(max-width:820px){.cta-panel{grid-template-columns:1fr;gap:28px;padding:28px}}

/* Footer nav. Added with the page split so every page is reachable from every
   page without opening the mobile menu, and so a crawler that lands on a deep
   page can still find the other four. */
.footnav{display:flex;flex-wrap:wrap;gap:8px 22px;justify-content:center}
.footnav a{font-family:var(--head);text-transform:uppercase;letter-spacing:.08em;
  font-size:13.5px;color:var(--steel)}
.footnav a:hover{color:var(--accent)}
@media(max-width:700px){.foot{justify-content:center;text-align:center}}

/* ============================================================================
   CONTACT FORM - live submission states
   ========================================================================== */

/* Honeypot. Moved off-screen rather than display:none - a person never sees or
   tabs to it, and it stays in the layout so naive bots still fill it in. */
.hp{position:absolute;left:-9999px;width:1px;height:1px;overflow:hidden}

label .opt{font-size:11px;letter-spacing:.1em;color:var(--muted);opacity:.95}

/* Result message. The failing state is styled as loudly as the succeeding one
   on purpose: it carries the phone number, and it is the message that matters
   most to someone who has just typed out a breakdown. */
.formmsg{margin:16px 0 0;padding:13px 15px;border-radius:2px;font-size:15px;line-height:1.5}
.formmsg[hidden]{display:none}
.formmsg.good{border:1px solid var(--ok);background:rgba(61,214,140,.08);color:var(--ok)}
.formmsg.bad{border:1px solid #FF8A6B;background:rgba(255,138,107,.08);color:#FFB39E}
.formmsg a{color:inherit;font-weight:600;text-decoration:underline}

form button[disabled]{opacity:.6;cursor:progress}

/* Turnstile renders in an iframe it sizes itself; this just gives it room and
   stops it butting against the button. */
.cf-turnstile{margin:0 0 16px}

/* ============================================================================
   MOBILE VERTICAL RHYTHM
   Phones are where the length actually hurt - over 70% of trade traffic, and
   every multi-column grid collapses to one column there, so a page that reads
   as three screens on a desktop ran to seven on a phone. Desktop spacing is
   generous by design; the phone just cannot afford it.
   ========================================================================== */
@media(max-width:700px){
  section{padding:44px 0}
  .pagehead{padding:38px 0 18px}
  .strip .in{padding:36px 0}
  .sec-head{margin-bottom:28px}
  .hero-in{padding:64px 22px 56px}
  .tags{margin-top:28px;padding-top:20px}
  .cta-row{margin-top:26px}
}
