/* Say hello: the page's layout at rest, desktop from 1100 px and phone below it.
   From the approved prototype (A4 Letter, 4 Nov 2026) with its switches taken out. Every value is a studio token or
   a measure Aaron set by eye; the dated notes are his calls. Tablet (768 to 1099) is the phone's order with the letter
   capped at 560 px (spec decision 1); on desktop the drawing hangs from the letter (spec decision 2). */

html, body { margin: 0; }
body {
  background: var(--paper); color: var(--ink);
  font-family: var(--font-text); font-size: var(--size-body); line-height: var(--leading-body);
  -webkit-font-smoothing: antialiased;
}

/* the concrete tile, lighter on this site only (Aaron, 5 Nov: "use whatever is the most efficient"): AVIF at quality 50
   (2x 39 KB, 1x 13 KB) where the browser takes it, WebP at quality 70 where it takes WebP but not AVIF, and the
   studio's PNG for anything else. The build encodes both from the studio's PNG (build_say_hello.py, textures()) into
   concrete/; the studio's components.css and its PNGs are unchanged. Three declarations, each overriding the one before
   only where the browser can read it: a plain url() (no image-set), image-set() without type() (as components.css),
   then image-set() with type() candidates, which a browser without type() drops whole */
.c-concrete-1 {
  background-image: url("studio/concrete-texture/concrete-c1@1x.png");
  background-image: image-set(url("studio/concrete-texture/concrete-c1@1x.png") 1x, url("studio/concrete-texture/concrete-c1.png") 2x);
  background-image: image-set(url("concrete/concrete-c1@1x.avif") type("image/avif") 1x, url("concrete/concrete-c1.avif") type("image/avif") 2x,
                              url("concrete/concrete-c1@1x.webp") type("image/webp") 1x, url("concrete/concrete-c1.webp") type("image/webp") 2x,
                              url("studio/concrete-texture/concrete-c1@1x.png") 1x, url("studio/concrete-texture/concrete-c1.png") 2x); }

/* the sheet: a paper band, a fine inked border, the concrete ground inside (rules.md 2.3) */
.sheet { position: relative; box-sizing: border-box; min-height: 100svh; padding: var(--space-5); overflow: hidden; }
.ground { position: relative; box-sizing: border-box; min-height: calc(100svh - 2 * var(--space-5));
  display: grid; grid-template-columns: 1fr 560px; grid-template-rows: minmax(50px, 1fr) auto auto; column-gap: var(--space-8);
  padding: var(--space-6); }   /* 560 px: the letter's width (spec, Layout) */
/* the right column is one stack, drawing, PS slip, letter, set on the ground's bottom padding: the drawing hangs from the
   letter (spec decision 2, Aaron, 4 Nov). The first row is the room above the drawing: at least 50 px, where the drawing
   sits at 1440 x 900 (82 px from the ground's edge, under the name's line). A shorter window scrolls and the composition
   stays the same; a taller one widens the room above the drawing */

/* type voices */
.label { font-family: var(--font-mono); font-size: var(--size-label); letter-spacing: var(--tracking-label);
  text-transform: uppercase; line-height: var(--leading-body); margin: 0; }
.brand { font-weight: var(--weight-bold); }
.disp { margin: 0; font-family: var(--font-sans); font-weight: var(--weight-bold); text-transform: uppercase;
  letter-spacing: var(--tracking-display); line-height: var(--leading-display); }
.lead { margin: 0; }
.small { margin: 0; font-size: var(--size-small); }
.nw { white-space: nowrap; }

/* fields (rules.md 5.0): each casts the panel's shadow */
.fld { box-shadow: var(--plastic-shadow); }
.paperf { background: var(--paper); color: var(--ink); }

/* graphic devices: drawn images, never over text */
.fm { position: absolute; display: block; pointer-events: none; }
/* Three lobes at 960 px, turned (Aaron, 7 Nov, option A): the lobe on its neck reaches along the top, and the body runs
   down the right edge, on behind the PS slip and the letter, which paint over it (z-index 3 and 2); it touches no word
   and no line of the drawing */
