/* ==========================================================================
   BN1 Systems — site styles
   Design tokens live in :root. The palette is monochrome: --accent carries every
   interactive state and --rule draws the chevron fold.
   ========================================================================== */

@font-face {
  font-family: 'Schibsted Grotesk';
  src: url('../fonts/schibsted-grotesk.woff2') format('woff2-variations'),
       url('../fonts/schibsted-grotesk.woff2') format('woff2');
  font-weight: 400 900;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: 'Averia Serif Libre';
  src: url('../fonts/averia-serif-libre-400.woff2') format('woff2');
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: 'Averia Serif Libre';
  src: url('../fonts/averia-serif-libre-700.woff2') format('woff2');
  font-weight: 700;
  font-style: normal;
  font-display: swap;
}

:root {
  --accent:       #000000;
  --accent-hover: #262626;
  /* The chevron fold sits on a black/white boundary, so the line that traces it
     has to read against both. A mid-grey does; pure black or white cannot. */
  --rule:         #8a8a8a;
  /* Hairlines inside the black services grid. --rule is far too bright for a full
     grid of them; this reads as a seam rather than a drawn line. */
  --hairline:     #2b2b2b;
  --ink:          #000000;
  --paper:        #ffffff;
  /* The hero band. The sticky header's chevron is a white shape; against a white
     hero it reads as a stray line rather than the header's own edge, so the band
     under it is a shade off-white. */
  --paper-2:      #f2f2f2;
  --grey-field:   #f1f1f1;
  --grey-field-2: #e8e8e8;
  --muted:        #6b6b6b;
  --muted-dark:   #a5a5a5;
  --error:        #d1483b;

  --serif: 'Averia Serif Libre', 'Iowan Old Style', Georgia, 'Times New Roman', serif;
  --sans:  'Schibsted Grotesk', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;

  --gutter:   clamp(20px, 8.2vw, 175px);
  --maxw:     2100px;
  --radius:   10px;
  --shell-pad: clamp(56px, 8vw, 130px);
  --chevron-h: clamp(44px, 6.9vw, 150px);
  /* Overlap that keeps a chevron's polygon edge off the opposite colour. See the
     section dividers below - this is load-bearing, not a nudge. */
  --seam: 2px;
}

/* ---------- reset ---------- */
*, *::before, *::after { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; scroll-behavior: smooth; }
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after { animation-duration: .01ms !important; transition-duration: .01ms !important; }
}
body {
  margin: 0;
  background: var(--paper);
  color: var(--ink);
  font-family: var(--sans);
  font-size: 17px;
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}
img, svg { display: block; max-width: 100%; }
[hidden] { display: none !important; }
a { color: inherit; }
h1, h2, h3, h4 { margin: 0; font-family: var(--serif); font-weight: 700; line-height: 1.06; }
p { margin: 0; }
button { font: inherit; }

.container { width: 100%; max-width: var(--maxw); margin-inline: auto; padding-inline: var(--gutter); }

.visually-hidden {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}
.skip-link {
  position: absolute; left: 12px; top: -100px; z-index: 200;
  background: var(--ink); color: #fff; padding: 12px 18px; border-radius: 6px;
  text-decoration: none; transition: top .15s ease;
}
.skip-link:focus { top: 12px; }

/* currentColor, not --accent: the footer links sit on black, where a black ring
   would be invisible. This inherits black on light sections, white on dark. */
:where(a, button, input, select, textarea, [tabindex]):focus-visible {
  outline: 3px solid currentColor;
  outline-offset: 2px;
  border-radius: 4px;
}

