/* THE VERSION ON THIS URL IS LOAD-BEARING. Everything under /fonts/ is served
   cached-for-a-year-immutable (Program.cs), on the rule that a font file's
   name changes whenever its bytes do. brand-fonts.css breaks that rule: it is
   edited under its own name. Without a new query string a returning visitor
   keeps the OLD declarations for a year, and a face renamed in --font-body
   below then matches nothing and falls back to Segoe UI - which is exactly
   what happened on this desk on 15 September 2026 when Roboto came in. Bump
   the value whenever brand-fonts.css changes. */
@import url("/fonts/brand-fonts.css?v=2026-09-15d");

/* =========================================================================
   aitechsoftware.co.uk

   One stylesheet, hand written, no build step. That is a deliberate choice
   for a site published to IIS by copying a folder: there is no node toolchain
   on the web server, and this way there does not need to be one on the
   machine that publishes either.

   THE DESIGN COMES FROM THE PART. The company's mark is a processor package -
   black epoxy body, gold leads on a 6-unit pitch, an engraved part marking,
   the pin-1 dot. So does this page: epoxy grounds, one gold, reversed type
   that reads as engraved, and a rule made of lead ticks at the mark's own
   pitch. Capabilities are set the way a datasheet sets characteristics, with
   a mono designator over each, because the company's visual argument is that
   it builds things that run on silicon.

   Colours come from $/AI Tech Software Solutions/Brand/README.md. Where a
   value here differs from the brand it says so and why, and the reason is
   always contrast: the brand names its colours, not their ratios, and a
   public site has to clear WCAG AA at body size.
   ========================================================================= */

/* --- tokens: light ------------------------------------------------------ */

:root {
  /* A neutral light grey, no warmth in it. The ground was a warm near-white
     until 4 September 2026; it is grey now because the mark is a ceramic and
     metal part and a grey bench reads as the surface such a thing is set down
     on. The gold still has something to sit against - grey is the one
     achromatic ground a warm accent cannot be dulled by. */
  --ground: #E9EAEC;
  --surface: #F7F8F9;           /* Cards and panels lift OFF the ground now
                                    rather than being white against off-white.
                                    Pure white would over-separate against a
                                    grey this dark and read as a paste-up. */
  --sunk: #DDDFE2;

  --ink: #14171B;                /* Epoxy */
  --ink-2: #57606A;              /* Graphite (#6C757E) DARKENED. The brand
                                    value fails at body size on this ground;
                                    this clears 5.3:1. Brand graphite is still
                                    used below for the job it does pass for -
                                    rules. */
  --rule: #D8DADD;              /* Neutralised with the ground - a warm rule
                                    on a grey ground reads as a stain. */
  --rule-strong: #BFC3C8;

  --accent: #C99B3D;             /* Gold. Leads, rules, marks. NEVER text. */
  --accent-text: #7E5A12;        /* Gold deep (#966C15) taken THREE steps down.
                                    It was #8A6314 on the old warm ground, at
                                    5.0:1. The grey ground is darker, which put
                                    that value at 4.5:1 - on the line, and a
                                    line is not a margin. This is 5.2:1. */
  --accent-soft: rgba(201, 155, 61, 0.14);

  /* LINKS ARE BLUE, NOT GOLD - Mark's call, 15 September 2026: #5C9BD3, in
     BOTH themes. Known and accepted: on this grey ground the value is 2.5:1,
     under the 4.5:1 AA asks of text, and a darker step of the same hue was
     offered and declined - "make all link colors #5c9bd3". On the dark ground
     it is 6.1:1. Every link on the site reads this: prose links, the address
     in the header, the current item in the navigation, ghost buttons on
     hover. */
  --link: #5C9BD3;

  --shadow: 0 1px 2px rgba(20, 23, 27, .05), 0 8px 24px -12px rgba(20, 23, 27, .18);

  /* Both faces are Roboto since 15 September 2026 (Mark's call; the brand's
     own are Saira for display and Source Sans 3 for text). The two names are
     kept apart because headings and body are still set differently - weight,
     tracking, size - and a single face today is not a promise of one
     tomorrow. */
  --font-display: "Roboto", "Segoe UI", system-ui, -apple-system, sans-serif;
  --font-body: "Roboto", "Segoe UI", system-ui, -apple-system, sans-serif;
  --font-mono: "Azeret Mono", ui-monospace, "Cascadia Mono", Consolas, monospace;

  --wrap: 68rem;
  --gutter: clamp(1.25rem, 4vw, 3rem);
  --band: clamp(3.5rem, 7vw, 6rem);
}

/* --- tokens: dark ------------------------------------------------------- */

