/* Direct Equipment Parts — token overrides on the Industry system + the few
   things inline styles cannot express (media queries, keyframes, print). */

/* ── typefaces ──────────────────────────────────────────────────────────────
   Self-hosted rather than pulled from Google Fonts: no third-party request,
   and the pages keep working offline and behind a firewall.

   Ubuntu ships as static weights and the headings only ever use 600, so one
   file covers them — declared across 600-700 so that 600 resolves to the real
   Bold rather than a synthesised one. Rubik ships as a variable font, so a
   single file per style spans every body weight (400 body, 500 accents, 700
   strong) instead of four separate downloads.

   TTF because this machine has no fontTools/brotli to convert. Re-encoding
   these three as WOFF2 would cut the ~950KB payload by roughly 70%. */
@font-face {
  font-family: "Ubuntu";
  src: url("../fonts/Ubuntu-Bold.ttf") format("truetype");
  font-weight: 600 700;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: "Rubik";
  src: url("../fonts/Rubik-VariableFont_wght.ttf") format("truetype-variations");
  font-weight: 300 900;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: "Rubik";
  src: url("../fonts/Rubik-Italic-VariableFont_wght.ttf") format("truetype-variations");
  font-weight: 300 900;
  font-style: italic;
  font-display: swap;
}

/* Header video: blank white until the file can actually paint, so no empty
   frame flashes on load. dep.js flips data-ready once the video has data.
   The border in the markup stays transparent at every stage - it is there to
   hold its 2px of space, not to be seen. */
[data-headervideo] video { opacity: 0; transition: opacity .3s ease; }
[data-headervideo][data-ready="true"] video { opacity: 1; }
@media (prefers-reduced-motion: reduce) {
  [data-headervideo] video { transition: none; }
}

/* Photographs carry a blue wash so they sit inside the flat colour-blocked
   palette rather than reading as a separate warm layer. Enough to unify the
   page, not so much that the machinery stops looking like machinery. */
.duotone::after { opacity: .14; }

/* Blueprint frames read too faint at DEP's scale — thicken the hairline and
   the corner registration marks, and darken them toward the deep blue. */
.blueprint { border-width: 2px; border-color: var(--dep-line); }
.blueprint > .corner {
  width: 16px; height: 16px;
  color: var(--dep-mid);
}
.blueprint > .corner::before { left: 7px; width: 2px; }
.blueprint > .corner::after  { top: 7px; height: 2px; }
.blueprint > .corner.tl { top: -8px; left: -8px; }
.blueprint > .corner.tr { top: -8px; right: -8px; }
.blueprint > .corner.bl { bottom: -8px; left: -8px; }
.blueprint > .corner.br { bottom: -8px; right: -8px; }

:root {
  --dep-cyan: #3AA5F2;
  --dep-steel: #0A3269;
  --dep-mid: #0F62B5;
  --dep-amber: #F9C21C;
  --dep-concrete: #EAF2FB;
  --dep-ink: #14181C;
  --dep-line: #BFD5EC;
  --dep-line-soft: #DAE6F5;
  --dep-mono: "IBM Plex Mono", ui-monospace, SFMono-Regular, Menlo, monospace;

  /* Ubuntu for headings, Rubik for everything else. Overridden here rather
     than in industry.css so the vendor design system stays untouched. */
  --font-heading: "Ubuntu", system-ui, sans-serif;
  --font-body: "Rubik", system-ui, sans-serif;

  /* Industry tokens retuned to the DEP palette */
  --color-bg: #FFFFFF;
  --color-surface: #EAF2FB;
  --color-text: #14181C;
  --color-accent: #0F62B5;
  --color-accent-2: #3AA5F2;
  --color-divider: #BFD5EC;
  --color-accent-100: #E7F3FE;
  --color-accent-200: #C9E6FD;
  --color-accent-300: #8FC9F5;
  --color-accent-400: #5FB3F5;
  --color-accent-500: #3AA5F2;
  --color-accent-600: #1279D8;
  --color-accent-700: #0F62B5;
  --color-accent-800: #0A3D7E;
  --color-accent-900: #0A3269;
}

