/* ==========================================================================
   Netbrain Integrated Solutions — core stylesheet
   Everything visual is driven by the token block below. js/synapse.js reads
   the --syn-* values off :root at startup, so retuning the palette here
   retunes the background effect too.
   ========================================================================== */

:root {
  /* Page chrome — dark, so the synapses actually glow */
  --bg:            #05070e;
  --bg-elevated:   #0b1020;
  --ink:           #e9edf7;
  --ink-dim:       #97a2ba;
  /* Was #5d6880, which measured 3.60:1 on the page background and failed
     WCAG AA. This is the lowest value that still clears 4.5:1 where a bright
     neuron lifts the backdrop behind a card (4.86:1), so it passes without
     creeping up on --ink-dim and flattening the type hierarchy. */
  --ink-faint:     #76829d;
  --hairline:      rgba(255, 255, 255, 0.09);
  /* The top bar and footer edges specifically — a solid white rule, unlike
     the faint --hairline used for cards and buttons. */
  --bar-border:    #ffffff;

  /* Accents */
  --accent:        #38e1ff;
  --accent-2:      #a274ff;
  --accent-3:      #ff5cc8;

  /* Synapse palette — consumed by js/synapse.js */
  --syn-bg:        #05070e;
  --syn-node:      #4fd8ff;
  --syn-edge:      #4f7dff;
  --syn-pulse:     #ff6ad5;
  --syn-charged:   #b98bff;

  /* Scale */
  --max-width:     1180px;
  /* Reading measure for the long-form pages. The product grid fills the whole
     shell because it needs the width; prose does not — at full
     shell width lines run past 150 characters. This column is centred so
     About and Contact sit in the page the way the panel does on the home
     page, rather than hugging the left edge with dead space beside them.

     Deliberately rem, not ch. `ch` resolves against each element's own
     font-size, so the smaller footnote text came out ~57px narrower than the
     cards above it and its left edge no longer lined up. rem keeps every
     block in the column exactly the same width. */
  --measure:       44.5rem;
  --topbar-h:      66px;
  --radius:        14px;
  --radius-sm:     9px;

  --space-1: 0.4rem;
  --space-2: 0.75rem;
  --space-3: 1.15rem;
  --space-4: 1.8rem;
  --space-5: 2.8rem;
  --space-6: 4.2rem;

  --font: "Inter", "Segoe UI", system-ui, -apple-system, "Helvetica Neue", Arial, sans-serif;
  --font-mono: "JetBrains Mono", "SF Mono", ui-monospace, Menlo, Consolas, monospace;

  --shadow-panel: 0 24px 60px -20px rgba(0, 0, 0, 0.75);
  --ease: cubic-bezier(0.22, 0.61, 0.36, 1);
}

/* --------------------------------------------------------------------------
   Reset / base
   -------------------------------------------------------------------------- */

*,
*::before,
*::after { box-sizing: border-box; }

html {
  scroll-behavior: smooth;
  /* Stops iOS bumping type size when the device is rotated. */
  -webkit-text-size-adjust: 100%;
}

body {
  margin: 0;
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  font-family: var(--font);
  font-size: 16px;
  line-height: 1.65;
  color: var(--ink);
  /* The canvas paints over this, but it matters for the first frame and for
     the moment before synapse.js runs. */
  background: var(--bg);
  -webkit-font-smoothing: antialiased;
  /* A full-viewport fixed canvas plus horizontal overflow anywhere produces a
     nasty scroll-jitter loop on mobile. Belt and braces. */
  overflow-x: hidden;
}

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

a {
  color: var(--accent);
  text-decoration: none;
  transition: color 0.18s var(--ease);
}
a:hover { color: #8beeff; }

h1, h2, h3 {
  line-height: 1.2;
  margin: 0 0 var(--space-3);
  letter-spacing: -0.02em;
  font-weight: 650;
}

:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 3px;
  border-radius: 4px;
}

/* Colours the leading segment of a heading, following the wordmark's
   Net|brain split. Used by the About and Contact card titles. */
.tint { color: var(--accent); }

.shell {
  width: 100%;
  max-width: var(--max-width);
  margin-inline: auto;
  padding-inline: var(--space-4);
}

/* Anchor targets must clear the sticky bar, or jumping to #products lands with
   the heading hidden underneath it. */
main[id],
section[id] {
  /* Bar height plus the header's own padding above and below it. */
  scroll-margin-top: calc(var(--topbar-h) + (var(--space-2) * 2) + var(--space-3));
}

.skip-link {
  position: absolute;
  left: var(--space-3);
  top: -100px;
  z-index: 100;
  padding: var(--space-2) var(--space-3);
  background: var(--accent);
  color: #04121a;
  font-weight: 600;
  border-radius: var(--radius-sm);
  transition: top 0.18s var(--ease);
}
.skip-link:focus { top: var(--space-3); }

/* --------------------------------------------------------------------------
   Synapse canvas — sits behind everything, never intercepts input
   -------------------------------------------------------------------------- */

