/* =====================================================================
   Stocktakes Online — Barcode Datalink
   ---------------------------------------------------------------------
   The look is warehouse, not startup: signage contrast, engineering
   greys, and one signal colour used the way amber is used on a loading
   dock — sparingly, and always to mean something.

   Two motifs carry the identity:
     .scan   a laser line sweeping a surface, as a scanner does
     .bars   barcode rules, used as dividers and ornament

   The logo runs in white here (assets/sto-logo-white.png); the blue
   original is kept for light backgrounds and the favicon.

   Everything is one stylesheet and no build step, matching the rest of
   the product.
   ===================================================================== */

/* =====================================================================
   The three typefaces, served from this site rather than from Google.

   **This used to be one @import from fonts.googleapis.com.** That handed
   Google the address of every visitor on every page load, and it made the
   privacy page's promise - "not a font, not a script, not an image" -
   untrue. Serving them ourselves is the fix, and it is faster too: no DNS
   lookup, no second connection, no round trip before any text can be set.

   Only the latin and latin-ext subsets are kept. Cyrillic, Greek and
   Vietnamese tripled the file count for glyphs this site will never set.
   The unicode-range lines are Google's own, so a browser still fetches
   only the subset a page actually needs.

   Archivo and IBM Plex are both under the SIL Open Font License, which
   permits this. The licences and the reasoning are in assets/fonts/.
   ===================================================================== */

