/* hud.css: the technical-drawing layer.
 *
 * Five pieces, each usable on its own page:
 *   .hud + .hudnote   a thing, with square anchors and leader lines to it
 *   canvas.plate      a generated lattice, drawn from a real count
 *   .winpanel         a titled chrome window, for asides that are not prose
 *   .plateno          plate furniture: number, rule, caption, folio
 *   .clock            London time, live
 *
 * The register is a drawing, not a poster: hairlines, mono caps at 9-10px,
 * one display size doing all the shouting, and one hot mark per screen plus
 * the label that names it. Everything here is drawn by the browser; there
 * are no images.
 */

:root {
  /* --hot is a mark, not a word. At 4.03:1 on cream it clears the 3:1 that
     a drawn marker needs and misses the 4.5:1 that 9.5px type needs, so it
     gets the same split the chrome accent already has: one value for the
     thing drawn, a darker one for anything set in it. Worst light surface is
     the sunken well, where --hot-text measures 4.76:1. */
  /* A sibling of the chrome accent, not the same value: the rules suite
     counts every element set in --hot as a hot mark, and the accent is on
     every link. #1f45c4 reads as the same blue and measures as its own. */
  --hot: #1f45c4;          /* the one hot mark, used once per screen: the house blue's sibling since v7.22 */
  --hot-text: #1b3ea8;
  --hud-label: max(9.5px, var(--floor));
  /* A leader line has to survive being a hairline on a lit sheet. --line-strong
     is tuned for dividing prose, which on the dark canvas leaves the line
     fainter than the label it is attached to. */
  --hud-stroke: rgba(24,22,12,.3);
}
[data-theme="dark"] {
  --hot: #6f9bff;
  --hot-text: #93b3ff;     /* 8.4:1 on the dark canvas; no darkening needed */
  --hud-stroke: rgba(236,232,222,.34);
}

/* ---- the drawing frame ------------------------------------------------ */

.hud {
  position: relative;
}

.hud-lines {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  overflow: visible;
  pointer-events: none;
  z-index: 3;
}

.hud-line {
  stroke: var(--hud-stroke);
  stroke-width: 1;
  vector-effect: non-scaling-stroke;
}

.hud-anchor {
  fill: none;
  stroke: var(--text-2);
  stroke-width: 1;
  vector-effect: non-scaling-stroke;
}

.hud-core { fill: var(--text); }

/* Nothing is visible until its line has been drawn, so the first paint is
   the object alone and the annotation arrives after, the way a drawing is
   marked up rather than printed with its marks already on. */
.hud-lines > g { opacity: 0; }
.hud-lines > g[data-drawn] {
  opacity: 1;
  transition: opacity 180ms linear;
}
.hud-lines > g[data-drawn] .hud-line {
  animation: hud-draw 620ms cubic-bezier(.4, .8, .3, 1) forwards;
}
.hud-lines > g[hidden] { display: none; }

@keyframes hud-draw { to { stroke-dashoffset: 0; } }

.hudnote {
  position: absolute;
  left: var(--nx, 50%);
  top: var(--ny, 50%);
  z-index: 4;
  margin: 0;
  max-width: var(--nw, 182px);
  font: 500 var(--hud-label) / 1.55 var(--mono);
  letter-spacing: .085em;
  text-transform: uppercase;
  color: var(--text-3);
  opacity: 0;
  transform: translateY(3px);
  transition: opacity 280ms linear, transform 280ms cubic-bezier(.3, .7, .4, 1);
}
.hudnote[data-shown] { opacity: 1; transform: none; }

/* A label on the right edge is positioned by its right edge. Pulling it back
   with translateX(-100%) looks the same until you measure it: the box is
   still laid out from --nx, so its shrink-to-fit width is capped by what is
   left of the container, and a 196px label collapses to 40px and sets itself
   six words deep. */
.hudnote[data-right] {
  left: auto;
  right: calc(100% - var(--nx, 50%));
}

.hudnote b {
  display: block;
  font-weight: 600;
  letter-spacing: .06em;
  color: var(--text);
}
.hudnote a {
  color: inherit;
  text-decoration: none;
  border-bottom: 1px solid var(--line-strong);
}
.hudnote a:hover { color: var(--text); border-bottom-color: currentColor; }
.hudnote em {
  font-style: normal;
  color: var(--hot-text);
}

/* ---- the plate -------------------------------------------------------- */

canvas.plate {
  display: block;
  width: 100%;
  height: 100%;
  --plate-ink: var(--text);
  --plate-accent: var(--hot);
  /* Where the lattice settles, as a fraction of the plate's height. The
     scatter always uses all of it; only the resolved grid is banded, so a
     plate can clear a headline without losing a node. */
  --plate-band: 0 1;
}