#synapse-canvas {
  position: fixed;
  inset: 0;
  z-index: -2;
  display: block;
  /* Sized by inset alone — an explicit width:100% resolves against the
     initial containing block, which includes the scrollbar gutter, so the
     canvas would hang a few pixels past the right edge. */
  pointer-events: none;
  background: var(--bg);
}

/* A soft scrim sitting between the canvas and the content. The network keeps
   its full brightness out at the edges of the viewport and calms down behind
   the column where body copy actually sits. */
body::before {
  content: "";
  position: fixed;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  background: radial-gradient(
    ellipse 85% 65% at 50% 42%,
    rgba(5, 7, 14, 0.82) 0%,
    rgba(5, 7, 14, 0.58) 42%,
    rgba(5, 7, 14, 0.20) 70%,
    rgba(5, 7, 14, 0) 88%
  );
}

/* --------------------------------------------------------------------------
   Top bar
   -------------------------------------------------------------------------- */

/* The <header> itself spans the full width and carries only a soft scrim, so
   that page content scrolling up underneath the floating bar fades out
   instead of meeting a hard edge. The visible bar is .topbar__inner. */
.topbar {
  position: sticky;
  top: 0;
  z-index: 50;
  padding-block: var(--space-2);
  background: linear-gradient(
    to bottom,
    rgba(5, 7, 14, 0.92) 0%,
    rgba(5, 7, 14, 0.72) 55%,
    rgba(5, 7, 14, 0) 100%
  );
}

/* The bar proper. It's nested inside a .shell, so its edges land on the
   shell's content box — exactly where the product grid's edges land, which is
   what keeps the two the same width. */
.topbar__inner {
  display: flex;
  align-items: center;
  gap: var(--space-4);
  min-height: var(--topbar-h);
  padding-inline: var(--space-3);
  /* Translucent so the neurons stay faintly visible through the bar. */
  background: rgba(5, 7, 14, 0.72);
  backdrop-filter: blur(14px) saturate(150%);
  -webkit-backdrop-filter: blur(14px) saturate(150%);
  border: 1px solid var(--bar-border);
  border-radius: var(--radius);
}

/* Brand ------------------------------------------------------------------- */

/* Stacked lockup: "Netbrain" on top, the rest of the legal name beneath.
   The full name on one line is ~330px, which doesn't fit beside the nav on a
   phone — stacking keeps it legible without shrinking it away. */
.brand {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  color: var(--ink);
  letter-spacing: -0.02em;
}
.brand:hover { color: var(--ink); }

.brand__text {
  display: flex;
  flex-direction: column;
  line-height: 1.15;
}

.brand__name {
  font-weight: 680;
  font-size: 1.06rem;
  white-space: nowrap;
}

.brand__suffix {
  font-size: 0.67rem;
  font-weight: 500;
  letter-spacing: 0.01em;
  color: var(--ink-dim);
  white-space: nowrap;
}

.brand__mark {
  width: 30px;
  height: 30px;
  flex: none;
  filter: drop-shadow(0 0 7px rgba(56, 225, 255, 0.45));
}

.brand__net { color: var(--accent); }

/* Nav --------------------------------------------------------------------- */

.nav {
  display: flex;
  align-items: center;
  gap: var(--space-1);
  margin-left: auto;
}

.nav__link {
  position: relative;
  padding: 0.45rem var(--space-3);
  border-radius: var(--radius-sm);
  color: var(--ink-dim);
  font-weight: 520;
  font-size: 0.95rem;
  transition: color 0.18s var(--ease), background-color 0.18s var(--ease);
}
.nav__link:hover {
  color: var(--ink);
  background: rgba(255, 255, 255, 0.05);
}
.nav__link[aria-current="page"] { color: var(--ink); }
.nav__link[aria-current="page"]::after {
  content: "";
  position: absolute;
  left: var(--space-3);
  right: var(--space-3);
  bottom: 1px;
  height: 2px;
  border-radius: 2px;
  background: linear-gradient(90deg, var(--accent), var(--accent-2));
}

/* Mobile menu toggle ------------------------------------------------------ */

.nav-toggle {
  display: none;
  width: 40px;
  height: 40px;
  padding: 0;
  border: 1px solid var(--hairline);
  border-radius: var(--radius-sm);
  background: rgba(255, 255, 255, 0.04);
  color: var(--ink);
  cursor: pointer;
  align-items: center;
  justify-content: center;
}
.nav-toggle:hover { background: rgba(255, 255, 255, 0.09); }
.nav-toggle__bar,
.nav-toggle__bar::before,
.nav-toggle__bar::after {
  display: block;
  width: 17px;
  height: 2px;
  border-radius: 2px;
  background: currentColor;
  transition: transform 0.2s var(--ease), opacity 0.2s var(--ease);
}
.nav-toggle__bar::before,
.nav-toggle__bar::after {
  content: "";
  position: absolute;
}
.nav-toggle__bar::before { transform: translateY(-6px); }
.nav-toggle__bar::after  { transform: translateY(6px); }

