/* TACTALIX placeholder site. Token names and values match the app
   (internal/web/static/app.css :root), so a colour change is made the same way
   in both places. The site is always dark. See docs/site/DESIGN.md. */

@font-face {
  font-family: "IBM Plex Sans";
  src: url("/assets/fonts/IBMPlexSans-Regular.woff2") format("woff2");
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}

@font-face {
  font-family: "IBM Plex Sans";
  src: url("/assets/fonts/IBMPlexSans-Medium.woff2") format("woff2");
  font-weight: 500;
  font-style: normal;
  font-display: swap;
}

@font-face {
  font-family: "IBM Plex Sans";
  src: url("/assets/fonts/IBMPlexSans-SemiBold.woff2") format("woff2");
  font-weight: 600;
  font-style: normal;
  font-display: swap;
}

/* Metric-matched fallback so the swap to IBM Plex Sans does not shift layout.
   Average advance width measured against Liberation Sans (Arial metrics): ratio 1.004.
   Vertical metrics are taken from the Plex hhea table. */
@font-face {
  font-family: "IBM Plex Sans Fallback";
  src: local("Arial"), local("Liberation Sans"), local("Helvetica");
  size-adjust: 100%;
  ascent-override: 102.5%;
  descent-override: 27.5%;
  line-gap-override: 0%;
}

:root {
  color-scheme: dark;

  --bg: #F5F5F2;
  --text: #1A2230;
  --text-2: #4B5260;
  --primary: #16233B;
  --link: #1F4FA8;
  --focus: #C2410C;
  --signal: #E8590C;
  --sel-bg: #EAEAE5;
  --sel-fg: #16233B;

  /* Site only: neutral hairline above the closing line. */
  --line: #D9D9D2;
  /* Site only: background map grid. */
  --grid-line: #E9E9E4;

  --f-body: "IBM Plex Sans", "IBM Plex Sans Fallback", system-ui, -apple-system, "Segoe UI", Arial, sans-serif;

  --step-0: 1rem;                                       /* body, 16px */
  --step-1: 1.25rem;                                    /* 404 home link, 20px */
  --step-2: clamp(1.375rem, 1.1rem + 1vw, 2rem);        /* lede, 22 to 32px */

  --space-1: 0.5rem;
  --space-2: 1rem;
  --space-3: 1.5rem;
  --space-4: 2rem;
  --space-5: 3rem;

  --gutter: clamp(1rem, 0.5rem + 4vw, 4rem);
  --measure: 40rem;                                     /* content column */
  --wordmark-w: 75%;                                    /* of the column; the one place to tune wordmark size */
  --rule: 2px;
  --hairline: 1px;
  --ease: 160ms cubic-bezier(0.2, 0, 0, 1);

  /* Motion. */
  --grid-cell: 40px;                                    /* must match the SVG tile below */
  --trace: 3s;                                          /* wordmark outline trace; must match the SVG timings in index.html */
  --fill-in: 500ms;                                     /* wordmark fill after the trace */
  --trace-out: 300ms;                                   /* stroke and crosshair fade after the trace */
}

/* The site is dark in every browser theme (owner decision, 12 November 2026). */
:root {
  --bg: #11151B;
  --text: #E8EAEE;
  --text-2: #B3B8C2;
  --primary: #E8EAEE;
  --link: #8FB0FF;
  --focus: #F08A4B;
  --sel-bg: #232A35;
  --sel-fg: #FFFFFF;
  --line: #2A313C;
  --grid-line: #161A21;                                 /* the one place to tune grid visibility */
}

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

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

body {
  margin: 0;
  min-height: 100vh;
  min-height: 100svh;
  display: grid;
  background: var(--bg);
  color: var(--text);
  font-family: var(--f-body);
  font-size: var(--step-0);
  line-height: 1.5;
  font-weight: 400;
}

/* Background: a static map grid. The SVG tile is a mask, so the colour comes
   from --grid-line. */
body::before {
  content: "";
  position: fixed;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  background-color: var(--grid-line);
  -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='40' height='40'%3E%3Cpath d='M0 0.5H40M0.5 0V40' stroke='%23000' stroke-width='1' fill='none'/%3E%3C/svg%3E");
  mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='40' height='40'%3E%3Cpath d='M0 0.5H40M0.5 0V40' stroke='%23000' stroke-width='1' fill='none'/%3E%3C/svg%3E");
  -webkit-mask-size: var(--grid-cell) var(--grid-cell);
  mask-size: var(--grid-cell) var(--grid-cell);
  -webkit-mask-position: 0 0;
  mask-position: 0 0;
}

/* Wordmark trace (index only), once on load. SMIL in index.html draws each
   letter outline in turn (stroke-dashoffset, pathLength 1) and moves the
   crosshair along the same outline with animateMotion; both run on the SVG
   timeline, linear by length, so the crosshair stays on the stroke tip. CSS only
   swaps the layers when the trace ends. Default styles are the end state: if
   CSS animations never run, the trace stays hidden and the fill shows. */
