/* ============================================================================
   ASTRAMIR — BACKGROUND FIELDS
   Requires astramir.tokens.css. Retheme automatically with [data-mode].

   Every field here is a real optical phenomenon drawn from the bench, not a
   texture. Nothing is a photograph, a mesh gradient, a noise overlay or a
   particle field: this brand's ground is an instrument, and an instrument does
   not have a mood layer.

   THE CONTRAST BUDGET, which is the rule that makes these safe to hand out.

   Astramir sells to entities bound by WCAG. A background that lightens the
   ground under body copy is a contrast defect, not a style choice, so every
   field is capped rather than eyeballed:

     · pattern ink never exceeds  8%  of --color-ink on Operate
     · pattern ink never exceeds  6%  of --color-ink on Publish
     · the aperture glow never exceeds 10% of --p-aperture
     · worst case, --color-ink-2 body copy stays above 7:1 on any pixel of any
       field, against a 4.5:1 legal floor

   Consequence: you may set body copy directly on any field in this file and it
   will still pass. That is the whole point of capping them. If a field ever
   looks too faint on a projector, raise the ROOM's brightness, not the alpha.

   USE

     <section class="bg bg-spectrum"> … </section>

   Fields are listed here in implementation order. The canonical order the
   company uses, quietest first, lives in this folder's index.html, which is
   the document people actually read; numbering them in both places is how the
   two drift apart.

   `.bg` establishes the ground and the stacking context. The modifier draws the
   field. One modifier per element: fields are never stacked, because two
   superimposed patterns stop reading as an instrument and start reading as a
   texture pack.
   ========================================================================= */

.bg {
  position: relative;
  background-color: var(--color-ground);
  isolation: isolate;
}

/* Pattern inks, capped. Do not raise these numbers in a consuming project;
   raise them here, once, after re-measuring. */
.bg {
  --bg-line:    color-mix(in oklab, var(--color-ink) 8%, transparent);
  --bg-line-2:  color-mix(in oklab, var(--color-ink) 5%, transparent);
  --bg-line-3:  color-mix(in oklab, var(--color-ink) 3%, transparent);
  --bg-glow:    color-mix(in oklab, var(--p-aperture) 10%, transparent);
}
[data-mode="publish"] .bg {
  --bg-line:    color-mix(in oklab, var(--color-ink) 6%, transparent);
  --bg-line-2:  color-mix(in oklab, var(--color-ink) 4%, transparent);
  --bg-line-3:  color-mix(in oklab, var(--color-ink) 2.5%, transparent);
  --bg-glow:    color-mix(in oklab, var(--p-azure) 7%, transparent);
}

/* Mask stops. NOT colours.
   A mask-image reads the ALPHA channel only; the compositor discards the rest.
   `#000` here means "opaque, keep this pixel" and carries no colour decision,
   which is why these are not palette tokens: a palette token would imply a
   choice that does not exist, and one carrying its own alpha would silently
   punch holes in every masked field.
   They are named and hoisted so the two literals live in one reviewable place
   instead of at four use sites, and so nobody later "fixes" them into tokens. */
.bg {
  --mask-opaque: #000;
  --mask-half:   rgb(0 0 0 / 0.35);
}

/* ---------------------------------------------------------------------------
   VOID — the default, and the most-used field in the system.
   Nothing. Flat ground. Listed as a named field on purpose: without it, every
   surface acquires a texture because a texture was available.
   ------------------------------------------------------------------------ */
.bg-void { background-image: none; }

/* ---------------------------------------------------------------------------
   GRATING — the workhorse.
   Fine parallel rules with a density falloff, the diffraction grating from the
   bench at page scale. Safe under dense content; use at any size.
   ------------------------------------------------------------------------ */
.bg-grating::before {
  content: ""; position: absolute; inset: 0; z-index: -1; pointer-events: none;
  background-image: repeating-linear-gradient(90deg,
    var(--bg-line) 0 1px, transparent 1px 14px);
  -webkit-mask-image: linear-gradient(105deg, var(--mask-opaque) 0%, var(--mask-half) 55%, transparent 100%);
          mask-image: linear-gradient(105deg, var(--mask-opaque) 0%, var(--mask-half) 55%, transparent 100%);
}