.nav-toggle[aria-expanded="true"] .nav-toggle__bar { background: transparent; }
.nav-toggle[aria-expanded="true"] .nav-toggle__bar::before { transform: rotate(45deg); }
.nav-toggle[aria-expanded="true"] .nav-toggle__bar::after  { transform: rotate(-45deg); }

/* --------------------------------------------------------------------------
   Hero — kept shallow so the products are visible without scrolling on a laptop
   -------------------------------------------------------------------------- */

/* Holds just the badge now that the headline and lede are gone, so it needs
   less vertical room than it did. */
.hero {
  padding: var(--space-5) 0 var(--space-4);
  text-align: center;
}

/* The page <h1> on every page — the badge on the home page and the page title
   on About, Contact and 404, so all four match. One shared class rather than
   two lookalike rules, or they drift apart the moment either is touched.
   The explicit font-weight, line-height and letter-spacing are what stop the
   global h1/h2/h3 rule from restyling it. */
.pill {
  display: inline-block;
  margin: 0;
  padding: 0.3rem 0.85rem;
  font-weight: 500;
  line-height: 1.5;
  border: 1px solid rgba(56, 225, 255, 0.28);
  border-radius: 999px;
  background: rgba(56, 225, 255, 0.07);
  color: var(--accent);
  font-family: var(--font-mono);
  font-size: 0.74rem;
  letter-spacing: 0.09em;
  text-transform: uppercase;
}

/* --------------------------------------------------------------------------
   Client area and product grid
   --------------------------------------------------------------------------
   The grid is plain HTML written by build-products.py from api/catalog.json.
   It replaced the Ecwid storefront in September 2026: nothing here is loaded
   from a third party, so there is no loading state and nothing to fail. */

.client-area {
  flex: 1 0 auto;
  padding-bottom: var(--space-6);
}

.section-more {
  margin: var(--space-4) 0 0;
  text-align: right;
  font-size: 0.95rem;
}

.products-intro {
  max-width: var(--measure);
  margin: 0 auto var(--space-4);
  color: var(--ink-dim);
}

.product-grid {
  display: grid;
  /* 300px holds three across in the full shell and drops to one on a phone. */
  grid-template-columns: repeat(auto-fill, minmax(min(300px, 100%), 1fr));
  gap: var(--space-3);
  margin: 0 0 var(--space-5);
  padding: 0;
  list-style: none;
}

/* Same panel as .value-card, the top bar and the footer: translucent fill,
   white rule, 14px corner. */
.product-card {
  display: flex;
  flex-direction: column;
  overflow: hidden;
  border: 1px solid var(--bar-border);
  border-radius: var(--radius);
  background: rgba(5, 7, 14, 0.72);
  backdrop-filter: blur(14px) saturate(150%);
  -webkit-backdrop-filter: blur(14px) saturate(150%);
}

/* A fixed frame so a row of cards lines up whatever the artwork's shape —
   square logos, wide diagrams and one portrait poster. `contain` rather than
   `cover`, because several images carry text that a crop would cut. */
.product-card__media {
  position: relative;
  display: block;
  aspect-ratio: 16 / 10;
  overflow: hidden;
  background: var(--bg);
  border-bottom: 1px solid var(--hairline);
}
/* Absolutely placed so the image's own intrinsic height can't stretch the
   frame — without it a square logo grows the box to a square. */
.product-card__media img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: contain;
}

.product-card__body {
  display: flex;
  flex: 1;
  flex-direction: column;
  padding: var(--space-3) var(--space-3) var(--space-4);
}

.product-card__cat {
  margin: 0 0 var(--space-1);
  color: var(--accent-2);
  font-family: var(--font-mono);
  font-size: 0.7rem;
  letter-spacing: 0.09em;
  text-transform: uppercase;
}

.product-card__name {
  margin: 0 0 var(--space-2);
  font-size: 1.08rem;
}
.product-card__name a { color: var(--ink); }
.product-card__name a:hover { color: var(--accent); }

.product-card__summary {
  margin: 0 0 var(--space-3);
  color: var(--ink-dim);
  font-size: 0.92rem;
}

/* Button (where there is one) and note, pinned to the bottom of the card by
   margin-top:auto so they line up across a row even when the summaries run to
   different lengths. Cards arranged by enquiry have no button, and their note
   still lands on the same line as their neighbours' notes. */
.product-card__foot {
  margin-top: auto;
}

.product-card__actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);  margin-bottom: var(--space-2);
}

/* Where to get it, under the button. Always given two lines of height, used
   or not: button and note are pinned to the bottom of the card together, so a
   note that wraps to a second line would otherwise lift its button out of line
   with the rest of the row. */
.product-card__note {
  min-height: calc(2 * 1.65em);
  margin: 0;
  color: var(--ink-faint);
  font-size: 0.8rem;
  line-height: 1.65;
}