.sg { width: 960px; right: -427px; top: -391px; transform: rotate(239deg); z-index: 1; }
@media (min-width: 1100px) and (max-width: 1279.98px) {   /* narrower desktops: the drawing sits nearer the edge */
  .sg { right: -583px; top: -363px; }
}
.sm { width: 120px; left: -44px; top: 300px; z-index: 1; }      /* Pad drops, across the left edge */
.sm-slip { display: none; }                                      /* Pad drops on phones: at the PS slip, below */

/* the name and work line. Aaron, 4 Nov: 2 px between them, and they move as one; the work line's last baseline sits on
   the first horizontal formwork seam (y 144 on the sheet), 12 px from the top padding since the work line took a third
   line. Measured in Space Grotesk */
.id { grid-column: 1; grid-row: 1 / 4; align-self: start; position: relative; z-index: 2; display: flex; flex-direction: column;
  gap: 2px; margin-top: 12px; }
.id .small { max-width: 470px; }   /* one line per sentence ("ecommerce" needs 460 px, Aaron, 4 Nov) */

/* the drawing, 470 px wide, at the top of the right column's stack. Its right edge 40 px inside the ground's (8 px inside
   the column), right up against the supergraphic (spec, Layout). Its bottom margin hangs it from the letter: the
   envelope's bottom corner sits 8 px above the PS slip (Aaron, 4 Nov), the slip rising 24 px over the letter and the
   corner 11.1 px above the drawing's box. 11.1 px is measured on air-4 (11.09998 at 470 px): a new drawing means
   measuring it again (the page tests' check_hang fails if the 8 px drifts). The drawing's ink and callout type come from
   the tokens (the build strips its baked values) */
.email { position: relative; z-index: 2; grid-column: 2; grid-row: 2; justify-self: end; width: 470px;
  margin: calc(-1 * var(--lift)) calc(40px - var(--space-6)) calc(var(--space-5) + 8px - 11.1px) 0; }
/* --lift: how much taller the letter is than at rest, while the form shows a state (form.js measures it; 0 at rest).
   The letter is anchored at the bottom, so it grows at its top edge (spec, The form), and the drawing, which hangs from
   it, rises with it into the room above: the page's height and everything else stay where they are. Desktop only: on
   phones the margin is the phone's own and the letter grows downward in the reading flow */
.ground { --lift: 0px; }
.email svg { display: block; width: 100%; height: auto; overflow: visible; fill: var(--line-ink); }
.email svg text { font-family: var(--font-mono); font-size: var(--size-label); letter-spacing: var(--tracking-label); }

.word { grid-column: 1; grid-row: 1 / 4; align-self: end; font-size: calc(var(--size-display) - var(--size-h1));
  position: relative; z-index: 2; }

/* the letter: the paper field outlined in fine ink (Aaron, 4 Nov), anchored at the bottom of the right column. Its
   holder (.post, no stacking context of its own) also carries the phone's Pad drops, painted under the letter */
.post { grid-column: 2; grid-row: 3; position: relative; }
.letter { position: relative; z-index: 2; padding: var(--space-6);
  display: flex; flex-direction: column; gap: var(--space-4); }
.head { display: flex; justify-content: space-between; }
.sig { font-family: var(--font-sans); font-weight: var(--weight-bold); font-size: var(--size-h2);
  line-height: var(--leading-heading); margin: 0 0 var(--space-3); }
.ps { position: absolute; z-index: 3; right: calc(-1 * var(--space-5)); top: calc(-1 * var(--space-5));
  padding: var(--space-3) 112px var(--space-3) var(--space-4); }
/* the slip overhangs the letter's corner 24 px each way; its right padding (112 px) is the empty right end, where the
   supergraphic's body passes behind the frost (spec, Colour: PS slip) */