/* ---------------------------------------------------------------------------
   SPECTRUM — the signature field.
   An absorption spectrum: fine lines at irregular spacing. Three superimposed
   series at coprime periods, which is how a real spectrum arises — several
   elements' line series overlaid — so the irregularity is physical rather than
   random. Reserve for title cards, covers and openings.
   ------------------------------------------------------------------------ */
.bg-spectrum::before {
  content: ""; position: absolute; inset: 0; z-index: -1; pointer-events: none;
  background-image:
    repeating-linear-gradient(90deg, var(--bg-line)   0 1px, transparent 1px 37px),
    repeating-linear-gradient(90deg, var(--bg-line-2) 0 1px, transparent 1px 61px),
    repeating-linear-gradient(90deg, var(--bg-line-3) 0 2px, transparent 2px 143px),
    repeating-linear-gradient(90deg, var(--bg-line-3) 0 1px, transparent 1px 233px);
  -webkit-mask-image: linear-gradient(90deg, transparent 0%, var(--mask-opaque) 18%, var(--mask-opaque) 82%, transparent 100%);
          mask-image: linear-gradient(90deg, transparent 0%, var(--mask-opaque) 18%, var(--mask-opaque) 82%, transparent 100%);
}

/* ---------------------------------------------------------------------------
   FRINGE — interference.
   Concentric fringes from an origin off the canvas. Quiet, directional, good
   for section breaks and anything that should feel like a transition.
   ------------------------------------------------------------------------ */
.bg-fringe::before {
  content: ""; position: absolute; inset: 0; z-index: -1; pointer-events: none;
  background-image: repeating-radial-gradient(circle at 8% 118%,
    var(--bg-line-2) 0 1px, transparent 1px 44px);
  -webkit-mask-image: radial-gradient(circle at 8% 118%, var(--mask-opaque) 0%, transparent 92%);
          mask-image: radial-gradient(circle at 8% 118%, var(--mask-opaque) 0%, transparent 92%);
}

/* ---------------------------------------------------------------------------
   APERTURE — one resolved point of light.
   A single soft field, off-centre. The quietest option that is not void.
   Title cards, closing slides, empty states.
   ------------------------------------------------------------------------ */
.bg-aperture {
  background-image: radial-gradient(ellipse 55% 65% at 74% 26%,
    var(--bg-glow) 0%, transparent 68%);
}

/* ---------------------------------------------------------------------------
   AIRY — resolving power, drawn.

   The diffraction pattern of a point source through a circular aperture: a
   bright central disc ringed by concentric fringes. This is the physical
   definition of how well an instrument resolves one point of light, which is
   the closest thing this brand has to a thesis in a single image.

   The ring RADII are physically correct. Airy zeros fall at 1.220, 2.233,
   3.238 and 4.241 (in units of lambda/D), so with the first zero placed at
   8.6% the rest land at 15.7%, 22.8% and 29.9% by ratio, and the bright rings
   peak at the midpoints between them.

   The ring INTENSITIES are exaggerated. A true Airy disc holds about 84% of
   the energy, leaving successive rings at roughly 1.7%, 0.4% and 0.2% — which
   at our alpha cap would render as nothing at all. They are lifted here so the
   structure reads, and this is the one place the system knowingly departs from
   the physics. It is a background, not a measurement.

   Use where radar would have been used, and never use radar: a sweeping scope
   reads as scanning for targets, which is the opposite of what this company
   does and a gift to anyone arguing against it in a public meeting.
   ------------------------------------------------------------------------ */
.bg-airy {
  background-image: radial-gradient(circle at 70% 32%,
    var(--bg-glow)   0%,
    var(--bg-glow)   5.5%,
    var(--bg-line-2) 7.2%,
    transparent      8.6%,    /* first zero  · 1.220 */
    var(--bg-line-2) 12.2%,   /* first ring  · midpoint */
    transparent      15.7%,   /* second zero · 2.233 */
    var(--bg-line-3) 19.3%,   /* second ring */
    transparent      22.8%,   /* third zero  · 3.238 */
    var(--bg-line-3) 26.4%,   /* third ring */
    transparent      29.9%,   /* fourth zero · 4.241 */
    transparent      100%);
}