/* ---- Product detail page ---------------------------------------------- */

.product-hero {
  margin: var(--space-4) 0;
  padding: var(--space-3);
  border: 1px solid var(--hairline);
  border-radius: var(--radius);
  background: var(--bg);
}
.product-hero img {
  width: 100%;
  height: auto;
  max-height: 440px;
  object-fit: contain;
  margin-inline: auto;
}

.product-get {
  margin: 0 0 var(--space-4);
  padding: var(--space-3);
  border: 1px solid var(--bar-border);
  border-radius: var(--radius);
  background: rgba(5, 7, 14, 0.72);
}
.product-get__title {
  margin: 0 0 var(--space-2);
  color: var(--ink);
  font-weight: 600;
}
.product-get__actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
}
.prose .product-get__notes {
  margin: var(--space-3) 0 0;
  font-size: 0.88rem;
}
.prose .product-get__notes li:last-child { margin-bottom: 0; }

/* The descriptions came over from Ecwid and use h3 for sub-sections. */
.prose h3 {
  margin: var(--space-4) 0 var(--space-2);
  color: var(--ink);
  font-size: 1.05rem;
}
.prose ol {
  margin: 0 0 var(--space-3);
  padding-left: 1.3rem;
}
.prose ol li::marker { color: var(--accent); }
.prose strong { color: var(--ink); }
.product-description code { overflow-wrap: anywhere; }

.product-shots {
  display: grid;
  gap: var(--space-3);
}
.product-shot {
  margin: 0;
  overflow: hidden;
  border: 1px solid var(--hairline);
  border-radius: var(--radius-sm);
  background: #ffffff;
}
.product-shot img { width: 100%; height: auto; }

/* --------------------------------------------------------------------------
   About page
   -------------------------------------------------------------------------- */

.page-head {
  max-width: var(--measure);
  margin-inline: auto;
  padding: var(--space-6) 0 var(--space-4);
}

.notfound__code {
  margin: 0 0 var(--space-2);
  font-family: var(--font-mono);
  font-size: 0.8rem;
  letter-spacing: 0.22em;
  color: var(--accent);
}

/* The pill is inline-block with no bottom margin of its own, so the gap to
   the lede is set here rather than on .pill — the home page badge needs no
   such gap.

   Centred to match the home page, where .hero does it with text-align. That
   won't work here: .page-head also holds the lede, and centring a paragraph
   of that length hurts to read. So the pill is centred on its own — made a
   block, shrunk to its text with fit-content, then pushed to the middle by
   auto margins — and the lede stays ranged left. */
.page-head .pill {
  display: block;
  width: fit-content;
  margin: 0 auto var(--space-3);
}

/* Contact has no lede, so its pill is the last thing in the head and that
   bottom margin would stack on .page-head's own padding — leaving a deeper
   gap above the cards than About and 404 have below their ledes. */
.page-head .pill:last-child {
  margin-bottom: 0;
}

.page-head__lede {
  margin: 0;
  color: var(--ink-dim);
  font-size: 1.08rem;
}

.prose {
  max-width: var(--measure);
  margin-inline: auto;
  padding-bottom: var(--space-6);
  color: var(--ink-dim);
}
.prose h2 {
  margin-top: var(--space-5);
  color: var(--ink);
  font-size: 1.28rem;
}
.prose p { margin: 0 0 var(--space-3); }
.prose ul {
  margin: 0 0 var(--space-3);
  padding-left: 1.15rem;
}
.prose li { margin-bottom: var(--space-1); }
.prose li::marker { color: var(--accent); }

/* Tables. Added for the privacy page, which is mostly "what is recorded" and
   "how long it is kept" — both of which are tables in the source document and
   read badly as prose.

   `display: block` + `overflow-x: auto` on the wrapper rather than the table
   itself: a table that is wider than the phone viewport has to scroll
   somewhere, and scrolling the whole page sideways breaks every other element
   on it. The wrapper keeps the overflow local to the table. */
.prose .table-scroll {
  overflow-x: auto;
  margin: 0 0 var(--space-3);
}
.prose table {
  width: 100%;
  border-collapse: collapse;
  font-size: 0.95rem;
}
.prose th,
.prose td {
  padding: var(--space-2);
  text-align: left;
  vertical-align: top;
  border-bottom: 1px solid var(--hairline);
}
.prose th {
  color: var(--ink);
  font-weight: 600;
  white-space: nowrap;
}
.prose td code,
.prose li code,
.prose p code {
  color: var(--accent);
  font-size: 0.92em;
}
.prose pre {
  overflow-x: auto;
  margin: 0 0 var(--space-3);
  padding: var(--space-3);
  border: 1px solid var(--hairline);
  border-radius: var(--radius);
  background: rgba(5, 7, 14, 0.5);
}
.prose pre code { color: var(--ink); }

/* A quoted aside — used for the "considered position, not legal advice" note,
   which needs to read as set apart from the claims around it. */