.trace {
  opacity: 0;
  pointer-events: none;
  /* Fades out so it is gone as the fill completes. */
  animation: trace-out var(--trace-out) ease-out calc(var(--trace) + var(--fill-in) - var(--trace-out)) backwards;
}

/* Lets the crosshair arms run past the wordmark's edges. */
.intro .wordmark svg {
  overflow: visible;
}

.trace-stroke {
  fill: none;
  stroke: var(--signal);
  stroke-width: 1.5px;
  stroke-dasharray: 1 2;
  stroke-dashoffset: 1;
  vector-effect: non-scaling-stroke;
}

/* Small crosshair on the laser point: arms 32 user units long with a 7-unit
   gap at the centre (about 14px and 3px at the 480px desktop wordmark). */
.trace-arms {
  fill: none;
  stroke: var(--text-2);
  stroke-width: 1px;
  vector-effect: non-scaling-stroke;
}

.intro .wordmark-fill {
  animation: fill-in var(--fill-in) ease-out var(--trace) backwards;
}

/* The rule and text appear in one step when the fill completes. Visible is
   the default, so they show if animations never run. */
.intro > :not(.wordmark) {
  animation: hold-hidden calc(var(--trace) + var(--fill-in)) step-end;
}

@keyframes hold-hidden {
  from {
    visibility: hidden;
  }
}

@keyframes trace-out {
  from {
    opacity: 1;
  }
}

@keyframes fill-in {
  from {
    opacity: 0;
  }
}

::selection {
  background: var(--sel-bg);
  color: var(--sel-fg);
}

.frame {
  width: 100%;
  max-width: calc(var(--measure) + 2 * var(--gutter));
  margin-inline: auto;
  padding-inline: var(--gutter);
}

main {
  /* The group sits just above the optical centre of the viewport: more space
     below than above. */
  display: grid;
  align-content: center;
  padding-block: clamp(3rem, 10vh, 7rem) clamp(3.5rem, 13vh, 9rem);
}

.wordmark {
  margin: 0;
  line-height: 0;
}

/* Left-aligned with the rule and text; width set by --wordmark-w only. */
.wordmark svg {
  display: block;
  width: var(--wordmark-w);
  height: auto;
  color: var(--primary);
}

/* The one signal element: a thin rule marking the section boundary. */
.boundary {
  height: 0;
  margin: var(--space-4) 0;
  border: 0;
  border-top: var(--rule) solid var(--signal);
}

.lede {
  margin: 0;
  max-width: 26ch;
  font-size: var(--step-2);
  line-height: 1.25;
  font-weight: 500;
  letter-spacing: -0.01em;
  text-wrap: balance;
}

.descriptor {
  margin: var(--space-2) 0 0;
  color: var(--text-2);
  text-wrap: pretty;
}

.status {
  margin: var(--space-1) 0 0;
  color: var(--text-2);
  text-wrap: pretty;
}

.lede + .status {
  margin-top: var(--space-2);
}

/* Closing line, set off by a neutral hairline: the status line on index,
   the home link on 404. */
.closing {
  margin-top: var(--space-5);
  padding-top: var(--space-3);
  border-top: var(--hairline) solid var(--line);
}

/* Index closing row: status line and enquiries email, side by side when
   there is room, stacked on narrow screens. */
div.closing {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  align-items: baseline;
  gap: var(--space-2) var(--space-4);
}

div.closing p {
  margin: 0;
}

.contact {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0.75rem;
}

.contact__label {
  color: var(--text-2);
  font-size: 0.75rem;
  font-weight: 500;
  letter-spacing: 0.08em;
  text-transform: uppercase;
}

.contact a {
  color: var(--text);
  font-weight: 500;
  text-decoration-color: var(--signal);
}

.return {
  margin-bottom: 0;
  font-size: var(--step-1);
  line-height: 1.4;
}

a {
  color: var(--link);
  text-decoration-line: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: 0.2em;
  overflow-wrap: anywhere;
}

@media (hover: hover) {
  a {
    transition: text-decoration-thickness var(--ease);
  }

  a:hover {
    text-decoration-thickness: 2px;
  }
}

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


.wordmark .home {
  display: block;
  width: var(--wordmark-w);
}

.wordmark .home svg {
  width: 100%;
}

.home:focus-visible {
  outline-offset: 4px;
}

@media (prefers-reduced-motion: reduce) {
  a {
    transition: none;
  }

  /* Final state at once; the grid stays, static. */
  /* No trace: the wordmark and text show at once. */
  .trace {
    display: none;
  }

  .intro .wordmark-fill,
  .intro > :not(.wordmark) {
    animation: none;
  }
}

@media (forced-colors: active) {
  body::before,
  .trace {
    display: none;
  }

  .boundary,
  .closing {
    border-top-color: CanvasText;
  }

  .wordmark svg {
    color: CanvasText;
  }

  .wordmark .home svg {
    color: LinkText;
  }
}

.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}