/* ---------------------------------------------------------------------------
   SHEET — the Publish counterpart.
   A plotting grid. Pair with `.bg-registration` for corner marks on covers and
   printed documents. This is the field for a council briefing.
   ------------------------------------------------------------------------ */
.bg-sheet {
  background-image:
    repeating-linear-gradient(90deg, var(--bg-line-3) 0 1px, transparent 1px 32px),
    repeating-linear-gradient(0deg,  var(--bg-line-3) 0 1px, transparent 1px 32px);
}

/* Corner registration marks. Explicit children rather than pseudo-elements,
   because four corners need four boxes and a container only has two. */
.bg-registration { position: absolute; inset: 20px; pointer-events: none; }
.bg-registration i {
  position: absolute; inline-size: 14px; block-size: 14px;
  border-color: var(--bg-line); border-style: solid; border-width: 0;
}
.bg-registration i:nth-child(1) { inset-block-start: 0; inset-inline-start: 0;  border-block-start-width: 1px; border-inline-start-width: 1px; }
.bg-registration i:nth-child(2) { inset-block-start: 0; inset-inline-end: 0;    border-block-start-width: 1px; border-inline-end-width: 1px; }
.bg-registration i:nth-child(3) { inset-block-end: 0;  inset-inline-start: 0;   border-block-end-width: 1px;  border-inline-start-width: 1px; }
.bg-registration i:nth-child(4) { inset-block-end: 0;  inset-inline-end: 0;     border-block-end-width: 1px;  border-inline-end-width: 1px; }

/* ---------------------------------------------------------------------------
   CHANNELS  and  FAN — the two figurative fields.
   These carry drawn geometry rather than a repeat, so they ship as SVG rather
   than as a gradient. Their strokes read the same tokens, so they retheme with
   everything else. Markup for both is in this folder's index.html; copy the
   <svg> in as the first child of a `.bg` element.
   ------------------------------------------------------------------------ */
.bg-figure {
  position: absolute; inset: 0; inline-size: 100%; block-size: 100%;
  z-index: -1; pointer-events: none;
}
.bg-figure [data-trace] { stroke: var(--bg-line);   fill: none; stroke-width: 1.5; }
.bg-figure [data-axis]  { stroke: var(--bg-line-3); fill: none; stroke-width: 1; }
.bg-figure [data-beam]  { stroke: var(--bg-line-2); fill: none; stroke-width: 2; }
.bg-figure [data-pulse] { stroke: var(--bg-line);   fill: none; stroke-width: 2.5; stroke-linejoin: round; stroke-linecap: round; }
.bg-figure [data-via]   { stroke: var(--bg-line-2); fill: none; stroke-width: 1.5; }
.bg-figure [data-pad]   { fill: var(--bg-line-3); }
.bg-figure [data-contour]       { stroke: var(--bg-line-2); fill: none; stroke-width: 1.25; }
.bg-figure [data-contour-index] { stroke: var(--bg-line);   fill: none; stroke-width: 2; }
.bg-figure [data-street]        { stroke: var(--bg-line-2); fill: none; stroke-width: 1; }
.bg-figure [data-avenue]        { stroke: var(--bg-line);   fill: none; stroke-width: 1.5; }
.bg-figure [data-arterial]      { stroke: var(--bg-line);   fill: none; stroke-width: 2.5; }
.bg-figure [data-taxiway]       { stroke: var(--bg-line-3); fill: none; stroke-width: 1.5; }
.bg-figure [data-apron]         { stroke: var(--bg-line-3); fill: none; stroke-width: 1; }
.bg-figure [data-runway]        { stroke: var(--bg-line);   fill: none; stroke-width: 2; }
.bg-figure [data-centreline]    { stroke: var(--bg-line-2); fill: none; stroke-width: 1.5; }
.bg-figure [data-threshold]     { stroke: var(--bg-line-2); fill: none; stroke-width: 2; }
.bg-figure [data-hold]          { stroke: var(--bg-line);   fill: none; stroke-width: 2; }
.bg-figure [data-route]         { stroke: var(--bg-line);   fill: none; stroke-width: 3.5; stroke-linejoin: round; stroke-linecap: round; }
.bg-figure [data-station]       { stroke: var(--bg-line-2); fill: none; stroke-width: 2; }
/* The ring knocks out to the ground so routes read as passing under it, the
   way an interchange marker works on a real network diagram. */
