@font-face { font-family: nhg; src: url(fonts/nhg-display-100.woff2) format('woff2'); font-weight: 100; font-style: normal; font-display: swap; }
@font-face { font-family: nhg; src: url(fonts/nhg-display-200.woff2) format('woff2'); font-weight: 200; font-style: normal; font-display: swap; }
@font-face { font-family: nhg; src: url(fonts/nhg-display-400.woff2) format('woff2'); font-weight: 400; font-style: normal; font-display: swap; }

:root {
  --bg:      #ffffff;
  --fg:      #111111;
  --muted:   #808080;
  --border:  #bbbbbb;
  --tint:    #eeeeee;
  --blue:    #0000cd;
  --red:     #cd0000;
  --yellow:  #ffe600;
  --font:    nhg, "Helvetica Neue", Helvetica, Arial, sans-serif;
}

/* the sheet at night: the site's front page, and only it. the same tokens the other way round — the paper is
   black and what is said on it is white, so every rule, veil and grey follows without being told. a photograph
   still speaks in white: on a cover the voice is the light colour, whichever of the two that is here. */
body.night { --bg: #000000; --fg: #ffffff; --muted: #8c8c8c; --border: #444444; --tint: #1a1a1a;
  /* the machine's blue is a pencil on white paper and cannot be seen on black: the same hue, lifted until it reads */
  --blue: #7080ff; }
.night .cover:not(.pale), .night header.sheet.over { color: var(--fg); }

/* the sheet */
body  { margin: 0; background: var(--bg); color: var(--fg); font: 400 20px/22px var(--font); overflow-x: hidden; }
text  { white-space: pre; font-family: var(--font); }
p     { margin: 0; }
a     { color: inherit; text-decoration: none; }
.sheet { max-width: 1840px; margin: 0 auto; padding: 0 40px; }

/* type: one face, the size is the role. caps and 100 are the display voice —
   the larger a line is set, the lighter it is drawn */
.title, .display { font-weight: 100; line-height: 1; letter-spacing: -.01em; text-transform: uppercase; }
.title   { font-size: 14vw; line-height: .9; text-wrap: balance; }
.display { font-size: 144px; }
.h1      { font-size:  64px; font-weight: 200; line-height:  76px; }
.h2      { font-size:  48px; font-weight: 200; line-height:  57px; }
.h3      { font-size:  32px; font-weight: 200; line-height:  38px; }
.h4      { font-size:  24px; font-weight: 400; line-height:  28px; }
.body    { font-size:  20px; font-weight: 400; line-height:  22px; }
.caption { font-size:  14px; font-weight: 400; line-height:  18px; }
.up      { text-transform: uppercase; letter-spacing: .04em; }
.max     { max-width: 40em; }

/* the wordmark. one face, one weight, title case, two lines. this is the name everywhere — a
   nav bar, a slide's title block, a card, a poster. the numbers are Neue Haas Grotesk Display 400
   and are measured, not chosen; regenerate them if the face changes.

   the second line is pulled left .047em because NHG's capital I carries that much more left
   sidebearing than its S. without it the I sits inset by 3px at 64px and the left edge — the whole
   reason to stack, since it puts the S over the I and hands you the initials for nothing — is lost.

   leading 1.02: Structural has no descender in it and NHG's l ascender is exactly its cap height,
   so both lines have flat tops and the first a flat bottom. no other pair of words would take it.

   never track it, never justify the two lines, never split the weight, never set it in capitals.
   .one is the horizontal lockup: one line, for a strip too short for two. its word space is .16em,
   the face's own — closing it further makes one word of two at the size the one-liner is for.
   those are the only two settings there are. */
.wordmark { font-weight: 400; font-size: 20px; line-height: 1.02; letter-spacing: 0;
            text-transform: none; white-space: nowrap; margin: 0; }
.wordmark > span { display: block; }
.wordmark > span + span { margin-left: -.047em; }
.wordmark.one > span { display: inline; }
.wordmark.one > span + span { margin-left: 0; }
.wordmark.one > span + span::before { content: ""; display: inline-block; width: .16em; }

/* the veil: the paper coming back over what runs under the signature. a board whose words fill the
   sheet lets them run to the edge and lays the signature on top; the veil is the sheet's own paper
   thickening towards the foot, so the words go quiet before they reach the name and the name reads
   on clean paper. it is var(--bg) and nothing else — white on a white sheet, black on a dark one,
   the same class. the ramp is eased, not straight: a linear fade shows its top edge as a band.
   --veil is how tall the ramp runs above what the veil holds; under that it is solid paper. */
.veil { --veil: 22vmin; position: absolute; left: 0; right: 0; bottom: 0; z-index: 1; padding-top: var(--veil);
  background: linear-gradient(
    color-mix(in srgb, var(--bg)   0%, transparent) 0,
    color-mix(in srgb, var(--bg)  18%, transparent) calc(var(--veil) * .2),
    color-mix(in srgb, var(--bg)  46%, transparent) calc(var(--veil) * .42),
    color-mix(in srgb, var(--bg)  74%, transparent) calc(var(--veil) * .64),
    color-mix(in srgb, var(--bg)  92%, transparent) calc(var(--veil) * .84),
    var(--bg) var(--veil)); }

/* colour: the signed sheet has none. colour is the conversation about it, the checker's drawer: see design/COLOR.md
   ink is the engineer. grey is existing, halftone. blue is the note: what the machine read, not drafted until the engineer takes it.
   red is the redline: change this, what should be, what does not hold. yellow is the highlight: read, and it holds. a wash, never a line.
   one name per colour and it sets the colour — text and fill together; .patch puts that same colour on the ground */
.fg      { color: var(--fg);     fill: var(--fg); }
.muted   { color: var(--muted);  fill: var(--muted); }
.blue    { color: var(--blue);   fill: var(--blue); }
.red     { color: var(--red);    fill: var(--red); }
.yellow  { color: var(--yellow); fill: var(--yellow); }
.patch   { background: currentColor; }
.stroke-fg     { fill: none; stroke: var(--fg); }
.stroke-muted  { fill: none; stroke: var(--muted); }
.stroke-blue   { fill: none; stroke: var(--blue); }
.stroke-red    { fill: none; stroke: var(--red); }
.stroke-border { fill: none; stroke: var(--border); }
/* a line on a photograph. ink is for paper and there is no paper under a picture, so a drawing laid
   over one is drawn in the sheet's white — the same word as .bg, said as a stroke */
.stroke-bg     { fill: none; stroke: var(--bg); }
/* the paper colour, and it comes after the strokes on purpose: a bubble is .bg .stroke-fg,
   a white disc with an ink edge, and the fill has to win over the stroke class's fill: none */
.bg      { color: var(--bg); fill: var(--bg); }
.on-ink  { background: var(--fg); color: var(--bg); }
.invert  { filter: invert(1); }

/* line weights, like a set */
.thin  { stroke-width: .75; }
.med   { stroke-width: 1.5; }
.thick { stroke-width: 3; }
.heavy { stroke-width: 6; }
.dash  { stroke-dasharray: 8 8; }
.cl    { stroke-dasharray: 24 6 3 6; }
.draw svg { width: 100%; height: auto; display: block; }
/* the identity's sheets, 028 to 049: one layout. the title under the title block, the notes on the bottom
   rule, and the figure centred in the field between them — both ways, so nothing sits left on one sheet
   and right on the next. the sheet is exactly the screen, so the field is a size a drawing can be fitted
   to, and the notes always take three lines of room, so the field is the same field on every one of them
   and a figure centred in it stands where the last one stood. */
.screen:has(> .figure) { height: 100vh; }
.screen:has(> .figure) > header.block + * { margin-top: 0; }
.screen:has(> .figure) > .bottom { min-height: calc(3lh + 32px); box-sizing: border-box; }
.figure { flex: 1 1 0; min-height: 0; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 40px; text-align: center; }
.figure > * { flex: 0 1 auto; min-height: 0; max-width: 100%; max-height: 100%; }
.figure > .grid, .figure > .row { align-self: stretch; justify-content: center; align-items: center; }
.figure object { max-width: 100%; }
/* the mark's place: one square in the middle of that field, the work point at its centre and the arms at
   mark.svg's own eighty percent of it. every sheet that shows the mark alone shows it here and this size,
   so turning the slides the mark does not move, only what is around it changes. every figure that stands
   here is registered to it — the drawing's work point on the centre, its arms on the mark's. */
.markplace { position: relative; flex: none; width: min(36vw, 44vh); aspect-ratio: 1; }
.markplace > object, .markplace > .joint { position: absolute; inset: 0; width: 100%; height: 100%; }
/* a drawing about the room round the mark: the same units, 200 to the place, with a margin of 62 all
   round, so the object reaches past the place by that much on every side and the mark does not move */
.markplace > object.around { inset: -31%; width: 162%; height: 162%; max-width: none; max-height: none; }
.markplace .at { position: absolute; transform: translate(-50%, -50%); white-space: nowrap; }
.markplace .at.from { transform: translate(0, -50%); }
/* a drawing bigger than the screen. it runs to both edges and off the bottom, and the svg is laid
   slice, so it fills whatever slot it is handed and what does not fit is cut. never shrunk to fit:
   a set that fits the frame is a set someone finished. the sheet's floor comes off with it, and it
   starts directly under the title block — on a story slide that block is the line the voice is
   reading, so the drawing's own head sits under the line and the two are one sheet. */
.bleed { width: 100vw; margin-left: calc(50% - 50vw); }
/* and the screen it is on is exactly the screen, on a page as on a slide, or there is no edge to be cut at */
.screen:has(> .bleed) { height: 100vh; overflow: hidden; }
.screen > .bleed:last-child { margin-bottom: -40px; }
.screen > header.block + .bleed { margin-top: 0; }
.bleed > object, .bleed > svg { display: block; width: 100%; height: 100%; }
.draw canvas { width: 100%; max-width: 100%; aspect-ratio: 10 / 7; display: block; }
/* a quoted drawing given the whole sheet: it takes the field it is handed, not the width's 10:7 and
   not a size written into the file. the drawing is centred in it by its own preserveAspectRatio —
   nothing here positions it, and there is no number to keep in step with the artwork */
.draw.fill { position: relative; }
.draw.fill > object { display: block; width: 100%; height: 100%; }
.draw.fill > [data-log] { position: absolute; inset: 0; }
/* a reading over the drawing it read: same origin, same size, so the ink is drawn once */
.draw.fill > object + object { position: absolute; inset: 0; }
.draw.fill > [data-log] > canvas { width: 100%; height: 100%; aspect-ratio: auto; }
/* the product on the table: the app itself, running, in a window 1440 by 810 — a screen, not a panel.
   the window is the sheet inset by one margin, and deck.js scales the app to it and centres it, so it
   is the same rectangle on every slide that shows it. the line stands on the wall above it and owes it
   nothing: when the voice goes and the scene is all that is left, nothing moves. what the slide is
   about is where the drawing inside it stands, which the url says. it arrives the way every sheet in
   this deck arrives, because it is one: see .lay below. */
.product { position: absolute; inset: 72px; }
.product .win { overflow: hidden; }
.product .win > iframe { position: absolute; top: 0; left: 0; width: 1440px; height: 810px; border: 0; transform-origin: 0 0; }

@property --p { syntax: "<number>"; inherits: true; initial-value: 0; }
/* a sheet on the table. it arrives rolled and rolls out: the paper already down never moves, the
   sheet ends at the line where it leaves the roll, and the roll lies on that line.
   there is no 3d here and there is nothing missing. seen from straight above — which is where the
   table is photographed from — a cylinder lit by one light is a band whose value at each row is how
   squarely that row faces the light, and that is one number per row of screen: a gradient. the stops
   below are that function, k = amb + (1-amb) cos(asin(s) + 30deg), sampled across the band. a real
   cylinder of strips in css 3d computes the same pixels and charges for them.
   it is a rolled sheet and not a tube, so what is wound on it is what is left of the sheet:
   r = R0 sqrt(1 - p), thinning the whole way, gone when the sheet is flat. drawings are rolled
   drawing-side in, so the outside of the roll is the back of the paper — blank, and the hard line
   where the sheet stops is the paper going under, not a cut. it throws its shadow ahead of itself.
   --p is how much of the sheet is down and it is the only thing that moves. say where the sheet
   lands with --l --t --w --h, how far off square with --skew, add .down to roll it out. */
.lay { --rad: calc(.06 * var(--h) * sqrt(1 - var(--p)));
  position: absolute; left: var(--l); top: var(--t); width: var(--w); height: calc(var(--p) * var(--h));
  transform: rotate(var(--skew, 0deg)); transform-origin: top center; background: var(--bg);
  box-shadow: 0 1px 1px rgba(0, 0, 0, .13), 0 7px 16px -7px rgba(0, 0, 0, .20); visibility: hidden; }
.lay > * { position: absolute; top: 0; left: 0; display: block; width: 100%; height: var(--h); max-width: none;
  clip-path: inset(0 0 calc(100% - var(--p) * 100%)); }
.lay::before, .lay::after { content: ""; position: absolute; left: 0; right: 0; pointer-events: none; }
.lay::before { top: 100%; height: calc(1.4 * var(--rad));
  background: linear-gradient(rgba(0, 0, 0, .30), rgba(0, 0, 0, .10) 34%, rgba(0, 0, 0, 0)); }
.lay::after { top: calc(100% - var(--rad)); height: calc(2 * var(--rad)); background-color: var(--bg);
  background-image: linear-gradient(rgba(0,0,0,.265) 0%, rgba(0,0,0,.071) 6.7%, rgba(0,0,0,.028) 12.5%, rgba(0,0,0,0) 25%, rgba(0,0,0,.019) 37.5%, rgba(0,0,0,.071) 50%, rgba(0,0,0,.152) 62.5%, rgba(0,0,0,.265) 75%, rgba(0,0,0,.393) 85.4%, rgba(0,0,0,.53) 93.3%, rgba(0,0,0,.53) 100%); }
/* set down, held for a beat, then pushed: slow off the mark, quick across, soft on landing */
.lay.down { visibility: visible; animation: roll-out 1.35s cubic-bezier(.66, 0, .34, 1) forwards; }
@keyframes roll-out { 0%, 18% { --p: 0 } 100% { --p: 1 } }
.lay.flat > * { clip-path: none; }

/* the grid. .stack lays its children out as blocks, so it is set before .grid — a .grid inside a .stack stays a grid */
.stack > * { display: block; }
.grid    { display: grid; grid-template-columns: repeat(12, minmax(0, 1fr)); gap: 40px; }
.grid > * { min-width: 0; }
.span-2  { grid-column: span  2; }
.span-3  { grid-column: span  3; }
.span-4  { grid-column: span  4; }
.span-6  { grid-column: span  6; }
.span-8  { grid-column: span  8; }
.span-12 { grid-column: span 12; }

/* a screen: a slide, a section of the site. as tall as the view, one idea */
.screen { position: relative; min-height: 100vh; box-sizing: border-box; padding-top: 40px; padding-bottom: 40px; display: flex; flex-direction: column; justify-content: center; }
.screen.grid { display: grid; align-content: center; }
.short  { min-height: 0; }
.center { text-align: center; }
.center .row { justify-content: center; }
.center object { display: block; margin: 0 auto; }
.mid    { display: flex; align-items: center; justify-content: center; }
/* a drawing in the hand is placed at the size it was drawn, one to one, the way type is: the line is the
   pen's and the numbers are the hand's height wherever it is, and it only gives way on a narrow screen */
.hand   { display: block; max-width: 100%; height: auto; }
.fill   { flex: 1; min-height: 320px; }
.photo img { width: 100%; height: 100%; object-fit: cover; display: block; }
.photo.fill { min-height: 0; overflow: hidden; }
.screen.photo { height: 100vh; }
/* a cover: the photograph is the whole screen and the line sits on it, in the sheet's white.
   a photograph is not paper. ink is what a pen leaves on a sheet, and there is no pen here — so
   everything the sheet says over a photograph is the sheet's white: the line, the caption, the rule,
   the drawing laid over the picture. one colour, and nothing to decide per slide. a photograph white
   does not read on is not a cover — it is the wrong photograph, or the wrong crop, and that is fixed
   in the picture, never in the ink. the one exception is .pale, below, and it is not a photograph. */
/* a cover is its own stack: the picture is behind the cover's words and in front of everything that is not
   the cover. without it the picture is behind the whole page, and a page long enough finds something to put over it. */
.cover { max-width: none; padding: 40px; color: var(--bg); overflow: hidden; isolation: isolate; }
/* a film is its photograph, moving a little: its still is its poster, and it sits where the still would */
.cover > img, .cover > video { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; z-index: -1; }
/* the site's first picture, shaded: the room is lit from its windows, so the top of it is the brightest
   thing in frame and the floor under the line is next. the shade is the evening coming into the picture
   from the top and from the foot, eased to nothing, the middle of the room left as it was shot. it takes
   the title block's own fade with it, so there is still one. the site's front page only, never a slide. */
.cover.shade::after { content: ""; position: absolute; inset: 0; z-index: -1; pointer-events: none;
  background:
    linear-gradient(rgba(0,0,0,.72) 0%, rgba(0,0,0,.55) 12%, rgba(0,0,0,.32) 28%, rgba(0,0,0,.13) 46%, rgba(0,0,0,.03) 62%, rgba(0,0,0,0) 74%),
    linear-gradient(to top, rgba(0,0,0,.62) 0%, rgba(0,0,0,.44) 12%, rgba(0,0,0,.22) 26%, rgba(0,0,0,.07) 40%, rgba(0,0,0,0) 52%); }
body:has(> .cover.shade:first-of-type) > header.sheet.over::before { display: none; }
/* the tour: the world's structures standing, one after another, each the whole screen and crossing into the
   next; the name in the sheet's white, quiet, coming up after the picture. the turning is in sheet.js.
   on a slide the name is the tour's own, at its foot. on the site it is the sheet's title, so it is in the
   title block after the name of the office, where a set says what a sheet is of; a narrow title block has no
   room for it and the name goes back to the foot of the picture, over the line. */
.tour video { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; z-index: -1; opacity: 0; transition: opacity .9s linear; }
.tour video.on { opacity: 1; }
.tour [data-with] { opacity: 0; transition: opacity .9s linear; } .tour [data-with].on { opacity: 1; }
.tour-name { opacity: 0; transition: opacity .45s linear; min-height: 1lh; }
.tour-name.on { opacity: .72; }
.tour > .tour-name { position: absolute; left: 40px; right: 40px; bottom: 40px; margin: 0; }
.bottom > .tour-name { display: none; margin-bottom: 12px; }
.tb .tour-name, .tb .no { white-space: nowrap; }
@media (max-width: 1100px) { .tb .tour-name { display: none; } .bottom > .tour-name { display: block; } }
/* the seal, once, bottom right, on what is signed: the last sheet of the deck, the last picture of a page.
   the line it shares a foot with stops short of it. */
.cover:has(> .stamp) { --stamp: clamp(96px, 11vw, 148px); }
.cover > .stamp { position: absolute; right: 56px; bottom: 56px; width: var(--stamp); height: auto; display: block; }
.cover:has(> .stamp) > .bottom { padding-right: calc(var(--stamp) + 40px); box-sizing: border-box; }
/* a material is not a scene: flat to the frame, open light, running off all four sides, nothing in
   it being photographed. that is not a picture the sheet speaks over — it is the sheet, in another
   stock, and on paper the voice is ink. a deck slide only, for the plan-room desk the product is
   laid on: a poster is read across a room and its voice on any picture is the sheet's white. */
.cover.pale { color: #111111; --fg: #111111; }
/* the sheet's greys and its rules go white with it. on a photograph a quiet thing is quiet by being
   thinner or fainter, never by being a second colour — grey ink on a picture is neither */
.cover .muted, header.sheet.over .muted { color: currentColor; opacity: .72; }
.cover text, header.sheet.over text { fill: currentColor; }
.cover :is(.stroke-fg, .stroke-muted, .stroke-border) { stroke: currentColor; }
/* the one thing on a cover that is not the sheet speaking: what is printed on a thing in the
   picture. that ink is on paper, on glass, on a card, on a hard hat — it is the object's and not the
   sheet's, so it is the only thing here set in ink, and it says so. on a dark stock the printed
   thing is white like everything else, so it simply does not say .printed. */
.printed { color: var(--fg); }
/* a plate: the photograph and a drawing over it, one frame, aligned at any size. the plate covers the screen the way the image would */
.plate { position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%); width: max(100vw, 177.78vh); height: max(56.25vw, 100vh); z-index: -1; }
.plate img, .plate svg, .plate object, .plate video { position: absolute; inset: 0; width: 100%; height: 100%; display: block; }
.plate video { object-fit: cover; }
/* on a photograph the plate is where things are placed: in hundredths of it, and a mark printed on
   paper takes the paper's tone. a cover's caption stands at the foot of the sheet */
.cover > .plate { container-type: size; }
/* the app on a photographed screen: its own desktop size, placed by sheet.js onto the measured corners */
.plate iframe, .plate [data-screen] { position: absolute; border: 0; pointer-events: none; }
.plate [data-screen] { left: 0; top: 0; width: 1440px; height: 900px; background: var(--bg); }
.plate .ink { mix-blend-mode: multiply; }
/* a poster in a photographed frame: it is printed on that paper, so the paper's light is multiplied over
   it and the shadow that crosses the frame crosses the print. the page says where the paper is */
.plate .paper { position: absolute; mix-blend-mode: multiply; }
.plate .paper img { object-fit: contain; }
/* a board on photographed paper is its own window, so it takes the sheet it is on. a row of sheets
   runs off the picture, and the plate is the picture's edge */
.plate .paper iframe { inset: 0; width: 100%; height: 100%; }
.plate:has(.paper + .paper) { overflow: hidden; }
main.cover > footer { margin-top: auto; }
/* the badge: the name, then the role and the seal, set like a title block. one design in --u, a
   hundredth of what it is on. drawn flat, its edge is drawn; photographed, it is printed on the card
   (design/photo, the printed things). it is the title block too: on a sheet of paper it is the same box,
   in the corner */
.badge, .paper, .envelope, .namecard { position: relative; box-sizing: border-box; border: 1px solid var(--fg); line-height: 1.02; }
.badge { --u: 3.2vw; width: calc(11.85 * var(--u)); height: calc(7.3125 * var(--u));
  padding: calc(1.05 * var(--u)) calc(1.1 * var(--u));
  display: flex; flex-direction: column; justify-content: space-between; }
.badge .rl { border-top: 1px solid var(--fg); padding-top: calc(.28 * var(--u));
  display: flex; justify-content: space-between; align-items: flex-end; }
.badge object { position: static; width: calc(2.1 * var(--u)); height: calc(2.1 * var(--u));
  display: block; margin-bottom: calc(-.35 * var(--u)); }
/* a sheet of paper: the border, the name at the top, the title block in the corner. --u is a hundredth
   of what it is on, as on the badge */
.paper { --u: min(1.6vh, 1.35vw); width: calc(30.3 * var(--u)); height: calc(42 * var(--u));
  padding: calc(.55 * var(--u)); display: flex; flex-direction: column; }
.paper .badge { --u: inherit; margin-top: auto; align-self: flex-end; }
.paper .rows { margin-top: calc(1.6 * var(--u)); }
.paper .rows div { border-top: 1px solid var(--border); height: calc(1.15 * var(--u)); }
/* drawn flat, the things stand side by side on their middles, not on the baseline of their first line */
.row:has(> .paper, > .badge, > .envelope, > .namecard) { align-items: center; }
/* a notebook's page is a sheet of paper with the grid on it, turned to stand; its cover carries the lockup */
.paper > .leaf { position: absolute; left: 50%; top: 50%; width: calc(42 * var(--u)); height: calc(30.3 * var(--u));
  transform: translate(-50%, -50%) rotate(90deg); }
.paper > .lockup { width: calc(14 * var(--u)); aspect-ratio: 713 / 192; height: auto; }
/* the delivery label is the badge, long: the name over the rule, the set and the seal */
.badge.label { --u: min(2vw, 3vh); width: calc(36.9 * var(--u)); height: calc(15.85 * var(--u)); }
/* the envelope: the name and the city on the face. the back is the flap, and the seal at its point,
   on the one thing an envelope does: it closes */
.envelope { --u: min(1.2vw, 2.4vh); width: calc(32.7 * var(--u)); height: calc(24.1 * var(--u)); padding: calc(1.7 * var(--u)) calc(1.9 * var(--u)); }
.envelope .ad { color: var(--muted); margin-top: calc(.55 * var(--u)); }
.envelope > svg { position: absolute; inset: 0; width: 100%; height: 100%; }
.envelope > object { position: absolute; inset: auto; left: 50%; top: 58%;
  width: calc(6.6 * var(--u)); height: calc(6.6 * var(--u)); transform: translate(-50%, -50%); }
/* the business card: a title block on the front, the seal on the back and nothing beside it */
.namecard { --u: min(1.4vw, 2.6vh); width: calc(28 * var(--u)); height: calc(17.66 * var(--u)); }
.namecard p { position: absolute; left: 3.9%; }
.namecard .wordmark { top: 4%; }
.namecard .role { color: var(--muted); top: 33%; letter-spacing: .04em; }
.namecard .who  { font-weight: 200; top: 45%; }
.namecard .a    { top: 71%; }
.namecard .b    { top: 79%; }
.namecard .c    { color: var(--muted); top: 90%; }
.namecard .no   { color: var(--muted); top: 71%; left: 70.6%; }
.namecard .e    { font-weight: 200; top: 82%; left: 70.6%; }
.namecard i { position: absolute; background: var(--fg); display: block; }
.namecard .r1 { left: 0; right: 0; top: 24%; height: 1px; }
.namecard .r2 { left: 0; right: 0; top: 63%; height: 1px; }
.namecard .r3 { left: 67.1%; top: 63%; bottom: 0; width: 1px; }
.namecard.back { display: grid; place-items: center; }
.namecard.back > object { position: static; width: calc(15 * var(--u)); height: calc(15 * var(--u)); }
/* what is printed on a thing is set in three sizes, in the thing's own --u: the name, the text, the note */
.badge .nm, .namecard .wordmark, .namecard .who, .namecard .e { font-size: calc(1.4 * var(--u)); }
.badge .rl, .paper .wordmark, .envelope .wordmark { font-size: calc(.8 * var(--u)); }
.envelope .ad, .namecard .role, .namecard .a, .namecard .b, .namecard .c, .namecard .no { font-size: calc(.6 * var(--u)); }
/* a sheet laid on a photograph is a hundredth of the plate; the slide says where */
.plate > .paper { --u: 1cqw; position: absolute; }
.screen > * + * { margin-top: 32px; }
.stack  > * + *, .frame > * + * { margin-top: 12px; }
.grid   > * + * { margin-top: 0; }
/* a drawing is not a paragraph. 12px is the gap between two lines of type; where a stack sets a line
   against a drawing it gets the sheet's gap instead, so the line has room to be read before the eye
   goes to the drawing, and the drawing is not a caption of the line above it */
.stack > * + object, .stack > object + *,
.stack > * + .draw,  .stack > .draw + *,
.stack > * + .grid,  .stack > .grid + *,
.stack > .row + * { margin-top: 56px; }
/* pushed to the edge of the sheet: after the gaps, or the gap wins and .bottom never lands.
   the padding is the floor — on a full sheet there is no free space for the margin to take.
   the sheet is read the way a sheet is read: the title block at the top, the notes on the bottom
   rule, the drawing in the field between them. two auto margins, so the field splits its slack
   evenly above and below and the drawing sits in the middle of what is left, never against an edge */
.top    { margin-bottom: auto; padding-bottom: 32px; }
.bottom { margin-top: auto; padding-top: 32px; }
.screen > header.block + * { margin-top: auto; }
/* and the field is only as tall as the field: a drawing set full width under a line is capped
   so it never pushes the notes off the bottom rule. it fits by its own preserveAspectRatio */
.screen .stack > .draw > object { max-height: 54vh; }
/* a quoted drawing in a wide column: the canvas keeps its 10:7 and gives back the height, so the sheet note under it still lands on the page */
.screen .stack .draw canvas { max-width: min(100%, calc(52vh * 10 / 7)); margin: 0 auto; }
.right  { margin-left: auto; }

/* a poster quoted on a slide: the sheet's own edge, so a print reads as an object and not as the page.
   the poster is white on white; without its edge the slide cannot say where it stops */
.print { border: 1px solid var(--border); display: inline-block; line-height: 0; max-width: 100%; }
.print > object, .print > img, .print > iframe { display: block; max-width: 100%; }
/* the posters, whole, at their own proportion. nothing around them and nothing on them: each carries its
   own lockup and says its own name. three to a slide, never three of a kind, and one to root two beside
   two to three, so the row hangs on one line and the widths differ, the way sheets pinned along a wall
   do. --k is what the three come to across, in heights. one alone stands at the height of the screen */
main.posters { display: grid; place-items: center; height: 100vh; }
.posters .print { aspect-ratio: 1200 / 1800; height: 90vh; max-width: 90vw; }
.posters .print.sheet-1r2 { aspect-ratio: 800 / 1131; }
.posters .print > * { width: 100%; height: 100%; border: 0; display: block; }
/* a drawing or a poster on a slide is looked at, never used — nothing in one takes a click — so a click
   on it is a click on the sheet, and turns it (turn.js). the product, quoted live, is the one thing you use */
.sheet object, .posters .print > iframe { pointer-events: none; }
.three { display: flex; align-items: center; justify-content: center; gap: 2.5vw; --k: 2; }
.three:has(.sheet-1r2) { --k: 2.0407; }
.posters .three { width: 100%; }
.posters .three .print { height: min(82vh, calc(85vw / var(--k))); max-width: none; }
/* a landscape board, 1600 by 900: alone it stands as wide as the screen lets it */
.posters .print.wide { aspect-ratio: 1600 / 900; height: auto; width: min(88vw, calc(84vh * 16 / 9)); }
/* the landmarks are one kind, so they are never three: a tower beside a bridge, two to a slide. a bridge
   is long, so its board is the wide one, and the pair hangs at one height like the three do */
.three.two { --k: 1.3333; }
.three.two:has(.wide) { --k: 2.4444; }
.posters .three .print.wide { width: auto; height: min(82vh, calc(85vw / var(--k))); }

/* rows, rules, frames */
.row     { display: flex; flex-wrap: wrap; gap: 40px; align-items: baseline; }
.between { justify-content: space-between; }
.rule    { border-top: 1px solid var(--border); padding-top: 24px; }
.frame   { border: 1px solid var(--fg); padding: 24px; box-sizing: border-box; display: flex; flex-direction: column; max-width: 100%; text-align: left; }
.sq        { aspect-ratio: 1; }
.landscape { aspect-ratio: 7 / 4; }
.enter   { display: inline-block; color: var(--blue); border-bottom: 1px solid var(--blue); }
/* the mark is set in the line's own size, like a letter: .8em is 16px in body, and it grows with a headline */
.enter svg, footer .body svg { width: .8em; height: .8em; vertical-align: -.1em; margin-right: .1em; }
.sheetno svg { display: block; }

/* the title block */
.tb { display: flex; align-items: center; gap: 40px; height: 72px; border-bottom: 1px solid; }
.tb nav { display: flex; gap: 32px; }
.tb nav a[aria-current] { border-bottom: 1px solid; }

/* the same title block, white, sitting on the photograph instead of on paper. one title block: its rules
   are drawn in whatever colour it is set in, so the wordmark, the rule and the nav all take the page's
   colour, and over a photograph that is the white a cover speaks in (.cover, above) */
header.sheet.over { position: absolute; top: 0; left: 0; right: 0; z-index: 2; color: var(--bg); }
/* the one fade there is. a cover's line is placed where the picture is dark, but the title block cannot
   be — it is at the top of every photograph, and the top of a room is its windows. so the title block
   brings its own shade: a little, eased to nothing, gone before the picture starts. never on a slide. */
header.sheet.over::before { content: ""; position: absolute; z-index: -1; top: 0; left: 0; right: 0; height: 240px; pointer-events: none;
  background: linear-gradient(rgba(0,0,0,.36) 0%, rgba(0,0,0,.27) 22%, rgba(0,0,0,.15) 48%, rgba(0,0,0,.05) 76%, rgba(0,0,0,0) 100%); }

/* the title block of a slide, drawn by deck.js from the file's title and number: the lockup on a grid line, the sheet's title
   after it, its number in a bubble at the end. fixed to the top of the screen; the slide sits below it. the line passes
   behind the words and keeps going, off the sheet, because a grid line does not stop at the label */
main.screen:has(> header.block) { padding-top: 112px; }
.block { --paper: var(--bg); position: fixed; top: 0; left: 0; right: 0; z-index: 2; max-width: 1840px; margin: 0 auto; padding: 40px 40px 20px; display: flex; align-items: center; gap: 24px; background: var(--paper); }
.block::before { content: ""; position: absolute; left: 0; right: 0; top: 60px; height: 1px; background: repeating-linear-gradient(90deg, currentColor 0 24px, transparent 24px 30px, currentColor 30px 33px, transparent 33px 39px); }
.block > * { position: relative; background: var(--paper); flex: none; padding: 0 12px; font-weight: 200; }
.block .wordmark { font-weight: 400; }
.block .bub { width: 40px; height: 40px; padding: 0; box-sizing: border-box; border: 1px solid currentColor; border-radius: 50%; display: flex; align-items: center; justify-content: center; font-size: 16px; letter-spacing: -.02em; }
.on-ink .block { --paper: var(--fg); }
.cover .block { --paper: transparent; } .cover .block::before { display: none; }
/* the story's line, in the title block's place. no rule behind it — there is no name and no number for a
   grid line to carry — and it starts at the top of the strip however many lines it runs to, so the line is
   in one spot on all twenty slides. it is set at h3 — the line is what the slide says, and at body size in
   halftone it could not be read across a room (inbal, 17 september). the 40 sits its first line where a title
   block's name sits; the field below clears two lines of it */
.block.voice { align-items: start; }
.block.voice::before { display: none; }
.block.voice > p { flex: 0 1 auto; min-width: 0; font-size: 32px; line-height: 38px; }
main.screen:has(> .block.voice) { padding-top: 150px; }

/* fields on a sheet */
label  { display: block; }
label span { display: block; color: var(--muted); margin-bottom: 8px; }
input, textarea { width: 100%; box-sizing: border-box; border: 0; border-bottom: 1px solid var(--fg); border-radius: 0; background: none; padding: 8px 0; font: 200 20px var(--font); color: var(--fg); }
textarea { min-height: 6em; resize: vertical; }
input:focus, textarea:focus { outline: none; border-color: var(--blue); }
button { background: none; border: 0; padding: 0; font: inherit; cursor: pointer; }

/* the thread, the sheet beside it, the field: one vocabulary for the product, the site and the deck.
   ink is the engineer, blue is the machine, grey is existing, red is the redline, yellow is read-and-it-holds */
[hidden] { display: none !important; }
.said { color: var(--blue); }
.turn { border-top: 1px solid var(--border); padding-top: 14px; }
.turn + .turn { margin-top: 14px; }
.turn > * + * { margin-top: 8px; }
.ask { border-top: 1px solid var(--fg); border-bottom: 1px solid var(--fg); padding: 14px 0; }
.caret { border-left: 1px solid var(--fg); margin-left: 1px; animation: blink 1.1s steps(1) infinite; }
@keyframes blink { 50% { border-color: transparent; } }
/* the ruled list: a hairline over every row and one under the last, the way a set carries a revision
   schedule. a log line is its time in the margin and the words beside it; a check is its box; a member's
   answer is its name */
.logs > *, .check > *, .kv > * { display: grid; gap: 12px; border-top: 1px solid var(--border); padding: 8px 0; }
.logs > :last-child, .check > :last-child, .kv > :last-child { border-bottom: 1px solid var(--border); }
.logs > * { grid-template-columns: 3.4em 1fr; align-items: baseline; }
/* the reasoning: blue, and it carries the drawing it read. a claim with no sheet is decoration */
.cite, .mark { border-left: 1px solid var(--blue); padding: 10px 0 10px 12px; }
.cite { display: grid; grid-template-columns: 96px 1fr; gap: 12px; }
.cite canvas { width: 96px; height: 72px; display: block; }
.cite ul { margin: 0; padding-left: 1.1em; }
.cite li + li { margin-top: 4px; }
/* the redline: what the engineer changes. the only red on the screen */
.mark { border-left-color: var(--red); }
/* the layers, the checker's list, a member's answer */
.layers > * { display: flex; gap: 10px; align-items: baseline; cursor: pointer; padding: 4px 0; }
.layers i { width: 20px; height: 0; border-top: 2px solid currentColor; display: inline-block; flex: none; transform: translateY(-4px); }
.layers .off { color: var(--border); }
.check > * { grid-template-columns: 18px 1fr; gap: 10px; align-items: start; }
.hl { background-image: linear-gradient(var(--yellow), var(--yellow)); background-size: 100% .92em; background-position: 0 .22em; background-repeat: no-repeat; }
.kv > * { grid-template-columns: 5em 1fr; }
.tight > * + * { margin-top: 6px; }
.mono { font-variant-numeric: tabular-nums; }
/* the seal is the only thing that arrives from off the sheet: it scales down and lands hard, once */
.seal.land { animation: land .24s cubic-bezier(.5, 0, 1, 1) both; }
@keyframes land { from { transform: scale(2.8); opacity: 0; } to { transform: scale(1); opacity: 1; } }

/* the app: a sheet that does not scroll. the title block, three panes, the foot */
body.app { height: 100vh; overflow: hidden; }
.appbar { display: flex; align-items: center; gap: 28px; height: 56px; padding: 0 24px; border-bottom: 1px solid var(--fg); }
.appbar svg, .appfoot svg { display: block; }
.appfoot { display: flex; align-items: center; gap: 24px; height: 40px; padding: 0 24px; border-top: 1px solid var(--fg); }
.appfoot a.off { color: var(--muted); pointer-events: none; }
.cols { display: grid; grid-template-columns: 360px 1fr 300px; height: calc(100vh - 96px); }
.pane { padding: 24px; overflow: auto; min-width: 0; display: flex; flex-direction: column; }
.pane + .pane { border-left: 1px solid var(--border); }
.pane > * + * { margin-top: 16px; }
.pane .ask { margin-top: auto; }
.thread { flex: 1; }
.stage { padding: 0; overflow: hidden; position: relative; }
.stage canvas { width: 100%; height: 100%; display: block; }
/* the ask at a point. one box, two positions: at the foot of the thread with nothing chosen,
   on the drawing with a leader to the member when there is one. the leader is the engineer's,
   so it is ink, and red only when the engineer is writing red. the deck and the site pin the
   same box on a quoted drawing: data-show="ask" in log.js. */
.pin { position: relative; }
.ask.at { position: absolute; z-index: 2; width: 300px; max-width: 55%; margin: 0; background: var(--bg); border: 1px solid var(--fg); padding: 12px 14px; }
.leader { position: absolute; inset: 0; width: 100%; height: 100%; z-index: 1; pointer-events: none; fill: none; stroke: var(--fg); stroke-width: 1; }
.leader.red { stroke: var(--red); }
.stage .seal { position: absolute; right: 32px; bottom: 32px; }
.stage .note { position: absolute; left: 24px; right: 24px; bottom: 20px; }
.hd { display: flex; align-items: baseline; gap: 12px; }


/* the field — the sheet's column grid, drawn. any sheet lays out on it with data-field="6x9": sheet.js
   draws the hairlines and bubbles their ends A B C / 1 2 3, the way a column line is called out, and
   the sheet's own children take grid-area on exactly that geometry. content is located on the sheet
   the way a member is — at an intersection, by address, never floated. --fx --fy are the margins the
   bubbles sit in. this is the whole layout language, and it is one language: a poster, a slide and a
   section of the site are the same field, and what changes between them is where on it the one thing
   lands, and how far past the edge it runs. cols by rows is the format's: a board is 6x9, a screen
   is wider than it is tall and takes 12x7, a section of the site is a field of its own height.
   the field takes the box it is given — a screen's, a board's — so it is said with the box's own
   weight, the way .screen.grid is, and the box's flex and padding give way to the grid. */
.field, .board.field { display: grid; padding: var(--fy, 9vmin) var(--fx, 8vmin); gap: 0;
  grid-template-columns: repeat(var(--cols, 6), minmax(0, 1fr));
  grid-template-rows: repeat(var(--rows, 9), minmax(0, 1fr)); }
.field > * { min-width: 0; min-height: 0; position: relative; z-index: 1; }
.field > .lines { position: absolute; inset: 0; z-index: 0; pointer-events: none; }
/* an overlay on the whole field: a leader, a dimension, anything drawn across cells rather than in
   one. it is a child of the field, so its box is the field's and a number the field hands out is the
   same number on the overlay. it is only ever a child of a field — header.sheet.over is the site's
   title block on a photograph, and neither rule reaches the other's element. */
.field > .over { position: absolute; inset: 0; z-index: 2; pointer-events: none; }
.lines svg, .over svg { display: block; width: 100%; height: 100%; }
/* a cell of the field. the type sits on the cell's own edge — top by default, because a grid line is
   read from above; .mid centres it, .bot hangs it from the line below */
.cell { display: flex; flex-direction: column; gap: 1.2vmin; }
.cell.mid { justify-content: center; }
.cell.bot { justify-content: flex-end; }
.field .right { text-align: right; align-items: flex-end; }
/* the label: what a thing is called, small, tracked, in the margin beside it */
.lab, .sched th { font-size: 1.7vmin; font-weight: 400; letter-spacing: .1em; text-transform: uppercase; color: var(--muted); }
.lab.ink { color: var(--fg); }
/* the schedule: the revision table's grammar — hairline rules, tabular figures, nothing but columns */
.sched { width: 100%; border-collapse: collapse; font-size: 2vmin; font-weight: 400; font-variant-numeric: tabular-nums; }
.sched th, .sched td { text-align: left; padding: 1vmin .6vmin; border-top: .1vmin solid var(--border); }
.sched tr:last-child td { border-bottom: .1vmin solid var(--border); }

/* the phone */
@media (max-width: 767px) {
  .sheet   { padding: 0 20px; }
  /* one column on a narrow sheet. twelve columns cannot fit: the columns collapse to nothing but the
     eleven gaps do not, and 11 x 40 = 440 is wider than the sheet. one column has no gap to pay for */
  .grid    { grid-template-columns: 1fr; }
  .grid > * { grid-column: 1 / -1; }
  .display { font-size: 64px; }
  .h1      { font-size: 44px; line-height: 52px; }
  .h2      { font-size: 36px; line-height: 42px; }
  .tb      { height: auto; flex-wrap: wrap; gap: 12px 20px; padding: 12px 0; }
  /* the nav wraps on a small sheet instead of running off it: left under the wordmark, two short rows, the way a title block stacks when the sheet is narrow */
  .tb nav  { flex-wrap: wrap; gap: 10px 14px; margin-left: 0; }
  .tb .no { display: none; }
  .cover:has(> .stamp) { --stamp: 56px; }
  .cover > .stamp { right: 20px; bottom: 24px; }
  .cover:has(> .stamp) > .bottom { padding-right: 76px; }
  .block   { padding: 40px 20px 20px; }
  .draw    { order: -1; }
}
