/* Self-hosted web fonts: the latin subsets Google Fonts serves, kept local so
   nothing render-blocking comes from a third party and the CSP can stay tight.
   Source Sans 3 is one variable file covering 400-700. */
@font-face{font-family:Oxygen;font-style:normal;font-weight:400;font-display:swap;src:url(fonts/oxygen-400.woff2) format("woff2")}
@font-face{font-family:Oxygen;font-style:normal;font-weight:700;font-display:swap;src:url(fonts/oxygen-700.woff2) format("woff2")}
/* Setronics: the brand heading face (converted from the supplied OTFs,
   renamed internally; public-use font). Regular and Bold. */
@font-face{font-family:Setronics;font-style:normal;font-weight:400;font-display:swap;src:url(fonts/setronics-400.woff2) format("woff2")}
@font-face{font-family:Setronics;font-style:normal;font-weight:700;font-display:swap;src:url(fonts/setronics-700.woff2) format("woff2")}
@font-face{font-family:"Source Sans 3";font-style:normal;font-weight:400 700;font-display:swap;src:url(fonts/source-sans-3.woff2) format("woff2")}
/* ==========================================================================
   Setronics — site stylesheet
   Palette and type sampled from setronics.com; layout rebuilt as static HTML.
   ========================================================================== */

:root{
  --red:#dd3333; --red-dark:#b32626;
  --navy:#05263e; --ink:#252626; --body:#59595b; --mute:#babdbf;
  --band:#f0f2f7; --rule:#e4e6ea; --cell-rule:#e4e1df;
  /* Sampled from the animated logo's square progression - brand colour that
     existed in the mark but had never made it into the interface. */
  --gold:#fab20b; --gold-2:#fbc64b; --gold-3:#fcd57a; --gold-4:#fddd92;
  --shell:1180px;
  --pad-section:clamp(2.2rem,4.6vw,3.4rem);
  /* Type scale. Before this there were 35 distinct font sizes, 12 of them
     between 14.4px and 17.6px - .94rem, .95rem, .97rem and .98rem all existed
     side by side, differences nobody can see but which guarantee two
     components that should match never quite do. Seven steps replace all 35;
     no value moved by more than 0.8px. Body copy is --t-md (17px), the size
     the site has always set on <body>. */
  --t-2xs:.78rem; --t-xs:.86rem; --t-sm:.95rem; --t-base:1rem;
  --t-md:1.0625rem; --t-lg:1.2rem; --t-xl:1.35rem;
  /* Fluid heading steps, unchanged in range - the four that were already
     tuned, now named, with the four near-duplicates folded into them. */
  --t-hero:clamp(2.1rem,6vw,3.5rem);
  --t-h1:clamp(1.85rem,4.2vw,2.45rem);
  --t-h2:clamp(1.6rem,4vw,2.25rem);
  --t-h3:clamp(1.4rem,3.4vw,1.9rem);
  --t-h4:clamp(1.15rem,2.6vw,1.45rem);
  --t-lead:clamp(1.02rem,2.1vw,1.2rem);
  /* Spacing, on a 4px grid. 38 distinct rem values were in use across 175
     declarations - .3, .32, .35, .4, .42, .44 and .45rem all at once, seven
     values inside a 0.15rem band. Every one snaps to the nearest step below;
     nothing moved more than 2px. Values under .25rem are left alone: those
     are optical nudges on icons and bullets, not spacing decisions. */
  --s-4:.25rem; --s-8:.5rem; --s-12:.75rem; --s-16:1rem; --s-20:1.25rem;
  --s-24:1.5rem; --s-28:1.75rem; --s-32:2rem; --s-40:2.5rem;
  /* The footer is the one place the site sets type on navy, so it carries its
     own three greys. They were loose hex values; named here they can be kept
     in step with each other. */
  --invalid-bg:#fdf6f6;
  /* Input boundaries need 3:1 against their background (WCAG 1.4.11);
     --rule measures 1.25:1 on white. This one measures 3.3:1. */
  --field-rule:#8a8d91;
  --foot-ink:#c6cdd4; --foot-mark:#7f8f9c; --foot-meta:#8fa0ad;
  --radius:6px;
  --shadow:0 1px 2px rgba(5,38,62,.05),0 8px 24px -12px rgba(5,38,62,.18);
}

*{box-sizing:border-box}
html{scroll-behavior:smooth}
@media (prefers-reduced-motion:reduce){
  html{scroll-behavior:auto}
  *,*::before,*::after{animation-duration:.01ms!important;animation-iteration-count:1!important;transition-duration:.01ms!important}
}
body{margin:0;background:#fff;color:var(--body);
  font-family:"Source Sans 3","Source Sans Pro",-apple-system,BlinkMacSystemFont,"Segoe UI",Helvetica,Arial,sans-serif;
  font-size:var(--t-md);line-height:1.7;-webkit-font-smoothing:antialiased;text-rendering:optimizeLegibility}
h1,h2,h3,h4,h5{font-family:Setronics,Oxygen,"Trebuchet MS",Verdana,sans-serif;color:var(--ink);font-weight:400;line-height:1.2;margin:0;text-wrap:balance}
/* Oxygen at 400 read tentative at display sizes. 700 with slightly negative
   tracking gives the top of the hierarchy authority; card and list headings
   stay at 400 so the two levels are told apart by weight, not just size. */
/* Large headings take the Regular cut - the face is sturdy enough that Bold at
   these sizes overwhelms the page. Bold stays on small titles (cards, footer). */
h1,.section-head h2,.cat-head h2,.hero h1,.pagehead h1,.cta h2{font-weight:400;letter-spacing:0}
p{margin:0}
p + p{margin-top:var(--s-16)}
/* --red-dark, not --red: #dd3333 on the #f0f2f7 band measures 4.07:1 and
   fails AA for body text (it only clears 4.5 on white). --red-dark is
   5.82:1 on the band and 6.52:1 on white. --red stays for icons, rules,
   hover and anything large or decorative, where the brand tone belongs. */
a{color:var(--red-dark);text-decoration:none}
/* A link sitting inside a sentence has to be tellable from the words around
   it by something other than colour: --red-dark against --body is 1.4:1, far
   under the 3:1 that would let colour carry it alone. Prose links get an
   underline; links that are already unmistakably controls - buttons, card
   headings, nav, the footer columns, list rails - do not, since their shape
   or position does that job. */
main p a:not(.btn),main li a:not(.btn),.quotes p a{
  text-decoration:underline;text-underline-offset:.15em;text-decoration-thickness:1px}
