/* ME&QC rebuild mockup — "drawing sheet"
   ---------------------------------------------------------------------------
   Design language is taken from the subject's own materials: engineering
   drawing sheets. A ruled frame with corner ticks, a mono title block above
   every section, numbered plates, tabular spec tables.

   Two modes, one domain (see Rebuild-Presentation-Strategy.md §1):
     default        commercial — daylight workshop, gets the work
     [data-mode=archive]  the record — deep water, single committed look
   The mode flip IS the "hard seam". It is meant to be felt.
*/

/* ---------- tokens: light (bare :root defines the complete palette) ------- */
:root {
  --ground:      #F0F1EF;   /* machined aluminium, cool — deliberately not cream */
  --surface:     #FFFFFF;
  --surface-2:   #E5E8E5;
  --rule:        #C6CBC7;
  --rule-soft:   #DCE0DC;
  --ink:         #13181C;
  --ink-2:       #59636B;   /* steel grey, blue bias */
  --ink-3:       #838D94;
  --accent:      #1C5680;   /* engineer's blue — layout fluid, marking blue */
  --accent-ink:  #FFFFFF;
  --accent-soft: #DCE7F0;
  --flag:        #8A5A11;   /* editorial flag: needs Igor's answer */
  --flag-soft:   #F6EDDC;
  --ok:          #3D6142;   /* settled: Igor has answered and this is the answer */
  --ok-soft:     #EAF1EA;

  --display: "Avenir Next Condensed", "Roboto Condensed", "Arial Narrow", ui-sans-serif, system-ui, sans-serif;
  --body:    Charter, "Bitstream Charter", "Iowan Old Style", "Source Serif 4", Georgia, serif;
  --mono:    ui-monospace, "SF Mono", SFMono-Regular, Menlo, "Cascadia Mono", Consolas, monospace;

  --sheet: 74rem;
  --gut: clamp(1rem, 4vw, 2.75rem);
}

/* ---------- tokens: dark via system preference (no explicit choice) ------- */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --ground:      #0F1317;
    --surface:     #171C22;
    --surface-2:   #1E252C;
    --rule:        #333C45;
    --rule-soft:   #262E36;
    --ink:         #E4E8EB;
    --ink-2:       #9BA6AF;
    --ink-3:       #6F7A83;
    --accent:      #7DB2DC;
    --accent-ink:  #0F1317;
    --accent-soft: #1B2A38;
    --flag:        #D6A354;
    --flag-soft:   #2A2116;
    --ok:          #7FB489;
    --ok-soft:     #16211A;
  }
}

/* ---------- tokens: dark via explicit toggle ------------------------------ */
:root[data-theme="dark"] {
  --ground:      #0F1317;
  --surface:     #171C22;
  --surface-2:   #1E252C;
  --rule:        #333C45;
  --rule-soft:   #262E36;
  --ink:         #E4E8EB;
  --ink-2:       #9BA6AF;
  --ink-3:       #6F7A83;
  --accent:      #7DB2DC;
  --accent-ink:  #0F1317;
  --accent-soft: #1B2A38;
  --flag:        #D6A354;
  --flag-soft:   #2A2116;
  --ok:          #7FB489;
  --ok-soft:     #16211A;
}

/* ---------- base --------------------------------------------------------- */
* { box-sizing: border-box; }

html { -webkit-text-size-adjust: 100%; scroll-behavior: smooth; }
@media (prefers-reduced-motion: reduce) { html { scroll-behavior: auto; } }

body {
  margin: 0;
  background: var(--ground);
  color: var(--ink);
  font: 400 clamp(1rem, 0.96rem + 0.2vw, 1.0625rem)/1.62 var(--body);
  font-synthesis-weight: none;
}

h1, h2, h3, h4 {
  font-family: var(--display);
  font-weight: 600;
  letter-spacing: 0.005em;
  line-height: 1.06;
  text-wrap: balance;
  margin: 0;
}
h1 { font-size: clamp(2.25rem, 1.5rem + 3.2vw, 3.75rem); letter-spacing: -0.01em; }
h2 { font-size: clamp(1.65rem, 1.25rem + 1.6vw, 2.4rem); }
h3 { font-size: clamp(1.2rem, 1.05rem + 0.6vw, 1.45rem); }
h4 { font-size: 1.0625rem; }

p { margin: 0 0 1em; }
p:last-child { margin-bottom: 0; }

a { color: var(--accent); text-underline-offset: 0.18em; text-decoration-thickness: 1px; }
a:hover { text-decoration-thickness: 2px; }
:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; border-radius: 1px; }

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

/* ---------- masthead ----------------------------------------------------- */
.masthead {
  position: sticky; top: 0; z-index: 40;
  background: var(--surface);
  border-bottom: 1px solid var(--rule);
}