@media (prefers-color-scheme: dark) {
  :root {
    --ground: #14171B;
    --surface: #1B1F26;
    --sunk: #101317;

    --ink: #EDF1F4;              /* Etch */
    --ink-2: #9AA3AC;            /* Graphite LIFTED - the brand value is
                                    3.0:1 on epoxy and fails; this is 7.1:1. */
    --rule: #2B323A;
    --rule-strong: #39414A;      /* Rim */

    --accent: #C99B3D;
    --accent-text: #D9A441;      /* The gold CAN be text on epoxy - 8.1:1 -
                                    which is exactly why Gold deep exists for
                                    the other direction and not this one. */
    --accent-soft: rgba(201, 155, 61, 0.12);
    --link: #5C9BD3;             /* Same in both themes - see the light block. */

    --shadow: 0 1px 2px rgba(0, 0, 0, .4), 0 8px 28px -14px rgba(0, 0, 0, .7);
  }
}

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

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

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
}

body {
  margin: 0;
  /* Explicit, from a token. The page composites over a ground the browser
     paints in ITS theme, so a transparent body borrows the wrong one. */
  background: var(--ground);
  color: var(--ink);
  font-family: var(--font-body);
  font-size: 1.0625rem;
  line-height: 1.65;
  font-synthesis-weight: none;
  text-rendering: optimizeLegibility;
  -webkit-font-smoothing: antialiased;
  display: flex;
  flex-direction: column;
  min-height: 100vh;
}

main { flex: 1 0 auto; }

img, svg { max-width: 100%; height: auto; display: block; }

/* --- typography --------------------------------------------------------- */

h1, h2, h3, h4 {
  font-family: var(--font-display);
  font-weight: 700;
  line-height: 1.1;
  letter-spacing: -0.012em;
  text-wrap: balance;
  margin: 0;
}

h1 { font-size: clamp(2.4rem, 1.3rem + 3.6vw, 4.1rem); }
h2 { font-size: clamp(1.75rem, 1.2rem + 1.8vw, 2.5rem); }
h3 { font-size: 1.2rem; letter-spacing: -0.004em; }

p, ul, ol, dl { margin: 0; }

/*
   Running text stays near 65 characters however wide the window gets.

   DO NOT PUT .prose ON THE SAME ELEMENT AS .wrap. Both set max-width at the
   same specificity, .wrap is declared later in this file, and so .wrap wins -
   the measure silently goes back to 68rem and the text runs the full width of
   the page. The paragraph spacing goes with it, because .prose > * + * then
   targets the section's layout blocks rather than its paragraphs. It happened
   once on the privacy notice and it looked like a spacing bug rather than a
   cascade collision, which is exactly why it took a while to see.

   .wrap is the page gutter; .prose is the measure. One goes inside the other.
*/
.prose { max-width: 62ch; }
.prose > * + * { margin-top: 1rem; }

.lede {
  font-size: clamp(1.1rem, 1rem + 0.5vw, 1.3rem);
  line-height: 1.55;
  color: var(--ink-2);
  max-width: 54ch;
}

.dim { color: var(--ink-2); }

/* The designator over a section or an entry: the mono face, spaced out, the
   way a part number is set on a datasheet. A LABEL, not a heading - it never
   carries the only copy of anything. */
.designator {
  font-family: var(--font-mono);
  font-weight: 500;
  font-size: 0.7rem;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--accent-text);
  margin: 0 0 0.75rem;
}

.designator--quiet { color: var(--ink-2); }

a {
  color: var(--link);
  text-decoration-thickness: 1px;
  text-underline-offset: 0.18em;
}
a:hover { text-decoration-thickness: 2px; }

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

/* --- layout ------------------------------------------------------------- */

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

.band { padding-block: var(--band); }
.band--tight { padding-block: calc(var(--band) * 0.6); }

.stack { display: flex; flex-direction: column; }
.stack--sm { gap: 0.75rem; }
.stack--md { gap: 1.5rem; }
.stack--lg { gap: 2.5rem; }

/* Wide things scroll inside themselves; the page never scrolls sideways. */
.scroller { overflow-x: auto; }

/* Present for a screen reader and a search engine, absent to the eye. Clipped
   to a 1px box rather than display:none or visibility:hidden, either of which
   would take it out of the accessibility tree along with the layout - which is
   the whole point of putting it there. */
.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/* THE LEAD PITCH RULE. Three units of gold, three of nothing, which is the
   mark's real lead geometry - 3 wide on a 6 pitch, on its 64-unit grid. It is
   the one ornament on the site, and it is measured rather than invented. */
.pitch-rule {
  height: 3px;
  border: 0;
  margin: 0;
  background-image: repeating-linear-gradient(
    to right,
    var(--accent) 0 6px,
    transparent 6px 12px
  );
}