/* ---- a titled chrome window ------------------------------------------- */

.winpanel {
  border: 1px solid var(--text);
  background: var(--bg-raised);
  font: 400 11.5px/1.65 var(--mono);
  color: var(--text-2);
  letter-spacing: .01em;
}

.winpanel > h3,
.winpanel > .wintitle {
  display: flex;
  align-items: center;
  gap: 8px;
  margin: 0;
  padding: 4px 6px 4px 8px;
  background: var(--text);
  color: var(--bg-raised);
  font: 600 max(9.5px,var(--floor)) var(--mono);
  letter-spacing: .14em;
  text-transform: uppercase;
}
.winpanel > .wintitle::after,
.winpanel > h3::after {
  content: '';
  margin-left: auto;
  width: 9px;
  height: 9px;
  border: 1px solid currentColor;
  flex: none;
}

.winpanel > .winbody { padding: 10px 11px 12px; }
.winpanel p { margin: 0 0 .7em; }
.winpanel p:last-child { margin-bottom: 0; }

/* The resize nub: three steps in the corner, drawn not downloaded. */
.winpanel .winbody { position: relative; }
.winpanel .winbody::after {
  content: '';
  position: absolute;
  right: 3px;
  bottom: 3px;
  width: 9px;
  height: 9px;
  background:
    linear-gradient(to top left, transparent 46%, var(--line-strong) 46%, var(--line-strong) 56%, transparent 56%),
    linear-gradient(to top left, transparent 70%, var(--line-strong) 70%, var(--line-strong) 80%, transparent 80%);
}

/* ---- plate furniture: 01 ... caption ... /01 ---------------------------- */

.plateno {
  display: grid;
  grid-template-columns: auto 1fr;
  align-items: start;
  gap: 0 18px;
  margin-bottom: 18px;
}
.plateno > .pn {
  font: 700 clamp(30px, 4vw, 44px) / .8 var(--display);
  letter-spacing: -.04em;
  color: var(--text);
  border-bottom: 2px solid var(--text);
  padding-bottom: 6px;
}
.plateno > .pc {
  justify-self: end;
  text-align: right;
  font: 500 var(--hud-label) / 1.6 var(--mono);
  letter-spacing: .12em;
  text-transform: uppercase;
  color: var(--text-3);
  max-width: 24ch;
}

.folio {
  display: flex;
  align-items: center;
  gap: 14px;
  margin-top: 14px;
  font: 500 var(--hud-label) var(--mono);
  letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--text-3);
}
.folio::before {
  content: '';
  flex: 1;
  height: 1px;
  background: var(--line);
  order: 1;
}
.folio > :last-child { order: 2; }

/* ---- the clock -------------------------------------------------------- */

.clock {
  display: inline-flex;
  gap: 10px;
  font: 500 max(10px,var(--floor)) var(--mono);
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--text-3);
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
}
.clock b { font-weight: 600; color: var(--text-2); }

/* ---- big display type, the one voice that shouts ---------------------- */

/* The display voice has to outrank the heading rules it is meant to replace.
   A bare class is (0,1,0) and lost to .pane h2 (0,1,1) and .cvhead .id h1
   (0,2,1), so the loudest element of the whole art direction was rendering
   at 24px on the landing screen and 40px on the CV. It only looked as if it
   worked because text-transform is not part of the font shorthand that was
   beating it. These selectors are listed rather than fought with !important,
   so the next rule that outranks them is findable. */
/* Sentence case, not caps.

   This was set in uppercase, which is the safe choice and the wrong one.
   Capitals flatten a typeface: every letter becomes a rectangle of the
   same height, so the only thing left to look at is weight and width.
   Everything that makes Bricolage worth having over any other heavy
   grotesque, the kinked g, the splayed a, the e that is slightly too
   open, lives in the lowercase, and caps threw all of it away. Setting a
   headline in caps is also how you get a headline that could belong to
   anybody; the lowercase is where a voice is. */
.bigtype,
.pane h2.bigtype,
.doc > header h1.bigtype,
.pane .overview h2.bigtype {
  margin: 0;
  font: 700 clamp(44px, 8.4vw, 118px) / .84 var(--display);
  letter-spacing: -.042em;
  text-transform: none;
  color: var(--text);
  text-wrap: balance;
}

/* ---- reduced motion & print ------------------------------------------- */

@media (prefers-reduced-motion: reduce) {
  .hud-lines > g,
  .hudnote { opacity: 1; transform: none; transition: none; }
  .hud-lines > g[data-drawn] .hud-line { animation: none; stroke-dashoffset: 0 !important; }
}