/* the form. The field and Send are the one live thing (rules.md 5.2): the field an electric bold inked frame, Send the
   live button; the focus ring is the same live thing. Its parts share one grid: the label, field and Send (.fields) and
   the SENT message (.done) in one place, so Sent replaces them where they stand; CHECK and NOT SENT (.notes) under them,
   16 px down (the prototype's). Each message is a mono word in a fine box with its line under it (rules.md 6b.3): CHECK
   and NOT SENT in ink, SENT in electric (Aaron, 5 Nov; the box's classes in index.html) */
.form { display: grid; align-content: start; margin: 0; position: relative; }   /* start: a phone's held height (form.js),
   while it eases back to the natural one, is empty paper at the bottom, never spread over the rows */
.fields { grid-area: 1 / 1; align-self: start; display: flex; flex-direction: column; gap: var(--space-2); }
.done { grid-area: 1 / 1; align-self: start; }          /* the status region: empty, it takes no room */
.done:focus { outline: none; }                         /* it takes the focus at Sent only to keep it on the page; no ring */
.notes { grid-area: 2 / 1; display: grid; }             /* the alert region: CHECK and NOT SENT, one place */
.notes > .note { grid-area: 1 / 1; margin-top: var(--space-4); }
.note { display: flex; flex-direction: column; align-items: flex-start; gap: var(--space-1); }
.note[hidden] { display: none; }
/* Sent: the label, field and Send give way to the SENT message (form.js clears the address and marks them gone once
   the crossfade is over). They keep their size out of the flow, unseen and inert, so the letter's growth can be
   measured against them */
.fields.gone { position: absolute; top: 0; left: 0; right: 0; visibility: hidden; }
/* the Sent line at the signature's size on desktop (spec, States), the body size on phones */
.sent-line { font-family: var(--font-sans); font-weight: var(--weight-bold); font-size: var(--size-h2);
  line-height: var(--leading-heading); letter-spacing: var(--tracking-heading); }
/* the bot test's box (form.js adds it): no room unless Cloudflare ever puts an interactive challenge in it; then
   (form.js marks it .on) it sits under the form with the messages' 16 px gap */
.ts { grid-row: 3; grid-column: 1; }
.ts:empty { display: none; }
.ts.on { margin-top: var(--space-4); }
/* the honeypot: off the page, so a person never meets it (the field is inert and hidden from screen readers too) */
.hp { position: absolute; left: -10000px; top: 0; width: 1px; height: 1px; overflow: hidden; }
.hp input { all: unset; }   /* no browser border on it either (ban 30), though nobody sees it */
.row { display: flex; gap: var(--space-3); align-items: stretch; }
.ring { position: relative; flex: 1; min-width: 0; }
/* the field's focus ring is drawn by its holder (.ring.ink-focus.is-focus), only while the field has focus: an input
   draws no ::after of its own, so the .ink-focus the input carries is the checker's mark, and the ring is this one.
   Three classes, so it outranks components.css's .ink-focus.is-focus::after whatever order the sheets load in */
.ring.ink-focus:not(:focus-within)::after { content: none; }
.field { position: relative; height: var(--button-live-h); }
.field input { all: unset; box-sizing: border-box; width: 100%; height: 100%; padding: 0 var(--space-4);
  font-family: var(--font-text); font-size: var(--size-body); color: inherit; }
.field input::placeholder { font-family: var(--font-mono); font-size: var(--size-label); letter-spacing: var(--tracking-label);
  text-transform: uppercase; color: currentColor; opacity: 1; }   /* the hint speaks in the label voice, so it never reads as typed text */
/* Send, the live button (.live-button names it, rules.md 5.3): an electric fill and its own fine electric outline, and
   no focus ring (rules.md 5.2; Aaron, 4 Nov). The label is large bold paper (ban 4's one exception). Not .c-button-live: that one is a fixed 232 px
   wide, and Send fits its label on desktop and runs full width on phones */
.btn { all: unset; box-sizing: border-box; height: var(--button-live-h); padding: 0 var(--space-6); flex: none;
  display: inline-flex; align-items: center; justify-content: center; cursor: pointer; position: relative;
  font-family: var(--font-sans); font-weight: var(--weight-bold); font-size: var(--size-h2); line-height: var(--leading-heading);
  background: var(--electric); color: var(--paper); }