.prose blockquote {
  margin: var(--space-4) 0;
  padding: var(--space-3) var(--space-4);
  border-left: 2px solid var(--accent);
  background: rgba(5, 7, 14, 0.5);
  border-radius: 0 var(--radius) var(--radius) 0;
}
.prose blockquote p:last-child { margin-bottom: 0; }

/* Scoped under .prose so it outranks the generic `.prose ul` rules above —
   otherwise the list padding and margin win and the grid sits indented. */
.prose .value-grid {
  display: grid;
  /* 260px, not 220px: inside the 44.5rem column a 220px minimum fits three
     across, which leaves the four cards as an awkward 3 + 1. 260px holds them
     at a clean 2 x 2 on desktop and still collapses to one column on a phone. */
  grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
  gap: var(--space-3);
  margin: var(--space-4) 0 var(--space-5);
  padding: 0;
  list-style: none;
}

.prose .value-grid > li { margin-bottom: 0; }

/* Panel-style cards, matching the top bar and footer: same translucent fill,
   same white rule, same 14px corner. */
.value-card {
  padding: var(--space-4);
  border: 1px solid var(--bar-border);
  border-radius: var(--radius);
  background: rgba(5, 7, 14, 0.72);
  backdrop-filter: blur(14px) saturate(150%);
  -webkit-backdrop-filter: blur(14px) saturate(150%);
}

.value-card h3 {
  margin: 0 0 var(--space-2);
  color: var(--ink);
  font-size: 1.05rem;
}

.value-card p {
  margin: 0;
  font-size: 0.92rem;
  color: var(--ink-dim);
}

/* --------------------------------------------------------------------------
   Contact page
   -------------------------------------------------------------------------- */

.contact-list {
  display: grid;
  gap: var(--space-4);
  max-width: var(--measure);
  margin: 0 auto var(--space-5);
  padding: 0;
  list-style: none;
}

/* Same panel treatment as the About page cards — top-bar fill, white rule,
   14px corner. */
.contact-card {
  padding: var(--space-4);
  border: 1px solid var(--bar-border);
  border-radius: var(--radius);
  background: rgba(5, 7, 14, 0.72);
  backdrop-filter: blur(14px) saturate(150%);
  -webkit-backdrop-filter: blur(14px) saturate(150%);
  color: var(--ink-dim);
}

.contact-card__num {
  font-family: var(--font-mono);
  font-size: 0.74rem;
  letter-spacing: 0.09em;
  color: var(--accent);
}

.contact-card__title {
  margin: var(--space-1) 0 var(--space-2);
  color: var(--ink);
  font-size: 1.2rem;
}

.contact-card p { margin: 0 0 var(--space-3); }

.contact-card__hint {
  margin-bottom: var(--space-1);
  color: var(--ink-faint);
  font-size: 0.86rem;
}

.contact-card__points {
  margin: 0 0 var(--space-4);
  padding-left: 1.15rem;
  font-size: 0.94rem;
}
.contact-card__points li { margin-bottom: var(--space-1); }
.contact-card__points li::marker { color: var(--accent); }

/* Every card used to end in an email button, so the element above it wanted a
   bottom margin. With the buttons gone that margin is the card's last thing,
   and it stacks on top of the card's own padding — a visibly deeper gap at the
   bottom of each panel than at the top. Whatever ends up last, it ends flush. */
.contact-card > :last-child {
  margin-bottom: 0;
}

/* Sits between the three routes and the footnote — a presence link rather
   than a contact route, so it gets its own quiet line instead of a card. */
.contact-elsewhere {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: var(--space-1) var(--space-3);
  max-width: var(--measure);
  margin: 0 auto var(--space-4);
}

.contact-elsewhere__label {
  font-family: var(--font-mono);
  font-size: 0.72rem;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: var(--ink-faint);
}

.contact-elsewhere a {
  font-size: 1rem;
  font-weight: 600;
  word-break: break-word;
}

/* The catch-all card, and the landing target for the product cards'
   "Contact us" buttons. Outside .contact-list on purpose: the lede promises
   three conversations, so this one is deliberately unnumbered rather than a
   fourth route. It borrows .contact-card for the panel treatment and only
   describes what the list would otherwise have given it — its own width and
   the space below it. */
.contact-anything {
  max-width: var(--measure);
  margin: 0 auto var(--space-6);
}

/* Arriving from a product card, the useful thing is knowing you landed on the
   right block — the jump alone leaves you looking at a page of similar
   panels. :target lights the accent edge for exactly as long as the hash is
   in the URL, and costs nothing when the page is reached normally. */
.contact-anything:target {
  border-color: var(--accent);
  box-shadow: 0 0 0 1px var(--accent), 0 0 34px -12px var(--accent);
}

/* --------------------------------------------------------------------------
   Enquiry form — /contact#enquiry, posted to api/enquiry.php
   --------------------------------------------------------------------------
   Wears .contact-card so it reads as one more panel in the same column as the
   four routes above it, and only describes what the list would otherwise have
   given it: its own width and the space below. */