/* The contact strip is the single most important fix in the rebuild: the old
   boatworks address lived inside a Flash movie and has been invisible since
   2020. Here it is HTML, on every page, above everything else. */
.contactbar {
  border-bottom: 1px solid var(--rule-soft);
  background: var(--surface-2);
  font: 500 0.75rem/1 var(--mono);
  letter-spacing: 0.04em;
}
.contactbar > div {
  max-width: var(--sheet); margin-inline: auto; padding: 0.55rem var(--gut);
  display: flex; flex-wrap: wrap; gap: 0.5rem 1.5rem; align-items: center;
}
.contactbar a { color: var(--ink-2); text-decoration: none; }
.contactbar a:hover { color: var(--accent); text-decoration: underline; }
.contactbar .spacer { margin-inline-start: auto; }

.masthead > nav {
  max-width: var(--sheet); margin-inline: auto; padding: 0.9rem var(--gut);
  display: flex; align-items: baseline; gap: clamp(1rem, 3vw, 2.5rem); flex-wrap: wrap;
}

.brand { text-decoration: none; color: inherit; display: block; line-height: 1; }
.brand b {
  display: block; font: 600 1.5rem/1 var(--display);
  letter-spacing: 0.01em;
}
.brand span {
  display: block; margin-top: 0.3rem;
  font: 500 0.6875rem/1 var(--mono); letter-spacing: 0.14em;
  text-transform: uppercase; color: var(--ink-3);
}

.nav { display: flex; gap: clamp(0.9rem, 2.2vw, 1.9rem); flex-wrap: wrap; margin-inline-start: auto; }
.nav a {
  font: 500 0.8125rem/1 var(--mono); letter-spacing: 0.1em; text-transform: uppercase;
  color: var(--ink-2); text-decoration: none; padding-bottom: 0.35rem;
  border-bottom: 2px solid transparent;
}
.nav a:hover { color: var(--ink); border-bottom-color: var(--rule); }
.nav a[aria-current="page"] { color: var(--ink); border-bottom-color: var(--accent); }

/* ---------- the sheet ---------------------------------------------------- */
.sheet {
  max-width: var(--sheet); margin: clamp(1.5rem, 4vw, 3.25rem) auto;
  padding-inline: var(--gut);
}

/* corner ticks: the drawing-sheet register marks */
.field {
  position: relative;
  border: 1px solid var(--rule);
  background: var(--surface);
  padding: clamp(1.5rem, 4vw, 3rem);
}
.field::before, .field::after {
  content: ""; position: absolute; width: 10px; height: 10px; pointer-events: none;
  border: 1px solid var(--accent);
}
.field::before { top: -1px; left: -1px; border-right: 0; border-bottom: 0; }
.field::after  { bottom: -1px; right: -1px; border-left: 0; border-top: 0; }

/* ---------- title block -------------------------------------------------- */
/* Mono strip carrying the fields a drawing sheet actually carries. The job
   number is not decoration: this is a job book, and the reference is how the
   entry is cited elsewhere on the site. */
.titleblock {
  display: flex; flex-wrap: wrap; gap: 0 1.25rem;
  font: 500 0.6875rem/1.7 var(--mono);
  letter-spacing: 0.11em; text-transform: uppercase;
  color: var(--ink-3);
  border-bottom: 1px solid var(--rule);
  padding-bottom: 0.6rem; margin-bottom: 1.4rem;
}
.titleblock b { color: var(--accent); font-weight: 600; }
.titleblock .sep { color: var(--rule); }

/* ---------- hero --------------------------------------------------------- */
.hero { display: grid; gap: clamp(1.75rem, 4vw, 3rem); }
@media (min-width: 62rem) {
  .hero { grid-template-columns: minmax(0, 1fr) minmax(0, 0.85fr); align-items: center; }
}
.hero .lede {
  font-size: clamp(1.125rem, 1rem + 0.55vw, 1.375rem);
  line-height: 1.5; color: var(--ink-2); margin-top: 1.1rem; max-width: 34ch;
}
.hero h1 em {
  font-style: normal; color: var(--accent);
}

.eyebrow {
  font: 500 0.6875rem/1 var(--mono); letter-spacing: 0.16em;
  text-transform: uppercase; color: var(--ink-3); margin-bottom: 0.9rem; display: block;
}

/* ---------- buttons ------------------------------------------------------ */
.actions { display: flex; flex-wrap: wrap; gap: 0.75rem; margin-top: 1.75rem; }
.btn {
  font: 500 0.8125rem/1 var(--mono); letter-spacing: 0.08em; text-transform: uppercase;
  padding: 0.85rem 1.35rem; text-decoration: none; border: 1px solid var(--accent);
  background: var(--accent); color: var(--accent-ink);
  transition: background 0.15s, color 0.15s;
}
.btn:hover { background: transparent; color: var(--accent); }
.btn.ghost { background: transparent; color: var(--accent); }
.btn.ghost:hover { background: var(--accent); color: var(--accent-ink); }