/* Archivo 500 - latin-ext */
@font-face {
  font-family: 'Archivo';
  font-style: normal;
  font-weight: 500;
  font-stretch: 100%;
  font-display: swap;
  src: url('fonts/archivo-500-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

/* Archivo 500 - latin */
@font-face {
  font-family: 'Archivo';
  font-style: normal;
  font-weight: 500;
  font-stretch: 100%;
  font-display: swap;
  src: url('fonts/archivo-500-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

/* Archivo 700 - latin-ext */
@font-face {
  font-family: 'Archivo';
  font-style: normal;
  font-weight: 700;
  font-stretch: 100%;
  font-display: swap;
  src: url('fonts/archivo-700-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

/* Archivo 700 - latin */
@font-face {
  font-family: 'Archivo';
  font-style: normal;
  font-weight: 700;
  font-stretch: 100%;
  font-display: swap;
  src: url('fonts/archivo-700-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

/* Archivo 900 - latin-ext */
@font-face {
  font-family: 'Archivo';
  font-style: normal;
  font-weight: 900;
  font-stretch: 100%;
  font-display: swap;
  src: url('fonts/archivo-900-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

/* Archivo 900 - latin */
@font-face {
  font-family: 'Archivo';
  font-style: normal;
  font-weight: 900;
  font-stretch: 100%;
  font-display: swap;
  src: url('fonts/archivo-900-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

/* IBM Plex Mono 400 - latin-ext */
@font-face {
  font-family: 'IBM Plex Mono';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('fonts/ibm-plex-mono-400-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

/* IBM Plex Mono 400 - latin */
@font-face {
  font-family: 'IBM Plex Mono';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('fonts/ibm-plex-mono-400-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

/* IBM Plex Mono 500 - latin-ext */
@font-face {
  font-family: 'IBM Plex Mono';
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url('fonts/ibm-plex-mono-500-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

/* IBM Plex Mono 500 - latin */
@font-face {
  font-family: 'IBM Plex Mono';
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url('fonts/ibm-plex-mono-500-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

/* IBM Plex Mono 600 - latin-ext */
@font-face {
  font-family: 'IBM Plex Mono';
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url('fonts/ibm-plex-mono-600-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

/* IBM Plex Mono 600 - latin */
@font-face {
  font-family: 'IBM Plex Mono';
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url('fonts/ibm-plex-mono-600-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

/* IBM Plex Sans 400 - latin-ext */
@font-face {
  font-family: 'IBM Plex Sans';
  font-style: normal;
  font-weight: 400;
  font-stretch: 100%;
  font-display: swap;
  src: url('fonts/ibm-plex-sans-400-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

/* IBM Plex Sans 400 - latin */
@font-face {
  font-family: 'IBM Plex Sans';
  font-style: normal;
  font-weight: 400;
  font-stretch: 100%;
  font-display: swap;
  src: url('fonts/ibm-plex-sans-400-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

/* IBM Plex Sans 500 - latin-ext */
@font-face {
  font-family: 'IBM Plex Sans';
  font-style: normal;
  font-weight: 500;
  font-stretch: 100%;
  font-display: swap;
  src: url('fonts/ibm-plex-sans-500-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

/* IBM Plex Sans 500 - latin */
@font-face {
  font-family: 'IBM Plex Sans';
  font-style: normal;
  font-weight: 500;
  font-stretch: 100%;
  font-display: swap;
  src: url('fonts/ibm-plex-sans-500-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

/* IBM Plex Sans 600 - latin-ext */
@font-face {
  font-family: 'IBM Plex Sans';
  font-style: normal;
  font-weight: 600;
  font-stretch: 100%;
  font-display: swap;
  src: url('fonts/ibm-plex-sans-600-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

/* IBM Plex Sans 600 - latin */
@font-face {
  font-family: 'IBM Plex Sans';
  font-style: normal;
  font-weight: 600;
  font-stretch: 100%;
  font-display: swap;
  src: url('fonts/ibm-plex-sans-600-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

:root {
  --ink:      #0c0f11;
  --ink-2:    #12171a;
  --steel:    #1c2429;
  --steel-2:  #2a343b;
  --concrete: #8792a0;
  --chalk:    #c9d1da;
  --paper:    #f3f0ea;
  --signal:   #ffb800;
  --ember:    #e8492c;
  --brand:    #2f7ae0;   /* the logo blue, lifted to read on near-black */

  --display: 'Archivo', 'Helvetica Neue', sans-serif;
  --body:    'IBM Plex Sans', system-ui, sans-serif;
  --mono:    'IBM Plex Mono', ui-monospace, monospace;

  --rule: 1px solid #232c33;
  --gutter: clamp(20px, 5vw, 72px);
  --max: 1240px;
}

* { box-sizing: border-box; }
html { scroll-behavior: smooth; }

body {
  margin: 0;
  background: var(--ink);
  color: var(--chalk);
  font: 400 17px/1.65 var(--body);
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
}

/* Engineering paper, barely there. Gives the dark a surface rather than
   a void, without ever competing with the text. */
body::before {
  content: '';
  position: fixed; inset: 0; pointer-events: none; z-index: 0;
  background-image:
    linear-gradient(rgba(255, 255, 255, .022) 1px, transparent 1px),
    linear-gradient(90deg, rgba(255, 255, 255, .022) 1px, transparent 1px);
  background-size: 64px 64px;
}

img { max-width: 100%; display: block; }
a { color: inherit; }

.wrap { width: 100%; max-width: var(--max); margin: 0 auto; padding: 0 var(--gutter); position: relative; z-index: 1; }

/* ---------- type ---------- */

h1, h2, h3, h4 { font-family: var(--display); font-weight: 900; line-height: .96; letter-spacing: -.02em; margin: 0; color: #fff; }
h1 { font-size: clamp(44px, 7.4vw, 104px); }
h2 { font-size: clamp(32px, 4.4vw, 60px); }
h3 { font-size: clamp(21px, 2vw, 27px); line-height: 1.12; letter-spacing: -.01em; }
h4 { font-size: 17px; font-weight: 700; letter-spacing: .01em; }
p { margin: 0 0 1.1em; }
p:last-child { margin-bottom: 0; }

.lede { font-size: clamp(18px, 1.6vw, 22px); line-height: 1.55; color: #e3e9ef; max-width: 62ch; }
.muted { color: var(--concrete); }
.small { font-size: 14.5px; line-height: 1.55; }

/* The mono eyebrow: a part number stencilled on a crate. */
.eyebrow {
  font: 500 12px/1 var(--mono);
  letter-spacing: .18em; text-transform: uppercase; color: var(--signal);
  display: inline-flex; align-items: center; gap: 10px; margin-bottom: 22px;
}
.eyebrow::before { content: ''; width: 26px; height: 2px; background: var(--signal); }
.eyebrow.plain { color: var(--concrete); }
.eyebrow.plain::before { background: var(--steel-2); }

/* ---------- barcode rule ---------- */

.bars {
  height: 26px;
  background-image: repeating-linear-gradient(
    90deg,
    var(--steel-2) 0 2px, transparent 2px 5px,
    var(--steel-2) 5px 6px, transparent 6px 11px,
    var(--steel-2) 11px 14px, transparent 14px 16px,
    var(--steel-2) 16px 17px, transparent 17px 23px
  );
  opacity: .85;
}
.bars.gold { background-image: repeating-linear-gradient(
    90deg,
    var(--signal) 0 2px, transparent 2px 5px,
    var(--signal) 5px 6px, transparent 6px 11px,
    var(--signal) 11px 14px, transparent 14px 16px,
    var(--signal) 16px 17px, transparent 17px 23px
  ); height: 14px; opacity: .9; }

/* ---------- header ---------- */

header.bar {
  position: sticky; top: 0; z-index: 40;
  background: rgba(12, 15, 17, .86);
  backdrop-filter: blur(14px);
  border-bottom: var(--rule);
}
header.bar .wrap { display: flex; align-items: center; gap: 26px; height: 80px; }

.brand { display: flex; align-items: center; text-decoration: none; margin-right: auto; }
/* 54px, not 40. The logo now spells out STOCKTAKES ONLINE beneath the mark,
   and that line is 12% of the artwork's height - at 40px it renders under
   5px and reads as a smudge. This is the size where it is actually legible,
   and it replaces the separate wordmark that used to sit beside it. */
.brand img { height: 54px; width: auto; }
footer .brand img { height: 54px; }

nav.main { display: flex; align-items: center; gap: 24px; }

/* :not(.btn) throughout, because Sign in is a button that happens to live
   in the nav. Without it these rules win on specificity over .btn-signal
   and paint pale grey link text on the amber fill - which is unreadable -
   and drag the underline sweep across the button as well. */
nav.main a:not(.btn) {
  font-size: 14.5px; font-weight: 500; text-decoration: none; color: var(--chalk);
  padding: 6px 0; position: relative; white-space: nowrap;
}
nav.main a:not(.btn)::after {
  content: ''; position: absolute; left: 0; right: 100%; bottom: 0; height: 2px;
  background: var(--signal); transition: right .28s cubic-bezier(.2,.7,.3,1);
}
nav.main a:not(.btn):hover::after,
nav.main a:not(.btn)[aria-current]::after { right: 0; }
nav.main a:not(.btn)[aria-current] { color: #fff; }

.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 9px;
  font: 600 14.5px var(--body); text-decoration: none; white-space: nowrap;
  padding: 12px 22px; border: 1px solid transparent; cursor: pointer;
  transition: transform .18s ease, background .18s ease, color .18s ease, border-color .18s ease;
}
.btn:active { transform: translateY(1px); }
.btn-signal { background: var(--signal); color: #17120a; }
.btn-signal:hover { background: #ffc633; }
.btn-ghost { border-color: var(--steel-2); color: #fff; }
.btn-ghost:hover { border-color: var(--signal); color: var(--signal); }
.btn-lg { padding: 16px 30px; font-size: 16px; }

.menu-btn { display: none; background: none; border: 1px solid var(--steel-2); color: #fff; padding: 9px 13px; font: 500 13px var(--mono); cursor: pointer; }

/* ---------- hero ---------- */

.hero { position: relative; padding: clamp(72px, 11vw, 148px) 0 clamp(56px, 8vw, 104px); overflow: hidden; }
.hero::after {
  content: ''; position: absolute; top: -30%; right: -12%; width: 62vw; height: 78vh;
  background: radial-gradient(closest-side, rgba(255, 184, 0, .13), transparent 72%);
  pointer-events: none;
}
/* The cap moved off the H1 and onto the slogan, so the second line inside
   the heading is not squeezed into fifteen characters. The slogan itself is
   unchanged: same cap, same uppercase, same amber "right". */
.hero h1 { text-transform: uppercase; }
.hero h1 .h1-slogan { display: block; max-width: 15ch; }
.hero h1 em { font-style: normal; color: var(--signal); }

/* A caption to the headline, not a second headline - so it keeps the body
   face, its own case, and a size close to the lede beneath it. */
.hero h1 .h1-line {
  display: block;
  margin-top: 26px;
  max-width: 46ch;
  font-family: var(--body);
  font-size: clamp(17px, 1.5vw, 21px);
  font-weight: 500;
  line-height: 1.45;
  letter-spacing: 0;
  text-transform: none;
  color: var(--chalk);
}
.hero .lede { margin-top: 30px; }
.hero .cta { display: flex; gap: 14px; flex-wrap: wrap; margin-top: 40px; }

/* The scan: an amber line travelling a surface, the way a scanner reads a
   label.

   It sweeps down and back up rather than restarting at the top. A laser in
   a scan engine is a mirror oscillating, so it has always gone both ways -
   the old version travelled down, vanished, and reappeared at the top,
   which is not a movement anything makes.

   `alternate` runs the keyframes forward then backward, so one declaration
   describes both passes and they cannot drift apart. `ease-in-out` does the
   rest: the line slows as it reaches each end, the way a mirror does when
   it turns around.

   It dims at the turns rather than going out. Holding full brightness makes
   it wallpaper; switching off makes it blink. Dimming reads as the thing
   slowing down, which is what is happening. */
.scan { position: relative; }
.scan::before {
  content: ''; position: absolute; left: 0; right: 0; height: 2px; top: 0; z-index: 2;
  background: linear-gradient(90deg, transparent, var(--signal), transparent);
  box-shadow: 0 0 18px 2px rgba(255, 184, 0, .45);
  animation: sweep 5.76s ease-in-out infinite alternate;
}

/* The hero is taller than a page head - an extra line of heading, the
   buttons and the note under them - so the same duration made the line
   cover more ground per second and read as faster. Timed by distance
   rather than by the clock, so the beam travels at about the same speed
   wherever it is. */
.hero.scan::before { animation-duration: 8s; }
@keyframes sweep {
  0%   { top: 0;    opacity: .2; }
  14%  { opacity: 1; }
  86%  { opacity: 1; }
  100% { top: 100%; opacity: .2; }
}

/* Somebody who has asked their machine to stop moving things has asked for
   this too. */
@media (prefers-reduced-motion: reduce) {
  .scan::before { animation: none; opacity: .5; }
}

/* ---------- stat strip ---------- */

.strip { border-top: var(--rule); border-bottom: var(--rule); background: var(--ink-2); }
.strip .wrap { display: grid; grid-template-columns: repeat(4, 1fr); }
/* **`.strip .wrap > div`, not `.strip div`.**

   `.wrap` is itself a div, so a bare descendant selector here matches the
   wrap as well as the four cells inside it - and at 0-1-1 it beats
   `.wrap`'s own 0-1-0. Left padding of 0 therefore deleted the page
   gutter outright, up to 72px of it, and the first number sat outside the
   column that every other row on the page lines up with.

   It hid because it looked deliberate: four numbers, evenly spaced,
   slightly proud of the column reads as a design decision until somebody
   puts a straightedge on it. Found on the Barcode Datalink site, which
   shares this stylesheet's lineage, and the same fault was here. */
.strip .wrap > div { padding: 34px 26px 34px 0; border-right: var(--rule); }
.strip .wrap > div:last-child { border-right: 0; }
.strip b { display: block; font-family: var(--display); font-weight: 900; font-size: clamp(30px, 3.4vw, 46px); color: #fff; line-height: 1; letter-spacing: -.03em; }
.strip span { display: block; font: 400 13px/1.4 var(--mono); color: var(--concrete); margin-top: 12px; text-transform: uppercase; letter-spacing: .1em; }

/* ---------- sections ---------- */

section { padding: clamp(64px, 9vw, 124px) 0; position: relative; }
section.tint { background: var(--ink-2); border-top: var(--rule); border-bottom: var(--rule); }
.head { max-width: 70ch; margin-bottom: clamp(40px, 5vw, 66px); }
.head p { margin-top: 22px; }

.grid { display: grid; gap: 1px; background: #1b2329; border: var(--rule); }
.grid.two   { grid-template-columns: repeat(2, 1fr); }
.grid.three { grid-template-columns: repeat(3, 1fr); }
.grid.four  { grid-template-columns: repeat(4, 1fr); }
.grid > * { background: var(--ink); padding: clamp(26px, 3vw, 40px); }
section.tint .grid > * { background: var(--ink-2); }

/* A cell that answers when you point at it - the amber edge lights, the
   way a scan confirms. */
.cell { position: relative; transition: background .25s ease; }
.cell::before {
  content: ''; position: absolute; left: 0; top: 0; bottom: 0; width: 3px;
  background: var(--signal); transform: scaleY(0); transform-origin: top;
  transition: transform .3s cubic-bezier(.2,.7,.3,1);
}
.cell:hover::before { transform: scaleY(1); }
.cell:hover { background: #10161a !important; }
.cell .n { font: 600 12px var(--mono); color: var(--signal); letter-spacing: .12em; display: block; margin-bottom: 18px; }
.cell h3 { margin-bottom: 12px; }
.cell p { color: var(--concrete); font-size: 15.5px; }

/* ---------- steps ---------- */

.steps { display: grid; gap: 1px; background: #1b2329; border: var(--rule); }
.step { background: var(--ink); padding: clamp(26px, 3.4vw, 44px); display: grid; grid-template-columns: 92px 1fr; gap: clamp(20px, 3vw, 44px); align-items: start; }
section.tint .step { background: var(--ink-2); }
.step .num { font-family: var(--display); font-weight: 900; font-size: 54px; line-height: .8; color: transparent; -webkit-text-stroke: 1.5px var(--steel-2); letter-spacing: -.04em; transition: -webkit-text-stroke-color .3s ease; }
.step:hover .num { -webkit-text-stroke-color: var(--signal); }
.step h3 { margin-bottom: 10px; }
.step p { color: var(--concrete); max-width: 68ch; }

/* ---------- device photography ---------- */

/* Zebra's shots are cut out on white. On a dark page they get a
   deliberate white plate rather than being blended into nothing - it
   reads as a datasheet inset, which is what it is. */
.shot { background: #fff; display: flex; align-items: center; justify-content: center; padding: 20px; }
.shot img { width: 100%; height: auto; }

.feature { display: grid; grid-template-columns: 1fr 1fr; gap: 1px; background: #1b2329; border: var(--rule); align-items: stretch; }
.feature > div { background: var(--ink); padding: clamp(28px, 3.6vw, 52px); display: flex; flex-direction: column; justify-content: center; }
.feature .shot { padding: clamp(22px, 3vw, 46px); }
.feature h2 { font-size: clamp(28px, 3.2vw, 42px); margin-bottom: 16px; }

.device .shot { margin: -10px 0 22px; padding: 14px; }
.device .shot img { max-height: 220px; width: auto; }

/* ---------- device cards ---------- */

.device { display: flex; flex-direction: column; }
.device .badge { font: 600 11px var(--mono); letter-spacing: .14em; text-transform: uppercase; color: var(--ink); background: var(--signal); padding: 5px 10px; align-self: flex-start; margin-bottom: 22px; }
.device .badge.alt { background: var(--steel-2); color: var(--chalk); }
.device h3 { margin-bottom: 6px; }
.device .role { font: 500 13px var(--mono); color: var(--signal); letter-spacing: .06em; margin-bottom: 18px; }
.device ul { margin: 0 0 24px; padding: 0; list-style: none; }
.device li { position: relative; padding-left: 20px; margin-bottom: 9px; font-size: 15px; color: var(--concrete); }
.device li::before { content: ''; position: absolute; left: 0; top: 10px; width: 9px; height: 2px; background: var(--steel-2); }
.device .foot { margin-top: auto; padding-top: 20px; border-top: var(--rule); font: 400 13.5px var(--mono); color: var(--concrete); }

/* ---------- downloads ---------- */

.downloads { border-top: var(--rule); }
.dl {
  display: flex; align-items: center; gap: 18px; padding: 20px 4px;
  border-bottom: var(--rule); text-decoration: none; transition: padding-left .25s ease, background .25s ease;
}
.dl:hover { padding-left: 14px; background: #10161a; }
.dl .kind {
  font: 600 10.5px var(--mono); letter-spacing: .14em; text-transform: uppercase;
  color: var(--ink); background: var(--signal); padding: 5px 9px; flex: none;
}
.dl .kind.alt { background: var(--steel-2); color: var(--chalk); }
.dl b { font-family: var(--display); font-weight: 700; font-size: 17px; color: #fff; display: block; }
.dl span span { font: 400 13px var(--mono); color: var(--concrete); }
.dl .go { margin-left: auto; font: 600 13px var(--mono); color: var(--signal); flex: none; }

/* ---------- pricing ---------- */

.tiers { display: grid; grid-template-columns: repeat(2, 1fr); gap: 1px; background: #1b2329; border: var(--rule); }
.tier { background: var(--ink); padding: clamp(30px, 3.6vw, 48px); display: flex; flex-direction: column; }
.tier.featured { background: #12171a; position: relative; }
.tier.featured::after { content: ''; position: absolute; inset: 0; border: 2px solid var(--signal); pointer-events: none; }
.tier .kind { font: 600 12px var(--mono); letter-spacing: .16em; text-transform: uppercase; color: var(--signal); margin-bottom: 18px; }
.tier h3 { font-size: clamp(26px, 2.6vw, 34px); margin-bottom: 14px; }
.tier .price { font-family: var(--display); font-weight: 900; font-size: clamp(38px, 4.6vw, 58px); color: #fff; line-height: 1; letter-spacing: -.03em; margin: 24px 0 6px; }
.tier .per { font: 400 14px var(--mono); color: var(--concrete); margin-bottom: 28px; }
.tier ul { list-style: none; margin: 0 0 32px; padding: 0; }
.tier li { padding-left: 26px; position: relative; margin-bottom: 12px; font-size: 15.5px; }
.tier li::before { content: '✓'; position: absolute; left: 0; color: var(--signal); font-weight: 700; }
.tier .btn { margin-top: auto; }

/* ---- rate table ------------------------------------------------------
   The tiers differ only in their numbers, so they read as a table rather
   than as four near-identical cards each repeating the same list. */
.rates-wrap { overflow-x: auto; border: var(--rule); margin-bottom: 30px; }
.rates { width: 100%; border-collapse: collapse; min-width: 620px; }
.rates th,
.rates td { padding: 18px 22px; border-bottom: var(--rule); text-align: left; }
.rates thead th {
  font: 600 11px var(--mono); letter-spacing: .16em; text-transform: uppercase;
  color: var(--signal); background: #12171a; white-space: nowrap;
}
.rates tbody tr:last-child td { border-bottom: 0; }
.rates tbody tr:hover { background: #12171a; }
.rates .who { font-weight: 600; color: #fff; }
.rates .n {
  font-family: var(--mono); text-align: center; color: var(--concrete);
  width: 1%; white-space: nowrap;
}
.rates .amt {
  font-family: var(--display); font-weight: 900; color: #fff; text-align: right;
  font-size: clamp(22px, 2.4vw, 30px); letter-spacing: -.02em; white-space: nowrap;
}
/* The ex-GST reference column.
   Inc GST stays the headline, because it is what an Australian buyer
   actually pays and it is the number this whole site is written in. This
   exists because equipment sellers routinely publish ex GST, so a buyer
   with two tabs open reads our $110 against their $100 and scores us 10%
   dearer without noticing the units differ. Quiet, mono and right
   aligned - a reference, not a competing headline. */
.rates .ex {
  font-family: var(--mono); text-align: right; color: var(--concrete);
  width: 1%; white-space: nowrap; font-size: 15px;
}

/* The ex-GST column comes out on a phone.
   .rates scrolls sideways inside .rates-wrap and Inc GST is the last
   column, so on a narrow screen the reference figure is what fits and
   the number the customer actually pays is the one off the edge. That
   is exactly backwards. Nobody comparing ex-GST quotes against a
   supplier's site is doing it on a phone at a rack.
   Every ex cell carries this class - header and body both - so hiding
   it drops a whole column and the rest stay aligned. */
@media (max-width: 720px) {
  .rates .ex { display: none; }
}

.rates .pick { background: rgba(245, 158, 11, .06); }
.rates .pick .amt { color: var(--signal); }
.rates .quote td { color: var(--concrete); }
.rates .quote .amt { font-size: 17px; font-family: var(--mono); font-weight: 400; }

/* The conditions on a price - the things a customer would otherwise have
   to ask on the phone, said plainly next to the number. */
.note {
  border-left: 3px solid var(--signal);
  padding: 4px 0 4px 22px;
  margin-top: 38px;
  max-width: 76ch;
}
.note p { color: var(--concrete); font-size: 15.5px; line-height: 1.7; }
.note p + p { margin-top: 16px; }
.note b { color: #fff; }

/* ---- history photographs ---------------------------------------------
   Prints from a film camera, scanned. They are meant to look their age:
   a slight tilt, a paper-white border, a real shadow. A 1994 photograph
   rendered pin-sharp and square looks like a stock image of 1994. */
.era-shots { display: grid; grid-template-columns: repeat(auto-fit, minmax(210px, 1fr)); gap: 26px; margin: 34px 0 10px; }
.era-shots figure { margin: 0; }
.era-shots img {
  width: 100%; height: auto; display: block; background: #fff;
  padding: 10px 10px 44px; border-radius: 2px;
  box-shadow: 0 14px 34px rgba(0,0,0,.55);
  transform: rotate(-1.1deg);
  transition: transform .3s cubic-bezier(.2,.7,.3,1);
}
.era-shots figure:nth-child(even) img { transform: rotate(1.4deg); }
.era-shots figure:nth-child(3n) img { transform: rotate(-.4deg); }
.era-shots figure:hover img { transform: rotate(0deg) scale(1.02); }
.era-shots figcaption {
  font: 400 12.5px/1.5 var(--mono); color: var(--concrete);
  margin-top: 16px; text-align: center;
}
@media (prefers-reduced-motion: reduce) {
  .era-shots img, .era-shots figure:hover img { transform: none; transition: none; }
}

/* ---- scan-to-install ------------------------------------------------
   A QR read off a monitor by the scanner itself. White quiet zone around
   it always, whatever the page is doing - a dark background bleeding into
   the border is the most common reason one of these will not read. */
.scan-install { display: grid; grid-template-columns: auto 1fr; gap: 34px; align-items: center; }
.scan-install .code {
  background: #fff; padding: 18px; border-radius: 4px; line-height: 0;
  box-shadow: 0 10px 30px rgba(0,0,0,.5);
}
.scan-install .code img { width: 220px; height: 220px; display: block; }
.scan-install ol { margin: 0; padding-left: 22px; }
.scan-install li { margin-bottom: 14px; padding-left: 6px; }
.scan-install li b { color: #fff; }
@media (max-width: 720px) {
  .scan-install { grid-template-columns: 1fr; gap: 24px; }
  .scan-install .code { justify-self: start; }
  .scan-install .code img { width: 180px; height: 180px; }
}

/* A barcode sitting inside the step that tells you to scan it, rather than
   further down the page. Same white surround and the same reason for it. */
.step-code {
  display: inline-block; background: #fff; padding: 12px; border-radius: 4px;
  line-height: 0; margin-top: 16px; box-shadow: 0 8px 24px rgba(0,0,0,.45);
}
.step-code img { width: 156px; height: 156px; display: block; }
.step-code + .step-note { margin-top: 10px; }

/* ---- hardware kit --------------------------------------------------
   What is actually in the box, itemised. The total is the headline and the
   parts are the evidence for it: a cradle costing 43% of a scanner reads as
   a mistake on its own, and makes sense the moment you can see it. */
.kit-total {
  display: flex; align-items: baseline; gap: 16px; flex-wrap: wrap;
  border: var(--rule); border-left: 3px solid var(--signal);
  padding: 22px 26px; margin-bottom: 26px; background: var(--ink-2);
}
.kit-total b {
  font-family: var(--display); font-weight: 900; color: #fff;
  font-size: clamp(30px, 3.6vw, 44px); line-height: 1; letter-spacing: -.02em;
}
.kit-total span { font: 400 14px var(--mono); color: var(--concrete); }

.kit-wrap { overflow-x: auto; border: var(--rule); }
.kit { width: 100%; border-collapse: collapse; min-width: 560px; }
.kit th, .kit td { padding: 14px 18px; border-bottom: var(--rule); text-align: left; vertical-align: middle; }
.kit thead th {
  font: 600 11px var(--mono); letter-spacing: .16em; text-transform: uppercase;
  color: var(--signal); background: #12171a; white-space: nowrap;
}
.kit tbody tr:last-child td { border-bottom: 0; }
.kit tbody tr:hover { background: #12171a; }
.kit .pic { width: 1%; padding: 8px 10px 8px 14px; }
.kit .pic img { width: 62px; height: 62px; object-fit: contain; background: #fff; border-radius: 3px; display: block; }
.kit .what b { color: #fff; font-weight: 600; display: block; }
.kit .what span { font: 400 11.5px var(--mono); color: var(--concrete); }
.kit .amt { font-family: var(--mono); text-align: right; color: var(--chalk); white-space: nowrap; }
.kit tfoot td { padding: 16px 18px; background: #12171a; font-weight: 600; color: #fff; }
.kit tfoot .amt { font-family: var(--display); font-weight: 900; font-size: 20px; color: var(--signal); }

/* A price nobody has set yet, marked so it cannot be mistaken for one. */
.tbc { color: var(--ember); font-size: clamp(26px, 3vw, 38px); }
.needs-input {
  border-left: 3px solid var(--ember);
  background: rgba(232, 73, 44, .07);
  padding: 16px 20px;
  font: 400 14px/1.6 var(--mono);
  color: #f0b3a6;
  margin: 26px 0;
}
.needs-input b { color: var(--ember); display: block; margin-bottom: 4px; font-family: var(--mono); }

/* ---------- faq ---------- */

.faq { border-top: var(--rule); }
.faq details { border-bottom: var(--rule); }
.faq summary {
  cursor: pointer; list-style: none; padding: 26px 44px 26px 0; position: relative;
  font-family: var(--display); font-weight: 700; font-size: clamp(17px, 1.7vw, 21px); color: #fff;
  transition: color .2s ease;
}
.faq summary::-webkit-details-marker { display: none; }
.faq summary:hover { color: var(--signal); }
.faq summary::after {
  content: '+'; position: absolute; right: 6px; top: 50%; transform: translateY(-50%);
  font: 400 26px var(--mono); color: var(--signal); transition: transform .25s ease;
}
.faq details[open] summary::after { transform: translateY(-50%) rotate(45deg); }
.faq .answer { padding: 0 60px 30px 0; color: var(--concrete); max-width: 78ch; }
.faq .answer a { color: var(--signal); }

/* ---------- contact ---------- */

.contact-grid { display: grid; grid-template-columns: 1.1fr .9fr; gap: 1px; background: #1b2329; border: var(--rule); }
.contact-grid > div { background: var(--ink); padding: clamp(28px, 3.4vw, 46px); }
.detail { margin-bottom: 30px; }
.detail dt { font: 600 11px var(--mono); letter-spacing: .16em; text-transform: uppercase; color: var(--concrete); margin-bottom: 8px; }
.detail dd { margin: 0; font-size: 18px; color: #fff; }
.detail dd a { text-decoration: none; border-bottom: 1px solid var(--steel-2); }
.detail dd a:hover { color: var(--signal); border-color: var(--signal); }

/* ---------- callout ---------- */

.callout { background: var(--signal); color: #17120a; }
.callout h2 { color: #17120a; }
.callout p { color: #3a2f14; max-width: 58ch; }
.callout .btn-dark { background: var(--ink); color: #fff; }
.callout .btn-dark:hover { background: var(--steel); }
.callout .wrap { display: flex; align-items: center; justify-content: space-between; gap: 44px; flex-wrap: wrap; }

/* ---------- footer ---------- */

footer { border-top: var(--rule); padding: clamp(48px, 6vw, 76px) 0 40px; background: var(--ink-2); }
.foot-grid { display: grid; grid-template-columns: 1.4fr repeat(3, 1fr); gap: 44px; margin-bottom: 48px; }
footer h4 { font-family: var(--mono); font-weight: 600; font-size: 11px; letter-spacing: .16em; text-transform: uppercase; color: var(--concrete); margin-bottom: 18px; }
footer ul { list-style: none; margin: 0; padding: 0; }
footer li { margin-bottom: 11px; }
footer a { text-decoration: none; font-size: 15px; color: var(--chalk); }
footer a:hover { color: var(--signal); }
.foot-end { border-top: var(--rule); padding-top: 26px; display: flex; justify-content: space-between; gap: 20px; flex-wrap: wrap; font: 400 13px var(--mono); color: var(--concrete); }

/* ---------- page header ---------- */

.page-head { padding: clamp(56px, 7vw, 96px) 0 clamp(40px, 5vw, 64px); border-bottom: var(--rule); position: relative; overflow: hidden; }
.page-head::after {
  content: ''; position: absolute; top: -40%; right: -8%; width: 48vw; height: 70vh;
  background: radial-gradient(closest-side, rgba(255, 184, 0, .09), transparent 70%);
  pointer-events: none;
}
.page-head h1 { font-size: clamp(38px, 5.6vw, 76px); text-transform: uppercase; max-width: 18ch; }
.page-head .lede { margin-top: 26px; }

/* A small photograph beside the heading rather than under it.
   Floated so the heading and lede run alongside it and the page does not
   grow a whole band taller for one 190px image. The heading already caps
   at 18ch, so there is room to its right on any desktop width.
   Below 720px it stops floating and centres, because a float that narrow
   squeezes a 38px heading into a column two words wide. */
.page-head .aside-shot {
  float: right; width: 190px; max-width: 34%;
  margin: 0 0 18px 28px; position: relative; z-index: 1;
}
.page-head .aside-shot img { display: block; width: 100%; height: auto; border: var(--rule); }
.page-head .aside-shot figcaption {
  margin-top: 10px; font-size: 13px; line-height: 1.5;
  color: var(--concrete); text-align: center;
}
@media (max-width: 720px) {
  .page-head .aside-shot { float: none; width: 170px; margin: 0 auto 22px; }
}

/* ---------- prose ---------- */

.prose { max-width: 72ch; }
.prose h2 { margin: 56px 0 20px; }
.prose h3 { margin: 38px 0 14px; color: var(--signal); }
.prose p, .prose li { color: var(--concrete); }
.prose strong { color: #fff; font-weight: 600; }
.prose ul { padding-left: 22px; }
.prose li { margin-bottom: 10px; }
.prose blockquote {
  margin: 34px 0; padding: 22px 28px; border-left: 3px solid var(--signal);
  background: var(--ink-2); color: var(--chalk); font-size: 17px;
}

/* ---------- entrance ---------- */

.rise { opacity: 0; transform: translateY(18px); animation: rise .8s cubic-bezier(.2,.7,.3,1) forwards; }
.rise:nth-child(2) { animation-delay: .07s; }
.rise:nth-child(3) { animation-delay: .14s; }
.rise:nth-child(4) { animation-delay: .21s; }
@keyframes rise { to { opacity: 1; transform: none; } }

@media (prefers-reduced-motion: reduce) {
  .rise { animation: none; opacity: 1; transform: none; }
  .scan::before { animation: none; opacity: .5; top: 0; }
  html { scroll-behavior: auto; }
}

/* ---------- print ---------- */

/* The signature block on the hire terms. Monospace so the rules line up,
   and it has to survive printing on white - which is the only reason the
   page carries it at all. */
.sign {
  font-family: var(--mono);
  font-size: 14px;
  line-height: 2.4;
  color: var(--chalk);
  white-space: pre-wrap;
  margin: 0;
  overflow-x: auto;
}

@media print {
  /* The hire terms are meant to be printed and signed, so the parts that
     only make sense on screen come off and the signature block goes black
     on white with the rest. */
  .sign { color: #000 !important; font-size: 12px; }
  .note, .grid, .cell { break-inside: avoid; }

  header.bar, .callout, body::before { display: none !important; }
  body { background: #fff; color: #000; }
  h1, h2, h3, h4, .dl b { color: #000 !important; }
  a { text-decoration: underline; }
}

/* ---------- responsive ---------- */

@media (max-width: 1000px) {
  .grid.four { grid-template-columns: repeat(2, 1fr); }
  .foot-grid { grid-template-columns: 1fr 1fr; gap: 34px; }
}

/* The nav collapses on its own measurement, not with the rest of the layout.
   Measured 13-09-2026: eight links plus the Sign in button are 697px wide,
   and between the logo and the right gutter there are only 704px at a 1000px
   viewport and 565px at 861px. **It was already spilling past the wrap by
   39px at 900px and 69px at 870px** - an overflow nobody had looked for,
   because the hamburger was set at 860 and the nav stopped fitting well
   before that.

   Adding Hire Scanners takes the row to 814px, so the whole thing needs
   128 (logo) + 24 (gap) + 814 + 144 (gutters) = 1110px to sit honestly.
   1120 is that, rounded up. Everything else in this file that changed at
   860 still changes at 860 - only the nav moved. */
@media (max-width: 1120px) {
  nav.main {
    position: fixed; inset: 80px 0 auto; flex-direction: column; align-items: stretch;
    gap: 0; background: var(--ink-2); border-bottom: var(--rule); padding: 12px var(--gutter) 24px;
    display: none;
  }
  nav.main.open { display: flex; }
  nav.main a:not(.btn) { padding: 15px 0; border-bottom: var(--rule); font-size: 16px; }
  nav.main .btn { margin-top: 16px; }
  .menu-btn { display: block; }
}

@media (max-width: 860px) {
  .strip .wrap { grid-template-columns: repeat(2, 1fr); }
  .strip .wrap > div { border-right: 0; padding: 26px 0; border-bottom: var(--rule); }
  .grid.two, .grid.three, .grid.four { grid-template-columns: 1fr; }
  .feature { grid-template-columns: 1fr; }
  .tiers { grid-template-columns: 1fr; }
  .contact-grid { grid-template-columns: 1fr; }
  .step { grid-template-columns: 1fr; gap: 14px; }
  .step .num { font-size: 40px; }
  .brand img { height: 42px; }
  .dl { flex-wrap: wrap; gap: 10px; }
  .dl .go { margin-left: 0; width: 100%; }
}

@media (max-width: 560px) {
  .foot-grid { grid-template-columns: 1fr; }
  .hero h1 { font-size: clamp(38px, 11vw, 56px); }
}

/* ---------- brand line ---------- */

/* The mark spells "STO"; this spells out what it stands for. A hairline
   between them so it reads as two things, not one crowded lockup. */

/* ---------- device cards, clickable ---------- */

a.device { text-decoration: none; color: inherit; }
a.device .shot { transition: transform .4s cubic-bezier(.2,.7,.3,1); }
a.device:hover .shot img { transform: scale(1.04); }
a.device .shot img { transition: transform .4s cubic-bezier(.2,.7,.3,1); }
a.device .more { margin-top: 18px; font: 600 13px var(--mono); color: var(--signal); letter-spacing: .06em; }
a.device:hover .more { text-decoration: underline; }

/* ---------- product page ---------- */

.hero-shot { background: #fff; padding: clamp(24px, 4vw, 60px); border: var(--rule); }
.hero-shot img { width: 100%; height: auto; }

.gallery { display: grid; grid-template-columns: repeat(3, 1fr); gap: 1px; background: #1b2329; border: var(--rule); }
.gallery figure { margin: 0; background: #fff; }
/* A fixed ratio so cells and their captions line up whatever shape the
   supplied photo is - `contain` letterboxes onto the white a product shot
   already sits on, rather than cropping the device. */
.gallery img { width: 100%; aspect-ratio: 3 / 2; object-fit: contain; background: #fff; display: block; }

/* A picture that is taller than the frame, filled rather than floated.
   `contain` is right for a product shot - it must not be cropped - but a
   portrait one then sits as a strip with white either side, and beside
   three landscape pictures that reads as a mistake. `cover` crops the top
   and bottom instead, and the bias upward keeps the numeric block, which
   is the half worth showing. */
.gallery img.fill { object-fit: cover; object-position: 50% 34%; }
.gallery figcaption { background: var(--ink); color: var(--concrete); font: 400 12.5px var(--mono); padding: 12px 14px; }

/* Four pictures in a row of three leaves one on its own. Two by two, and
   each of them gets to be bigger for it. */
.gallery.two-up { grid-template-columns: repeat(2, 1fr); }

.spec { display: grid; gap: 1px; background: #1b2329; border: var(--rule); }
.spec div { background: var(--ink); padding: 20px 24px; display: grid; grid-template-columns: 240px 1fr; gap: 24px; }
.spec dt { font: 600 12px var(--mono); letter-spacing: .1em; text-transform: uppercase; color: var(--signal); margin: 0; }
.spec dd { margin: 0; color: var(--chalk); }

.crumbs { font: 500 12.5px var(--mono); letter-spacing: .08em; text-transform: uppercase; color: var(--concrete); margin-bottom: 20px; }
.crumbs a { color: var(--concrete); text-decoration: none; }
.crumbs a:hover { color: var(--signal); }

@media (max-width: 860px) {
    .gallery { grid-template-columns: 1fr; }
  .spec div { grid-template-columns: 1fr; gap: 8px; }
}

/* ---------- report list ---------- */

.reports { border-top: var(--rule); }
.report {
  display: grid; grid-template-columns: 64px 1fr 260px; gap: clamp(18px, 3vw, 40px);
  padding: 30px 4px; border-bottom: var(--rule); align-items: start;
  transition: background .25s ease, padding-left .25s ease;
}
.report:hover { background: #10161a; padding-left: 14px; }
.report .n { font: 600 13px var(--mono); color: var(--signal); letter-spacing: .1em; padding-top: 4px; }
.report h3 { margin-bottom: 8px; }
.report p { color: var(--concrete); font-size: 15.5px; max-width: 62ch; margin-bottom: 12px; }
.report .cols {
  font: 400 12.5px/1.7 var(--mono); color: var(--chalk);
  background: var(--ink-2); border-left: 2px solid var(--steel-2); padding: 9px 14px;
}
.report .files { display: flex; flex-direction: column; gap: 8px; padding-top: 4px; }
.report .files a {
  display: inline-flex; align-items: center; gap: 9px; text-decoration: none;
  font: 600 13px var(--mono); letter-spacing: .04em; padding: 9px 14px;
  border: 1px solid var(--steel-2); color: var(--chalk); transition: all .2s ease;
}
.report .files a:hover { border-color: var(--signal); color: var(--signal); }
.report .files a .tag { font-size: 10.5px; letter-spacing: .12em; color: var(--ink); background: var(--signal); padding: 3px 6px; }
.report .files a:hover .tag { background: var(--signal); }
.report .files a.csv .tag { background: var(--steel-2); color: var(--chalk); }

/* The plain CSV is a real option, not an equal one.
   Two exports exist: the Excel-safe file, which is what almost everybody
   wants, and the plain one for feeding an ERP. Giving them matching
   buttons would say they are equally likely and would put fifteen
   controls in the "in detail" block, which lists five operators. So the
   plain file is a text link under its button - findable by the person
   looking for it, quiet for everyone else. */
.report .files a.plain {
  border: 0; padding: 2px 0 6px; font-size: 12px; letter-spacing: .02em;
  color: var(--concrete); font-weight: 400;
}
.report .files a.plain:hover { color: var(--signal); border: 0; }

/* The per-operator set, where five names already appear twice. One line
   of plain links rather than five more of anything. */
.report .files .plainset {
  font-size: 12px; color: var(--concrete); line-height: 2; padding-top: 6px;
}
.report .files .plainset a {
  display: inline; border: 0; padding: 0 3px; font-size: 12px;
  font-weight: 400; letter-spacing: .02em; color: var(--chalk);
}
.report .files .plainset a:hover { color: var(--signal); border: 0; }

@media (max-width: 900px) {
  .report { grid-template-columns: 40px 1fr; }
  .report .files { grid-column: 2; flex-direction: row; flex-wrap: wrap; }
}

/* ---------- flag ---------- */

/* Sized in em so it tracks the figure beside it rather than being pinned
   to a pixel height that only looks right at one screen width. The hairline
   keeps the flag's navy from dissolving into the dark strip behind it. */
.flag {
  display: inline-block;
  height: .58em;
  width: auto;
  vertical-align: baseline;
  margin-left: .22em;
  border: 1px solid rgba(255, 255, 255, .35);
}

/* =====================================================================
   The app, screen by screen
   ---------------------------------------------------------------------
   These read as a sequence, so they are laid out as a strip rather than
   a grid: a grid invites the eye to wander, and the order is the whole
   point. On a wide screen all six sit in a row; below that the strip
   scrolls sideways inside its own box, because the page itself must
   never scroll horizontally.

   The screenshots come off the device at 480x800 and are shown far
   smaller, so they stay sharp on a high-density display without a second
   set of assets. The Android status and navigation bars are left in on
   purpose - cropping them turns a photograph into an illustration, and
   the point of putting real screens on a sales page is that they are
   real.
   ===================================================================== */
.screens {
  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: minmax(178px, 1fr);
  gap: 16px;
  overflow-x: auto;
  overscroll-behavior-x: contain;
  scroll-snap-type: x proximity;
  padding-bottom: 14px;
}

/* A strip that can scroll should look like one before it is touched. */
.screens::-webkit-scrollbar { height: 6px; }
.screens::-webkit-scrollbar-track { background: #10161a; }
.screens::-webkit-scrollbar-thumb { background: var(--steel-2); }
.screens::-webkit-scrollbar-thumb:hover { background: var(--signal); }

.screens figure { margin: 0; scroll-snap-align: start; }

/* White surround, so the screenshot's own near-white background does not
   bleed into the dark page and lose its edges. */
.screens .shot {
  background: #fff;
  border: var(--rule);
  line-height: 0;
}
.screens img { width: 100%; height: auto; display: block; }

.screens figcaption {
  padding: 12px 2px 0;
  font-size: 14px;
  line-height: 1.45;
  color: var(--concrete);
}
.screens figcaption b {
  display: block;
  font: 600 11.5px var(--mono);
  letter-spacing: .12em;
  text-transform: uppercase;
  color: var(--signal);
  margin-bottom: 5px;
}

/* One screen on its own - the install page - has no sequence to keep, so
   it sits at a readable size beside its text instead of in a strip. */
.screen-single {
  max-width: 240px;
  background: #fff;
  border: var(--rule);
  line-height: 0;
}
.screen-single img { width: 100%; height: auto; display: block; }


/* =====================================================================
   A barcode, shown as an example of one
   ---------------------------------------------------------------------
   White panel, because that is what a barcode is: ink on paper. Bars
   reversed out of a dark background are unreadable to a scanner and would
   teach the wrong thing about the very object being explained.

   The quiet zone - the blank margin either side of the bars - is part of
   the symbol rather than decoration, which is why the padding is generous
   even though nothing will ever be scanned off a web page.
   ===================================================================== */
.barcode-example {
  margin-top: 18px;
  border: var(--rule);
  background: #10161a;
}
.barcode-example img {
  display: block;
  width: 100%;
  max-width: 420px;
  height: auto;
  background: #fff;
  padding: 14px 18px;
}
.barcode-example p {
  margin: 0;
  padding: 12px 16px;
}


/* A kit needs naming once there is more than one. Sits on the price it
   belongs to, so the two cannot drift apart down a long page. */
.kit-name {
  margin: 46px 0 10px;
  font: 600 13px var(--mono);
  letter-spacing: .12em;
  text-transform: uppercase;
  color: var(--signal);
}


/* Availability, beside the kit it belongs to.
   Green for what can ship today, plain grey for what cannot - the lead time
   is information, not a warning, and colouring it as a problem would make an
   ordinary wait look like one. "Currently" is doing real work in the in-stock
   wording: stock moves, and a page that claims otherwise ages badly. */
.stock {
  display: inline-block;
  margin-left: 12px;
  padding: 3px 9px;
  border-radius: 3px;
  font: 600 11px var(--mono);
  letter-spacing: .08em;
  text-transform: uppercase;
  vertical-align: middle;
}
.stock.now  { color: #0c0f11; background: #6fcf7f; }
.stock.wait { color: var(--chalk); border: var(--rule); }


/* =====================================================================
   An archive photograph
   ---------------------------------------------------------------------
   Framed in white, because it is a print and printed things sit on paper.
   Deliberately not filtered, straightened or colour-matched to the page:
   it is thirty years old, it looks it, and pretending otherwise would
   cost the only thing it is here for.
   ===================================================================== */
.archive {
  margin: 28px 0 8px;
  max-width: 720px;
}
.archive img {
  display: block;
  width: 100%;
  height: auto;
  background: #fff;
  padding: 10px;
  border: var(--rule);
}
.archive figcaption {
  margin-top: 12px;
  font-size: 14.5px;
  line-height: 1.6;
  color: var(--concrete);
}
.archive figcaption b { color: var(--chalk); }

/* Detail worth leaning into gets the room to be leaned into. */
.archive.wide { max-width: 100%; }


/* =====================================================================
   A keypad, beside what is said about it
   ---------------------------------------------------------------------
   The keypad is markedly taller than it is wide and every photograph in
   the gallery above is 3:2. Sharing that frame meant either floating it in
   white or cropping the keys off the ends, so it sits here instead, at its
   own shape, next to the paragraph it belongs to.
   ===================================================================== */
.keypad-note {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: clamp(24px, 3vw, 40px);
  align-items: center;
  margin-top: clamp(28px, 3.4vw, 44px);
  border: var(--rule);
  background: var(--ink-2);
  padding: clamp(20px, 2.6vw, 34px);
}
.keypad-note .shot { background: #fff; padding: 16px; border-radius: 4px; line-height: 0; }
.keypad-note .shot img { display: block; width: 190px; height: auto; }
.keypad-note h3 { margin-bottom: 12px; }
.keypad-note p { margin-bottom: 12px; }
.keypad-note p:last-child { margin-bottom: 0; }

@media (max-width: 720px) {
  .keypad-note { grid-template-columns: 1fr; }
  .keypad-note .shot { justify-self: start; }
  .keypad-note .shot img { width: 150px; }
}


/* =====================================================================
   The parts every kit shares
   ---------------------------------------------------------------------
   Both kits on a device page differ in one part - the scanner - and carry
   the same six accessories. Showing those six once, here, at a size worth
   looking at, beats repeating them down two tables at sixty-two pixels and
   makes the difference between the two prices a single line against a
   single line.

   One row on a wide screen, because the point is that this is one kit
   rather than six things.
   ===================================================================== */
.kit-parts {
  display: grid;
  grid-template-columns: repeat(6, 1fr);
  gap: 1px;
  background: #1b2329;
  border: var(--rule);
}
.kit-parts figure { margin: 0; background: var(--ink); }
.kit-parts img {
  display: block; width: 100%; aspect-ratio: 1 / 1;
  object-fit: contain; background: #fff;
}
.kit-parts figcaption { padding: 10px 12px 12px; }
.kit-parts figcaption b { display: block; color: var(--chalk); font-size: 13px; }
.kit-parts figcaption span { font: 400 10.5px var(--mono); color: var(--concrete); word-break: break-all; }

@media (max-width: 900px) { .kit-parts { grid-template-columns: repeat(3, 1fr); } }
@media (max-width: 480px) { .kit-parts { grid-template-columns: repeat(2, 1fr); } }


/* =====================================================================
   A photograph of the real thing
   ---------------------------------------------------------------------
   Edge to edge, no white frame: this is a warehouse shelf rather than a
   print, and a mount would make it look like an illustration of one. The
   caption sits under it in body text, because it is saying something
   rather than labelling something.
   ===================================================================== */
/* Held to a reading measure rather than the full 1240px column. It is a
   photograph of one label on one shelf - at full width it was the largest
   thing on the page and read as a hero, which is not what it is doing
   there. Narrower also puts it nearer its caption's own measure, so the
   two look like one block instead of a picture with a note under it. */
.photo-wide { margin: clamp(30px, 4vw, 52px) 0 0; max-width: 720px; }
.photo-wide img {
  display: block; width: 100%; height: auto;
  border: var(--rule);
}
.photo-wide figcaption {
  margin-top: 14px;
  font-size: 15px;
  line-height: 1.6;
  color: var(--concrete);
  max-width: 74ch;
}
.photo-wide figcaption b { color: var(--chalk); }

/* ---------------------------------------------------------------------
   The enquiry form.

   Ported from the Barcode Datalink site rather than invented, because the
   two sites are one business and a customer who has seen both should not
   have to learn a second set of boxes. Same grid, same honeypot, same
   uppercase mono labels the rest of this stylesheet uses for eyebrows.

   Two colours differ: this palette has no --bright or --placeholder, so
   input text takes --paper (the most legible thing here, and a form is
   read while it is being filled in) and the placeholder takes a literal
   dimmer than --concrete, so a hint never competes with an answer.
   --------------------------------------------------------------------- */
.form { display: grid; grid-template-columns: 1fr 1fr; gap: 18px; max-width: 780px; }
.form .full { grid-column: 1 / -1; }

.form label {
  display: block; font: 500 12px/1 var(--mono);
  letter-spacing: .14em; text-transform: uppercase;
  color: var(--concrete); margin-bottom: 9px;
}
.form label .req { color: var(--signal); }

.form input, .form select, .form textarea {
  width: 100%; background: var(--steel); color: var(--paper);
  border: 1px solid var(--steel-2); border-radius: 2px;
  padding: 13px 14px; font: 400 16px/1.4 var(--body);
  transition: border-color .16s, background .16s;
}
.form textarea { min-height: 150px; resize: vertical; }

/* The focused box is named in amber, because a form somebody is halfway
   through is exactly when they need to know which box they are in. */
.form input:focus, .form select:focus, .form textarea:focus {
  outline: none; border-color: var(--signal); background: var(--ink);
}
.form input::placeholder, .form textarea::placeholder { color: #6d7885; }

/* The honeypot. A real person never sees it and never fills it in; the
   robots that scrape forms fill in everything they find. Positioned off
   screen rather than display:none, which some bots check for. */
.form .trap { position: absolute; left: -9999px; width: 1px; height: 1px; overflow: hidden; }

.form .actions { grid-column: 1 / -1; display: flex; align-items: center; gap: 20px; flex-wrap: wrap; }
.form .actions p { font-size: 15px; color: var(--concrete); margin: 0; }
.form .actions p a { color: var(--chalk); }

@media (max-width: 620px) { .form { grid-template-columns: 1fr; } }