.contact-enquiry {
  max-width: var(--measure);
  margin: 0 auto var(--space-6);
}

.enquiry {
  display: grid;
  gap: var(--space-3);
  margin-top: var(--space-4);
}

/* Two columns where there is room for two. Name and email are short and
   related; everything else earns the full width. */
.enquiry__row {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--space-3);
}

.enquiry__field {
  display: grid;
  gap: var(--space-1);
  min-width: 0;   /* or a long placeholder stretches the grid column */
}

/* Same treatment as .contact-elsewhere__label — a quiet mono caption rather
   than a heading, so the fields do not compete with the card titles. */
.enquiry__label {
  font-family: var(--font-mono);
  font-size: 0.72rem;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: var(--ink-faint);
}

.enquiry__optional {
  text-transform: none;
  letter-spacing: 0;
  opacity: 0.75;
}

.enquiry__control {
  width: 100%;
  padding: 0.62rem 0.8rem;
  /* NOT var(--hairline). That token is 9% white, which is right for a card
     edge — it only has to separate two dark surfaces that are already
     obviously different. A form control has the opposite job: it has to
     announce where you can type, before you have clicked anything. At 9% the
     fields read as flat text on the panel and the boundary only appears on
     focus, which is exactly when you no longer need it. */
  border: 1px solid rgba(255, 255, 255, 0.30);
  border-radius: var(--radius-sm);
  background: rgba(255, 255, 255, 0.04);
  color: var(--ink);
  /* Inherit the page font rather than the browser's form default, which is
     the difference between the field belonging to this page and sitting on
     top of it. */
  font: inherit;
  /* Helps the native widgets — scrollbar, select arrow — pick dark styling.
     It is NOT sufficient on its own for the option list; see the explicit
     option/optgroup rule below. */
  color-scheme: dark;
  transition: border-color 0.18s var(--ease), background-color 0.18s var(--ease);
}

/* The dropdown list, which is drawn by the operating system rather than by
   this page.

   `color-scheme: dark` on the select was supposed to cover this and does not:
   the popup inherited the select's colour but painted its own background, so
   the option text came out the same shade as the surface behind it and only
   became readable under the hover highlight, which supplies its own colours.
   Invisible until you happen to point at it.

   The fix is an explicit, OPAQUE background on the options themselves. The
   translucent rgba() the closed control uses cannot be reused here — a
   see-through popup composites against whatever it is drawn over and the
   contrast becomes a lottery. */
.enquiry__control option {
  background-color: var(--bg-elevated);
  color: var(--ink);
}

.enquiry__control optgroup {
  background-color: var(--bg-elevated);
  color: var(--ink-dim);
}

.enquiry__control:hover {
  border-color: rgba(255, 255, 255, 0.45);
}

/* The outline alone marks focus. An earlier version also set the border
   transparent to avoid a double ring — which, now that the border is visible
   at rest, reads as the field's edge vanishing at the moment you click into
   it. Leave the border where it is and let the outline sit outside it. */
.enquiry__control:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 1px;
}

.enquiry__control--area {
  min-height: 9rem;
  /* Horizontal dragging breaks the column; vertical is the useful axis. */
  resize: vertical;
}

/* :user-invalid, not :invalid — the latter marks every required field red
   before the visitor has typed a character. Browsers without it simply show
   no colour, which is the correct fallback. */
.enquiry__control:user-invalid {
  border-color: var(--accent-3);
}

.enquiry__hint {
  margin: 0;
  font-size: 0.84rem;
  color: var(--ink-faint);
}

/* The honeypot. Off-screen rather than display:none — the field must be
   reachable to a bot parsing the markup and unreachable to everyone else.
   aria-hidden and tabindex="-1" in the markup keep it out of the
   accessibility tree and the tab order. */
.enquiry__trap {
  position: absolute;
  left: -9999px;
  width: 1px;
  height: 1px;
  overflow: hidden;
}

.enquiry__actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-2) var(--space-3);
  margin-top: var(--space-1);
}

.enquiry__note {
  flex: 1 1 16rem;
  margin: 0;
  font-size: 0.84rem;
  color: var(--ink-faint);
}

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

/* The confirmation and failure pages rendered by api/enquiry.php. They carry
   no top bar or footer — one card on the page background — so the centring
   the shell normally inherits from the page layout has to be stated here. */
.enquiry-result {
  display: grid;
  place-items: center;
  min-height: 100vh;
  padding-block: var(--space-5);
}

.enquiry-receipt {
  max-width: var(--measure);
  margin-inline: auto;
}

.cta-row {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
  max-width: var(--measure);
  margin: var(--space-4) auto 0;
}

.btn {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
  padding: 0.62rem 1.25rem;
  border: 1px solid transparent;
  border-radius: var(--radius-sm);
  font-weight: 600;
  font-size: 0.95rem;
  cursor: pointer;
  transition: transform 0.16s var(--ease), background-color 0.18s var(--ease),
              border-color 0.18s var(--ease);
}
.btn:hover { transform: translateY(-1px); }