/* ---------- sections ----------------------------------------------------- */
.band { margin-top: clamp(2.5rem, 6vw, 4.5rem); }
.band > h2 + .lede,
.band > h2 + p { margin-top: 0.85rem; max-width: 62ch; color: var(--ink-2); }

.prose { max-width: 66ch; }
.prose h3 { margin-top: 2rem; margin-bottom: 0.6rem; }
.prose ul, .prose ol { margin: 0 0 1em; padding-inline-start: 1.2rem; }
.prose li { margin-bottom: 0.4em; }
.prose li::marker { color: var(--ink-3); }

/* ---------- plates ------------------------------------------------------- */
/* Source photographs top out at 800 px, so nothing is stretched full-bleed.
   Plates sit at honest size inside a hairline rule, the way drawings mount. */
.plates { display: grid; gap: 1.5rem; grid-template-columns: repeat(auto-fill, minmax(15rem, 1fr)); margin-top: 1.75rem; }
.plates.wide { grid-template-columns: repeat(auto-fill, minmax(21rem, 1fr)); }

figure.plate { margin: 0; }
figure.plate img {
  width: 100%; border: 1px solid var(--rule); background: var(--surface-2);
  cursor: zoom-in;
}
/* Every cell in a grid is the same box, so a row of plates reads as one
   register rather than a ragged edge.

   The box is SQUARE. Source photographs run from 0.51 (a step ladder shot on
   its end) to 1.49 (a workshop panorama), and a landscape box would slice the
   tall stainless instruments — which are most of the catalogue — down to a
   horizontal band. A square treats both orientations symmetrically and suits
   the sheet grid.

     cover    photographs — fill the box, crop the overflow. Anything markedly
              portrait is focused near the top by the build script, because on
              a splitter or a platform the identifying end is the top one.
     contain  drawings, CAD renders, FEA screenshots — anything where a crop
              removes the thing the plate exists to show. Letterboxed in the
              SAME box, so the grid stays even either way.

   The uncropped original is always one click away in the lightbox. */
.plates figure.plate img {
  aspect-ratio: 1 / 1;
  object-fit: cover;
  object-position: center;
}
.plates figure.plate img.fit {
  object-fit: contain;
  padding: 0.5rem;
}

/* A lead plate stands outside the grid and keeps its natural proportions —
   the hero is the one image that should not be cropped to match anything. */
figure.plate.lead img { aspect-ratio: auto; object-fit: fill; }

figure.plate figcaption {
  font: 400 0.8125rem/1.5 var(--mono); color: var(--ink-2);
  margin-top: 0.6rem; display: flex; gap: 0.6rem;
}
figure.plate figcaption .no {
  color: var(--accent); font-weight: 600; flex: none; letter-spacing: 0.06em;
}

/* ---------- spec tables -------------------------------------------------- */
.tablewrap { overflow-x: auto; margin-top: 1.5rem; border: 1px solid var(--rule); }
table { border-collapse: collapse; width: 100%; font-variant-numeric: tabular-nums; }
th, td {
  text-align: left; padding: 0.7rem 1rem; border-bottom: 1px solid var(--rule-soft);
  font: 400 0.875rem/1.45 var(--mono); vertical-align: top;
}
thead th {
  font-weight: 600; font-size: 0.6875rem; letter-spacing: 0.11em; text-transform: uppercase;
  color: var(--ink-3); background: var(--surface-2); border-bottom: 1px solid var(--rule); white-space: nowrap;
}
tbody th { font-weight: 500; color: var(--ink-2); }
tbody tr:last-child td, tbody tr:last-child th { border-bottom: 0; }
td.num { text-align: right; }