/* a mono word in a fine box (rules.md 6b.3) */
.bx { position: relative; display: inline-block; padding: var(--space-1) var(--space-2); white-space: nowrap; }
/* sent.html, the plain Sent page (scripts off): its link back, underlined in fine ink (rules.md 3.1), with the focus ring */
.back { color: inherit; text-decoration: none; display: inline-block; }
.back > span { position: relative; display: inline-block; padding-bottom: var(--space-1); }
h1.bx { font-weight: var(--weight-regular); }   /* sent.html's heading is the SENT box, in the label voice */

/* the opening (opening.js; spec, Motion breakdown). Its frames sit over the master drawing, one shown at a time */
.seqf { position: absolute; inset: 0; pointer-events: none; }
.seqf .sf { display: none; }
.seqf .sf.on { display: block; }
/* its first frame, before opening.js takes over: index.html's head marks html.opening before anything paints (never
   with reduced motion or without scripts). The sealed envelope (f00) stands large in the middle of the screen, sized
   to the drawing alone (x 150 to 470 of its 470 x 320 frame, a square) as large as fits 90% of the width or 80% of the
   height, the close-up opening.js measures and takes over with; the letter, SAY HELLO and the master are not shown
   yet. Fixed, so it is placed before the page's own layout settles (the fonts) */
html.opening .letter, html.opening .word, html.opening .email .art { visibility: hidden; }
html.opening .seqf { --closeup: min(90vw, 80svh); position: fixed; right: auto; bottom: auto;
  left: calc(50vw - var(--closeup) * 310 / 320); top: calc(50svh - var(--closeup) / 2); width: calc(var(--closeup) * 470 / 320); }
html.opening .seqf .sf[data-f="0"] { display: block; }
/* the tri-fold copy (C1, Aaron, 4 Nov): three panels of the letter, the top and bottom folded over the middle.
   Transforms belong to the timeline, not to CSS */
.gfold { position: fixed; z-index: 9; pointer-events: none; visibility: hidden; }
.gfold .gin { position: absolute; inset: 0; perspective: 1400px; }   /* carries the lift, scaled about its centre */
/* the field's own shadow, as paper: raised and soft in the air, the contact shadow once it is down (Aaron, 4 Nov;
   rules.md 4.1, ban 15: the raised shadow only for a field in the air, resolving to the contact shadow as it sets) */
.gfold .gsh { position: absolute; inset: 0; opacity: 0; }
.gfold .gsh.lift { box-shadow: var(--raised-shadow); }
.gfold .gsh.contact { box-shadow: var(--plastic-shadow); }
.gfold .fp { position: absolute; left: 0; right: 0; transform-style: preserve-3d; }
.gfold .ff, .gfold .fb { position: absolute; inset: 0; -webkit-backface-visibility: hidden; backface-visibility: hidden; }   /* Safari and the LinkedIn app on iPhone need the prefix, or the paper back covers the letter */
/* each face is its third of the letter, plus 8 px either side (and above the top, below the bottom) for the ink
   outline; a box with hidden overflow, not a clip-path, which WebKit (Safari, the LinkedIn app) fails to draw in 3D */
.gfold .ff { overflow: hidden; inset: 0 -8px; }
.gfold .top .ff { inset: -8px -8px 0; }
.gfold .bot .ff { inset: 0 -8px -8px; }
.gfold .fb { background: var(--paper); transform: rotateX(180deg); }
.gfold .top { z-index: 3; } .gfold .bot { z-index: 2; } .gfold .mid { z-index: 1; }
.word .wl { display: inline-block; }   /* SAY HELLO split in lines while it enters */