.btn--sm {
  padding: 0.45rem 0.9rem;
  font-size: 0.86rem;
}

.btn--primary {
  background: linear-gradient(100deg, var(--accent), var(--accent-2));
  color: #04121a;
}
.btn--primary:hover { color: #04121a; }

.btn--ghost {
  border-color: var(--hairline);
  background: rgba(255, 255, 255, 0.04);
  color: var(--ink);
}
.btn--ghost:hover {
  color: var(--ink);
  border-color: rgba(255, 255, 255, 0.22);
}

/* --------------------------------------------------------------------------
   Share sidebar — markup is generated by js/share.js
   -------------------------------------------------------------------------- */

.sharebar {
  position: fixed;
  left: 0;
  top: 0;
  z-index: 60;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--space-1);
  padding: var(--space-1);
  background: rgba(5, 7, 14, 0.78);
  backdrop-filter: blur(14px) saturate(150%);
  -webkit-backdrop-filter: blur(14px) saturate(150%);
  /* Same cyan as the hero pill's text. */
  border: 1px solid var(--accent);
  /* Docked at the edge it reads as a tab growing out of the side, so the
     left-hand corners stay square. js/share.js adds .is-detached once it's
     dragged away, and then all four are rounded. */
  border-left: 0;
  border-radius: 0 var(--radius) var(--radius) 0;
  transition: box-shadow 0.18s var(--ease), border-color 0.18s var(--ease);
}

.sharebar.is-detached {
  border-left: 1px solid var(--accent);
  border-radius: var(--radius);
  box-shadow: 0 18px 40px -18px rgba(0, 0, 0, 0.85);
}

.sharebar.is-dragging {
  /* Brighter than the resting cyan, not dimmer — the feedback has to read as
     "picked up". */
  border-color: #a5f2ff;
  box-shadow: 0 20px 44px -16px rgba(0, 0, 0, 0.9);
  /* Stops the pointer selecting page text mid-drag. */
  user-select: none;
}

/* Grip ------------------------------------------------------------------- */

.sharebar__handle {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  height: 22px;
  padding: 0;
  border: 0;
  border-radius: var(--radius-sm);
  background: transparent;
  color: var(--ink-faint);
  cursor: grab;
  /* Required, or a touch-drag scrolls the page instead of moving the bar. */
  touch-action: none;
}
.sharebar__handle:hover { color: var(--ink-dim); }
.sharebar__handle:active { cursor: grabbing; }
.sharebar__handle svg { width: 16px; height: 16px; fill: currentColor; }

.sharebar__list {
  display: flex;
  flex-direction: column;
  gap: 2px;
}

/* Buttons ---------------------------------------------------------------- */

.sharebar__btn {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 40px;
  height: 40px;
  padding: 0;
  border: 0;
  border-radius: var(--radius-sm);
  background: transparent;
  color: var(--ink-dim);
  cursor: pointer;
  transition: color 0.16s var(--ease), background-color 0.16s var(--ease);
}

.sharebar__icon {
  width: 18px;
  height: 18px;
  fill: currentColor;
  pointer-events: none;
}

.sharebar__btn:hover {
  background: rgba(255, 255, 255, 0.07);
  color: var(--ink);
}

/* Each network takes its own colour on hover — enough of a cue to aim at,
   without a wall of brand colour sitting over the page at rest. */