html { scroll-behavior: smooth; }
body { margin: 0; background: #fff; font-size: 16px; line-height: 1.6; }

/* The success panels toggle on [hidden], but they carry an inline display:
   that outranks the UA rule — without this the footer's "You're on the list"
   confirmation shows on every page load. */
[hidden] { display: none !important; }
[id] { scroll-margin-top: calc(var(--dep-band, 123px) + 14px); }

a { color: var(--dep-mid); text-underline-offset: 3px; }
a:hover { color: var(--dep-steel); }

/* Link motion — the hero cards lift on hover, so every link answers with a
   movement of its own. Body links nudge, list links track right, solid CTA
   links lift like the cards. */
[data-main] a, [data-side] a, [data-reveal] a {
  transition: color .16s ease, transform .16s ease, background .16s ease,
              box-shadow .16s ease, background-size .2s ease;
}
/* Inline prose links can't take a transform (inline boxes), so their motion is
   an underline that thickens and turns cyan. A 1px rule is always present. */
[data-main] p > a, [data-side] p > a {
  text-decoration: none;
  background-image: linear-gradient(currentColor, currentColor);
  background-repeat: no-repeat;
  background-position: left 100%;
  background-size: 100% 1px;
  padding-bottom: 2px;
}
[data-main] p > a:hover, [data-side] p > a:hover {
  background-image: linear-gradient(var(--dep-cyan), var(--dep-cyan));
  background-size: 100% 3px;
  color: var(--dep-steel);
}
[data-main] ul a, [data-side] ul a { display: inline-block; }
[data-main] ul a:hover, [data-side] ul a:hover {
  transform: translateX(4px);
  color: var(--dep-cyan);
}
[data-main] a[style*="padding"]:hover, [data-reveal] a[style*="padding"]:hover {
  transform: translateY(-3px);
  box-shadow: 0 10px 24px rgba(10, 50, 105, .3);
}
[data-main] a[style*="padding"]:active, [data-reveal] a[style*="padding"]:active {
  transform: translateY(-1px);
  box-shadow: 0 4px 12px rgba(10, 50, 105, .26);
}
@media (prefers-reduced-motion: reduce) {
  [data-main] a, [data-side] a, [data-reveal] a { transition: color .16s ease; }
  [data-main] a:hover, [data-side] a:hover, [data-reveal] a:hover { transform: none; }
  [data-main] p > a:hover, [data-side] p > a:hover { background-size: 100% 3px; }
}
h1, h2, h3, h4, h5, h6 { text-wrap: balance; }
/* Ticket form field labels (Name, Company, Email...). At the original 9.5px,
   uppercase and widely tracked, they were easy to skim straight past - so the
   form read as boxes with no questions. Bigger, heavier and in the deep blue
   so the question is the first thing the eye lands on. Sizes and colours are
   set inline on each label, hence !important; the weight is not, so it wins
   on its own. The 700 cut is loaded from Google Fonts alongside 400/500/600.
   The *required marker steps back down so it does not compete. */
[data-request-form] label {
  font-weight: 700;
  font-size: 12px !important;
  letter-spacing: .08em !important;
  color: #0A3269 !important;
  margin-bottom: 2px;
}
[data-request-form] label span { font-size: .82em; font-weight: 600; }

input::placeholder, textarea::placeholder { color: #5D7085; opacity: 1; }
p { text-wrap: pretty; }

:focus-visible { outline: 3px solid var(--dep-cyan); outline-offset: 2px; }
::selection { background: #C9E6FD; }

[data-navlink][aria-current="page"] { color: var(--dep-cyan); border-bottom-color: var(--dep-cyan); }

/* Scroll reveal — CSS-only, no JS, degrades to "always visible". */
@supports (animation-timeline: view()) {
  [data-reveal] {
    animation: dep-reveal both ease-out;
    animation-timeline: view();
    animation-range: entry 0% entry 42%;
  }
}
@keyframes dep-reveal {
  from { opacity: 0; transform: translateY(14px); }
  to { opacity: 1; transform: none; }
}

img, svg, table { max-width: 100%; }
input, textarea, select, button { font-size: inherit; max-width: 100%; }

/* Two-column shell: sidebar left on desktop, below the article on mobile. */
[data-shell] { display: flex; align-items: flex-start; gap: 37px; }
[data-side] { flex: 0 0 23%; min-width: 258px; position: sticky; top: calc(var(--dep-band, 123px) + 14px); }
[data-main] { flex: 1 1 0; min-width: 0; }

/* The sidebar carrying the request form runs ~1630px — taller than most
   viewports — so pinning it to the top parked its lower half, submit button
   included, permanently below the fold.

   It wants to scroll normally until its foot reaches the bottom of the screen
   and only then hold. `bottom:` cannot express that: sticky shifts an element
   UP toward a bottom inset, and only within its containing block — this column
   starts flush at the top of the shell, so there is no room above it to shift
   into and the constraint never engages. The offset has to be a negative `top`
   derived from the measured height, which only JS knows: initSidebar() in
   dep.js sets it inline and keeps it current on resize.

   Without JS this falls back to scrolling normally, which is imperfect but
   still leaves the entire form reachable. Scoped with :has() because the
   Contact and Hiring sidebars omit the form and are short enough that pinning
   to the top remains correct for them. */
@media (min-width: 1001px) {
  [data-side]:has([data-request-form]) { top: auto; }
}

/* Below 901px there is no room for three items across, so the video drops to
   its own full-width row beneath the logo and the tagline instead of being
   hidden. Grid rather than flex wrapping: the two rows are then explicit, and
   the tagline can never be bumped off row 1 by a few pixels of rounding. */
@media (max-width: 900px) {
  [data-idbar] {
    display: grid !important;
    grid-template-columns: auto minmax(0, 1fr);
    grid-template-areas: "logo tagline" "video video";
    align-items: center;
  }
  [data-idbar] > a { grid-area: logo; }
  [data-tagline] { grid-area: tagline; }
  /* Full width now, so the height has to grow with it: the clamp inherited
     from the beside-the-logo slot left a 9:1 letterbox that showed under a
     fifth of the frame. This keeps it near 3-5:1 without the header eating
     the screen. */
  [data-headervideo] {
    grid-area: video;
    width: 100%;
    height: clamp(95.63px, 19.13vw, 191.27px) !important;
  }
}

/* One nav item runs to two lines, so let every link stretch to the row height
   and centre its own label. Without this the shorter items sit at their own
   height and their active underlines land at different heights. Desktop only:
   below 901px the nav collapses to the stacked disclosure menu. */
@media (min-width: 901px) {
  [data-navlist] > li { display: flex; }
  [data-navlist] > li > a { display: flex; align-items: center; justify-content: center; }

  /* Fill the bar rather than bunching at the left: the list takes whatever
     width the delivery button leaves, and shares it out between the links.
     The gap values further down stay as the minimum spacing, so the links
     never close up on each other when the row is tight. */
  [data-navlist] { flex: 1 1 auto; justify-content: space-between; }
}

/* The tagline + phone box occupy a quarter of the id bar and sit hard right,
   so the logo keeps the rest. Desktop only: below 900px the bar has its own
   rules (and a quarter of a phone screen is narrower than the phone button). */
@media (min-width: 901px) {
  [data-tagline] { flex: 0 0 25%; max-width: 25%; }
}

/* Desktop: hold the CTA off the edges of the menu bar so it reads as a button
   sitting on the bar rather than a coloured segment of it. */
@media (min-width: 901px) {
  /* Vertical only in the narrow desktop band: below ~1024px the nav links and
     the button together leave no room for side margins, and the button wraps
     to a second row. */
  [data-deliverycta] { margin: 25px 0; }
}
/* Seven nav links, two of them running to two lines, plus the delivery button.
   The button shrinks below 1280px and the gap values set the minimum spacing
   the spread may close to. Under about 1060px the two cannot share a row at
   any readable size, so the button takes the second line. */
@media (min-width: 901px) and (max-width: 1279px) {
  [data-deliverycta] { font-size: 14px !important; padding: 9px 15px !important; }
}
@media (min-width: 901px) and (max-width: 1059px) {
  [data-navlist] { gap: 13px !important; }
}
@media (min-width: 1060px) and (max-width: 1279px) {
  [data-navlist] { gap: 12px !important; }
}

/* Full container width: room for the button at its normal size, back on the
   right of the row with margins all round - but only if the links give up a
   few pixels of spacing. At 26px they overrun 1280px by one. */
@media (min-width: 1280px) {
  [data-navlist] { gap: 16px !important; }
  [data-deliverycta] { margin: 25px; }
}

/* The pinned header carries its own copy of the action pills. dep.js reveals
   it only once the page's own pill row has scrolled up behind the nav, so the
   two buttons are never on screen twice. */
[data-stickypills] {
  display: none;
  /* Hangs off the bottom of the band rather than sitting inside its box.
     If it added to the band's height, showing it would fire the band's
     ResizeObserver, which re-measures and re-pins - and the resulting shift
     moves the first pill row back into range, so the two flip-flop. */
  position: absolute;
  top: 100%;
  left: 0;
  right: 0;
}
[data-stickypills][data-show="true"] { display: block; }

/* Delivery CTA. On desktop it lives at the right-hand end of the menu bar -
   the nav row is space-between, so the links sit left and this sits right.
   At 900px and below the nav collapses to the Menu button, and the CTA takes
   a full-width line of its own directly beneath it (order 4 keeps it below
   the disclosure list when that is open). */
@media (max-width: 900px) {
  [data-navrow] { padding-bottom: 12px !important; }
  [data-deliverycta] {
    order: 4;
    flex: 0 0 100%;
    width: 100%;
  }
}

/* The action pills (Request a Part + phone) stay centred at every width and in
   both the static and the pinned state. Unconditional rather than relying on
   the inline justify-content in the header markup, so no media query or later
   edit can knock them off centre on any device. */
[data-pillrow] { justify-content: center !important; }

[data-mobilebar] { display: none; }

/* The fixed Call / Request bar is parked off-screen until dep.js reports the
   visitor is 3% into the page, then slides up. Hidden state also drops
   pointer events so it cannot be tapped while it is off-screen. */
[data-mobilebar] {
  transform: translateY(110%);
  pointer-events: none;
  transition: transform .25s ease;
}
[data-mobilebar][data-shown="true"] { transform: none; pointer-events: auto; }
@media (prefers-reduced-motion: reduce) {
  [data-mobilebar] { transition: none; }
}
[data-wide-only] { display: revert; }

/* Tablet & down: sidebar drops under the article. */
@media (max-width: 1000px) {
  [data-shell] { flex-direction: column; gap: 44px; }
  [data-side] { flex: 1 1 auto; width: 100%; min-width: 0; position: static; order: 2; }
  [data-main] { order: 1; width: 100%; }
  [data-wide-only] { display: none !important; }
  [data-mobilebar] { display: flex !important; }
  body { padding-bottom: 62px; }
}

/* Tablet: the stacked sidebar reads as two columns instead of one long strip. */
@media (min-width: 700px) and (max-width: 1000px) {
  [data-side] aside { display: grid !important; grid-template-columns: 1fr 1fr; align-items: start; }
  [data-side] aside > * { min-width: 0; }
  [data-side] aside > #sidebar-request { grid-column: 1 / -1; }
}

/* Nav collapses to a disclosure menu; the action pills become one scrolling row. */
@media (max-width: 900px) {
  [data-navrow] { justify-content: flex-start !important; }
  [data-navtoggle] { display: inline-flex !important; }
  [data-navlist] { display: none !important; }
  [data-navlist][data-open="true"] { display: flex !important; flex-direction: column; gap: 0 !important; width: 100%; order: 3; border-top: 1px solid #17518F; }
  [data-navlist][data-open="true"] > li > a { padding: 14px 2px !important; border-bottom: 1px solid #154C88 !important; border-left: 3px solid transparent; padding-left: 11px !important; text-align: left !important; justify-content: flex-start !important; }
  [data-navlist][data-open="true"] > li > a[aria-current="page"] { border-left-color: #3AA5F2; }
  [data-pillrow] > a { flex: none; }
  /* Stacked category lists: rules move from the side to the top. */
  /* The make, type and component block is now a full width brand row above a
     pair of icon lists, so the old three column rules no longer apply. The
     brand tiles simply take fewer per row as the space narrows. */
  [data-makerow] { grid-template-columns: repeat(auto-fit, minmax(120px, 1fr)) !important; }
}

@media (max-width: 720px) {
  [data-stack-sm] { flex-direction: column !important; align-items: stretch !important; }

  /* Once the row stacks, the figure's `flex: 1 1 300px` basis switches from
     width to HEIGHT, pinning it at 300px tall while the 16/10 image box inside
     was only ~160px — that was the band of white figure background under the
     photo. Its inline align-self:flex-start also outranked the row's stretch,
     holding it at its 260px min-width instead of filling the column.

     width:100% rather than align-self:stretch, deliberately: stretch resolves
     the cross size only after the main size, so the figure would measure its
     content height against a zero max-content width — the <img> is absolutely
     positioned, so the aspect-ratio box carries no intrinsic width — and
     collapse to just its borders. An explicit percentage is definite up front,
     so the 16/10 box can resolve a real height from it. */
  [data-stack-sm] > figure,
  [data-stack-sm] > .blogslider {
    flex: 0 0 auto !important;
    width: 100% !important;
    min-width: 0 !important;
  }

  /* Stacked, the reasons to call DEP should land before the reading material,
     so the slider drops below them. Only the slider is ordered - its sibling
     keeps the default 0 - and only here, so the desktop row still opens with
     the slider on the left. */
  [data-stack-sm] > .blogslider { order: 1; }
  [data-hide-sm] { display: none !important; }
  [data-fieldgrid] > div { border-right: 0 !important; }

  /* Phone header: the tagline and its phone button stay beside the logo rather
     than dropping out. The row must not wrap for them to sit side by side, so
     the logo and the tagline are both scaled down to fit a 320px screen. */
  [data-idbar] {
    flex-wrap: nowrap !important;
    gap: 12px !important;
    padding: 12px 16px !important;
  }
  /* Fill the white space beside the tagline: grow the logo toward the height
     of that block. Viewport-scaled rather than flat, because the tagline sits
     in a 1fr track - every pixel the logo gains, it loses, and it still has to
     fit the phone button on a 320px screen. */
  [data-idbar] img { height: clamp(62.4px, 20.4vw, 81.6px) !important; }
  /* Phone only: 5% off the video row. Tablet (721-900px) keeps the taller
     clamp set in the max-width:900px block above. */
  [data-headervideo] { height: clamp(90.87px, 18.17vw, 181.75px) !important; }
  /* Phone: tighten the framing a little. object-fit already covers the slot,
     so this scales the painted frame inside it; the slot clips the overflow. */
  [data-headervideo] video { transform: scale(1.066); }

  /* Phone: scale the delivery CTA down to match the tighter bar. */
  [data-deliverycta] {
    padding: 9px 16px !important;
    font-size: 13px !important;
    gap: 7px !important;
  }
  [data-deliverycta] svg { width: 14px !important; height: 14px !important; }
  [data-tagline] { min-width: 0; text-align: right !important; }
  [data-tagline] > span { font-size: 13px !important; }
  [data-tagline] a {
    margin-top: 5px !important;
    padding: 4px 10px !important;
    font-size: 14px !important;
    white-space: nowrap;
  }
}

/* Phones: single-column everything, and the ticket forms keep 16px inputs so
   iOS does not zoom on focus. */
@media (max-width: 560px) {
  [data-shell] { gap: 33px; }
  [data-side] aside { gap: 22px !important; }
  input, textarea { font-size: 16px !important; }
  /* Both labels must hold one line - the phone number broke to two on the
     narrowest phones, which doubled the bar height. */
  [data-mobilebar] a { font-size: 15px !important; padding: 14px 6px !important; white-space: nowrap; }
  /* The two pills stay on one row at every phone width - they must never
     stack. flex:0 1 auto undoes the flex:none set in the 900px block so they
     can give way instead of wrapping. */
  [data-pillrow] { gap: 8px !important; flex-wrap: nowrap !important; }
  [data-pillrow] > a {
    flex: 0 1 auto !important;
    min-width: 0;
    white-space: nowrap;
    padding: 8px 14px !important;
    font-size: 13.5px !important;
    gap: 6px !important;
  }
}

/* Narrow phones: at 13.5px the pills needed ~71px more than the row had, so
   they wrapped. Tighter type, padding, icon and row gutters buy that back.
   Must come after the 560px block - both use !important, so source order
   decides which wins. */
@media (max-width: 400px) {
  [data-pillrow] { gap: 6px !important; padding-left: 10px !important; padding-right: 10px !important; }
  [data-pillrow] > a { padding: 8px 10px !important; font-size: 12px !important; gap: 5px !important; }
  [data-pillrow] > a svg { width: 13px !important; height: 13px !important; }
  [data-mobilebar] a { font-size: 12.5px !important; padding: 14px 4px !important; letter-spacing: .03em !important; }
}


/* ------------------------------------------------------------------ gallery
   One slide at a time on a flex track. The stage holds a fixed 4:3 box so the
   row beside it does not jump as slides change, and dep.js only ever sets a
   translateX on the track. */
/* The gallery is a column so the stage can take up whatever height the box
   beside it forces on the row; the aspect ratio is its starting size, not a
   ceiling, and object-fit keeps the picture filling it either way. */
[data-gallery] { display: flex; flex-direction: column; }
[data-gallery] .stage {
  position: relative;
  overflow: hidden;
  flex: 1 1 auto;
  aspect-ratio: 4 / 3;
  background: #0A3269;
  border: 1px solid var(--dep-line);
}
[data-gallery] .track {
  display: flex;
  height: 100%;
  margin: 0;
  padding: 0;
  list-style: none;
  transition: transform .45s cubic-bezier(.22,.61,.36,1);
}
[data-gallery] .track > li { flex: 0 0 100%; height: 100%; min-width: 0; }
[data-gallery] .track img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  cursor: zoom-in;
}

/* Arrows sit on the image, so they carry their own ground for contrast. */
[data-gallery] .nav {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  display: grid;
  place-items: center;
  width: 38px;
  height: 38px;
  padding: 0;
  background: rgba(10,50,105,.86);
  color: #fff;
  border: 1px solid rgba(255,255,255,.4);
  cursor: pointer;
  transition: background .15s ease;
}
[data-gallery] .nav:hover { background: #0F62B5; }
[data-gallery] .nav.prev { left: 10px; }
[data-gallery] .nav.next { right: 10px; }

[data-gallery] .dots {
  display: flex;
  justify-content: center;
  gap: 9px;
  margin-top: 13px;
}
[data-gallery] .dot {
  width: 10px;
  height: 10px;
  padding: 0;
  border: 1px solid #0F62B5;
  background: transparent;
  cursor: pointer;
}
[data-gallery] .dot[aria-current="true"] { background: #0F62B5; }

@media (prefers-reduced-motion: reduce) {
  [data-gallery] .track { transition: none; }
}

/* Blog slider (home page, beside Why Work With DEP). Same component as the
   photo galleries, but the slides are cards, so two things change: the stage
   sizes to its tallest slide rather than holding 4:3, and the arrows move
   into a bar under it - floated over a card they would sit on the summary. */
[data-gallery].blogslider .stage {
  aspect-ratio: auto;
  background: #fff;
  border: 0;
  border-top: 1px solid var(--dep-line);
  border-bottom: 1px solid var(--dep-line);
}
[data-gallery].blogslider .track > li { height: auto; display: flex; flex-direction: column; }
[data-gallery].blogslider .track img { cursor: default; }
[data-gallery].blogslider .sliderbar {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 15px;
  padding: 13px 12px;
}
[data-gallery].blogslider .nav {
  position: static;
  transform: none;
  width: 34px;
  height: 34px;
}
[data-gallery].blogslider .dots { margin-top: 0; }

/* ----------------------------------------------------------------- lightbox
   Fixed over the page, image centred and never taller than the viewport.
   Autoplay is suspended while this is open - dep.js handles that. */
[data-lightbox] {
  position: fixed;
  inset: 0;
  z-index: 100;
  display: grid;
  grid-template-columns: auto 1fr auto;
  align-items: center;
  gap: clamp(6px, 2vw, 20px);
  padding: clamp(12px, 4vw, 40px);
  background: rgba(6,24,50,.95);
}
[data-lightbox][hidden] { display: none; }
[data-lightbox] .lb-figure {
  margin: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 12px;
  min-width: 0;
}
[data-lightbox] .lb-figure img {
  display: block;
  max-width: 100%;
  max-height: 78vh;
  width: auto;
  height: auto;
  border: 1px solid rgba(255,255,255,.25);
}
[data-lightbox] figcaption {
  margin: 0;
  max-width: 70ch;
  text-align: center;
  font-size: 14.5px;
  line-height: 1.5;
  color: #C3DAF2;
}
[data-lightbox] .lb-nav,
[data-lightbox] .lb-close {
  display: grid;
  place-items: center;
  padding: 0;
  background: rgba(255,255,255,.08);
  color: #fff;
  border: 1px solid rgba(255,255,255,.4);
  cursor: pointer;
  transition: background .15s ease;
}
[data-lightbox] .lb-nav:hover,
[data-lightbox] .lb-close:hover { background: #0F62B5; }
[data-lightbox] .lb-nav { width: clamp(40px, 7vw, 54px); height: clamp(40px, 7vw, 54px); }
[data-lightbox] .lb-close {
  position: absolute;
  top: clamp(10px, 3vw, 22px);
  right: clamp(10px, 3vw, 22px);
  width: 42px;
  height: 42px;
}

/* Phones: the arrows would eat the image, so they drop below it. */
@media (max-width: 560px) {
  [data-lightbox] {
    grid-template-columns: 1fr 1fr;
    grid-template-areas: "fig fig" "prev next";
    align-content: center;
  }
  [data-lightbox] .lb-figure { grid-area: fig; }
  [data-lightbox] .lb-prev { grid-area: prev; justify-self: end; }
  [data-lightbox] .lb-next { grid-area: next; justify-self: start; }
  [data-lightbox] .lb-figure img { max-height: 62vh; }
}

/* Narrowest phones: the delivery CTA still broke to three lines at 13px, so
   trim the type and side padding again to hold it at two. */

/* Hero card headings are broken to two lines by hand, so their type has to
   answer to the width of the card, not the width of the window. A viewport
   clamp cannot: the grid holds three columns down to about 880px, where the
   cards are at their narrowest while 4vw is still near its ceiling, and
   "Pickup & Delivery" spilled onto a third line. Sizing off the container
   keeps the longest line inside its own card at every width, and keeps all
   three headings the same size as each other. The viewport clamp in the
   markup stays as the fallback where container queries are missing. */
@supports (container-type: inline-size) {
  section[aria-label="Ways to reach us"] article { container-type: inline-size; }
  section[aria-label="Ways to reach us"] article > h2 {
    font-size: clamp(22px, 10cqi, 37px) !important;
  }
}

/* Narrow phones: "Guaranteed to Find a Fix" is the longest of the three hero
   buttons and, set nowrap at 16px, it grew wider than its own card and pushed
   the page sideways. Trim the type on the smallest screens. */
@media (max-width: 400px) {
  section[aria-label="Ways to reach us"] article > a {
    font-size: 14px !important;
    padding: 9px 12px !important;
    letter-spacing: .03em !important;
  }
}

@media (max-width: 380px) {
  [data-deliverycta] { padding: 9px 12px !important; font-size: 12px !important; }
}

/* Narrowest phones: a bigger logo steals width from the tagline track, the
   headline takes an extra line and the gap it was meant to close gets wider.
   Keep the original size here. */
@media (max-width: 340px) {
  [data-idbar] img { height: 50px !important; }
}

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after {
    animation-duration: 0.001ms !important;
    transition-duration: 0.001ms !important;
  }
  [data-reveal] { opacity: 1 !important; transform: none !important; }
  [data-reveal] div[style*="height:3px"][style*="#3AA5F2"] { transform: none !important; }
}

@media print {
  [data-mobilebar], [data-side] { display: none; }
}

/* ---------------------------------------------------------------
   Hover / focus states.
   Generated from the design's style-hover / style-focus attributes,
   which are Design-app authoring metadata that no browser honours.
   Converted to real CSS so the interaction design works when hosted.

   !important is required, not lazy: every one of these elements also
   carries an inline style= attribute, which outranks a class selector.
   --------------------------------------------------------------- */
.hv-1:hover { color:#fff !important; }
.hv-2:hover { background:#0A3269 !important; color:#fff !important; }
.hv-3:hover { background:#0F62B5 !important; border-color:#0F62B5 !important; }
.hv-4:hover { color:#3AA5F2 !important; }
.hv-5:hover { background:#5FB3F5 !important; border-color:#5FB3F5 !important; }
.hv-6:hover { background:#0A3269 !important; }
/* Every gold-filled button, one hover: navy ground, gold label, 2px gold edge.
   .hv-jump rides along: the services index pills are not gold-filled, but they
   sit ON navy, so the fill reads as nothing and the gold ring and label are all
   that appears - the same hover, arrived at from the other side.
   The edge is an inset shadow, not a border - these buttons carry 0px, 1px and
   2px borders between them, so recolouring the border would give an uneven
   edge and adding one would shift the layout. It is not an outline either,
   because :focus-visible uses outline and this would outrank it. */
.hv-7:hover, .hv-8:hover, .hv-9:hover, .hv-11:hover,
.hv-19:hover, .hv-gold-soft:hover, .hv-svc-primary:hover, .hv-delivery-cta:hover,
.hv-jump:hover {
  background: #0A3269 !important;
  color: #F9C21C !important;
  border-color: #0A3269 !important;
  box-shadow: inset 0 0 0 2px #F9C21C !important;
}
.hv-10:hover { color:#0A3269 !important; }
.hv-12:hover { background:#EAF2FB !important; border-left-color:#3AA5F2 !important; color:#0F62B5 !important; }
.hv-13:hover { transform:translateY(-4px) !important; box-shadow:0 12px 32px rgba(10,50,105,.28) !important; }
.hv-14:hover { border-color:#3AA5F2 !important; }
.hv-15:hover { color:#0F62B5 !important; }
.hv-16:hover { border-color:#3AA5F2 !important; background:#E7F3FE !important; }
.hv-17:hover { background:#0F62B5 !important; color:#fff !important; }
.hv-18:hover { border-color:#3AA5F2 !important; color:#3AA5F2 !important; }

/* Form fields. Hover is gold, focus is blue, and hover comes first in the
   file so the blue still wins when a field is both hovered and focused -
   which is every field you are actually typing into. The
   hover media query keeps it off touch screens, where a tap would otherwise
   leave the tint stuck on the last field touched. Every field in every form
   on the site carries fc-1, so this covers the part request, contact,
   sidebar, delivery and job application forms alike. */
@media (hover: hover) {
  /* The same blue the field takes when it is focused, so pointing at a
     field and being in it read as one state. */
  .fc-1:hover { background:#E7F3FE !important; }
}
.fc-1 { transition: background .14s ease; }
.fc-1:focus { background:#E7F3FE !important; }
@media (prefers-reduced-motion: reduce) { .fc-1 { transition: none; } }

/* Make, machine type and component tiles. The icon plate inverts on hover,
   the same navy/gold flip every gold button on the site performs, so the
   whole tile reads as one target rather than a link with a picture on it. */
.hv-tile:hover { border-color: #0A3269 !important; background: #F7FBFF !important; }

/* The photo thumbnails carry their own frame so a studio shot on white does
   not bleed into the tile. On hover that frame takes the navy the tile border
   takes, which is the job the icon plate used to do. */
.tile-pic {
  flex: none;
  width: 78px;
  height: 78px;
  object-fit: cover;
  border: 1px solid #DAE6F5;
  background: #fff;
  transition: border-color .16s ease;
}
.hv-tile:hover .tile-pic { border-color: #0A3269; }

/* Brand tiles. There is no logo artwork any more - the name IS the tile, so
   it is set large enough to read as a wordmark in its own right. */
.brand-name {
  font-family: var(--font-heading);
  font-weight: 700;
  font-size: clamp(20px, 2.8vw, 30px);
  letter-spacing: .02em;
  text-transform: uppercase;
  color: #0A3269;
  line-height: 1.05;
}
.brand-tile:hover .brand-name,
.brand-tile:focus-visible .brand-name {
  color: #0F62B5;
  text-decoration: underline;
  text-decoration-color: #3AA5F2;
  text-decoration-thickness: 3px;
  text-underline-offset: 6px;
}

@media (prefers-reduced-motion: reduce) { .tile-pic { transition: none; } }


/* Services page CTA pattern. Primary is an orange fill, secondary a white
   outline; both invert to the deep ground on hover. Hand-written rather than
   generated, so they are not overwritten if the state block is regenerated. */
.hv-svc-secondary:hover { background:#0A3269 !important; color:#fff !important; border-color:#0A3269 !important; }

/* Header delivery CTA band. Named rather than generated (hv-N) so a future
   regeneration of the state block cannot clobber it. */
/* The delivery CTA keeps its lift on top of the shared gold hover above.
   The inset edge is repeated here because the drop shadow shares the
   property - dropping it would lose the outline on this one button. */
.hv-delivery-cta:hover {
  transform: translateY(-2px);
  box-shadow: inset 0 0 0 2px #F9C21C,
              0 6px 16px rgba(10, 50, 105, .35) !important;
}

/* The three Our Services buttons sit left against the copy on desktop. Below
   1000px the sidebar has stacked and the main column runs full width, so the
   row is centred instead - including each wrapped line of it, which is what
   makes the stacked phone layout look intentional rather than ragged. */
@media (max-width: 1000px) {
  [data-servicecta] { justify-content: center; }
}

/* ---- reveal, JS driven ------------------------------------------------
   Only applies once dep.js has set the flag, which it does only where
   animation-timeline is unsupported. No JS means nothing is ever hidden. */
html[data-revealjs] [data-reveal] { opacity: 0; transform: translateY(14px); }
html[data-revealjs] [data-reveal][data-revealed] {
  opacity: 1;
  transform: none;
  transition: opacity .5s ease-out, transform .5s ease-out;
}

/* ---- the cyan rule under each section heading ------------------------
   74px of a 3px bar drawing itself in. Almost no movement, but it is the
   mark that repeats on nearly every section, so the page stops feeling
   frozen. One version per reveal mechanism. */
@keyframes dep-rule { from { transform: scaleX(0); } to { transform: scaleX(1); } }

@supports (animation-timeline: view()) {
  @media (prefers-reduced-motion: no-preference) {
    [data-reveal] div[style*="height:3px"][style*="#3AA5F2"] {
      transform-origin: left center;
      animation: dep-rule both cubic-bezier(.22,.61,.36,1);
      animation-timeline: view();
      animation-range: entry 0% entry 55%;
    }
  }
}
@media (prefers-reduced-motion: no-preference) {
  html[data-revealjs] [data-reveal] div[style*="height:3px"][style*="#3AA5F2"] {
    transform: scaleX(0);
    transform-origin: left center;
  }
  html[data-revealjs] [data-reveal][data-revealed] div[style*="height:3px"][style*="#3AA5F2"] {
    transform: scaleX(1);
    transition: transform .55s .1s cubic-bezier(.22,.61,.36,1);
  }
}

/* ---- tile photographs ------------------------------------------------- */
@media (hover: hover) and (prefers-reduced-motion: no-preference) {
  .hv-tile:hover .tile-pic { transform: scale(1.04); }
}
.tile-pic { transition: border-color .16s ease, transform .28s cubic-bezier(.22,.61,.36,1); }

/* ---- downtime calculator sliders --------------------------------------
   A range input is an OS widget until every part of it is styled, and the
   two engines expose different pseudo elements, so track and thumb are
   written twice. WebKit has no ::-moz-range-progress, so the filled part of
   the track is a gradient driven by --fill, which dep.js sets as the value
   changes. The thumb is square to match the rest of the site.

   These deliberately do not carry fc-1: that class force-paints a background
   on hover and focus, which here would put a box behind the track. */
[data-calc] input[type="range"] {
  -webkit-appearance: none;
  appearance: none;
  display: block;
  width: 100%;
  height: 20px;
  margin: 7px 0 0;
  padding: 0;
  background: transparent;
  cursor: pointer;
}
[data-calc] input[type="range"]::-webkit-slider-runnable-track {
  height: 4px;
  border: 0;
  background: linear-gradient(to right,
    #0F62B5 var(--fill, 0%), #DAE6F5 var(--fill, 0%));
}
[data-calc] input[type="range"]::-moz-range-track {
  height: 4px;
  border: 0;
  background: #DAE6F5;
}
[data-calc] input[type="range"]::-moz-range-progress {
  height: 4px;
  background: #0F62B5;
}
[data-calc] input[type="range"]::-webkit-slider-thumb {
  -webkit-appearance: none;
  appearance: none;
  width: 13px;
  height: 18px;
  margin-top: -7px;
  border: 0;
  border-radius: 0;
  background: #0F62B5;
  transition: background .14s ease;
}
[data-calc] input[type="range"]::-moz-range-thumb {
  width: 13px;
  height: 18px;
  border: 0;
  border-radius: 0;
  background: #0F62B5;
  transition: background .14s ease;
}
@media (hover: hover) {
  [data-calc] input[type="range"]:hover::-webkit-slider-thumb { background: #F9C21C; }
  [data-calc] input[type="range"]:hover::-moz-range-thumb { background: #F9C21C; }
}
[data-calc] input[type="range"]:focus-visible::-webkit-slider-thumb { background: #0A3269; }
[data-calc] input[type="range"]:focus-visible::-moz-range-thumb { background: #0A3269; }
@media (prefers-reduced-motion: reduce) {
  [data-calc] input[type="range"]::-webkit-slider-thumb { transition: none; }
  [data-calc] input[type="range"]::-moz-range-thumb { transition: none; }
}

/* ---- cost breakdown disclosure ----------------------------------------
   The five cost lines collapse; the total band underneath never does. The
   default marker is removed in both engines and replaced by the chevron in
   the markup, which flips when the panel is open. */
[data-calcdetails] > summary { list-style: none; flex-wrap: wrap; }
/* keeps the label and chevron to the right whether they share the
   heading's line or wrap below it */
[data-calcdetails] > summary > span { margin-left: auto; }
@media (max-width: 560px) {
  [data-calcdetails] > summary [data-calcmore],
  [data-calcdetails] > summary [data-calcless] { font-size: 11.5px !important; }
  /* !important because the label carries its size inline, as everything
     in this design export does */
  [data-calcdetails] > summary { padding-left: 14px; padding-right: 14px; }
}
[data-calcdetails] > summary::-webkit-details-marker { display: none; }
[data-calcdetails] > summary::marker { content: ""; }
[data-calcdetails] > summary [data-calcchevron] { transition: transform .2s ease; }
/* one label at a time, swapped off the open attribute. The hidden one is
   display:none so it is out of the accessibility tree too. */
[data-calcdetails] > summary [data-calcless] { display: none; }
[data-calcdetails][open] > summary [data-calcmore] { display: none; }
[data-calcdetails][open] > summary [data-calcless] { display: block; }
[data-calcdetails][open] > summary [data-calcchevron] { transform: rotate(180deg); }
@media (hover: hover) {
  [data-calcdetails] > summary:hover { background: #0F62B5; }
}
[data-calcdetails] > summary:focus-visible { outline: 3px solid var(--dep-cyan); outline-offset: -3px; }
@media (prefers-reduced-motion: reduce) {
  [data-calcdetails] > summary [data-calcchevron] { transition: none; }
}

/* ---- quick delivery form ----------------------------------------------
   The collapsed form at the top of the delivery page. Same disclosure
   mechanics as the calculator's cost breakdown: the native marker is removed
   in both engines, one label shows at a time off the [open] attribute, and
   the chevron turns. */
[data-quickdetails] > summary { list-style: none; }
[data-quickdetails] > summary::-webkit-details-marker { display: none; }
[data-quickdetails] > summary::marker { content: ""; }
[data-quickdetails] > summary [data-quickchevron] { transition: transform .2s ease; }
[data-quickdetails] > summary [data-quickless] { display: none; }
[data-quickdetails][open] > summary [data-quickmore] { display: none; }
[data-quickdetails][open] > summary [data-quickless] { display: block; }
[data-quickdetails][open] > summary [data-quickchevron] { transform: rotate(180deg); }
/* a tint, not an inversion: the row is white with blue text now, so the
   navy hover the calculator uses would hide its own label */
@media (hover: hover) {
  [data-quickdetails] > summary:hover { background: #D8E6F7; }
}
[data-quickdetails] > summary:focus-visible { outline: 3px solid var(--dep-cyan); outline-offset: -3px; }
@media (prefers-reduced-motion: reduce) {
  [data-quickdetails] > summary [data-quickchevron] { transition: none; }
}

/* ---- Types of Consulting rows -----------------------------------------
   Photo left, copy right. Under 620px the photo column would leave the text
   a few words wide, so the row stacks with the photo on top. The grid columns
   are set inline, hence !important. */
@media (max-width: 620px) {
  [data-consultrow] { grid-template-columns: minmax(0, 1fr) !important; }
}