.bg-figure [data-interchange] circle { stroke: var(--bg-line); fill: var(--color-ground); stroke-width: 3; }

/* ---------------------------------------------------------------------------
   PULSE — a life being monitored.

   A real EKG beat: flat baseline, P wave, the QRS complex (small Q dip, tall R
   spike, S undershoot), then the rounded T wave. Drawn once and repeated, so
   every beat is identical rather than an approximated squiggle.

   It belongs to this system because a continuous measured trace on a time axis
   is already the grammar — CHANNELS carries four of them. What PULSE adds is
   whose signal it is. Reserve it for the surfaces that are about people rather
   than data: EMS and first responders, the human consequence of a decision.

   Use it sparingly. A heartbeat behind a claim about response times reads as
   opportunistic, and the same field behind a section about care reads as true.
   ------------------------------------------------------------------------ */

/* ---------------------------------------------------------------------------
   SUBSTRATE — the silicon the Brain runs on.

   Die geometry: orthogonal buses, traces turning at 45 degrees the way real
   routing does, vias at the junctions, pad arrays at the edge.

   A NOTE ON THE IMAGERY RULE. This system bans "circuit-board motifs" as one
   of the decorative shorthands for technology, alongside glowing brains and
   neural-network clouds. That ban stands and this is the stated exception, so
   the difference has to be held:

     · Banned  — the stock PCB: green board, glowing traces, lens flare,
                 diagonal "energy" lines, used to mean "this is advanced".
     · Here    — photomask linework at background alpha. No glow, no green, no
                 depth, no motion. It is the same hairline vocabulary as every
                 other field, drawing a real object.

   Earned rather than assumed: compute is genuinely part of this product's
   story, and NVIDIA is a named engineering partner. Use it on infrastructure,
   partnership and performance surfaces. Anywhere else it is decoration, and
   the ban applies again.
   ------------------------------------------------------------------------ */



/* ---------------------------------------------------------------------------
   TERRAIN — the ground an incident happens on.

   Real contours. A height field is sampled on a 132x76 grid, contours are
   extracted by marching squares with linear interpolation, and each level is
   emitted as joined polylines. Index contours run heavier every fifth line,
   the convention a survey sheet has used for a century.

   PLAN VIEW, deliberately. The first pass offset each level vertically to
   imply a stacked elevation model; nested rings slid apart into ribbons and
   the result read as a flow field rather than terrain. Making that work needs
   an oblique shear, which means faking perspective, and this system bans faked
   physicality outright. Plan view is the honest answer and the more legible
   one: nested contours with heavier index lines ARE how a map reads elevation.

   Geospatial is one of the four modalities, and terrain is the modality that
   decides how a flood, a fire or a search actually unfolds. Use it where
   ground truth is the subject: emergency management, mapping, coverage,
   anything about the physical world a decision happens in.

   Regenerate with scripts/terrain.mjs rather than hand-editing the path data.
   ------------------------------------------------------------------------ */

/* ---------------------------------------------------------------------------
   CITY — a street network with a real hierarchy.

   Three weights, the way a base map draws them: local streets fine, avenues
   medium, arterials heavy. Flat hairlines, no perspective.

   The detail that keeps it from reading as graph paper is the SEAM. Most
   American cities carry two grids meeting at an angle, where an older town
   plat met a later survey. This frame is two grids at 17 degrees with an
   arterial running the join, a diagonal boulevard, a belt road, and a park the
   streets route around.

   Permitted imagery by the brand's own rule: the city as civic fabric, the
   infrastructure a government maintains for people. It is the opposite of the
   surveillance frame — a street map shows what a municipality is responsible
   FOR, never who is on it.

   Use on municipal, transit and jurisdiction surfaces. Regenerate with
   scripts/city.mjs rather than hand-editing path data.
   ------------------------------------------------------------------------ */

/* ---------------------------------------------------------------------------
   AIRPORT — the movement area, drawn the way a chart draws it.

   Read from the FAA airport diagram: the plan of runways, taxiways and aprons
   that governs everything moving on the ground. Each runway carries what makes
   one legible from above — an outline, a dashed centreline, threshold bars at
   both ends. Hold-short bars sit where a taxiway meets a runway, which is the
   detail separating an airport diagram from three rectangles.

   Use on aviation surfaces. Regenerate with scripts/airport.mjs.
   ------------------------------------------------------------------------ */