main .benefits li a,main .aside-links a,main .jump a,main .cards li a{text-decoration:none}
a:hover{text-decoration:underline}
img{max-width:100%;height:auto}
:where(a,button,input,select,textarea,summary):focus-visible{outline:2px solid var(--red);outline-offset:3px}
.shell{width:min(100% - 2.5rem,var(--shell));margin-inline:auto}
.narrow{max-width:74ch}
.skip{position:absolute;left:-9999px}
.skip:focus{left:1rem;top:1rem;z-index:99;background:#fff;padding:var(--s-8) var(--s-16);box-shadow:0 2px 10px rgba(0,0,0,.2)}
.visually-hidden{position:absolute;width:1px;height:1px;margin:-1px;padding:0;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap;border:0}

/* ---------- utility bar ---------- */
.util{background:var(--red);color:#fff;font-size:var(--t-sm)}
.util .shell{display:flex;flex-wrap:wrap;align-items:center;gap:var(--s-8) var(--s-24);padding:var(--s-8) 0}
.util a{color:#fff;display:inline-flex;align-items:center;min-height:24px}
.util a[aria-current]{border-bottom:2px solid #fff;padding-bottom:2px}
.util .social{display:flex;gap:var(--s-8);margin-right:auto}
.util .social a{padding:var(--s-4);margin:calc(-1 * var(--s-4)) 0;min-width:24px;justify-content:center}
.util svg{width:15px;height:15px;fill:currentColor;display:block}

/* ---------- header ---------- */
.site-head{position:sticky;top:0;z-index:40;background:#fff;border-bottom:1px solid var(--rule)}
.site-head .shell{display:flex;align-items:center;gap:var(--s-16) var(--s-32);padding:var(--s-20) 0}
.brand{display:block;line-height:0}
.brand:hover{text-decoration:none}
/* The source art is 653x184, so displaying it around 190px wide gives roughly
   3x pixel density - crisp on retina. width/height attributes stay intrinsic
   so the aspect ratio is reserved before the image loads (no layout shift). */
.brand img{display:block;width:100%;height:auto;max-width:min(190px,46vw)}
/* Top menu, dropdown items and the phone Menu button share the heading face. */
.site-nav,.nav-toggle{font-family:Setronics,Oxygen,"Trebuchet MS",sans-serif}
.site-nav{margin-left:auto;display:flex;align-items:center;gap:var(--s-8) var(--s-32);font-size:var(--t-base)}
.site-nav > a,.site-nav > .has-sub > a,.site-nav > .has-sub > button{color:var(--ink);background:none;border:0;padding:0;
  font:inherit;cursor:pointer;display:inline-flex;align-items:center;gap:var(--s-4);white-space:nowrap}
.has-sub{display:inline-flex;align-items:center;gap:var(--s-4)}
/* The caret is its own control, so it needs a real target of its own - the
   glyph is 9px, well under the 24x24 minimum for a tappable control. The
   negative margin keeps the visual spacing tight without shrinking the target. */
.sub-toggle{display:inline-flex;align-items:center;justify-content:center;
  min-width:26px;min-height:26px;padding:0;margin-inline:calc(-1 * var(--s-4))}
/* Light the section while any of its pages is open. */
.has-sub[data-section] > a{color:var(--red)}
.site-nav a:hover,.site-nav button:hover{color:var(--red);text-decoration:none}
.site-nav [aria-current="page"]{color:var(--red);box-shadow:inset 0 -2px 0 var(--red)}
.has-sub{position:relative}
.has-sub > button svg{width:9px;height:9px;fill:currentColor;transition:transform .18s ease;display:block}
.has-sub > button[aria-expanded="true"] svg{transform:rotate(180deg)}
.has-sub > .sub{position:absolute;top:calc(100% + .9rem);left:-1rem;min-width:250px;background:#fff;border:1px solid var(--rule);
  box-shadow:var(--shadow);padding:var(--s-8) 0;display:none;z-index:50;border-radius:var(--radius);overflow:hidden}
.has-sub > .sub[data-open]{display:block}
/* The last menu item sits at the right edge of the header, so its panel
   opens leftward; left-aligned it ran 90px off a 1280px screen. */
.site-nav > .has-sub:last-of-type > .sub{left:auto;right:-1rem}
/* The panel sits .9rem below the button. That gap is outside .has-sub, so a
   pointer crossing it fires mouseleave and shuts the menu before it can be
   used. This transparent strip bridges the gap as part of the panel. */
@media (min-width:1000px){
  .has-sub > .sub::before{content:"";position:absolute;left:0;right:0;top:-1.1rem;height:1.1rem}
}
.has-sub > .sub a{display:block;padding:var(--s-8) var(--s-20);color:var(--ink);font-size:var(--t-sm)}
.has-sub > .sub a:hover{background:var(--band);color:var(--red);text-decoration:none}
.nav-toggle{display:none;margin-left:auto;background:none;border:1px solid var(--rule);border-radius:2px;
  padding:var(--s-8) var(--s-12);cursor:pointer;color:var(--ink);align-items:center;gap:var(--s-8);font:inherit;font-size:var(--t-xs)}
.nav-toggle svg{width:18px;height:18px;stroke:currentColor;fill:none;stroke-width:2;stroke-linecap:round}
.head-call{display:none;align-items:center;gap:var(--s-8);color:var(--ink);font-family:Oxygen,sans-serif;
  font-weight:700;font-size:var(--t-sm);margin-left:auto;padding:var(--s-8) var(--s-12);min-height:44px;white-space:nowrap}
.head-call svg{width:18px;height:18px;stroke:var(--red);fill:none;stroke-width:1.8;stroke-linecap:round;stroke-linejoin:round}
.head-call:hover{color:var(--red);text-decoration:none}
@media (min-width:700px) and (max-width:999px){
  .head-call{display:inline-flex}
  .head-call + .nav-toggle{margin-left:0}
}
/* The button says what it will do: Menu when closed, Close when open. */
.nav-toggle .i-close,.nav-toggle .l-close{display:none}
.nav-toggle[aria-expanded="true"] .i-menu,.nav-toggle[aria-expanded="true"] .l-menu{display:none}
.nav-toggle[aria-expanded="true"] .i-close,.nav-toggle[aria-expanded="true"] .l-close{display:block}

/* ---------- hero ---------- */
.hero{position:relative;background:var(--navy) center/cover no-repeat;color:#fff;padding:clamp(4rem,11vw,8rem) 0;text-align:center;isolation:isolate}
.hero-slides{position:absolute;inset:0;z-index:-2;overflow:hidden}
.hero-slide{position:absolute;inset:0;background:center center no-repeat;opacity:0;
  animation:heroFade 32s infinite;
  background-size:cover}
/* Slide 1 only, and after .hero-slide: that rule's `background` shorthand
   resets background-image, so this has to follow it. The other three arrive
   from data-bg after the load event; keeping this one in the stylesheet means
   no page needs a style attribute. */
.hero-slide-1{background-image:url("img/background/cannabis.webp")}
.hero-slide:nth-child(2){animation-delay:8s}
.hero-slide:nth-child(3){animation-delay:16s}
.hero-slide:nth-child(4){animation-delay:24s}
@keyframes heroFade{
  0%{opacity:0}      4%{opacity:1}      25%{opacity:1}
  29%{opacity:0}     100%{opacity:0}
}
/* Anyone who has asked for less motion gets the first slide, held still. */
@media (prefers-reduced-motion:reduce){
  .hero-slide{animation:none}
  .hero-slide:nth-child(1){opacity:1}
}

/* Showing the whole banner and never re-cropping it are the same problem: give
   the hero the artwork's own 1400:577 ratio and `cover` fits exactly, so every
   slide is fully visible edge to edge with nothing cut off. It scales with the
   window, but uniformly - the framing never changes.
   Below 1000px that ratio would leave the band too short for the headline, so
   the hero goes back to a normal padded block and the image crops instead. */
@media (min-width:1000px){
  .hero{aspect-ratio:1400/577;display:grid;place-items:center;padding-block:0}
}
.hero::before{content:"";position:absolute;inset:0;background:linear-gradient(180deg,rgba(5,38,62,.55),rgba(5,38,62,.68));z-index:-1}
.hero h1{color:#fff;font-size:var(--t-hero);letter-spacing:.01em}
.hero p{max-width:62ch;margin:var(--s-20) auto 0;color:rgba(255,255,255,.88);font-size:var(--t-lead)}
.hero .btnrow{margin-top:var(--s-32)}

/* ---------- proof strip (home, under the hero) ---------- */
.proof{background:#fff;border-bottom:1px solid var(--rule);padding:clamp(1.6rem,3.4vw,2.4rem) 0}
.proof .shell{display:grid;gap:var(--s-24) clamp(1.5rem,4vw,3rem);align-items:center}
.proof-list{list-style:none;margin:0;padding:0;display:grid;gap:var(--s-16) var(--s-24);
  grid-template-columns:repeat(2,minmax(0,1fr))}
@media (min-width:1000px){.proof-list{grid-template-columns:repeat(4,minmax(0,1fr))}}
.proof-list li{border-left:3px solid var(--red);padding-left:var(--s-12)}
.proof-list b{display:block;font-family:Setronics,Oxygen,sans-serif;font-weight:400;color:var(--ink);
  font-size:var(--t-lg);line-height:1.2}
.proof-list span{display:block;font-size:var(--t-xs);color:var(--body);line-height:1.45;margin-top:var(--s-4)}
@media (max-width:559px){.proof-list{grid-template-columns:1fr 1fr;gap:var(--s-12)}.proof-list b{font-size:var(--t-base)}}

/* ---------- page title band ---------- */
.pagehead{background:var(--band);border-bottom:1px solid var(--rule);padding:clamp(1.5rem,3vw,2.1rem) 0;text-align:center}
.pagehead h1{font-size:var(--t-h1)}
.pagehead .kicker{color:var(--red-dark);letter-spacing:.18em;text-transform:uppercase;font-size:var(--t-2xs);font-weight:600;margin-bottom:var(--s-8)}
.pagehead .kicker a{color:inherit;display:inline-block;padding:var(--s-4) 0;min-height:24px}
.pagehead .kicker a::before{content:"\2190";margin-right:.5em}
.pagehead .kicker a:hover{text-decoration:underline;text-underline-offset:.3em}
.pagehead p{max-width:68ch;margin:var(--s-12) auto 0}
/* A lead paragraph that opens a section of body copy. It was in the markup on
   the brochure page with no rule behind it at all, so it set as plain body
   text - the one place the generator asks for emphasis and did not get it. */
.lede{font-size:var(--t-lg);line-height:1.5;color:var(--ink);max-width:62ch}
/* The band is a title strip, not a hero: the rule under the heading sits
   tighter here than the one used to separate content sections. */
.pagehead .divider{margin:var(--s-12) auto 0}
.pagehead .divider:last-child{margin-bottom:0}

/* ---------- generic section ---------- */
.section{padding:var(--pad-section) 0}
.section.band{background:var(--band)}
.section-head{text-align:center;margin-bottom:clamp(1.8rem,4vw,2.6rem)}
/* When the head is the whole section (home About band) nothing follows it,
   so its bottom margin would stack onto the section padding. */
.section-head:last-child{margin-bottom:0}
.section-head h2{font-size:var(--t-h2)}
.section-head p{max-width:70ch;margin:var(--s-16) auto 0}
/* The logo animates a run of gold squares that resolve into the red wordmark.
   That progression is the most distinctive thing the brand owns, so the section
   divider reuses it rather than being an anonymous rule. */
.divider{width:50px;height:5px;border:0;margin:var(--s-20) auto var(--s-20);background-repeat:no-repeat;background-size:5px 5px;
  background-image:linear-gradient(var(--gold-4),var(--gold-4)),linear-gradient(var(--gold-3),var(--gold-3)),
    linear-gradient(var(--gold-2),var(--gold-2)),linear-gradient(var(--gold),var(--gold)),
    linear-gradient(var(--red),var(--red)),linear-gradient(var(--red),var(--red));
  background-position:0 0,9px 0,18px 0,27px 0,36px 0,45px 0}

/* ---------- cards ---------- */
.cards{list-style:none;margin:0;padding:0;display:grid;gap:var(--s-24);
  grid-template-columns:repeat(auto-fit,minmax(260px,1fr))}
/* Six paragraph-length cards at the default 260px track fall into a ragged
   4 + 2. A wider track gives 3 + 3 and lets the prose set on a readable measure. */
.cards-3{grid-template-columns:repeat(auto-fit,minmax(320px,1fr))}
@media (min-width:1000px){.cards-3{grid-template-columns:repeat(3,1fr)}}
.card{background:#fff;border:1px solid var(--rule);border-radius:var(--radius);overflow:hidden;padding:var(--s-28) var(--s-24);display:flex;flex-direction:column;gap:var(--s-12);
  transition:border-color .18s ease,box-shadow .18s ease,transform .18s ease}
.card:hover{border-color:var(--red);box-shadow:var(--shadow);transform:translateY(-2px)}
/* Cards on white sections take the band tint so they read as a surface rather
   than an outline; on grey band sections they stay white for the same reason.
   Hover lifts a tinted card to white so the movement has a colour cue too. */
/* The section tells its cards what surface to sit on, rather than every card
   rule re-deriving it with .section:not(.band). One class of specificity
   instead of four, so a later card rule can actually override it - the old
   chain silently beat anything shorter, which is a trap this file has already
   sprung once. */
.section{--card-bg:var(--band);--card-bg-hover:#fff}
.section.band{--card-bg:#fff;--card-bg-hover:#fff}
.card{background:var(--card-bg)}
.card:hover,.card:focus-within{background:var(--card-bg-hover)}
/* A hairline that widens from the left edge on hover - the same left-to-right
   reveal the logo uses, at card scale. */
.card::before{content:"";position:absolute;left:-1px;top:-1px;height:3px;width:0;background:var(--red);
  transition:width .22s ease}
.card:hover::before,.card:focus-within::before{width:calc(100% + 2px)}
.card h3{font-size:var(--t-lg)}
.card p{font-size:var(--t-sm)}
/* Icon and title share a line: at three-up the stacked icon spent a lot of
   vertical space on decoration, and sitting it beside the heading turns it into
   a label you scan. It aligns to the heading's first line, not the block's
   centre, so multi-line titles stay tidy. */
.card{display:grid;grid-template-columns:auto minmax(0,1fr);column-gap:var(--s-12);align-content:start}
.card-ico{grid-column:1;width:30px;height:30px;color:var(--red);margin-top:.15rem}
.card h3{grid-column:2;align-self:start}
.card p,.card .more,.card > :not(.card-ico):not(h3){grid-column:1/-1}
/* Cards built without an icon keep the heading on the full width. */
.card:not(:has(.card-ico)) h3{grid-column:1/-1}
/* Linked cards get a single arrow on the heading rather than a repeated
   "Learn more" row; it nudges on hover so the card still feels actionable. */
.card:has(a) h3::after{content:"\2192";margin-left:.4em;color:var(--red);
  display:inline-block;transition:transform .18s ease}
.card:has(a):hover h3::after,.card:has(a):focus-within h3::after{transform:translateX(4px)}
.card a::after{content:"";position:absolute;inset:0}
.card{position:relative}

/* "Open" cards (Services page): no box. A large outlined icon sits above a
   dark title, then the copy, then a red action link - the whole tile is still
   one link via the h3 anchor's ::after overlay. */
.cards.open{gap:var(--s-40) clamp(1.5rem,4vw,3.5rem)}
.section:not(.band) .cards.open .card,.cards.open .card{background:transparent;border:0;border-radius:0;
  overflow:visible;padding:0;box-shadow:none;transform:none;display:flex;flex-direction:column;gap:var(--s-12)}
.cards.open .card::before{display:none}
.cards.open .card-ico{width:54px;height:54px;margin:0 0 var(--s-4)}
.cards.open .card-ico .ico{stroke-width:1.35}
.cards.open .card h3{font-size:var(--t-xl);font-weight:400}
.cards.open .card h3 a{color:var(--ink)}
.cards.open .card h3::after{content:none}
.cards.open .card p{margin:0;color:var(--body);font-size:var(--t-base)}
.cards.open .card .more{margin-top:auto;font-weight:600;color:var(--red-dark);font-size:var(--t-base)}
.cards.open .card .more::after{content:"\2192";margin-left:.4em;display:inline-block;transition:transform .18s ease}
.cards.open .card:hover .more,.cards.open .card:focus-within .more{text-decoration:underline}

/* Compact tiles, phones only. The nine home-page service tiles are the
   longest run on that page at 390px; the blurb inside each is what makes them
   267px tall. Dropping it pairs them two across as icon, title and arrow -
   still the same tile, still the whole thing one link, but a grid you take in
   at a glance rather than scroll through. The copy is one tap away on the
   service page, which is the only place it was going to be read properly. */
/* Boxed, at every width. The open (borderless) treatment is used in only two
   places on the site; every other grid - the quick actions directly above
   this one, the industry cards, the 404 cards - is a boxed card, so the home
   Services grid was the odd one out, and boxing it only on phones made the
   same nine tiles two different things. These take the standard card surface,
   border, hover lift and left-edge hairline. The tint rules already in the
   card system keep them distinct from the boxed grid above: cards on a white
   section take the band tint, cards on a band section stay white. */
/* Scoped through .section to clear `.section:not(.band) .cards.open .card`,
   which strips the box and outranks a bare .cards.open.compact-sm selector. */
.cards.open.compact-sm{gap:var(--s-24)}
.section .cards.open.compact-sm .card{border:1px solid var(--rule);
  border-radius:var(--radius);overflow:hidden;padding:var(--s-24) var(--s-24);gap:var(--s-12);
  transition:border-color .18s ease,box-shadow .18s ease,transform .18s ease}
.section .cards.open.compact-sm .card{background:var(--card-bg)}
.cards.open.compact-sm .card:hover{border-color:var(--red);box-shadow:var(--shadow);
  transform:translateY(-2px)}
.section .cards.open.compact-sm .card:hover,
.section .cards.open.compact-sm .card:focus-within{background:var(--card-bg-hover)}
.cards.open.compact-sm .card::before{display:block}
.section .cards.open.compact-sm .card-ico{width:44px;height:44px}

@media (max-width:699px){
  .cards.open.compact-sm{grid-template-columns:1fr 1fr;gap:var(--s-12)}
  .section .cards.open.compact-sm .card{padding:var(--s-16) var(--s-12);gap:var(--s-8)}
  .cards.open.compact-sm .card p,
  .cards.open.compact-sm .card .more{display:none}
  .section .cards.open.compact-sm .card-ico{width:30px;height:30px;margin:0}
  .cards.open.compact-sm .card h3{font-size:var(--t-base);line-height:1.3}
  /* The per-card action link is gone, so the arrow has to carry the
     affordance again - .cards.open suppresses it by default. */
  .cards.open.compact-sm .card h3::after{content:"\2192";margin-left:.35em;
    color:var(--red);display:inline-block}
}
.cards.open .card:hover .more::after,.cards.open .card:focus-within .more::after{transform:translateX(4px)}
.cards.open .card:hover h3 a,.cards.open .card:focus-within h3 a{color:var(--ink);text-decoration:none}

/* ---------- service page opener ---------- */
@media (min-width:800px){
  .row{grid-template-columns:var(--row-icon) minmax(0,1fr)}
}
/* The service mark at the head of its own page. Floated so the opening
   sentence sets beside it and the copy then closes back under it, the way a
   magazine sets an opening ornament - rather than the mark sitting on a line
   of its own with the text pushed below. */
.row-ico{float:left;width:52px;height:52px;color:var(--red);margin:.15rem var(--s-16) var(--s-8) 0}
/* The closing line has a red left rule, so it must never sit in the float's
   indent - it clears instead. */
.shell.split .close{clear:left}
/* No tinted tile here: the live site shows a bare 40px mark, and a filled
   square at this size would out-weigh the heading beside it. */

/* ---------- benefit list ---------- */
.benefits{list-style:none;margin:var(--s-24) 0 0;padding:0;display:grid;gap:var(--s-12);
  grid-template-columns:repeat(auto-fit,minmax(280px,1fr))}
.benefits li{display:flex;gap:var(--s-12);align-items:flex-start;font-size:var(--t-base)}
.benefits li::before{content:"";flex:none;width:18px;height:18px;margin-top:var(--s-4);border-radius:50%;
  background:var(--red) url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23fff' stroke-width='3.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m4 12.5 5.5 5.5L20 6.5'/%3E%3C/svg%3E") center/11px no-repeat}

/* ---------- jump nav ---------- */
.jump{padding-bottom:clamp(1.6rem,3.6vw,2.4rem)}
/* Below 1000px the header is not sticky, and the partner page runs to
   several thousand pixels on a phone, so the category row pins itself to
   the top and scrolls sideways rather than wrapping to three lines. */
@media (max-width:999px){
  .jump{position:sticky;top:0;z-index:30;background:#fff;border-bottom:1px solid var(--rule);
    padding:var(--s-8) 0}
  nav.jump ul{flex-wrap:nowrap;justify-content:flex-start;overflow-x:auto;scrollbar-width:none;
    -webkit-overflow-scrolling:touch;padding-bottom:2px}
  nav.jump ul::-webkit-scrollbar{display:none}
  nav.jump a{white-space:nowrap}
  /* Fade on the right edge: the row scrolls, the last chip is not cut off. */
  nav.jump::after{content:"";position:absolute;top:0;right:0;bottom:0;width:40px;pointer-events:none;
    background:linear-gradient(90deg,rgba(255,255,255,0),#fff 75%)}
  nav.jump ul > li:last-child{margin-right:var(--s-32)}
  .cat{scroll-margin-top:4rem}
}
.jump ul{list-style:none;margin:0;padding:0;display:flex;flex-wrap:wrap;justify-content:center;gap:var(--s-8)}
.jump a{display:inline-block;border:1px solid var(--rule);color:var(--body);
  padding:var(--s-8) var(--s-16);font-size:var(--t-xs);letter-spacing:.09em;text-transform:uppercase}
.jump a:hover{border-color:var(--red);color:var(--red);text-decoration:none}

/* ---------- partner logo grid ---------- */
.cat{padding:clamp(2.2rem,5vw,3.2rem) 0;scroll-margin-top:5.5rem}
.cat:nth-of-type(even){background:var(--band)}
.cat-head{text-align:center;margin-bottom:clamp(1.5rem,3.4vw,2.1rem)}
.cat-head h2{font-size:var(--t-h2)}
.cat-head .divider{margin:var(--s-12) auto var(--s-16)}
.cat-head .icon{width:46px;height:46px;border-radius:50%;background:var(--red);color:#fff;
  display:inline-flex;align-items:center;justify-content:center;margin-bottom:var(--s-16)}
.cat-head .icon svg{width:23px;height:23px;fill:currentColor}
/* The rules between cells are drawn by each cell rather than by a coloured
   container showing through a 1px gap - with the container technique, a part
   final row leaves a block of bare grid colour where the missing cells are. */
.grid{list-style:none;margin:0;padding:0;display:grid;gap:0;
  background:#fff;border:1px solid var(--cell-rule);border-right:0;border-bottom:0;
  /* 260px gave 4 across the 1180px shell, 210 gave 5; 180 gives 6 and still
     collapses to 5/4/3/2/1 as the viewport narrows. Tighter tiles so the mark
     fills more of its cell instead of floating in whitespace. */
  grid-template-columns:repeat(auto-fill,minmax(180px,1fr))}
.cell{background:#fff;display:flex;align-items:center;justify-content:center;padding:var(--s-12) var(--s-12);min-height:84px;
  box-shadow:inset -1px -1px 0 var(--cell-rule)}
/* Every logo ships on an identical 600x200 transparent canvas, pre-scaled for
   even optical weight, so the tile only has to size the canvas. Shown in the
   manufacturers' own colours: these are brand marks and partners expect them
   reproduced accurately. No blend mode - it alters colour over any non-white. */
.cell img{display:block;width:100%;max-width:160px;height:auto}
/* Phones: 180px tracks collapsed to one logo per row, 88 rows deep. Three
   across keeps every mark legible and cuts the page to a third. */
@media (max-width:699px){
  .grid{grid-template-columns:repeat(3,1fr)}
  .cell{min-height:64px;padding:var(--s-8)}
}

/* ---------- logo strip ---------- */
/* Manufacturer partners - a continuous marquee rather than a static grid, to
   match the carousel on the WordPress home page. It runs at that carousel's
   pace (one logo every ~4s) but scrolls continuously instead of stepping, so
   there is no library to load and nothing to initialise. Logos are shown in
   their own colours. */
/* No panel and no border: multiply blending drops the logos' white grounds out,
   so they sit straight on the band and the row reads as a passing ribbon of
   marks rather than a white slab pasted onto the section. The mask fades to
   transparent at both ends so logos enter and leave instead of being clipped. */
.logo-marquee{overflow:hidden;
  -webkit-mask-image:linear-gradient(90deg,transparent,#000 10%,#000 90%,transparent);
  mask-image:linear-gradient(90deg,transparent,#000 10%,#000 90%,transparent)}
.strip{list-style:none;margin:0;padding:0;display:flex;width:max-content;
  animation:logoScroll var(--marquee-dur,112s) linear infinite}
.strip li{flex:0 0 auto;width:clamp(158px,21vw,226px);display:flex;
  align-items:center;justify-content:center;padding:var(--s-4) var(--s-16);min-height:66px}
/* Sized by width, not height. Every mark is area-normalised inside its own
   canvas - 600x220 for the partner set the strip now shares, 600x186 for the
   six that appear here only - so one width gives the row even optical weight
   whichever canvas a logo came from. No blend mode: these tiles carry a real
   alpha channel, and multiply would shift the brand colours. */
.strip img{width:100%;max-width:191px;height:auto}
/* 191px is the ceiling, not an arbitrary stop: the Setronics mark in the
   header renders 190px wide, and a partner logo should never out-scale the
   brand whose page it sits on. */
@keyframes logoScroll{from{transform:translateX(0)}to{transform:translateX(-50%)}}
/* Let a visitor stop it to read a logo. */
.logo-marquee:hover .strip,.logo-marquee:focus-within .strip{animation-play-state:paused}
/* Reduced motion stops the strip moving on its own - it must not turn it into
   something else. It used to become a wrapped grid, which on a phone read as a
   static block of logos rather than the partner carousel, so the row stays a
   row and the visitor drives it: one line, swipeable, scroll-snapped. The
   duplicate run goes, since nothing is looping any more. */
@media (prefers-reduced-motion:reduce){
  .strip{animation:none;flex-wrap:nowrap;scroll-snap-type:x proximity}
  .strip li{scroll-snap-align:center}
  .strip .dup{display:none}
  .logo-marquee{overflow-x:auto;scrollbar-width:none;-webkit-overflow-scrolling:touch}
  .logo-marquee::-webkit-scrollbar{display:none}
  /* Given a tab stop by site.js in this mode, so it is reachable without a
     pointer; the ring needs to sit inside the clipped box to be seen. */
  .logo-marquee:focus-visible{outline:2px solid var(--red);outline-offset:-2px}
}

/* Phones. The desktop ribbon does not scale down: at 390px the 335px window
   fits barely two 158px tiles, and the 10% end masks fade most of both, so
   roughly one logo is legible at a time while a full 87-logo pass takes 348s.
   Nobody sees past the first handful. Three moves fix it without changing the
   idea - smaller tiles so three or four are on screen, a 5% mask so the
   readable middle is 90% of a narrow strip rather than 80%, and a shorter
   cycle. 0.35 of the desktop duration lands the 87 at ~122s: still a third
   of the 348s crawl, but ~80px/s against smaller tiles keeps a mark on screen
   long enough to read it. 0.3 measured at 94px/s and read as a blur. Raise
   the 0.35 to slow it down further. */
@media (max-width:699px){
  .strip{animation-duration:calc(var(--marquee-dur,112s) * .35)}
  .strip li{width:clamp(104px,29vw,124px);padding:var(--s-4) var(--s-8);min-height:56px}
  .strip img{max-width:110px}
  .logo-marquee{
    -webkit-mask-image:linear-gradient(90deg,transparent,#000 5%,#000 95%,transparent);
    mask-image:linear-gradient(90deg,transparent,#000 5%,#000 95%,transparent)}
}

/* The partners strip is a trust signal, not a destination: it earns a glance,
   not a screen. It gets its own tighter vertical rhythm so the eye moves from
   the executive team straight on to the testimonials. Everything here is
   scoped to .partners so the shared .section/.section-head rhythm that the
   other 32 pages depend on is untouched. */
.section.partners .section-head{margin-bottom:var(--s-20)}
.section.partners .section-head p{margin-top:var(--s-8)}
.partners-more{text-align:center;margin-top:var(--s-16)}

/* ---------- team ---------- */
.team{list-style:none;margin:0;padding:0;display:grid;gap:var(--s-32);grid-template-columns:repeat(auto-fit,minmax(290px,1fr))}
.team article{background:#fff;border:1px solid var(--rule);height:100%}
.team img{width:100%;aspect-ratio:1/1;object-fit:cover;object-position:center;display:block;background:var(--band)}
.team .txt{padding:var(--s-24)}
.team h3{font-size:var(--t-xl)}
.team .role{color:var(--red);letter-spacing:.1em;text-transform:uppercase;font-size:var(--t-2xs);font-weight:600;margin:var(--s-4) 0 var(--s-16)}
.team p{font-size:var(--t-sm)}
.team .bio{margin-top:var(--s-12)}
.team .bio summary{cursor:pointer;list-style:none;display:inline-flex;align-items:center;gap:var(--s-8);
  color:var(--red-dark);font-family:Oxygen,sans-serif;font-size:var(--t-xs);font-weight:700;letter-spacing:.04em;
  padding:var(--s-4) 0;min-height:24px}
.team .bio summary::-webkit-details-marker{display:none}
.team .bio summary::after{content:"\203A";transform:rotate(90deg);transition:transform .18s ease;font-size:var(--t-md);line-height:1}
.team .bio[open] summary::after{transform:rotate(-90deg)}
.team .bio summary:hover,.team .bio summary:focus-visible{text-decoration:underline}
.team .bio p{margin-top:var(--s-12)}

/* ---------- testimonial carousel ---------- */
/* The track is just a scroll-snapping overflow container, so it works with no
   JS: swipe on touch, arrow keys once focused (hence tabindex on the region).
   The arrows are an enhancement site.js reveals. */
.carousel{position:relative}
.car-track{display:flex;gap:var(--s-24);list-style:none;margin:0;padding:var(--s-4) 0;
  overflow-x:auto;scroll-snap-type:x mandatory;scroll-behavior:smooth;
  -webkit-overflow-scrolling:touch;
  scrollbar-width:none;-ms-overflow-style:none}
.car-track::-webkit-scrollbar{display:none}
.car-track:focus-visible{outline:2px solid var(--red);outline-offset:3px}
.car-slide{flex:0 0 100%;scroll-snap-align:start}
@media (min-width:700px){.car-slide{flex-basis:calc((100% - 1.6rem) / 2)}}
@media (min-width:1000px){.car-slide{flex-basis:calc((100% - 3.2rem) / 3)}}
/* Stretch keeps every card the height of the tallest in view, so the row does
   not jump as quotes of different lengths scroll past. */
.car-slide figure{margin:0;height:100%;display:flex;flex-direction:column;
  background:#fff;border:1px solid var(--rule);border-radius:var(--radius);border-top:3px solid var(--red);
  padding:var(--s-24) var(--s-24) var(--s-20)}
.car-slide blockquote{margin:0;flex:1;font-size:var(--t-base);line-height:1.55}
.car-slide blockquote p{margin:0}
.car-slide figcaption{margin-top:var(--s-16);font-family:Oxygen,sans-serif;
  font-weight:700;font-size:var(--t-sm);color:var(--ink)}
.car-btn{position:absolute;top:50%;transform:translateY(-50%);z-index:2;
  width:40px;height:40px;display:flex;align-items:center;justify-content:center;
  background:#fff;color:var(--ink);border:1px solid var(--rule);border-radius:50%;
  cursor:pointer;box-shadow:0 1px 4px rgba(0,0,0,.12)}
.car-btn svg{width:18px;height:18px;fill:none;stroke:currentColor;stroke-width:2;
  stroke-linecap:round;stroke-linejoin:round}
.car-btn:hover{background:var(--red);color:#fff;border-color:var(--red)}
.car-btn[disabled]{opacity:.3;cursor:default;box-shadow:none}
.car-btn[disabled]:hover{background:#fff;color:var(--ink);border-color:var(--rule)}
.car-prev{left:-20px}
.car-next{right:-20px}
.car-pause{position:static;transform:none;margin:var(--s-16) auto 0;width:44px;height:44px}
.car-pause svg{width:16px;height:16px}
.car-pause .i-play{display:none}
.car-pause[data-paused="true"] .i-pause{display:none}
.car-pause[data-paused="true"] .i-play{display:block}
@media (max-width:1240px){.car-prev{left:4px}.car-next{right:4px}}
/* Below 1000px the track has no gutter for the arrows to float in, so they
   sat on top of the quotes. Drop them onto their own row underneath instead. */
@media (max-width:999px){
  /* The full-width track would otherwise stretch the button columns and fling
     them apart, so the buttons get content-sized columns with 1fr gutters
     either side to absorb the slack and centre the group. */
  .carousel{display:grid;grid-template-columns:1fr auto auto auto 1fr;
    align-items:center;gap:var(--s-12) var(--s-12)}
  .car-track{grid-row:1;grid-column:1 / -1;justify-self:stretch}
  .car-prev{grid-row:2;grid-column:2}
  .car-pause{grid-row:2;grid-column:3;margin:0}
  .car-next{grid-row:2;grid-column:4}
  .car-btn{position:static;transform:none}
}
@media (prefers-reduced-motion:reduce){.car-track{scroll-behavior:auto}}

/* ---------- quotes ---------- */
.quotes{columns:2 330px;column-gap:var(--s-28);margin:0;padding:0}
.quotes .quote{position:relative;margin:0 0 var(--s-28);break-inside:avoid;background:#fff;
  border:1px solid var(--rule);padding:var(--s-40) var(--s-24) var(--s-24);border-radius:var(--radius);
  transition:border-color .18s ease,box-shadow .18s ease}
/* An open quote set in the brand gold, sitting behind the text rather than
   indenting it. Decorative only - the <blockquote> already carries the
   semantics, so this must never reach the accessibility tree. */
.quotes .quote::before{content:"\201C";position:absolute;left:1rem;top:.1rem;
  font:700 3.6rem/1 Georgia,'Times New Roman',serif;color:var(--gold);opacity:.42;
  pointer-events:none}
.quotes .quote:hover{border-color:var(--mute);box-shadow:0 2px 14px rgb(5 38 62 / .07)}
.quotes blockquote{margin:0;font-size:var(--t-base)}
.quotes blockquote p + p{margin-top:var(--s-12)}
/* The claim the card is making, sized to be read at a glance. */
.quotes .pull{margin:0;color:var(--ink);font-size:var(--t-md);line-height:1.5;font-weight:600}
/* Progressive disclosure: the full quote stays available but never competes
   with the claim above it. */
.quotes details{margin-top:var(--s-16)}
.quotes summary{cursor:pointer;list-style:none;display:inline-flex;align-items:center;
  gap:var(--s-8);color:var(--red);font-family:Oxygen,sans-serif;font-size:var(--t-xs);font-weight:700;
  letter-spacing:.04em;padding:var(--s-4) 0;min-height:24px}
.quotes summary::-webkit-details-marker{display:none}
.quotes summary::after{content:"\203A";transform:rotate(90deg);transition:transform .18s ease;
  font-size:var(--t-md);line-height:1}
.quotes details[open] summary::after{transform:rotate(-90deg)}
.quotes summary:hover,.quotes summary:focus-visible{text-decoration:underline}
.quotes details p{margin-top:var(--s-12);font-size:var(--t-sm)}
/* Name in ink, role in grey: one line of hierarchy instead of a bold run-on. */
.quotes figcaption{margin-top:var(--s-20);padding-top:var(--s-16);border-top:1px solid var(--rule);
  font-family:Oxygen,sans-serif;font-size:var(--t-sm)}
.quotes figcaption b{display:block;color:var(--ink);font-weight:700}
.quotes figcaption span{display:block;color:var(--body);font-weight:400;font-size:var(--t-xs);
  margin-top:.15rem}

/* ---------- trust ---------- */
.trust{border-top:1px solid var(--rule);background:#fff;padding:clamp(2rem,4.4vw,2.8rem) 0}
.trust ul{list-style:none;margin:0;padding:0;display:grid;gap:var(--s-24);grid-template-columns:repeat(4,1fr)}
.trust li{display:flex;align-items:center;gap:var(--s-16);justify-content:center}
.trust svg{width:32px;height:32px;flex:none;stroke:var(--red);fill:none;stroke-width:1.5}
/* Every inline icon on the site is drawn on one 24 grid with one stroke. */
.ico{display:block;width:100%;height:100%;fill:none;stroke:currentColor;stroke-width:1.6;
  stroke-linecap:round;stroke-linejoin:round}
.trust b{display:block;font-family:Oxygen,sans-serif;color:var(--ink);font-weight:700;font-size:var(--t-base);line-height:1.25}
.trust span{font-size:var(--t-xs);color:var(--body)}
/* Breakpoints sit after the base .trust li rule so their justify-content wins. */
@media (max-width:799px){.trust ul{grid-template-columns:repeat(2,1fr);gap:var(--s-24) var(--s-16)}.trust li{justify-content:flex-start}}
/* 399px = the point where .grid logo cells (minmax 180px) collapse to one per row inside the 2.5rem-padded shell */
@media (max-width:560px){.trust ul{grid-template-columns:1fr;gap:var(--s-16)}}

/* ---------- utilities ---------- */
/* The generator used to emit 123 inline style attributes across the 42 built
   pages, and the same handful over and over: justify-content:flex-start 26
   times, margin-top:2rem 25 times, margin-inline:0 23 times. They are classes
   wearing a disguise. Naming them also removes the only reason the CSP needs
   style-src 'unsafe-inline'. Values come from the spacing scale, so a tweak
   here moves every instance at once. */
.u-mt-8{margin-top:var(--s-8)}
.u-mt-16{margin-top:var(--s-16)}
.u-mt-20{margin-top:var(--s-20)}
.u-mt-24{margin-top:var(--s-24)}
.u-mt-28{margin-top:var(--s-28)}
.u-mt-32{margin-top:var(--s-32)}
.u-mt-40{margin-top:var(--s-40)}
.u-center{text-align:center}
/* .btnrow sets justify-content:center later in the file; the compound
   selector outranks it, so the utility works regardless of order. */
.u-start,.btnrow.u-start{justify-content:flex-start}
/* Section rules and dividers centre themselves by default; inside a left-set
   column they should sit flush with the text. */
.u-flush{margin-inline:0}
/* A sub-heading that opens a list or capability block inside a page body:
   smaller than a section head, with air above it. */
.subhead{margin-top:var(--s-40);font-size:var(--t-lg)}
.u-measure{max-width:62ch}
.benefits.wide{grid-template-columns:repeat(auto-fit,minmax(230px,1fr))}

/* ---------- buttons + cta ---------- */
.btnrow{display:flex;flex-wrap:wrap;gap:var(--s-12);justify-content:center}
.btn{display:inline-block;background:var(--red);color:#fff;padding:var(--s-12) var(--s-28);font-weight:600;font-size:var(--t-sm);
  letter-spacing:.09em;text-transform:uppercase;border:0;cursor:pointer;font-family:inherit;transition:background .18s ease,color .18s ease}
/* Primary buttons stay in the brand on hover. --red-dark was already in the
   palette for this and had never been used; it darkens enough to register as
   feedback without dropping the button out of the brand into near-black. */
.btn:hover{background:var(--red-dark);color:#fff;text-decoration:none}
.btn.ghost{background:transparent;color:var(--red-dark);box-shadow:inset 0 0 0 1px currentColor}
.btn.ghost:hover{background:var(--red);color:#fff;box-shadow:none}
/* Pressed. Hover does not exist on a touchscreen, so without this every tap on
   a phone is silent until the page changes. One pixel down is enough. */
.btn:active{transform:translateY(1px)}
/* Disabled. The submit button disables itself while a lead is in flight and
   used to look exactly like a live one - same red, same pointer - so the only
   sign anything was happening was the status line below it. */
.btn[disabled],.btn[disabled]:hover{background:var(--red);opacity:.55;
  cursor:not-allowed;transform:none;box-shadow:none}
.btn.ghost[disabled],.btn.ghost[disabled]:hover{background:transparent;color:var(--red-dark);
  box-shadow:inset 0 0 0 1px currentColor}
.cta{background:var(--red);color:#fff;padding:clamp(2.2rem,4.8vw,3rem) 0;text-align:center}
.cta h2{color:#fff;font-size:var(--t-h3)}
.cta p{margin:var(--s-8) auto var(--s-24);max-width:56ch;color:#fff}
.cta .btn{background:#fff;color:var(--red)}
.cta .btn:hover{background:var(--ink);color:#fff}
.cta .btn.ghost{background:transparent;color:#fff;box-shadow:inset 0 0 0 1px #fff}
.cta .btn.ghost:hover{background:#fff;color:var(--red);box-shadow:none}
/* The ghost button's red-on-navy in the hero fell far below AA. On any dark
   ground the outline button goes white. */
.hero .btn.ghost{color:#fff;box-shadow:inset 0 0 0 1px #fff}
/* One filled button per hero. The second action is a text link with an
   arrow, so the eye lands on Request a Quote first. */
.btn-link{display:inline-flex;align-items:center;gap:.35em;align-self:center;color:#fff;font-weight:600;
  font-size:var(--t-sm);letter-spacing:.09em;text-transform:uppercase;padding:var(--s-12) var(--s-8);min-height:44px}
.btn-link::after{content:"\2192";display:inline-block;transition:transform .18s ease}
.btn-link:hover{text-decoration:underline;text-underline-offset:.2em}
.btn-link:hover::after{transform:translateX(4px)}
.hero .btn.ghost:hover{background:#fff;color:var(--navy);box-shadow:none}

/* ---------- forms ---------- */
.form-grid{display:grid;gap:var(--s-16);grid-template-columns:repeat(auto-fit,minmax(240px,1fr))}
.field{display:flex;flex-direction:column;gap:var(--s-8)}
.field.full{grid-column:1/-1}
.field label{font-family:Oxygen,sans-serif;color:var(--ink);font-size:var(--t-xs);letter-spacing:.05em}
.field .req{color:var(--red)}
.field input,.field select,.field textarea{font:inherit;font-size:var(--t-base);color:var(--ink);background:#fff;
  border:1px solid var(--field-rule);padding:var(--s-12) var(--s-12);border-radius:2px;width:100%}
.field textarea{min-height:150px;resize:vertical}
.field input:focus,.field select:focus,.field textarea:focus{border-color:var(--red);outline:none;box-shadow:0 0 0 3px rgba(221,51,51,.28)}
/* A pointer focus gets the tinted ring above; a keyboard focus gets the same
   solid outline every other control on the site uses, because that is the one
   a visitor is navigating by. */
.field input:focus-visible,.field select:focus-visible,.field textarea:focus-visible{
  outline:2px solid var(--red);outline-offset:2px}
/* Invalid fields carried no styling at all: the browser's own bubble was the
   entire error state, and it disappears on the next click leaving nothing
   behind. :user-invalid, not :invalid - a required field is invalid the moment
   the page loads, and nobody should be shouted at for a form they have not
   filled in yet. */
.field input:user-invalid,.field select:user-invalid,.field textarea:user-invalid{
  border-color:var(--red-dark);background:var(--invalid-bg)}
.field input:user-invalid:focus,.field select:user-invalid:focus,
.field textarea:user-invalid:focus{box-shadow:0 0 0 3px rgba(179,38,38,.28)}
.form-note{font-size:var(--t-xs);color:var(--body);margin-top:var(--s-16)}
.field input::placeholder{color:#8a8d91;opacity:1}
fieldset.field{border:0;padding:0;margin:0;min-width:0}
fieldset.field legend{font-family:Oxygen,sans-serif;color:var(--ink);font-size:var(--t-xs);letter-spacing:.05em;
  padding:0;margin-bottom:var(--s-4)}
.urgency label{display:flex;gap:var(--s-12);align-items:flex-start;font-family:inherit;font-size:var(--t-sm);
  letter-spacing:0;color:var(--body);padding:var(--s-8) 0;min-height:44px;cursor:pointer}
.urgency label b{color:var(--ink)}
.urgency input{width:20px;height:20px;flex:none;margin:.15rem 0 0;accent-color:var(--red)}
.split{display:grid;gap:clamp(2rem,5vw,3.5rem);grid-template-columns:minmax(0,1fr)}
@media (min-width:1000px){.split{grid-template-columns:minmax(0,1.25fr) minmax(0,1fr)}}
.info-list{list-style:none;margin:var(--s-24) 0 0;padding:0;display:grid;gap:var(--s-16)}
.info-list li{display:flex;gap:var(--s-16);align-items:flex-start}
.info-list svg{width:20px;height:20px;flex:none;margin-top:var(--s-4);stroke:var(--red);fill:none;stroke-width:1.7}
.info-list b{display:block;font-family:Oxygen,sans-serif;color:var(--ink)}


/* ---------- footer ---------- */
/* Every link group is a <details>, shipped open. On phones site.js closes
   all but Contact so the footer is seven headings, not 1,700px of links;
   from 560px up the summary is inert and the groups read as columns. */
.foot-group summary{list-style:none;cursor:default;display:flex;align-items:center;justify-content:space-between}
.foot-group summary::-webkit-details-marker{display:none}
.foot-group summary::after{content:"";width:9px;height:9px;flex:none;margin-left:var(--s-12);
  border-right:2px solid var(--foot-mark);border-bottom:2px solid var(--foot-mark);
  transform:rotate(45deg);transition:transform .18s ease;display:none}
.foot-group + .foot-group{margin-top:var(--s-24)}
.foot{background:var(--navy);color:var(--foot-ink);padding:clamp(2.6rem,5vw,3.6rem) 0 1.6rem;font-size:var(--t-sm)}
.foot .cols{display:grid;gap:2rem clamp(1.5rem,4vw,3rem);grid-template-columns:repeat(auto-fit,minmax(210px,1fr))}
.foot h2,.foot h3{color:#fff;font-size:var(--t-lg);font-weight:400;letter-spacing:.02em;margin-bottom:var(--s-4)}
.foot h2::after,.foot h3::after{content:"";display:block;width:26px;height:2px;background:rgba(255,255,255,.35);margin:var(--s-8) 0 var(--s-12)}
.foot ul{list-style:none;margin:0 0 var(--s-8);padding:0;display:grid;gap:var(--s-8)}
.foot li{display:flex;gap:var(--s-8);align-items:flex-start}
.foot li::before{content:"";width:5px;height:5px;background:var(--foot-mark);flex:none;margin-top:var(--s-8)}
.foot a{color:var(--foot-ink)}
.foot a:hover{color:#fff}
.foot .contact li::before{display:none}
.foot .contact svg{width:15px;height:15px;stroke:var(--red);fill:none;stroke-width:1.7;flex:none;margin-top:var(--s-8)}
@media (max-width:559px){
  .foot-group{border-bottom:1px solid rgba(255,255,255,.13)}
  /* Contact is the last group and the legal strip rules itself off just
     below it, so its own divider would double up. */
  .foot-contact{border-bottom:0}
  .foot-group summary{cursor:pointer;padding:var(--s-12) 0;min-height:44px}
  .foot-group summary::after{display:block}
  .foot-group[open] summary::after{transform:rotate(225deg)}
  .foot-group summary:focus-visible{outline:2px solid #fff;outline-offset:2px}
  .foot .foot-group h2,.foot .foot-group h3{margin:0}
  .foot .foot-group h2::after,.foot .foot-group h3::after{display:none}
  .foot-group ul{margin-bottom:var(--s-16)}
  .foot-group + .foot-group{margin-top:0}
  .foot .cols{gap:0}
}
.legal{border-top:1px solid rgba(255,255,255,.13);margin-top:var(--s-40);padding-top:var(--s-20);
  display:flex;flex-wrap:wrap;gap:var(--s-8) var(--s-24);justify-content:space-between;font-size:var(--t-xs);color:var(--foot-meta)}
.legal p{margin:0}

/* ---------- responsive nav ---------- */
/* Larger utility type needs tighter spacing before it wraps to a second row. */
@media (max-width:999px){
  .util .shell{gap:var(--s-4) var(--s-16);font-size:var(--t-xs)}
}
/* Tablets: three quick actions and nine service tiles set 2+1 and 4+1 at
   the default 260px track; three across has no orphan row. The footer's
   four columns set 3+1 at 768px, so it goes to two. */
@media (min-width:700px) and (max-width:999px){
  .cards.quick,.cards.open{grid-template-columns:repeat(3,1fr)}
  .foot .cols{grid-template-columns:repeat(2,1fr)}
}
@media (max-width:999px){
  .nav-toggle{display:inline-flex}
  .site-nav{position:absolute;top:100%;left:0;right:0;background:#fff;border-bottom:1px solid var(--rule);
    box-shadow:var(--shadow);flex-direction:column;align-items:stretch;gap:0;margin:0;padding:var(--s-8) 0;display:none}
  .site-head{position:relative}
  .site-nav[data-open]{display:flex}
  .site-nav > a{padding:var(--s-12) var(--s-20);width:100%;justify-content:space-between}
  /* Stacked nav: the label takes the row, the caret sits at its end. */
  .site-nav > .has-sub{display:flex;justify-content:space-between;align-items:stretch;flex-wrap:wrap;gap:0}
  .site-nav > .has-sub > a{flex:1;padding:var(--s-12) var(--s-20)}
  .site-nav > .has-sub > .sub-toggle{padding:var(--s-12) var(--s-20)}
  .has-sub > .sub{flex-basis:100%}
  .site-nav [aria-current="page"]{box-shadow:inset 3px 0 0 var(--red)}
  .has-sub > .sub{position:static;border:0;box-shadow:none;background:var(--band);min-width:0;padding:var(--s-4) 0}
  .has-sub > .sub a{padding-left:var(--s-32)}
}
@media (max-width:699px){
  /* The six utility links wrapped onto three lines, so roughly 100px of red
     band pushed every page down before any content appeared. Held on one line
     that scrolls sideways instead: the links stay reachable and the bar costs
     one row. */
  .util .shell{font-size:var(--t-xs);gap:0 var(--s-16);flex-wrap:nowrap;overflow-x:auto;
    scrollbar-width:none;-ms-overflow-style:none}
  .util .shell::-webkit-scrollbar{display:none}
  .util a{white-space:nowrap}
  /* A fade on the right edge says the row continues; the last link gets
     a margin so it can scroll clear of the fade. */
  .util{position:relative}
  .util::after{content:"";position:absolute;top:0;right:0;bottom:0;width:44px;pointer-events:none;
    background:linear-gradient(90deg,rgba(221,51,51,0),var(--red) 70%)}
  .util .shell > a:last-child{margin-right:var(--s-40)}
  .util .social{margin-right:var(--s-8);gap:var(--s-12)}
  .quotes{columns:1}
}

@media (max-width:560px){
  /* Side-by-side buttons wrapped to different widths and read as a ragged
     stack. Full width on one axis is tidier and gives a bigger tap target. */
  .btnrow{flex-direction:column;align-items:stretch}
  .btnrow .btn{text-align:center;width:100%}
  /* Footer links sat about 5px apart, well under a comfortable touch target. */
  .foot ul{gap:var(--s-8)}
  .foot li::before{margin-top:var(--s-12)}
  .foot a{display:inline-block;padding:.2rem 0}
}
/* Touch devices: link lists in the footer and the sibling rail get a 24px
   minimum hit area (WCAG 2.5.8) without changing the type size. */
@media (max-width:699px){
  .aside-links a{display:inline-block;padding:.2rem 0}
  .foot .contact a{display:inline-block;padding:.2rem 0}
}
@media print{
  .util,.jump,.site-nav,.nav-toggle,.cta{display:none}
  .site-head{position:static;border:0}
  .foot{background:#fff;color:#000}
  .cat,.card,.row{break-inside:avoid;background:#fff!important}
  .quote{break-inside:avoid}
  /* A closed <details> hides its body, so force every testimonial open on
     paper rather than printing nine one-line cards. */
  .quotes summary,.team .bio summary,.faq-item summary{display:none}
  .quotes details > *,.team .bio > *,.faq-item > *{display:block!important}
}

/* Line-card download link on the Our Partners page head. */
.pagehead .pdf{margin-top:var(--s-24);font-size:var(--t-sm)}
.pagehead .pdf a{display:inline-flex;align-items:center;gap:var(--s-8);border:1px solid var(--rule);
  color:var(--body);padding:var(--s-8) var(--s-16);letter-spacing:.06em;text-transform:uppercase;font-size:var(--t-xs);background:#fff}
.pagehead .pdf a:hover{border-color:var(--red);color:var(--red);text-decoration:none}
.pagehead .pdf svg{width:15px;height:15px;stroke:var(--red);fill:none;stroke-width:1.7}

/* ---------- numbered steps (thank-you page) ---------- */
.steps{list-style:none;counter-reset:step;margin:var(--s-24) 0 0;padding:0;display:grid;gap:var(--s-16)}
.steps li{counter-increment:step;position:relative;padding-left:3rem;font-size:var(--t-base)}
.steps li::before{content:counter(step);position:absolute;left:0;top:0;width:32px;height:32px;border-radius:50%;
  background:var(--red);color:#fff;font-family:Oxygen,sans-serif;font-weight:700;font-size:var(--t-sm);
  display:flex;align-items:center;justify-content:center}
.steps b{color:var(--ink)}

/* ---------- FAQ (services page) ---------- */
.faq{display:grid;gap:var(--s-8)}
.faq-item{background:#fff;border:1px solid var(--rule);border-radius:var(--radius);padding:0 var(--s-20)}
.faq-item summary{cursor:pointer;list-style:none;display:flex;justify-content:space-between;align-items:center;
  gap:var(--s-16);padding:var(--s-16) 0;min-height:44px;font-family:Setronics,Oxygen,sans-serif;color:var(--ink);font-size:var(--t-md)}
.faq-item summary::-webkit-details-marker{display:none}
.faq-item summary::after{content:"";flex:none;width:9px;height:9px;border-right:2px solid var(--red);border-bottom:2px solid var(--red);
  transform:rotate(45deg);transition:transform .18s ease;margin-right:3px}
.faq-item[open] summary::after{transform:rotate(225deg)}
.faq-item summary:hover{color:var(--red)}
.faq-item p{padding:0 0 var(--s-16);font-size:var(--t-base)}

/* ---------- article aside ---------- */
.aside{display:grid;gap:var(--s-24);align-content:start}
@media (min-width:1000px){.aside{position:sticky;top:6.5rem}}
.aside-card{border:1px solid var(--rule);border-radius:var(--radius);padding:var(--s-24) var(--s-24);background:#fff}
.aside-card.accent{background:var(--band);border-color:transparent}
.aside-card h2{font-size:var(--t-md);letter-spacing:.02em}
.aside-card h2::after{content:"";display:block;width:26px;height:2px;background:var(--red);margin:var(--s-8) 0 var(--s-16)}
.aside-card p{font-size:var(--t-sm)}
.aside-links{list-style:none;margin:0;padding:0;display:grid;gap:var(--s-8)}
.aside-links a{color:var(--ink);font-size:var(--t-sm)}
.aside-links a:hover{color:var(--red);text-decoration:none}
.aside-call{margin-top:var(--s-16);font-size:var(--t-xs);color:var(--body)}
.close{font-family:Oxygen,"Trebuchet MS",sans-serif;color:var(--ink);font-size:var(--t-md);
  border-left:3px solid var(--red);padding-left:var(--s-16)}

/* ---------- brochure: image + text features ---------- */
.framed{display:block;width:100%;height:auto;border:1px solid var(--rule)}
.features{display:grid;gap:clamp(2rem,4.5vw,3.2rem)}
.feature{display:grid;gap:1.4rem clamp(1.5rem,4vw,3rem);align-items:center}
@media (min-width:800px){
  .feature{grid-template-columns:minmax(0,1fr) minmax(0,1fr)}
  /* Alternate the image side so the column does not read as a rigid list. */
  .feature:nth-child(even) .feature-img{order:2}
}
.feature-img img{display:block;width:100%;height:auto;border:1px solid var(--rule);aspect-ratio:1200/427;object-fit:cover}
/* Product cut-outs sit on the page rather than in a frame: no border, no
   forced crop, and a little breathing room so they do not touch the text. */
.feature-img.product img{border:0;aspect-ratio:auto;object-fit:contain;padding:var(--s-8);
  max-width:390px;margin-inline:auto}
.feature-txt h2{font-size:var(--t-h3)}
/* Product strapline under a feature heading - same voice as .row .tag. */
.feature-txt .tagline{color:var(--red);font-size:var(--t-sm);font-style:italic;margin-bottom:var(--s-12)}

/* Pull quote and values line carried over from the printed brochure. */
.values{font-family:Oxygen,"Trebuchet MS",sans-serif;color:var(--red-dark);letter-spacing:.06em;
  font-size:var(--t-md);margin-top:var(--s-24)}
.pull{margin:var(--s-20) auto 0;max-width:34ch;font-family:Oxygen,"Trebuchet MS",sans-serif;
  color:var(--ink);font-size:var(--t-h4);line-height:1.4;quotes:"\201C" "\201D"}
.pull::before{content:open-quote}
.pull::after{content:close-quote}

/* Capability blocks inside a solution page's main column. */
.caps{display:grid;gap:var(--s-20);margin-top:var(--s-20)}
.cap{border-left:3px solid var(--rule);padding-left:var(--s-16)}
.cap h3{font-size:var(--t-md);margin-bottom:var(--s-4)}
.cap p{font-size:var(--t-sm)}

/* Per-category line card download, under each category heading. */
.cat-dl{margin-top:var(--s-16);font-size:var(--t-xs)}
.cat-dl a{display:inline-flex;align-items:center;gap:var(--s-8);border:1px solid var(--rule);background:#fff;
  color:var(--body);padding:var(--s-8) var(--s-16);letter-spacing:.06em;text-transform:uppercase;font-size:var(--t-2xs)}
.cat-dl a:hover{border-color:var(--red);color:var(--red);text-decoration:none}
.cat-dl svg{width:14px;height:14px;stroke:var(--red);fill:none;stroke-width:1.7}

/* SMART photography is 700-1200px originals; hold the display width down so
   nothing renders below ~1.9x and looks soft on a retina screen. */
.smart-shot img{max-width:430px;margin-inline:auto;display:block;width:100%;height:auto}

/* ---------- video embed ---------- */
/* YouTube embed on topic pages (cannabis). Sized by the column, held at 16:9
   so nothing jumps when the player loads; privacy-enhanced domain. */
.video-embed{margin:var(--s-28) 0 0;border:1px solid var(--rule);background:var(--band)}
.video-embed iframe{display:block;width:100%;aspect-ratio:16/9;height:auto;border:0}
/* Band under the contact split: one map per office, side by side when
   there is room. */
.map-band{border-top:1px solid var(--rule);background:var(--band);padding:var(--s-32) 0}
.maps{display:grid;gap:var(--s-24)}
@media (min-width:800px){.maps{grid-template-columns:1fr 1fr}}
.map-embed{margin:0;background:#fff;border:1px solid var(--rule);border-radius:var(--radius);overflow:hidden}
.map-embed figcaption{font-family:Oxygen,sans-serif;font-weight:700;color:var(--ink);font-size:var(--t-sm);
  padding:var(--s-12) var(--s-16);border-bottom:1px solid var(--rule)}
.map-embed iframe{display:block;width:100%;height:clamp(260px,30vw,380px);border:0}
/* ---------- phone quick bar ---------- */
.quick-bar{display:none}
@media (max-width:699px){
  .quick-bar{position:fixed;left:0;right:0;bottom:0;z-index:45;display:grid;grid-template-columns:1fr 1fr;
    gap:1px;background:var(--rule);border-top:1px solid var(--rule);
    padding-bottom:env(safe-area-inset-bottom)}
  .quick-bar a{display:inline-flex;align-items:center;justify-content:center;gap:var(--s-8);min-height:52px;
    background:#fff;color:var(--ink);font-family:Oxygen,sans-serif;font-weight:700;font-size:var(--t-sm);
    text-decoration:none;padding:var(--s-8) var(--s-8);white-space:nowrap;font-size:var(--t-xs)}
  .quick-bar a svg{width:18px;height:18px;stroke:var(--red);fill:none;stroke-width:1.8;stroke-linecap:round;stroke-linejoin:round}
  .quick-bar .qb-quote{background:var(--red);color:#fff;letter-spacing:.06em;text-transform:uppercase;font-size:var(--t-xs)}
  .quick-bar a:active{transform:translateY(1px)}
  /* Keep the footer's last lines clear of the bar. */
  body:has(.quick-bar){padding-bottom:calc(53px + env(safe-area-inset-bottom))}
}
@media print{.quick-bar,.head-call{display:none}}

/* Anti-spam honeypot: real visitors never see it, bots fill it in. */
.hp{position:absolute;left:-9999px;width:1px;height:1px;overflow:hidden}
[data-status][data-kind="error"]{color:var(--red-dark)}
/* Field errors: colour, an icon and a sentence under the field that names
   the problem. site.js adds them on blur and on a failed submit and clears
   each one as soon as its field is fixed. */
.field-error{display:flex;gap:var(--s-8);align-items:flex-start;margin:0;color:var(--red-dark);
  font-size:var(--t-xs);line-height:1.45}
.field-error::before{content:"";flex:none;width:16px;height:16px;margin-top:.1rem;border-radius:50%;
  background:var(--red-dark) url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23fff' stroke-width='3' stroke-linecap='round'%3E%3Cpath d='M12 7v6M12 17h.01'/%3E%3C/svg%3E") center/14px no-repeat}
.field [aria-invalid="true"]{border-color:var(--red-dark);background:var(--invalid-bg)}
.field [aria-invalid="true"]:focus{box-shadow:0 0 0 3px rgba(179,38,38,.28)}
/* Sending: the button stays in the tab order and keeps focus, so a
   screen reader hears the status line rather than silence. */
.btn[aria-busy="true"]{opacity:.7;cursor:progress}
.btn[aria-busy="true"]::after{content:"";display:inline-block;width:.9em;height:.9em;margin-left:.6em;
  vertical-align:-.1em;border:2px solid rgba(255,255,255,.45);border-top-color:#fff;border-radius:50%;
  animation:spin .8s linear infinite}
@keyframes spin{to{transform:rotate(360deg)}}