.sharebar__btn[data-net="x"]:hover        { color: #ffffff; }
.sharebar__btn[data-net="facebook"]:hover { color: #4c8bf5; }
.sharebar__btn[data-net="linkedin"]:hover { color: #4bb0e8; }
.sharebar__btn[data-net="whatsapp"]:hover { color: #43d854; }
.sharebar__btn[data-net="email"]:hover    { color: var(--accent); }
.sharebar__btn--copy:hover                { color: var(--accent-2); }

.sharebar__btn--copy.is-flashing {
  color: #43d854;
  background: rgba(67, 216, 84, 0.12);
}

/* Label that flies out on hover/focus. Positioned to the right, which is
   where there's room while the bar is docked at the left edge. */
.sharebar__btn::after {
  content: attr(data-label);
  position: absolute;
  left: calc(100% + 8px);
  top: 50%;
  transform: translateY(-50%);
  padding: 0.28rem 0.6rem;
  border: 1px solid var(--hairline);
  border-radius: var(--radius-sm);
  background: rgba(5, 7, 14, 0.95);
  color: var(--ink);
  font-size: 0.78rem;
  white-space: nowrap;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.14s var(--ease);
}
.sharebar__btn:hover::after,
.sharebar__btn:focus-visible::after { opacity: 1; }

/* Mid-drag the labels are only noise. */
.sharebar.is-dragging .sharebar__btn::after { display: none; }

/* On a phone the left rail overlays the edge of the product grid, so it
   becomes a horizontal pill centred along the bottom instead. js/share.js
   stops writing inline left/top below this breakpoint, which is what lets
   these rules take effect without !important. Dragging is off here — a
   bottom bar that moves when you touch it is worse than a fixed one. */
@media (max-width: 720px) {
  .sharebar,
  .sharebar.is-detached {
    left: 50%;
    top: auto;
    bottom: var(--space-2);
    transform: translateX(-50%);
    flex-direction: row;
    gap: 0;
    padding: 4px var(--space-1);
    border: 1px solid var(--accent);
    border-radius: 999px;
    box-shadow: 0 12px 30px -12px rgba(0, 0, 0, 0.9);
  }

  /* Nothing to drag, so the grip would only be a false affordance. */
  .sharebar__handle { display: none; }

  .sharebar__list { flex-direction: row; }

  .sharebar__btn { width: 40px; height: 40px; border-radius: 999px; }
  .sharebar__icon { width: 17px; height: 17px; }

  /* No hover on touch, and a flyout would run off a narrow screen. */
  .sharebar__btn::after { display: none; }

  /* Reserve room so the fixed bar can't sit on top of the footer card when
     the page is scrolled to the end. */
  body { padding-bottom: 68px; }
}

/* --------------------------------------------------------------------------
   Footer
   -------------------------------------------------------------------------- */

/* Transparent, mirroring the top bar's padding so the card sits the same
   distance off the bottom edge as the bar does off the top. Unlike the bar
   this needs no scrim — nothing scrolls underneath it. */
.footer {
  flex: none;
  margin-top: auto;
  padding-block: var(--space-2);
}

/* The footer card. Nested in a .shell exactly like .topbar__inner, so all
   three — bar, product grid, footer — share the same edges. */
.footer__inner {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-3) var(--space-4);
  padding: var(--space-4) var(--space-3);
  background: rgba(5, 7, 14, 0.72);
  backdrop-filter: blur(14px) saturate(150%);
  -webkit-backdrop-filter: blur(14px) saturate(150%);
  border: 1px solid var(--bar-border);
  border-radius: var(--radius);
}

/* Sits on the same row as the brand, pushed to the far edge. */
.footer__legal {
  margin: 0 0 0 auto;
  text-align: right;
  color: var(--ink-faint);
  font-size: 0.82rem;
}

/* --------------------------------------------------------------------------
   Responsive
   -------------------------------------------------------------------------- */

@media (max-width: 720px) {
  :root { --topbar-h: 58px; }

  .shell { padding-inline: var(--space-3); }

  .nav-toggle { display: inline-flex; }

  /* Nav becomes a sheet that drops out of the bar. Order in the flex row:
     brand | toggle, with the nav breaking to its own line below. */
  .topbar__inner {
    flex-wrap: wrap;
    gap: var(--space-2);
    padding-block: var(--space-2);
  }

  /* The hamburger belongs at the far edge, not tucked up against the
     wordmark. */
  .nav-toggle {
    order: 2;
    margin-left: auto;
  }

  .nav {
    order: 3;
    display: none;
    width: 100%;
    flex-direction: column;
    align-items: stretch;
    gap: 2px;
    margin: 0;
    padding-bottom: var(--space-2);
    border-top: 1px solid var(--hairline);
    padding-top: var(--space-2);
  }
  .nav.is-open { display: flex; }

  .nav__link { padding: 0.65rem var(--space-2); }
  .nav__link[aria-current="page"]::after { display: none; }
  .nav__link[aria-current="page"] {
    background: rgba(56, 225, 255, 0.10);
    color: var(--accent);
  }

  /* On one line the suffix makes the lockup ~203px, which together with the
     hamburger overflows a 375px bar and wraps the toggle onto its
     own row. Letting it break into "Integrated Solutions" / "(Pty) Ltd" keeps
     everything on a single row down to 320px. */
  .brand__name { font-size: 1rem; }

  .brand__suffix {
    font-size: 0.62rem;
    white-space: normal;
    max-width: 20ch;
  }

  .hero { padding: var(--space-5) 0 var(--space-4); }

  /* Too narrow to share the row with the brand, so the copyright drops to its
     own line and wraps to two. Right-aligned that leaves a ragged left edge
     that's awkward to read — left-align it once it's no longer beside the
     logo. Delete this block to keep it right-aligned at every width. */
  .footer__legal {
    margin-left: 0;
    text-align: left;
  }
}

/* Below ~360px even the wrapped suffix can't share the bar with the
   hamburger without pushing the toggle onto a second row. Drop back to the
   short form here — the full legal name is still in the footer, the page
   title and the About page. */
@media (max-width: 360px) {
  .brand__suffix { display: none; }
}

/* --------------------------------------------------------------------------
   Reduced motion — synapse.js also drops to a static render (see CONFIG)
   -------------------------------------------------------------------------- */

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }

  *,
  *::before,
  *::after {
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
  }

  .btn:hover { transform: none; }
}