/* ---------- buttons ---------- */
.btn {
  display: inline-flex; align-items: center; justify-content: center;
  font-family: var(--sans); font-weight: 500;
  font-size: clamp(12px, .9vw, 14px);
  letter-spacing: .09em; text-transform: uppercase; text-decoration: none;
  padding: 1.15em 2.4em; border: 0; border-radius: 999px;
  cursor: pointer; transition: background-color .18s ease, transform .18s ease, color .18s ease;
}
.btn:active { transform: translateY(1px); }
.btn--accent { background: var(--accent); color: var(--paper); }
.btn--accent:hover { background: var(--accent-hover); }
.btn--dark { background: var(--ink); color: #fff; }
.btn--dark:hover { background: #262626; }
.btn[disabled] { opacity: .55; cursor: not-allowed; transform: none; }

/* ---------- header ---------- */
.site-header {
  position: sticky; top: 0; z-index: 100;
  background: var(--paper);
  transition: box-shadow .2s ease, filter .2s ease;
}
.site-header[data-scrolled='true'] { box-shadow: 0 1px 0 rgba(0,0,0,.09), 0 8px 24px -18px rgba(0,0,0,.35); }

/* Chevron-edged sticky header (home page).
   The bar keeps the white; the SVG below it continues that white down into the
   chevron and leaves everything under the line transparent, so the black section
   - pulled up by --chevron-h - shows through the notch and scrolls beneath it. */
.site-header--chevron { background: transparent; }
/* The chevron header's bottom edge is the notch, not a straight line, so a box-shadow
   would cast a rectangle across it. drop-shadow follows the painted shape instead -
   the bar plus the chevron - so the scroll shadow traces the notch. */
.site-header--chevron[data-scrolled='true'] {
  box-shadow: none;
  filter: drop-shadow(0 1px 0 rgba(0,0,0,.09)) drop-shadow(0 4px 8px rgba(0,0,0,.13));
}
.site-header--chevron .site-header__bar {
  background: var(--paper);
  /* Only the slack between the logo row and the chevron. The logo already sits in
     a min-height row that gives it its own breathing space below, and the chevron
     line starts a little way into the SVG on top of that, so this stays small or
     the mark floats. */
  padding-bottom: clamp(6px, .8vw, 16px);
}
.site-header__chevron { line-height: 0; pointer-events: none; }
.site-header__chevron .chev { display: block; width: 100%; height: var(--chevron-h); }
.site-header__inner {
  display: flex; align-items: center; justify-content: space-between; gap: 20px;
  min-height: clamp(74px, 7vw, 104px);
}
.site-header__logo { display: inline-flex; align-items: center; }
.site-header__logo img,
.site-header__logo svg { height: clamp(26px, 2.3vw, 33px); width: auto; }

/* ---------- section dividers (the chevrons) ---------- */
.divider { line-height: 0; }
.divider svg { width: 100%; height: var(--chevron-h); display: block; }

/* Every chevron - the header's own edge and both dividers - is drawn by the same
   pair of shapes, so the line that traces the fold is coloured here rather than
   on each inline SVG. var() does not work in a presentation attribute, so the
   markup carries no stroke of its own. */
.chev polyline,
.divider svg polyline { stroke: var(--rule); }

/* ---------- hero ---------- */
/* Slides up under the sticky header's chevron by exactly its height, the same way
   .services does under the divider below. Without this the notch has nothing behind
   it, so it shows the body background and the colour break runs straight across
   instead of following the fold. */
.hero {
  background: var(--paper-2);
  margin-top: calc(var(--chevron-h) * -1 + var(--seam));
}
/* The chevron overlaps the top of this section, so the content clears it first. */
.hero__inner {
  padding-block: calc(var(--chevron-h) + clamp(12px, 1.8vw, 32px)) clamp(56px, 8vw, 132px);
}
.hero__eyebrow {
  font-size: clamp(11px, .82vw, 13px);
  letter-spacing: .17em; text-transform: uppercase; color: var(--muted);
  margin-bottom: clamp(20px, 2.4vw, 34px);
}
.hero__title {
  font-size: clamp(38px, 6.1vw, 96px);
  line-height: 1.02;
  letter-spacing: -.035em;
  max-width: 17ch;
}
.hero__lead {
  margin-top: clamp(22px, 2.4vw, 36px);
  max-width: 58ch;
  color: #3a3a3a;
  font-size: clamp(16px, 1.28vw, 21px);
  line-height: 1.5;
}
.hero__cta { margin-top: clamp(26px, 3vw, 44px); }

/* ---------- hero schematic ----------
   A plain hub-and-spoke diagram of the thing the company actually sells: six
   systems that do not talk to each other, and the layer that makes them.
   Decorative reinforcement of the copy beside it, so it is aria-hidden - the
   labels say nothing the hero and the service cards do not already say.

   Every wire is drawn twice, once as a hairline and once as a pulse on the
   identical path. pathLength="100" rescales each path to the same nominal
   length, so a single dash pattern and a single keyframe drive all six no
   matter how long the real geometry is; only duration and delay differ. */
.hero__schematic { display: none; }

@media (min-width: 1080px) {
  .hero__inner {
    display: grid;
    grid-template-columns: minmax(0, 1.08fr) minmax(0, .92fr);
    gap: clamp(32px, 4vw, 80px);
    align-items: center;
  }
  .hero__schematic {
    display: block;
    /* Capped like the headline's font-size is capped. Uncapped, the diagram keeps
       growing with the column and the labels grow with it - they are inside the
       SVG, so they scale with the whole drawing rather than staying put. */
    width: 100%; max-width: 640px; height: auto;
    margin-inline-start: auto;
  }
}

.sch__wire { stroke: #cdcdcd; stroke-width: 1; }
.sch__node { fill: var(--paper); stroke: #cdcdcd; stroke-width: 1; }
.sch__label {
  font-family: var(--sans); font-size: 9.5px; font-weight: 500;
  letter-spacing: .13em; text-transform: uppercase;
  fill: var(--muted); text-anchor: middle;
}
.sch__hub { fill: var(--ink); }
.sch__hub-label {
  font-family: var(--sans); font-size: 10.5px; font-weight: 500;
  letter-spacing: .13em; text-transform: uppercase;
  fill: var(--paper); text-anchor: middle;
}
.sch__pulse {
  stroke: var(--ink); stroke-width: 2.5; stroke-linecap: round;
  stroke-dasharray: 4 96;
  animation: sch-flow var(--dur, 6s) linear infinite;
  animation-delay: var(--delay, 0s);
}
/* Half the traffic runs the other way, so the diagram reads as an exchange
   rather than six things feeding one thing. */
.sch__pulse--out { animation-direction: reverse; }
@keyframes sch-flow {
  from { stroke-dashoffset: 0; }
  to   { stroke-dashoffset: 100; }
}
/* Paused wholesale while the hero is off screen - see site.js. */
.hero__schematic[data-paused='true'] .sch__pulse { animation-play-state: paused; }

@media (prefers-reduced-motion: reduce) {
  /* The global rule collapses the duration, which would strand a dash mid-wire.
     The diagram is still the diagram without them. */
  .sch__pulse { display: none; }
}

/* ---------- services ---------- */
.services {
  background: var(--ink); color: #fff;
  margin-top: calc(var(--chevron-h) * -1 + var(--seam));
  padding-block: clamp(64px, 10.2vw, 187px) var(--shell-pad);
}
.services__head {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: clamp(24px, 4vw, 72px);
  align-items: end;
  margin-bottom: clamp(36px, 5vw, 76px);
}
.services__title {
  font-size: clamp(36px, 5vw, 71px);
  line-height: 1.05;
  letter-spacing: -.058em;
}
.services__lead {
  color: #b9b9b9;
  font-size: clamp(15px, 1.12vw, 19px);
  line-height: 1.55;
  max-width: 54ch;
  padding-bottom: .6em;
}

/* The grid is one object, not six floating boxes: cells share hairline seams
   drawn by the 1px gap letting the wrapper's background through. */
.services__grid {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 1px;
  background: var(--hairline);
  border: 1px solid var(--hairline);
}
.card {
  background: var(--ink); color: var(--paper);
  display: flex; flex-direction: column;
  padding: clamp(24px, 2.3vw, 42px);
  min-height: clamp(250px, 19vw, 330px);
  transition: background-color .22s ease, color .22s ease;
}
/* Inverting on hover reuses the palette's only two colours instead of
   introducing a third. Everything inside rides on currentColor, so it follows. */
@media (hover: hover) {
  .card:hover { background: var(--paper); color: var(--ink); }
}
.card:focus-within { background: var(--paper); color: var(--ink); }

.card__index {
  font-size: clamp(11px, .82vw, 13px);
  letter-spacing: .16em; opacity: .45;
}
.card__icon {
  width: clamp(40px, 3.4vw, 54px); height: auto; color: currentColor;
  margin-top: clamp(26px, 2.6vw, 44px);
}
.card__title {
  font-size: clamp(20px, 1.65vw, 28px);
  margin-top: clamp(20px, 2vw, 32px);
  margin-bottom: .55em;
  letter-spacing: -.012em;
}
.card__text { line-height: 1.5; opacity: .74; font-size: clamp(14px, 1.02vw, 16px); }
.card__tags {
  list-style: none; margin: auto 0 0; padding: clamp(22px, 2.2vw, 34px) 0 0;
  display: flex; flex-wrap: wrap; gap: 6px;
}
.card__tags li {
  font-size: clamp(10px, .72vw, 11.5px);
  letter-spacing: .07em; text-transform: uppercase;
  border: 1px solid currentColor; border-radius: 999px;
  padding: .45em .72em; opacity: .5;
  transition: opacity .18s ease;
}
/* Hovering one term pulls it out on every card that carries it. The tags are one
   shared vocabulary, so this is the overlap made visible rather than decoration -
   Specification lights four cards, Migration links EPOS to ERP. Added by site.js;
   without it the tags simply sit at their resting opacity. */
.services__grid.is-linking .card__tags li { opacity: .16; }
.services__grid.is-linking .card__tags li.is-linked { opacity: 1; }

/* ---------- footer ---------- */
.footer {
  position: relative;
  color: #fff;
  background-color: #141414;
  overflow: hidden;
  isolation: isolate;
}
/* animated isometric cube field — the SVG carries its own keyframes */
.footer__pattern {
  position: absolute; inset: 0; z-index: 0;
  width: 100%; height: 100%;
  object-fit: cover;
  pointer-events: none; user-select: none;
}
.footer::after {
  content: ''; position: absolute; inset: 0; pointer-events: none; z-index: 1;
  background: linear-gradient(105deg, rgba(0,0,0,.55) 0%, rgba(0,0,0,.15) 45%, rgba(0,0,0,.5) 100%);
}
/* A chevron is a polygon in the OUTGOING colour, with the incoming section showing
   through the notch beneath it. That polygon's top edge lands exactly on the box
   edge, so whatever sits behind it there is what a stray rasterised row will show -
   and rounding produces stray rows routinely, at fractional scroll offsets and at
   browser zoom levels that headless rendering at scroll 0 never reproduces.

   So the rule for every chevron on the page: the incoming section starts --seam
   BELOW the divider's top edge, and the divider overlaps the outgoing section by
   --seam above it. That puts the outgoing colour - the polygon's own colour -
   behind the polygon's top edge, where a stray row is the same colour as the
   polygon and therefore invisible. Level them up again and the line comes back.

   --seam must stay under 6.6% of --chevron-h: that is how far down the polygon
   still reaches at the far left edge, and the incoming section has to start above
   that point or a sliver opens there instead. At the 44px floor that allows 2.9px. */
.divider--to-dark { position: relative; z-index: 2; margin-top: calc(var(--seam) * -1); }
.divider--to-light { background: transparent; margin-top: calc(var(--seam) * -1); }
.divider--to-footer { position: relative; z-index: 2; margin-top: calc(var(--seam) * -1); }
.footer__inner { position: relative; z-index: 2; padding-block: clamp(52px, 7vw, 116px) clamp(28px, 3vw, 44px); }
.footer__title {
  font-size: clamp(34px, 4.6vw, 64px);
  letter-spacing: -.015em;
  margin-bottom: clamp(20px, 2.4vw, 34px);
}
.footer__contact { display: flex; flex-direction: column; gap: 2px; }
.footer__contact a { text-decoration: none; width: fit-content; }
.footer__contact a:hover { text-decoration: underline; text-underline-offset: 3px; }
.footer__legal { margin-top: clamp(48px, 7vw, 118px); color: #e6e6e6; font-size: .92em; }

/* ---------- contact page ---------- */
.contact {
  background: var(--paper);
  scroll-margin-top: var(--header-h, 260px);
  padding-block: clamp(72px, 8.5vw, 150px) clamp(90px, 10.5vw, 185px);
}
.contact__grid {
  display: grid;
  grid-template-columns: minmax(0, 0.85fr) minmax(0, 1fr);
  gap: clamp(28px, 5vw, 92px);
  align-items: start;
}
.contact__media { position: sticky; top: calc(var(--header-h, 260px) + 24px); }
.contact__media img {
  width: 100%; height: clamp(380px, 74vh, 900px); object-fit: cover; border-radius: 2px; background: var(--grey-field);
}
.contact__intro { margin-bottom: clamp(24px, 3vw, 40px); }
.contact__intro h2 {
  font-size: clamp(28px, 3.4vw, 46px);
  letter-spacing: -.015em;
  margin-bottom: .35em;
}
.contact__intro p { color: var(--muted); max-width: 52ch; }

/* ---------- form ---------- */
.form { display: flex; flex-direction: column; gap: clamp(18px, 2vw, 26px); }
.field { display: flex; flex-direction: column; gap: 8px; }
.field__label, .fieldset__legend {
  font-size: clamp(14px, 1vw, 16px); font-weight: 500; color: var(--ink); padding: 0;
}
.field__label .req, .fieldset__legend .req { color: var(--muted); font-weight: 400; }
.field__group { display: grid; grid-template-columns: 1fr 1fr; gap: clamp(12px, 1.4vw, 20px); }
.field__hint { font-size: .86em; color: var(--muted); }

.input, .select, .textarea {
  width: 100%; font: inherit; color: var(--ink);
  background: var(--grey-field); border: 1px solid transparent;
  border-radius: var(--radius); padding: .85em 1em;
  transition: background-color .15s ease, border-color .15s ease;
}
.input::placeholder, .textarea::placeholder { color: #9a9a9a; }
.input:hover, .select:hover, .textarea:hover { background: var(--grey-field-2); }
.textarea { min-height: 150px; resize: vertical; background: #f7f7f7; border-color: #dcdcdc; }
.select {
  appearance: none;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23111' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M5 9l7 7 7-7'/%3E%3C/svg%3E");
  background-repeat: no-repeat; background-position: right 1em center; background-size: 20px;
  padding-right: 3em; cursor: pointer;
}
.input[aria-invalid='true'], .select[aria-invalid='true'], .textarea[aria-invalid='true'] {
  border-color: var(--error); background: #fdf3f2;
}
.field__error { font-size: .86em; color: var(--error); min-height: 0; }
.field__error:empty { display: none; }

/* money input with a fixed £ prefix */
.input-money { display: inline-flex; align-items: center; background: var(--grey-field); border-radius: var(--radius); padding-left: 1em; max-width: 260px; }
.input-money span { color: var(--muted); }
.input-money .input { background: transparent; padding-left: .4em; }
.input-money:focus-within { outline: 3px solid currentColor; outline-offset: 2px; }
.input-money .input:focus-visible { outline: none; }

fieldset { border: 0; margin: 0; padding: 0; }
/* Six options stack into a tall column in a half-width form; let them pair up
   whenever the column is wide enough for two. */
.choices { display: grid; grid-template-columns: repeat(auto-fit, minmax(215px, 1fr));
           gap: 12px 20px; margin-top: 12px; }
.choice { display: flex; align-items: center; gap: 12px; cursor: pointer; width: fit-content; }
.choice input {
  appearance: none; margin: 0; flex: none;
  width: 20px; height: 20px; border: 1.5px solid #b6b6b6; border-radius: 5px;
  background: #fff; cursor: pointer; display: grid; place-content: center;
  transition: background-color .12s ease, border-color .12s ease;
}
.choice input::after {
  content: ''; width: 11px; height: 11px; transform: scale(0);
  transition: transform .12s ease-in-out;
  background: var(--paper);
  clip-path: polygon(14% 44%, 0 60%, 39% 100%, 100% 18%, 84% 3%, 38% 68%);
}
.choice input:checked { background: var(--accent); border-color: var(--accent); }
.choice input:checked::after { transform: scale(1); }
.choice:hover input:not(:checked) { border-color: #8a8a8a; }

.form__actions { display: flex; align-items: center; gap: 18px; flex-wrap: wrap; margin-top: 6px; }
.form__status { font-size: .95em; }
.form__status[data-state='error']   { color: var(--error); }
.form__status[data-state='success'] { color: #1d7a33; }
.hp { position: absolute; left: -9999px; width: 1px; height: 1px; overflow: hidden; }

.form-success {
  border: 1px solid var(--accent); background: #f7f7f7;
  border-radius: var(--radius);
  padding: clamp(32px, 4vw, 56px) clamp(28px, 3.5vw, 48px);
}
.form-success h2 { font-size: clamp(22px, 2vw, 26px); margin-bottom: .6em; line-height: 1.2; }
.form-success p { color: #333; max-width: 54ch; }

/* ---------- responsive ---------- */
@media (max-width: 1080px) {
  .services__grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .card { min-height: 260px; }
}
@media (max-width: 900px) {
  .services__head { grid-template-columns: minmax(0, 1fr); align-items: start; }
  .services__lead { padding-bottom: 0; }
}
@media (max-width: 900px) {
  .contact__grid { grid-template-columns: minmax(0, 1fr); }
  .contact__media { position: static; order: -1; }
  .contact__media img { height: clamp(240px, 46vw, 420px); }
}
@media (max-width: 620px) {
  .services__grid { grid-template-columns: minmax(0, 1fr); }
  .card { min-height: 0; }
  .field__group { grid-template-columns: minmax(0, 1fr); }
  .site-header__inner { gap: 12px; }
  .btn { padding: 1.05em 1.5em; letter-spacing: .06em; }
}
@media (max-width: 380px) {
  .btn { padding: 1em 1.15em; font-size: 11px; }
  .site-header__logo img, .site-header__logo svg { height: 24px; }
}