/* phone, below 1100 px: one column in reading order */
@media (max-width: 1099.98px) {
  .sheet { padding: var(--space-3); }
  .ground { min-height: calc(100svh - 2 * var(--space-3)); display: flex; flex-direction: column;
    padding: var(--space-5) var(--space-4); gap: var(--space-5); }
  .id { margin-top: 21px; align-self: auto; }   /* the last baseline on the first seam, which falls at y 132 here */
  .email { width: 100%; max-width: 470px; margin: var(--space-5) 0 0; }   /* the column's width up to its desktop size, kept
     left in line with the name (spec, Phone; Aaron, 4 Nov) */
  .word { align-self: flex-start; margin-top: var(--space-6); font-size: calc(var(--size-h1) * 1.75); }
  .post { align-self: stretch; margin-top: var(--space-8); }
  .letter { padding: var(--space-5) var(--space-4); }
  /* the PS overlaps the letter 16 px and hangs 12 px off its right edge, like desktop (Aaron, 4 Nov), however tall it
     wraps; its empty right end is 72 px here, so the slip fits the column */
  .ps { right: -12px; top: auto; bottom: calc(100% - 16px); padding: var(--space-3) 72px var(--space-3) var(--space-4); }
  .row { flex-direction: column; }
  .btn { width: 100%; }
  /* the 640 px file here (the <picture>): the lobe on its neck along the top, the body down the edge, as on desktop */
  .sg { width: 640px; right: -400px; top: -290px; transform: rotate(145deg); }
  /* Pad drops at the PS slip's top right, behind its right end (Aaron, 4 Nov: "move the design device to the top right
     of the frosted panel on mobile"). It hangs from the same line as the slip, the letter's top edge: 6 px past the
     slip's right edge (the slip is 12 px past the letter's), its foot 5.73 px above the slip's bottom edge (16 px into
     the letter), where Aaron placed it at 390. In the letter's holder, before the letter, so the letter paints over it */
  .sm { display: none; }
  .sm-slip { display: block; width: 84px; right: -18px; bottom: calc(100% - 10.265625px); z-index: 2; }
  .head { flex-direction: column; gap: var(--space-1); }
  .sent-line { font-size: var(--size-body); line-height: var(--leading-body); }   /* the prototype's: still bold */
  /* the page never scrolls by itself as the letter grows (spec, The form). Chromium's scroll anchoring would: once the
     letter has eased back (form.js) with the page scrolled to its end, the next message's growth scrolled the page down
     by the whole difference in one frame, to put the focused field back where it stood before the shrink. Nothing on
     the page needs anchoring: nothing above the letter changes size */
  html { overflow-anchor: none; }
}

/* tablet, 768 to 1099 px: the phone's order, with the letter capped at 560 px wide and kept left, in line with the
   name, so the letter's lines stay a readable length (spec decision 1, Aaron, 4 Nov); the drawing keeps its desktop
   470 px, as on phones. The PS slip and Pad drops ride on the letter as on phones */
@media (min-width: 768px) and (max-width: 1099.98px) {
  .post { max-width: 560px; }
}

/* forced colours (the system's high-contrast override): every inked line is a mask over its background colour, which
   the browser repaints to Canvas, so the lines vanish (the field's edge, Send's edge, the focus ring). Set the masks'
   colour to the system's own: the lines stay drawn in the ink hand, in the visitor's colours. Send takes no ring in
   the design, but here, where the visitor's colours replace the design, its edge becomes a Highlight ring on focus,
   bold and 3 px outside, like the field's */
@media (forced-colors: active) {
  .ink-frame::after { background-color: CanvasText; }
  .ink-focus:focus-visible::after, .ink-focus.is-focus::after { background-color: Highlight; }
  .btn:focus-visible { --ink-offset: calc(var(--focus-offset) + var(--line-bold) / 2);
    --ink-h0: var(--ink-rule-h-bold-a-start); --ink-h1: var(--ink-rule-h-bold-a-end);
    --ink-h2: var(--ink-rule-h-bold-b-start); --ink-h3: var(--ink-rule-h-bold-b-end);
    --ink-v0: var(--ink-rule-v-bold-a-start); --ink-v1: var(--ink-rule-v-bold-a-end);
    --ink-v2: var(--ink-rule-v-bold-b-start); --ink-v3: var(--ink-rule-v-bold-b-end); }
  .btn:focus-visible::after { background-color: Highlight; }
}