.pitch-rule--fade {
  -webkit-mask-image: linear-gradient(to right, #000 0%, #000 55%, transparent 100%);
          mask-image: linear-gradient(to right, #000 0%, #000 55%, transparent 100%);
}

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

/* THE HEADER'S HEIGHT, NAMED, because the sheets need it: their panel starts
   below the bar rather than under it, and a stylesheet cannot measure. It is
   the bar's min-height, and the bar is never taller than that - three short
   labels on one line - so the name is the height. The phone value is set
   where the phone header is, below. */
:root { --header-h: 4.25rem; }

.site-header {
  position: sticky;
  top: 0;
  /* ABOVE THE SHEETS, which sit at 50. The menu stays live while a sheet is
     open, so clicking Home or Privacy leaves the page and takes the sheet
     with it - a sheet is a fragment of the URL, and a new URL has none. Mark's
     call, 15 September 2026: a menu that could be seen but not clicked read
     as broken. The cost is that the panel has to start below the bar, which
     the sheet block handles with --header-h. */
  z-index: 60;
  /* Solid, and no backdrop-filter. A frosted header is the fashionable
     answer and it buys nothing here: it forces a compositing layer on every
     scroll frame, and content sliding under a translucent bar is exactly the
     kind of soft edge a mark built out of engineering drawings should not
     have. A flat bar and one hairline is also more like the thing the brand
     is about.

     BLACK IN BOTH THEMES, and a literal rather than a token - Mark's call,
     15 September 2026. It is the epoxy the hero and the footer are already
     painted in, so the page is one black at the top and the same black at
     the bottom rather than two nearly-blacks. A themed bar would be pale for
     a light-theme reader, which is the one thing a band above a dark video
     must not be. Its ink is set with the navigation below, for the same
     reason and in the same literals. */
  background: #14171B;
  border-bottom: 1px solid #39414A;
}

.site-header__inner {
  display: flex;
  align-items: center;
  gap: 1.5rem;
  min-height: var(--header-h);
  /* THE FULL WIDTH OF THE WINDOW, not the page column - Mark's call,
     15 September 2026: the navigation at the far left and the address at
     the far right of the browser. The bar keeps .wrap's gutter so nothing
     touches the glass, and drops its max-width so on a wide window the two
     ends are the window's ends rather than the column's. The page below
     keeps its column. */
  max-width: none;
}

/* The address in the RIGHT corner, home page only - Mark's call, 15 September
   2026, after a morning on the left. It is the _Email partial - mono, the
   link colour - in a <p> so it is a block the header can place; the auto
   start margin is what puts it at the far edge, with the navigation at the
   left on every page whether or not the address is there. */
.site-header__email { margin: 0 0 0 auto; font-size: 0.95rem; text-align: end; }

.nav { display: flex; align-items: center; gap: clamp(0.9rem, 2.2vw, 1.9rem); }
.nav a {
  font-size: 0.95rem;
  /* THE BAR'S INK, NOT THE READER'S. The bar is epoxy in both themes, so a
     themed --ink - #EDF1F4 dark, #14171B light - would be near-black on
     near-black for every light-theme visitor. These are the literals the hero
     used while the bar floated over it, kept for the same reason now that the
     bar is a band of its own. */
  color: #EDF1F4;
  text-decoration: none;
  padding-block: 0.35rem;
  border-bottom: 2px solid transparent;
  /* A two-word label allowed to wrap makes the whole header twice as tall and
     puts the underline through the middle of it. These are navigation labels;
     they either fit on one line or the header stacks - see below. */
  white-space: nowrap;
}
.nav a[aria-current="page"] {
  color: var(--link);
  border-bottom-color: var(--link);
}

/*
   THE BAR NO LONGER FLOATS ON THE HERO - Mark's call, 15 September 2026: a
   black band ABOVE the video, with the navigation and the address in it.

   It used to be absolute and transparent on any page with a hero, so the video
   rose to the top of the page and the labels sat on the picture. Two rules did
   that and both are gone; the bar is the ordinary sticky, solid one on every
   page, and the video starts underneath it. Nothing had to be done to make
   room: main is a flex column that stretches, so the hero takes the height the
   bar does not.

   The ink and the hover that those rules also set have moved up into .nav a,
   because they are no longer conditional - the bar is epoxy everywhere now, so
   the light literals are simply what the bar's labels are.
*/
.nav a:hover { border-bottom-color: #39414A; }

/* Below 40rem three labels cannot hold their full size on one line. This used
   to drop the wordmark's second line first and leave the navigation alone;
   there is no wordmark any more, so the labels themselves give way. */
@media (max-width: 40rem) {
  .nav { gap: 0.85rem; }
  .nav a { font-size: 0.88rem; }
}

/*
   Below 30rem - a phone held upright - the three labels cannot share a line at
   any size worth reading, so the navigation spreads across the full width of
   the bar instead of sitting at its natural size.

   It used to be a two-row header: the mark and name on top, the navigation
   beneath with a hairline between them. With the lockup gone there is no first
   row, so the rule and the space above it went with it - a line drawn under
   nothing is not a divider.

   Still no hamburger. A menu behind a button needs JavaScript to open, and this
   site ships none and forbids it in the Content-Security-Policy. It would also
   hide a three-item navigation behind a tap, which is a worse answer than
   showing all three - the pattern exists for menus of fifteen, and it gets
   copied onto menus of three.
*/
@media (max-width: 30rem) {
  :root { --header-h: 3.5rem; }
  /* On the home page the address in the corner wraps to two lines at this
     width and the bar is 4.3rem, not 3.5 - so the pop-up, which starts a
     gutter below --header-h, would start 3px under the bar. Named on body,
     which the bar and the sheet both inherit from, and only where the
     address is present. */
  body:has(.site-header__email) { --header-h: 4.5rem; }
  .site-header__inner { min-height: var(--header-h); padding-block: 0.6rem; }
  /* Two labels at the left, the address at the right; the nav no longer
     spreads across the full width, which it did when it was three labels
     and nothing else in the bar. */
  .nav { gap: 0.9rem; }
}

/* --- buttons ------------------------------------------------------------ */

.btn {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  font-family: var(--font-display);
  font-weight: 600;
  font-size: 0.95rem;
  letter-spacing: 0.01em;
  padding: 0.7rem 1.35rem;
  border-radius: 2px;              /* the package's own 1.5-on-64 corner */
  border: 1px solid transparent;
  text-decoration: none;
  cursor: pointer;
  transition: background-color .15s, border-color .15s, color .15s;
}

.btn--primary {
  background: var(--accent);
  border-color: var(--accent);
  color: #14171B;                  /* fixed: the gold is a gold in both
                                      themes, so what sits on it must be too */
}
.btn--primary:hover { background: #D9A441; border-color: #D9A441; }

.btn--ghost {
  background: transparent;
  border-color: var(--rule-strong);
  color: var(--ink);
}
.btn--ghost:hover { border-color: var(--link); color: var(--link); }

.btn-row { display: flex; flex-wrap: wrap; gap: 0.75rem; }

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

/* Epoxy in BOTH themes. This is the part on the bench; it is the one region
   of the site that does not follow the reader's preference, because a black
   package rendered pale is not the mark. The colour is what paints in the
   moment before the poster arrives, and it is close enough to the frame's own
   background that the swap does not read as a flash. */
.hero {
  background: #14171B;
  color: #EDF1F4;
  position: relative;
  overflow: hidden;
  border-bottom: 1px solid #39414A;
  /* THE HERO TAKES THE HEIGHT THE PAGE DOES NOT NEED FOR ANYTHING ELSE, which
     on the home page is everything except the footer. The rule that grows it
     is below; this is the floor it can never go under, and it is the height
     the hero used to be fixed at.

     It stopped being a fixed height on 14 September 2026. The old cap existed
     because the frame is 16:9 and full bleed would make it 1080px tall on a
     1920px screen, pushing four sections of content off the fold. There are no
     sections any more - the page is the video and the footer - so the thing
     the cap protected is gone, and what the cap produced instead was a band of
     empty ground between the two. */
  min-height: clamp(20rem, 44vw, 34rem);
}

/* Only the page that HAS a hero becomes a flex column, so nothing changes on
   /contact or /privacy. main already carries flex: 1 0 auto and so already
   stretches to push the footer down; this hands that stretch on to the hero
   instead of leaving it as empty ground. */
body:has(.hero) main { display: flex; flex-direction: column; }
body:has(.hero) .hero { flex: 1 1 auto; }

/* ON A PHONE, HEIGHT IS BOUGHT WITH WIDTH, so the growth above is capped.
   object-fit: cover scales a 16:9 frame to fill the box, so a 375px-wide box
   616px tall displays the frame 1096px wide and shows a third of it - which
   cropped the lettering off the chip. That lettering matters more than it
   looks: since the wordmark left the header there is nowhere else on this page
   the company name appears to the eye.

   90vw is the widest box that still leaves the chip whole at phone widths -
   337px at 375, showing about 62% of the frame. Checked against the picture,
   not calculated and assumed. */
@media (max-width: 40rem) {
  body:has(.hero) .hero { max-height: 90vw; }
}

/* Fills the box and crops from the centre, which is where the chip sits in
   every frame - so the crop only ever takes board, never the mark. object-fit
   governs the poster as well as the video, so the still and the loop are
   framed identically and the handover between them does not move. */
.hero__video {
  position: absolute;
  inset: 0;
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* THERE IS NO prefers-reduced-motion FALLBACK, and that is a decision.
   One was built - reduce hid the video and left the poster frame - and it came
   out on 14 September 2026 at Mark's call, after it turned out that Windows
   reports reduced motion whenever "Animation effects" is off under Settings >
   Accessibility > Visual effects. That switch is off on more machines than
   anyone expects, including the one this was written on, and on those the hero
   was a photograph with no way to tell it apart from a broken video.

   The cost is real and belongs written down rather than rediscovered: a reader
   who has genuinely asked their operating system for less motion gets the loop
   regardless. Putting it back is three lines, and nothing else has to change,
   because poster= already supplies the frame it would fall back to:

     @media (prefers-reduced-motion: reduce) { .hero__video { display: none; } }
*/

/* --- the datasheet grid ------------------------------------------------- */

/*
   A sheet of hairline-ruled cells, square-cornered the way a datasheet's
   characteristics table is.

   The rules are a 1px RING ON EACH CELL, not a background on the container
   showing through a 1px gap. The container-background trick is the shorter
   CSS and it is wrong here: auto-fit gives three columns at desktop width, so
   a four-item sheet leaves two EMPTY cells in the last row, and a background
   behind them paints a grey block sitting next to the last card with nothing
   in it. Rings live on the cells that exist, so an incomplete last row simply
   ends. The 1px gap is exactly consumed by the two neighbouring rings, which
   is what keeps the join looking like one line rather than two.
*/
.specs {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(17rem, 1fr));
  gap: 1px;
}

/* For a sheet of two side by side - the contact form and the details beside
   it - which want more room each than a capability card does. */
.specs--split { grid-template-columns: repeat(auto-fit, minmax(20rem, 1fr)); }

.spec {
  background: var(--surface);
  box-shadow: 0 0 0 1px var(--rule);
  padding: clamp(1.4rem, 3vw, 2rem);
  display: flex;
  flex-direction: column;
  gap: 0.6rem;
}

.spec p { color: var(--ink-2); font-size: 0.97rem; }

/* Cards that stand alone rather than in a sheet. */
.card {
  background: var(--surface);
  border: 1px solid var(--rule);
  border-radius: 3px;
  padding: clamp(1.4rem, 3vw, 2.1rem);
  box-shadow: var(--shadow);
}

/* --- forms -------------------------------------------------------------- */

.form { display: grid; gap: 1.15rem; max-width: 34rem; }
.field { display: flex; flex-direction: column; gap: 0.35rem; }

.field label {
  font-family: var(--font-display);
  font-weight: 600;
  font-size: 0.9rem;
  letter-spacing: 0.01em;
}
.field .hint { font-size: 0.85rem; color: var(--ink-2); }

.field input,
.field textarea {
  font: inherit;
  font-size: 1rem;
  color: var(--ink);
  background: var(--surface);
  border: 1px solid var(--rule-strong);
  border-radius: 2px;
  padding: 0.6rem 0.75rem;
  width: 100%;
}
.field textarea { min-height: 9rem; resize: vertical; }
.field input:focus, .field textarea:focus { border-color: var(--accent); }

.field-error { color: #B3261E; font-size: 0.88rem; }
.field input.input-validation-error,
.field textarea.input-validation-error { border-color: #B3261E; }

@media (prefers-color-scheme: dark) {
  .field-error { color: #F2B8B5; }
  .field input.input-validation-error,
  .field textarea.input-validation-error { border-color: #F2B8B5; }
}

.notice {
  border-left: 3px solid var(--accent);
  background: var(--accent-soft);
  padding: 1rem 1.15rem;
  border-radius: 0 3px 3px 0;
}
.notice h2 { font-size: 1.25rem; margin-bottom: .35rem; }

/* The honeypot. Hidden from sight AND from assistive technology, and out of
   the tab order, so no real visitor can reach it by any route - which is what
   makes a value in it proof of a robot rather than an accident. */
.trap {
  position: absolute;
  left: -9999px;
  width: 1px;
  height: 1px;
  overflow: hidden;
}

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

/* A heading INSIDE a panel, which needs to sit below the page's own h2 in
   size without sitting below an h3 in the document outline. */
.panel-title { font-size: 1.25rem; margin-bottom: 1.25rem; }

/* <address> arrives italic and tight from the user agent stylesheet, which is
   wrong for a postal address set in the body face. */
.address { font-style: normal; line-height: 1.6; }

/* A reference somebody reads back to us over the phone: mono, so a 1 and an l
   cannot be confused, and slightly smaller so it does not shout. */
.ref { font-family: var(--font-mono); font-size: 0.9em; }

/*
   The contact address, which is an IMAGE - see Pages/Shared/_Email.cshtml and
   Site/EmailImage.cs. inline-block with a baseline nudge so it sits on the
   line of text around it rather than on the bottom of its own box, which is
   where an img aligns by default and which drops it a few pixels low in the
   middle of a sentence.

   The base img rule sets height:auto, and that must not apply here: the width
   and height attributes are measured from the text, and letting the height be
   derived from a scaled width makes the address grow and shrink with the
   column. It is type, not a picture, so it holds its size.
*/
/*
   The contact address.

   Mono, because it is a string somebody has to read accurately and possibly
   read aloud - the brand reserves the mono face for exactly that, and it also
   keeps a 1 from an l and a 0 from an O. Slightly smaller than the body face
   because a monospace at the same size reads larger than the text around it.

   Everything else it needs it already has: it is an ordinary link, so it takes
   the link colour, the underline, the hover and the focus ring from the rules
   at the top of this file, in both themes, with nothing added here. That is
   the whole argument for plain text over the image this replaced - a dozen
   lines of positioning, baseline nudging and per-theme handling stopped being
   necessary rather than being rewritten.

   overflow-wrap rather than word-break: the partial puts a <wbr> after the @,
   so the address breaks into the mailbox and the domain where a reader would
   break it. This is the fallback for a column too narrow even for that -
   break somewhere ugly rather than push the whole page sideways - and it must
   NOT be word-break, which breaks at the edge every time and ignores the
   <wbr> hint entirely. That is what produced "customerservices@aitechsoftwar"
   on the first attempt.
*/
.email {
  font-family: var(--font-mono);
  font-size: 0.92em;
  overflow-wrap: break-word;
}

.detail-list { display: grid; gap: 1.25rem; }
.detail-list dt {
  font-family: var(--font-mono);
  font-weight: 500;
  font-size: 0.68rem;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--ink-2);
  margin-bottom: 0.25rem;
}
.detail-list dd { margin: 0; }

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

.site-footer {
  background: #14171B;
  color: #AEB6BE;
  /* SMALL - Mark's call, 15 September 2026. The footer is two short lines of
     statutory text and a copyright; it gets the padding of two short lines,
     not of a section. */
  padding-block: 1.25rem;
}

/* Nothing uses the mark at the moment: the footer's logo, its blurb, the
   Company list and the Get in touch column all came out on 14 September 2026,
   and the statutory disclosure is what is left. The rule stays beside
   _MarkMono.cshtml, which is still in the tree - the partial draws in
   currentColor, and this is what gives it the gold, so dropping the rule would
   leave a working partial that renders the wrong colour the day it is used
   again. The grid, the blurb, the column headings and the link styling went
   with the markup that was the only thing they could match. */
.site-footer__mark { width: 30px; height: 30px; color: #C99B3D; }

.site-footer__legal {
  /* NO RULE ABOVE IT - Mark's call, 15 September 2026; the footer's own
     ground against the page is the division. Two centred lines, the
     copyright under the disclosure - see the layout. */
  text-align: center;
  font-size: 0.8rem;
  line-height: 1.5;
  color: #6C757E;
}
.site-footer__legal p { margin: 0; }
.site-footer__legal p + p { margin-top: 0.3rem; }

/* --- cookie consent ------------------------------------------------------ */

/*
   The prompt, pinned to the bottom. It carries [hidden] until consent.js
   decides to show it, and [hidden] must be honoured - a flex container
   overrides it unless display:none is stated again, which is exactly how a
   consent banner ends up visible to somebody who already answered.
*/
.consent[hidden] { display: none; }

.consent {
  position: fixed;
  left: 1rem;
  right: 1rem;
  bottom: 1rem;
  z-index: 60;
  margin-inline: auto;
  max-width: 46rem;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 1rem 1.5rem;
  padding: 1.1rem 1.35rem;
  background: var(--surface);
  border: 1px solid var(--rule-strong);
  border-radius: 3px;
  box-shadow: 0 12px 40px -12px rgba(0, 0, 0, .45);
}

.consent__text {
  flex: 1 1 22rem;
  font-size: 0.94rem;
  color: var(--ink-2);
  max-width: 46ch;
}

/* Both buttons the same size and weight. Making "Allow" the loud one and
   "No thanks" a grey whisper is the dark pattern the ICO names; these differ
   only in fill, which is enough to show which is the affirmative. */
.consent__buttons { display: flex; gap: 0.6rem; flex: 0 0 auto; }
.consent__buttons .btn { font-size: 0.9rem; padding: 0.55rem 1.1rem; }

@media (max-width: 34rem) {
  .consent { left: 0.6rem; right: 0.6rem; bottom: 0.6rem; padding: 1rem; }
  .consent__buttons { width: 100%; }
  .consent__buttons .btn { flex: 1; justify-content: center; }
}

/* --- skip link ---------------------------------------------------------- */

.skip-link {
  position: absolute;
  left: -9999px;
  background: #C99B3D;
  color: #14171B;
  padding: 0.6rem 1rem;
  z-index: 100;
}
.skip-link:focus { left: 1rem; top: 0.6rem; }

/* --- reduced motion ----------------------------------------------------- */

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after {
    animation-duration: .001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .001ms !important;
  }
}

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

@media print {
  .site-header, .site-footer, .btn-row { display: none; }
  body { background: #fff; color: #000; }

  /* Neither the loop nor the still goes on paper: a full-bleed photograph of
     a circuit board is a page of ink for no information. What the hero is FOR
     on paper is the heading, so the one that is hidden on screen comes back
     here - otherwise a printed copy of the home page begins at "What we do"
     with no title on it at all. */
  .hero { height: auto; background: #fff; color: #000; border: 0; }
  .hero__video { display: none; }
  .hero .sr-only {
    position: static;
    width: auto;
    height: auto;
    margin: 0;
    padding-block: 2rem;
    clip-path: none;
    white-space: normal;
  }
}

/* --- the privacy page ---------------------------------------------------- */

/*
   THE PRIVACY PAGE IS SET IN LATO - Mark's call, 15 September 2026, after an
   hour in Inter - and only the notice is: the header and footer are shared
   chrome and stay in Roboto with every other page.

   Redefining the two font tokens on the section is enough for the headings,
   because the h1-h4 rule reads var(--font-display) on the element and the
   custom property inherits. It is NOT enough for the paragraphs: body sets
   font-family once, and what a <p> inherits is the computed family, not the
   variable - so font-family is set here again, explicitly, to pick the new
   value up. The address keeps the mono face; that rule is on .email itself.
*/
.privacy-page {
  --font-display: "Lato", "Segoe UI", system-ui, -apple-system, sans-serif;
  --font-body: "Lato", "Segoe UI", system-ui, -apple-system, sans-serif;
  font-family: var(--font-body);
  /* SMALLER TEXT THAN THE REST OF THE SITE - Mark's call, 15 September 2026:
     15px against the site's 17px. In rem, so it is a size and not a ratio of
     whatever the body happens to be. The h2 below is in rem too, so it does
     not move with this; the address keeps its own em-based size on .email,
     so it shrinks in step with the text around it. */
  font-size: 0.9375rem;
}

/* THE SECTION HEADINGS ARE SMALLER HERE - Mark's call, 15 September 2026. The
   site-wide h2 runs 28-40px with the viewport; on a notice of six short
   sections that is a poster heading over two lines of text. 1.4rem (22.4px
   against 15px body) is one clear step up from the text and no more, and it
   is fixed rather than fluid because a legal notice has no hero width to
   scale with. */
.privacy-page h2 {
  font-size: 1.4rem;
  /* NOT BOLD - Mark's call, 15 September 2026, asking for medium. Lato as
     Google serves it has no medium: the family is 100, 300, 400, 700 and
     900, and a request for 500 would resolve to the 400 file anyway, with
     nothing to tell a reader of this file why. So 400, said plainly. If a
     true medium is wanted it is a different family or Lato's own
     Semibold, which the API does not carry. */
  font-weight: 400;
}

/* --- the sheets ---------------------------------------------------------- */

/*
   AN OVERLAY OPENED BY A LINK, WITH NO SCRIPT. A link points at #privacy,
   the sheet carries that id, and :target is what shows it. (The contact
   sheet, #contact, went on 15 September 2026 with the /contact page.) Closing
   is the backdrop, a link back to "#" - or any menu item, which leaves the
   page and so the fragment. There is no Close control: Mark's call,
   15 September 2026, once the menu could be clicked with a sheet open.

   display:none while closed, so the notice is not read out, not tabbed into
   and not laid out until it is asked for - it is in the markup of every page,
   which is the cost of doing this without script, but it costs nothing at
   runtime while it is shut.
*/
.sheet { display: none; }
.sheet:target {
  /* A FLEX BOX THAT PLACES ITS PANEL: at the TOP, just under the bar, and at
     the LEFT edge across - Mark's calls, 15 September 2026: centred both
     ways first, then to the left, then further up. flex-start on both axes
     rather than margins on the panel, so the gutters are the box's own
     padding. The top padding is the header's height plus a gutter, so the
     panel can never slide under the bar. */
  display: flex;
  align-items: flex-start;
  justify-content: flex-start;
  padding-block: calc(var(--header-h) + 1rem) 1rem;
  padding-inline: clamp(0.75rem, 3vw, 2.5rem);
  position: fixed;
  inset: 0;
  z-index: 50;             /* under the header at 60: the menu stays clickable */
}

/* The backdrop is a LINK, not a div, so that clicking away closes the sheet
   the way a reader expects. tabindex -1 and aria-hidden keep it out of the tab
   order and off the accessibility tree: it is a click target, not a control
   anybody should land on. */
.sheet__backdrop {
  position: absolute;
  inset: 0;
  background: rgba(10, 12, 15, 0.45);
}

/*
   80% TRANSPARENT, AND BLURRED BEHIND - Mark's call, 15 September 2026. The
   panel is a 20% tint of epoxy over whatever is behind it, and the video
   carries on underneath as a blurred wash.

   THE INK IS FIXED LIGHT AND THE TINT IS FIXED DARK, in both themes, and
   that is the consequence of 80% transparent: at that opacity the panel's
   own colour is not what the text sits on, the page behind it is. The only
   page this sheet opens over is the home page (on /privacy the menu item is
   the page itself), and the home page is epoxy in both themes - the hero
   and the footer both - under a 45% dark backdrop. So the ground under the
   text is dark whatever the reader's theme says, and a themed --ink would
   be near-black on near-black for every light-theme visitor. The literals
   are the hero's own: #EDF1F4 for the ink, and the dim line takes the dark
   theme's --ink-2. Over the board itself - most of the frame, most of the
   time - that ink is far above AA. Over the lit lettering on the chip it is
   not: white, dimmed by the backdrop and the tint, is still a mid grey, and
   the ratio there falls to about 3:1 for the lines that cross it. The panel
   opens on the left and the chip sits right of centre, so at desktop widths
   the overlap is a corner; on a phone the panel is the whole width and the
   overlap is whatever the loop is showing. Known, and accepted with the 80%.

   backdrop-filter is what makes this readable at all: what shows through
   is a wash rather than circuit traces with legible edges. -webkit- prefix
   for Safari, which still wants it. A browser without it gets an opaque
   panel - the fallback has to be the readable state, not the pretty one -
   and that too is dark, so the fixed ink is right there as well.
*/
.sheet__panel {
  position: relative;
  /* PLACED by the flex box above; the panel itself carries no margin. Left
     from changeset 2110, centred for an hour on 15 September 2026, then left
     again and, later that afternoon, up to the top. The lit chip in the
     video sits right of centre, so a left panel crosses less of it - see
     the contrast note below. */
  width: 100%;
  max-width: 46rem;
  max-height: 100%;        /* of the flex box, which already excludes the bar */
  /* IT STILL SCROLLS, WITHOUT A SCROLLBAR - Mark's call, 15 September 2026.
     On a short viewport the notice is taller than the panel, and a panel
     that could not scroll would cut the notice off with no way to read the
     rest. So overflow stays auto and the BAR is hidden: scrollbar-width for
     Firefox and the standard, the -webkit- pseudo for Chrome and Safari.
     Wheel, trackpad, touch and keyboard all still move it. */
  overflow-y: auto;
  scrollbar-width: none;
  overscroll-behavior: contain;
  background: rgba(20, 23, 27, 0.2);   /* 80% transparent, dark in both themes */
  color: #EDF1F4;                       /* fixed light - see above */
  -webkit-backdrop-filter: blur(16px) saturate(1.15);
          backdrop-filter: blur(16px) saturate(1.15);
  border: 1px solid rgba(237, 241, 244, 0.18);
  border-radius: 3px;
  box-shadow: 0 24px 60px rgba(0, 0, 0, .45);
  padding: clamp(1.5rem, 4vw, 2.75rem);
  display: flex;
  flex-direction: column;
  gap: 1.5rem;
}

.sheet__panel::-webkit-scrollbar { display: none; }

/* A browser without backdrop-filter gets the opaque panel back rather than a
   translucent one with a sharp video behind it - the fallback has to be the
   readable state, not the pretty one. */
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  .sheet__panel { background: #14171B; }
}

/* NO TYPE OF ITS OWN. The panel carries .privacy-page, so the notice is set in
   the sheet exactly as it is on the page - one face, one size, one heading
   weight, one partial - and a change to the page's type is a change to the
   pop-up's. The separate, smaller sheet scale that used to live here went
   with that. Two things the page's rule does not cover are set below: the
   panel's ink, which must not follow the theme, and the dim line under it. */
.sheet__panel .dim { color: #9AA3AC; }