@media print {
  .hud-lines { display: none; }
  .hudnote {
    position: static;
    opacity: 1;
    transform: none;
    max-width: none;
    margin: 0 0 .5em;
  }
  .hudnote[data-right] { right: auto; }
  .winpanel { border-color: #000; }
  .winpanel > h3, .winpanel > .wintitle { background: none; color: #000; border-bottom: 1px solid #000; }
}

/* ---- a plate inside a document sheet ---------------------------------- */

/* Inside a case file the drawing is evidence, not a hero: it sits in the
   section whose numbers it is made of, at the width of the prose, with its
   two labels in the margins and the lattice cut around them. */
.caseplate {
  position: relative;
  margin: 26px 0 30px;
  padding: 0;
}
.caseplate canvas.plate {
  height: clamp(150px, 18vw, 210px);
  margin-top: 10px;
  --plate-band: 0 1;
}
/* The labels sit under the drawing, one at each end, and the leader runs
   down out of the lattice into the top of the label. */
.caseplate .hudnote {
  --nw: 236px;
  top: auto;
  bottom: 0;
}
.caseplate { padding-bottom: 86px; }
.caseplate .hudnote[data-right] { text-align: right; }

/* A lattice one row deep does not need the full plate height behind it. */
.caseplate.thin canvas.plate { height: clamp(104px, 11vw, 132px); }

/* Below the width where the margins exist, the labels become a list under
   the drawing and the leaders have nothing left to point across. */
@media (max-width: 1080px) {
  .caseplate .hud-lines { display: none; }
  .caseplate .hudnote {
    position: static;
    opacity: 1;
    transform: none;
    max-width: none;
    padding: 9px 0;
    border-top: 1px solid var(--line);
    letter-spacing: .07em;
  }
  .caseplate .hudnote[data-right] { right: auto; }
  .caseplate canvas.plate { height: clamp(120px, 26vw, 170px); }
  /* the 86px under the drawing is where the notes hang on a desk; here
     they are in the flow, and it was an empty band under every plate */
  .caseplate { padding-bottom: 0; }
}

/* ---- a field inside a document sheet ---------------------------------- */

/* The landing page puts its headline over the field. A case file has its
   argument to get on with, so the object sits on its own, full width of the
   sheet, with its two labels in the margins. */
.fieldfig {
  position: relative;
  margin: 0 0 30px;
  padding: 0 0 86px;
}
.fieldfig .fieldbox {
  display: block;
  height: clamp(240px, 30vw, 380px);
  margin-top: 10px;
  --plate-ink: var(--text);
  --plate-accent: var(--hot);
}
.fieldfig canvas.field { display: block; }
.fieldfig .hudnote { bottom: 0; top: auto; }
.fieldfig .hudnote[data-right] { text-align: right; }

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

@media (max-width: 1080px) {
  .fieldfig { padding-bottom: 0; }
  .fieldfig .hud-lines { display: none; }
  .fieldfig .hudnote {
    position: static; opacity: 1; transform: none; max-width: none;
    padding: 9px 0; border-top: 1px solid var(--line); letter-spacing: .07em;
  }
  .fieldfig .hudnote[data-right] { right: auto; }
  .fieldfig .fieldbox { height: clamp(200px, 42vw, 280px); }
}

/* ===== the chassis =====================================================
   The frame that is on every page, so a figure is part of a drawing surface
   rather than a picture dropped into a document. Four pieces: the scale down
   the right edge (built in chassis.js, because it is measured), registration
   marks at the corners of the sheet, the folio rail at the foot of a section,
   and a bracketed label. */

/* ---- the scale -------------------------------------------------------- */

/* Fixed to the window, not to the scrolling box. Absolutely positioned
   inside .view it was laid out against the scroll content, so it was as tall
   as the whole document and scrolled away with it: an instrument frame that
   leaves the frame. The view runs to the right edge of the window, so the
   offsets are just the chrome above and below it. */
.scale {
  position: fixed;
  top: calc(var(--topbar) + var(--viewhead));
  right: 0;
  bottom: 27px;
  width: 34px;
  pointer-events: none;
  z-index: 44;
}
.scale::before {
  content: '';
  position: absolute;
  inset: 10px auto 10px 0;
  width: 1px;
  background: var(--line);
}
.scale .tk {
  position: absolute;
  left: 0;
  top: calc(10px + (100% - 20px) * var(--at, 0) / 100%);
  display: flex;
  align-items: center;
  gap: 5px;
  transform: translateY(-50%);
}
.scale .tk::before {
  content: '';
  width: 5px;
  height: 1px;
  background: var(--line-strong);
}
.scale .tk.lb::before { width: 9px; }
.scale .tk b {
  font: 600 max(8.5px,var(--floor)) var(--mono);
  letter-spacing: .08em;
  color: var(--text-3);
  opacity: .75;
}
.scale .tk[data-here]::before { background: var(--text); width: 13px; }
.scale .tk[data-here] b { color: var(--text); opacity: 1; }

/* The cursor is where you have read to. It is the only part of the chassis
   that moves, and a hand caused it by scrolling. */
.scale .cur {
  position: absolute;
  left: 0;
  top: calc(10px + (100% - 20px) * var(--at, 0) / 100%);
  width: 15px;
  height: 7px;
  transform: translateY(-50%);
  /* The chrome accent, not the hot mark. A reading cursor is on every page
     and the hot vermilion is allowed one thing per screen, so the cursor
     wears the same blue the status bar's reading bar already uses. */
  border: 1px solid var(--accent);
  border-right: 0;
  background: color-mix(in srgb, var(--accent) 16%, transparent);
}
.scale[data-still] .cur { transition: none; }

@media (max-width: 1080px) { .scale { display: none; } }
@media print { .scale { display: none; } }

/* ---- registration marks ----------------------------------------------- */

/* A drawing is registered to its sheet. These are the corner marks that say
   where the sheet is, drawn in the corners of the open document. */
.doc { position: relative; }
.doc::before,
.doc::after {
  content: '';
  position: absolute;
  width: 14px;
  height: 14px;
  pointer-events: none;
  opacity: .5;
}
.doc::before {
  top: 10px; left: 10px;
  border-top: 1px solid var(--line-strong);
  border-left: 1px solid var(--line-strong);
}
.doc::after {
  bottom: 10px; right: 10px;
  border-bottom: 1px solid var(--line-strong);
  border-right: 1px solid var(--line-strong);
}

/* ---- the folio rail ---------------------------------------------------- */

/* The foot of a section: what it moved, with a rule running out to the plate
   number. Taken from the poster plates, where every one closes the same way. */
.secfoot {
  display: flex;
  align-items: center;
  gap: 14px;
  margin-top: 26px;
  padding-top: 10px;
  border-top: 1px solid var(--line);
  font: 500 var(--hud-label) var(--mono);
  letter-spacing: .13em;
  text-transform: uppercase;
  color: var(--text-3);
}
.secfoot .from { color: var(--text-2); }
.secfoot .arrow {
  flex: 1;
  height: 1px;
  background: linear-gradient(to right, var(--line-strong), var(--line));
  position: relative;
}
.secfoot .arrow::after {
  content: '';
  position: absolute;
  right: 0; top: -2px;
  border-left: 4px solid var(--line-strong);
  border-top: 2.5px solid transparent;
  border-bottom: 2.5px solid transparent;
}
/* Ink, not vermilion: a rail at the foot of every section would put the
   hot colour on the page thirty-four times, and rule eight exists so that
   it means something the once. */
.secfoot .to { color: var(--text); }
/* No opacity on dim ink: --text-3 clears 4.5:1 by exactly as much as it
   was computed to, and 80% of it over the paper read 3.44. */
.secfoot .fo { color: var(--text-3); }

/* ---- bracketed label --------------------------------------------------- */

.brk::before { content: '[ '; opacity: .55; }
.brk::after { content: ' ]'; opacity: .55; }

/* A document headline in the display voice. About and the colophon take the
   same uppercase shout the landing page uses; the CV does not, because it is
   printed and read by an applicant tracking system, and a name set in 100px
   caps is a poster rather than a document. */
.doc > header > h1.bigtype { max-width: 14ch; margin-bottom: 4px; }
.cvbig,
.cvhead .id h1.cvbig {
  font: 700 clamp(30px, 4.4vw, 52px) / .94 var(--display);
  letter-spacing: -.035em;
  margin: 0 0 2px;
}
@media print { .cvbig, .cvhead .id h1.cvbig { font-size: 30px; } }

/* The bracket notation the reference uses for an index label. Applied to the
   navigator's group headings, which is where this site's indexes are. */
.grouplabel::before { content: '[ '; opacity: .5; }
.grouplabel::after { content: ' ]'; opacity: .5; }

/* The contour ground sits under the object, inside the same box. */
.fieldbox { position: relative; }
.fieldbox canvas.terrain {
  position: absolute;
  inset: 0;
  display: block;
  pointer-events: none;
}
.fieldbox canvas.field { position: relative; z-index: 1; }