/* ---------------------------------------------------------------------------
   TRANSIT — a network diagram in Beck's grammar.

   Harry Beck's 1933 Underground map set the constraint every metro diagram has
   followed since: routes run at 0, 45 or 90 degrees only, spacing is uniform,
   and geography gives way to legible topology. The generator enforces the
   constraint rather than trusting it, and interchange rings are solved from
   real segment intersections rather than placed by eye.

   Drawn monochrome. Colour is a metro map's other signature, but the band is
   rationed here and a five-colour field would spend it. When this geometry is
   used as a DIAGRAM rather than a background, the band's five stops may colour
   the routes — that is the chart-series form the band rules already permit.

   Use on transit surfaces. Regenerate with scripts/transit.mjs.
   ------------------------------------------------------------------------ */

/* The one field permitted to carry the dispersion band at strength, because it
   IS the dispersion. Once per deck, once per page, never behind body copy, and
   never on a surface that also carries a chart — the chart needs the band for
   its series and the two readings would collide. */
.bg-fan [data-band-1] { stroke: color-mix(in oklab, var(--p-violet)   38%, transparent); }
.bg-fan [data-band-2] { stroke: color-mix(in oklab, var(--p-indigo)   38%, transparent); }
.bg-fan [data-band-3] { stroke: color-mix(in oklab, var(--p-azure)    38%, transparent); }
.bg-fan [data-band-4] { stroke: color-mix(in oklab, var(--p-cyan)     38%, transparent); }

/* ---------------------------------------------------------------------------
   CONTENT SAFE AREA
   Backgrounds are laid out for content, not the other way round. Every field
   above is masked or positioned so its densest region sits away from this box.
   ------------------------------------------------------------------------ */
.bg-safe {
  position: relative; z-index: 1;
  padding: clamp(2rem, 6vw, 5rem);
  max-inline-size: var(--measure);
}
.bg-safe--wide { max-inline-size: none; }

/* ---------------------------------------------------------------------------
   MOTION
   A background never animates behind body copy: it is a readability problem
   before it is a WCAG one. The single exception is SWEEP on a title card that
   carries no paragraph text, and it obeys the same in-page motion control as
   everything else, because WCAG 2.2.2 does not exempt decoration.
   ------------------------------------------------------------------------ */
.bg-sweep [data-sweepline] {
  animation: bg-sweep var(--dur-sweep) var(--ease-sweep) infinite;
}
@keyframes bg-sweep { from { transform: translateX(-4%); } to { transform: translateX(104%); } }

@media (prefers-reduced-motion: reduce) {
  :root:not([data-motion="full"]) .bg-sweep [data-sweepline] { animation: none; transform: translateX(50%); }
}
:root[data-motion="reduced"] .bg-sweep [data-sweepline] { animation: none; transform: translateX(50%); }

/* ---------------------------------------------------------------------------
   FORCED COLORS
   Gradients are dropped, so every field above resolves to flat ground. That is
   the correct outcome: these are decorative, they carry no information, and
   nothing is lost. Do not attempt to reinstate them with system colours.
   ------------------------------------------------------------------------ */
@media (forced-colors: active) {
  .bg, .bg-figure { background-image: none; }
  .bg-figure { display: none; }
}

/* ---------------------------------------------------------------------------
   PRINT
   Operate is never printed. Fields drop out entirely: at 300dpi a 6% hairline
   either disappears or moirés against the halftone screen, and neither is worth
   the ink. A printed cover uses SHEET plus registration marks or nothing.
   ------------------------------------------------------------------------ */
@media print {
  .bg { background-color: var(--p-sheet-000) !important; background-image: none !important; }
  .bg-figure { display: none; }
  /* --p-sheet-200 rather than the screen ink: a 6% hairline drops out of a
     halftone screen entirely, and the documented sheet rule reproduces. */
  .bg-sheet { background-image:
    repeating-linear-gradient(90deg, var(--p-sheet-200) 0 1px, transparent 1px 32px),
    repeating-linear-gradient(0deg,  var(--p-sheet-200) 0 1px, transparent 1px 32px) !important; }
}