/* ---------- cards -------------------------------------------------------- */
.cards { display: grid; gap: 1px; background: var(--rule); border: 1px solid var(--rule); margin-top: 1.75rem; }
@media (min-width: 46rem) { .cards { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 68rem) { .cards.three { grid-template-columns: repeat(3, 1fr); } }

.card {
  background: var(--surface); padding: 1.4rem; display: flex; flex-direction: column;
  gap: 0.7rem; text-decoration: none; color: inherit;
  transition: background 0.15s;
}
a.card:hover { background: var(--accent-soft); }
.card .ref {
  font: 500 0.6875rem/1 var(--mono); letter-spacing: 0.11em; text-transform: uppercase;
  color: var(--ink-3);
}
a.card:hover .ref { color: var(--accent); }
.card h3 { font-size: 1.25rem; }
.card p { font-size: 0.9375rem; color: var(--ink-2); line-height: 1.5; }
.card img {
  border: 1px solid var(--rule); margin-bottom: 0.2rem;
  width: 100%; aspect-ratio: 3 / 2; object-fit: cover; object-position: center;
}
.card .more {
  margin-top: auto; font: 500 0.75rem/1 var(--mono); letter-spacing: 0.08em;
  text-transform: uppercase; color: var(--accent);
}

/* a card that is deliberately only a card — no URL of its own */
.card.stub { background: var(--surface-2); }
.card.stub .more { color: var(--ink-3); }

/* ---------- stat strip --------------------------------------------------- */
/* The praise on the profile page is carried by verifiable numbers rather than
   adjectives, so the numbers get the display face and the room. */
.stats {
  display: grid; gap: 1px; background: var(--rule);
  border: 1px solid var(--rule); margin-top: 1.75rem;
  grid-template-columns: repeat(auto-fit, minmax(11rem, 1fr));
}
.stats > div { background: var(--surface); padding: 1.3rem 1.2rem; }
.stats b {
  display: block; font: 600 clamp(2rem, 1.4rem + 1.8vw, 2.9rem)/1 var(--display);
  letter-spacing: -0.015em; color: var(--accent); font-variant-numeric: tabular-nums;
}
.stats span {
  display: block; margin-top: 0.55rem;
  font: 400 0.8125rem/1.45 var(--body); color: var(--ink-2);
}
.stats em {
  display: block; margin-top: 0.3rem; font-style: normal;
  font: 500 0.625rem/1.4 var(--mono); letter-spacing: 0.11em;
  text-transform: uppercase; color: var(--ink-3);
}

/* ---------- timeline ---------------------------------------------------- */
/* A dated spine. The years are real and the order carries information, which
   is the only reason this is numbered at all. */
.timeline { margin-top: 1.75rem; border-top: 1px solid var(--rule); }
.timeline > div {
  display: grid; gap: 0.35rem 1.5rem; padding: 1.1rem 0;
  border-bottom: 1px solid var(--rule-soft);
}
@media (min-width: 44rem) {
  .timeline > div { grid-template-columns: 8.5rem minmax(0, 1fr); align-items: baseline; }
}
.timeline dt, .timeline .yr {
  font: 600 0.8125rem/1.5 var(--mono); letter-spacing: 0.06em;
  color: var(--accent); font-variant-numeric: tabular-nums;
}
.timeline .ev { color: var(--ink); }
.timeline .ev b { font-weight: 600; }
.timeline .ev small {
  display: block; margin-top: 0.3rem; font-size: 0.875rem; color: var(--ink-2);
}

/* ---------- profile split ------------------------------------------------ */
.split { display: grid; gap: clamp(1.75rem, 4vw, 3rem); }
@media (min-width: 60rem) {
  .split { grid-template-columns: minmax(0, 1.15fr) minmax(0, 1fr); align-items: start; }
}

/* ---------- notes and flags ---------------------------------------------- */
.note {
  border-inline-start: 3px solid var(--rule); padding: 0.2rem 0 0.2rem 1rem;
  margin: 1.75rem 0; color: var(--ink-2); font-size: 0.9375rem;
}
.note.flag { border-inline-start-color: var(--flag); background: var(--flag-soft); padding: 0.85rem 1rem; }
.note.flag b { color: var(--flag); font-family: var(--mono); font-size: 0.75rem; letter-spacing: 0.1em; text-transform: uppercase; display: block; margin-bottom: 0.3rem; }
.note.decided { border-inline-start-color: var(--ok); background: var(--ok-soft); padding: 0.85rem 1rem; }
.note.decided b { color: var(--ok); font-family: var(--mono); font-size: 0.75rem; letter-spacing: 0.1em; text-transform: uppercase; display: block; margin-bottom: 0.3rem; }

/* ---------- video -------------------------------------------------------- */
video { width: 100%; border: 1px solid var(--rule); background: #000; }
.videonote { font: 400 0.75rem/1.5 var(--mono); color: var(--ink-3); margin-top: 0.55rem; }

/* ---------- footer ------------------------------------------------------- */
.foot {
  border-top: 1px solid var(--rule); margin-top: clamp(3rem, 7vw, 5rem);
  background: var(--surface);
}
.foot > div {
  max-width: var(--sheet); margin-inline: auto; padding: 2.5rem var(--gut);
  display: grid; gap: 2rem;
}
@media (min-width: 48rem) { .foot > div { grid-template-columns: 1.2fr 1fr 1fr; } }
.foot h4 {
  font: 600 0.6875rem/1 var(--mono); letter-spacing: 0.13em; text-transform: uppercase;
  color: var(--ink-3); margin-bottom: 0.9rem;
}
.foot ul { list-style: none; margin: 0; padding: 0; display: grid; gap: 0.45rem; }
.foot a { color: var(--ink-2); text-decoration: none; font-size: 0.9375rem; }
.foot a:hover { color: var(--accent); text-decoration: underline; }
.foot .legal {
  border-top: 1px solid var(--rule-soft); grid-column: 1 / -1; padding-top: 1.4rem;
  font: 400 0.75rem/1.6 var(--mono); color: var(--ink-3);
  display: flex; flex-wrap: wrap; gap: 0.4rem 1.5rem;
}

/* the provenance line — every page says where its copy came from */
.provenance {
  font: 400 0.75rem/1.6 var(--mono); color: var(--ink-3);
  border-top: 1px dashed var(--rule); margin-top: 2.5rem; padding-top: 0.9rem;
}
.provenance b { color: var(--ink-2); font-weight: 500; }

/* ---------- lightbox ----------------------------------------------------- */
.lightbox {
  position: fixed; inset: 0; z-index: 90; display: none;
  background: rgba(10, 14, 18, 0.93); padding: clamp(1rem, 4vw, 3rem);
  align-items: center; justify-content: center; flex-direction: column; gap: 1rem;
}
.lightbox[open], .lightbox.on { display: flex; }
.lightbox img { max-width: 100%; max-height: 82vh; border: 1px solid #3a444d; cursor: zoom-out; }
.lightbox p { font: 400 0.8125rem/1.5 var(--mono); color: #C6CED5; text-align: center; max-width: 60ch; }
.lightbox button {
  position: absolute; top: 1rem; right: 1rem; background: transparent; border: 1px solid #4a555f;
  color: #E4E8EB; font: 500 0.75rem/1 var(--mono); letter-spacing: 0.1em; padding: 0.6rem 0.9rem; cursor: pointer;
}
.lightbox button:hover { border-color: #8FBBDD; color: #8FBBDD; }

/* =========================================================================
   ARCHIVE MODE — the hard seam.
   Declared last so it wins over both theme blocks in all three theme states.
   This section commits to one look on purpose: it is a record, not a
   shopfront, and it should not feel like the pages that take orders.
   ========================================================================= */
:root[data-mode="archive"] {
  --ground:      #0B1016;
  --surface:     #111820;
  --surface-2:   #16202A;
  --rule:        #2A3742;
  --rule-soft:   #1D2831;
  --ink:         #DEE5EA;
  --ink-2:       #9DACB8;
  --ink-3:       #71818E;
  --accent:      #8FBBDD;
  --accent-ink:  #0B1016;
  --accent-soft: #16242F;
  --flag:        #D6A354;
  --flag-soft:   #241C10;
  --ok:          #7FB489;
  --ok-soft:     #131E17;
}

/* Archive prose reads as a book: wider measure, larger serif, real leading. */
[data-mode="archive"] .prose { max-width: 68ch; font-size: 1.0625rem; line-height: 1.72; }
[data-mode="archive"] .prose p + p { text-indent: 0; }
[data-mode="archive"] h1 { font-family: var(--body); font-weight: 600; letter-spacing: -0.015em; }
[data-mode="archive"] .brand span { color: var(--accent); }

/* Russian body text gets its own language and a face that carries Cyrillic. */
[lang="ru"] { font-family: Georgia, "Times New Roman", serif; }

/* Plate captions in the archive carry a date and place, so they run longer. */
[data-mode="archive"] figure.plate figcaption { font-family: var(--body); font-size: 0.875rem; }
[data-mode="archive"] figure.plate figcaption .no { font-family: var(--mono); font-size: 0.75rem; padding-top: 0.15rem; }


/* ===========================================================================
   ME&QC — production layer
   ---------------------------------------------------------------------------
   Appended to the mockup's site.css, which is the design and stays the single
   source of the drawing-sheet language. Nothing here restates a token or a
   component. What is here is only what the public site has and the review
   mockup did not:

     1. the animated section backgrounds
     2. a print sheet
     3. the small production affordances — skip link, focus, selection

   The rule the backgrounds are held to comes from 3d/USING-THESE.md and is not
   negotiable: it sits behind and stays there, it never carries a dimension,
   and `prefers-reduced-motion: reduce` stops it dead.
   =========================================================================== */

/* ---------- 1. section backgrounds --------------------------------------- */

/* One figure per section, not one per page, and each one much larger than the
   section that holds it. The drawing is therefore always a crop — you see a
   hub, a blade root, the middle of a scissor — which is what a drawing sheet
   at full size actually looks like when you stand close to it. */

/* A figure field is wider than the sheet, so the page would otherwise gain a
   horizontal scrollbar. `clip` rather than `hidden`: hidden makes a scroll
   container and would break the sticky masthead. */
body { overflow-x: clip; }

/* The symbol library. One <symbol> per distinct drawing per page; the sections
   reference it with <use>. It must be in the flow but occupy nothing, and it
   must not be `display:none`, which stops SMIL in some engines. */
.figdefs { position: absolute; width: 0; height: 0; overflow: hidden; }

main.sheet section { position: relative; isolation: isolate; }

/* The drawing sits on the card, under the words. Behind the card it would show
   only in the margins, which on a laptop is a hundred pixels of nothing. */
main.sheet section > *:not(.figfield) { position: relative; z-index: 1; }

.figfield {
  position: absolute;
  inset: 0;
  overflow: hidden;
  pointer-events: none;
  z-index: 0;
  /* Fades at the edges rather than ending. A hard edge under a drawing reads
     as a second border, and this design already has enough borders. */
  -webkit-mask-image: radial-gradient(115% 105% at 62% 44%, #000 34%, transparent 100%);
  mask-image: radial-gradient(115% 105% at 62% 44%, #000 34%, transparent 100%);
}

/* Very large, and sized against the *section* so a short band and a long one
   both get a whole-looking drawing rather than a random horizontal slice.
   The clamp stops a 2,000 px section from getting a 4,000 px propeller. */
.figfield .fig {
  position: absolute;
  color: var(--accent);
  height: clamp(30rem, 190%, 78rem);
  width: auto;
  top: 50%;
  left: 58%;
  translate: -50% -50%;
}
.figfield svg { display: block; width: 100%; height: 100%; overflow: visible; }

/* Contrast. Low enough that a spec table always wins, and lower again in the
   archive's dark register, where a light line on a dark ground reads hotter
   than the same line the other way round. */
.figfield .fig { opacity: 0.115; }
@media (prefers-color-scheme: dark) { :root:not([data-theme="light"]) .figfield .fig { opacity: 0.14; } }
:root[data-theme="dark"] .figfield .fig { opacity: 0.14; }
:root[data-mode="archive"] .figfield .fig { opacity: 0.08; }

/* -- per figure: aspect ratio, and how much bigger than its section --------- */

/* Two families, sized differently on purpose.

   The propeller is radially symmetric about the middle of its own viewBox, so
   a hard crop of it still reads as a propeller — a hub and three blade roots
   running off the edges. It can go very large, and Igor asked for exactly
   that.

   Everything else is an object with a top and a bottom. Crop a splitter to a
   third of itself and it stops being a splitter and becomes a set of lines,
   so those sit at about half again the height of their section: larger than
   the box, edges running out, still legible as the thing they are. */
.fig-propeller         { aspect-ratio: 1; height: clamp(42rem, 310%, 124rem); }
.fig-propeller-hub     { aspect-ratio: 1; height: clamp(34rem, 250%, 98rem); }
.fig-propeller-oblique { aspect-ratio: 1; height: clamp(30rem, 205%, 82rem); }

.fig-ttprs            { aspect-ratio: 700 / 1000;  height: clamp(24rem, 158%, 62rem); }
.fig-splitter         { aspect-ratio: 1000 / 888;  height: clamp(22rem, 148%, 56rem); }
.fig-splitter-oblique { aspect-ratio: 1000 / 760;  height: clamp(22rem, 152%, 58rem); }
.fig-scissor          { aspect-ratio: 669 / 1000;  height: clamp(24rem, 158%, 62rem); }
.fig-scissor-oblique  { aspect-ratio: 1000 / 780;  height: clamp(22rem, 152%, 58rem); }
.fig-gold-jig         { aspect-ratio: 932 / 1000;  height: clamp(22rem, 152%, 58rem); }
.fig-jig-oblique      { aspect-ratio: 1000 / 800;  height: clamp(22rem, 152%, 58rem); }
.fig-spinnaker        { aspect-ratio: 1000 / 633;  height: clamp(20rem, 142%, 52rem); }
.fig-sheet            { aspect-ratio: 1000 / 700;  height: clamp(20rem, 138%, 54rem); }
.fig-toolpath         { aspect-ratio: 1000 / 620;  height: clamp(20rem, 142%, 54rem); }

/* -- narrow screens -------------------------------------------------------- */

/* A phone inverts the problem. Sections there are narrow and tall, so sizing a
   figure against the section's HEIGHT gives a drawing three screens tall and
   two fingers wide — a texture, not a drawing. Below this width every figure
   is sized against the section's WIDTH instead and centred, so the whole
   object is in frame, and the contrast comes down because on a small screen
   the copy and the drawing are never far apart. */
@media (max-width: 46rem) {
  .figfield .fig {
    height: auto;
    width: 132%;
    left: 50%;
    top: 50%;
    translate: -50% -50%;
    opacity: 0.075;
  }
  .fig-propeller     { width: 205%; }
  .fig-propeller-hub { width: 172%; }
  .fig-propeller-oblique { width: 150%; }
  .fig-sheet, .fig-toolpath { width: 118%; }
  :root[data-mode="archive"] .figfield .fig { opacity: 0.062; }

  /* The radial mask is tuned for a landscape box; on a portrait one it eats
     the middle of the drawing. Straight vertical falloff instead. */
  .figfield {
    -webkit-mask-image: linear-gradient(to bottom, transparent, #000 22%, #000 78%, transparent);
    mask-image: linear-gradient(to bottom, transparent, #000 22%, #000 78%, transparent);
  }
}

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

/* Everything moves, and everything moves slowly. The slowest thing here takes
   six minutes to come back to where it started; the fastest full cycle is
   fourteen seconds and belongs to a crank that really does turn that fast.
   The figures are large enough now that a small angular rate is a long way at
   the edge of the drawing, so the numbers went up, not down.

   The base is a drift: the sheet itself easing under the viewport, a couple of
   per cent either way. It is the honest motion for a figure that cannot turn
   — an oblique projection is not a 2D rotation, so spinning one would be a
   drawing tumbling rather than a propeller turning. */
.figfield .fig svg {
  animation: meqc-drift 150s ease-in-out infinite alternate;
  will-change: transform;
}
@keyframes meqc-drift {
  from { transform: translate3d(-2.4%, 1.6%, 0) scale(1.00); }
  to   { transform: translate3d( 2.4%, -1.6%, 0) scale(1.06); }
}

.fig-ttprs svg            { animation-duration: 190s; }
.fig-spinnaker svg        { animation-duration: 170s; animation-direction: alternate-reverse; }
.fig-splitter-oblique svg { animation-duration: 210s; animation-direction: alternate-reverse; }
.fig-scissor-oblique svg  { animation-duration: 230s; }
.fig-jig-oblique svg      { animation-duration: 200s; animation-direction: alternate-reverse; }
.fig-propeller-oblique svg{ animation-duration: 260s; }
.fig-sheet svg            { animation-duration: 240s; }
.fig-toolpath svg         { animation-duration: 220s; animation-direction: alternate-reverse; }

/* The propeller. One rotation, forever, about its own axis — the bake squares
   the viewBox about the projected model origin, so 50%/50% is the axis and
   this is a true rotation rather than a wobble. 300 seconds is 1.2 degrees a
   second: at this size a blade tip still travels, and you notice it only if
   you stop and look, which is the whole brief. */
.fig-propeller svg,
.fig-propeller-hub svg {
  transform-origin: 50% 50%;
  animation: meqc-spin 300s linear infinite;
}
.fig-propeller-hub svg { animation-duration: 380s; animation-direction: reverse; }
@keyframes meqc-spin { to { transform: rotate(360deg); } }

/* The drawing sheet ruling itself in — once, on arrival — and then drifting
   with everything else. Per-path length and delay are inline custom properties
   on the SVG, because only the file that drew the path knows how long it is. */
.fig-sheet .drawn path {
  stroke-dasharray: var(--len);
  stroke-dashoffset: var(--len);
  animation: meqc-draw 2.4s ease-out var(--wait, 0s) forwards;
}
@keyframes meqc-draw { to { stroke-dashoffset: 0; } }

/* The lathe pass. The tool traverses right to left and the cut profile is
   revealed behind it, so the two motions are one motion. */
.fig-toolpath .cut  { animation: meqc-cut 26s linear infinite; }
.fig-toolpath .tool { animation: meqc-tool 26s linear infinite; }
@keyframes meqc-cut  { 0%, 4% { stroke-dashoffset: 100; } 82%, 100% { stroke-dashoffset: 0; } }
@keyframes meqc-tool {
  0%, 4%    { transform: translate(880px, 0); }
  82%, 100% { transform: translate(150px, 0); }
}

/* The sample stream. Falls from the hopper mouth, reaches the riffle bank at
   just past half the cycle, and leaves to whichever side its lane is on. */
.fig-splitter .stream circle {
  animation: meqc-fall 7s linear infinite;
  animation-delay: var(--t, 0s);
  opacity: 0;
}
@keyframes meqc-fall {
  0%        { transform: translateY(0);     opacity: 0; }
  7%        { opacity: 1; }
  52%       { transform: translateY(330px); opacity: 1; }
  100%      { transform: translateY(560px) translateX(var(--s)); opacity: 0; }
}

/* Off screen, and under reduced motion, everything stops. site.js sets this
   class; it covers the CSS half, and pauseAnimations() covers the SMIL half. */
.figfield .fig.paused, .figfield .fig.paused * { animation-play-state: paused !important; }

/* The non-negotiable. A slow rotation behind body copy is exactly what this
   media query exists for. SMIL does not observe it, so site.js pauses those
   documents directly — this half covers everything driven by CSS. */
@media (prefers-reduced-motion: reduce) {
  .figfield .fig svg,
  .figfield .fig .cut,
  .figfield .fig .tool { animation: none !important; }
  .fig-sheet .drawn path { stroke-dashoffset: 0; animation: none !important; }
  .fig-splitter .stream { display: none; }
}

/* ---------- 1b. interactive model viewers --------------------------------- */

/* The demonstration blocks on the product and job pages. Dark, because the
   viewer inside them is dark and a light frame round a dark stage reads as a
   mistake. Press-to-run: nothing is fetched until the button is used. */
figure.demo {
  margin: 1.75rem 0 0;
  border: 1px solid var(--rule);
  background: #15181c;
}

.demo-stage {
  position: relative;
  /* 4:3 rather than 16:9. The viewer gives 300 px of its width to the control
     panel, so a wide stage is a short one, and these are tall objects — a
     scissor at full lift, a three-tiered splitter. */
  aspect-ratio: 4 / 3;
  display: grid;
  place-content: center;
  gap: .9rem;
  justify-items: center;
  background:
    radial-gradient(120% 100% at 50% 0%, #1e242b 0%, #15181c 62%);
}
/* A phone is portrait and a 16:10 stage on one is a letterbox slit. The
   viewer stacks its control panel under the stage below 820 px, so the frame
   needs to be taller there, not wider. */
@media (max-width: 51rem) { .demo-stage { aspect-ratio: 3 / 4; } }

.demo-stage iframe {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  border: 0; display: block;
}
figure.demo.running .demo-stage { background: #15181c; }

.demo-run {
  font-family: var(--mono);
  font-size: .8125rem;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: #d7dde4;
  background: #232931;
  border: 1px solid #2c333c;
  border-radius: 2px;
  padding: .75rem 1.4rem;
  cursor: pointer;
}
.demo-run:hover { background: #2c333c; border-color: #3c454f; }
.demo-run:focus-visible { outline: 2px solid #7fb2e5; outline-offset: 3px; }

.demo-hint {
  font-family: var(--mono);
  font-size: .6875rem;
  letter-spacing: .09em;
  text-transform: uppercase;
  color: #8a949f;
}

figure.demo figcaption {
  padding: .9rem 1.1rem 1rem;
  border-top: 1px solid #2c333c;
  color: #a8b2bc;
  font-size: .9375rem;
  line-height: 1.55;
}
figure.demo figcaption b { color: #e2e8ee; }
/* Only the title is a heading. The notes use <b> inline — "SF 600 is a model
   designation" — and a blanket rule turned every one of those into a second
   heading mid-sentence. */
figure.demo figcaption b.demo-title {
  display: block; margin-bottom: .25rem;
  font-family: var(--display); font-size: 1.0625rem; letter-spacing: .01em;
}
figure.demo figcaption a { color: #7fb2e5; white-space: nowrap; }

/* The standing caveat. It is the same sentence the viewer itself carries, and
   it is not small print — 3d/USING-THESE.md is explicit that a model looks
   authoritative whether or not it is. */
.demo-caveat {
  margin-top: 1.4rem;
  padding: .85rem 1.1rem;
  border-left: 3px solid var(--flag);
  background: var(--flag-soft);
  color: var(--ink-2);
  font-size: .9375rem;
  line-height: 1.55;
  max-width: 62ch;
}
.demo-caveat b { color: var(--ink); }

/* ---------- 2. print ------------------------------------------------------ */

@media print {
  .figfield, .masthead .contactbar, .lightbox, .actions,
  figure.demo .demo-stage { display: none; }
  .masthead { position: static; border-bottom: 1px solid #000; }
  :root { --ground: #fff; --surface: #fff; --ink: #000; --ink-2: #333; --accent: #000; }
  main.sheet { max-width: none; }
  a { text-decoration: none; color: #000; }
  /* A printed page loses every href, so put the important ones back. */
  .prose a[href^="http"]::after,
  .prose a[href^="mailto"]::after { content: " (" attr(href) ")"; font-size: .8em; }
  figure.plate, section { break-inside: avoid; }
}

/* ---------- 3. production affordances ------------------------------------ */

.skip {
  position: absolute; left: -9999px; top: 0; z-index: 100;
  background: var(--accent); color: var(--accent-ink);
  padding: .6rem 1rem; font-family: var(--mono); font-size: .8125rem;
}
.skip:focus { left: 0; }

::selection { background: var(--accent-soft); color: var(--ink); }
