/* Shared components: used by more than one section or page. Reads every value from tokens.css.
   Defined once here; sections compose them and never restyle their basics. */

/* ---------- Eyebrow: the small label that opens a section ("The studio") ---------- */

.eyebrow {
  font-size: var(--text-small);
  font-weight: var(--weight-demi);
  letter-spacing: var(--tracking-body);
  line-height: var(--leading-text);
  color: var(--stone);
}
.on-ink .eyebrow { color: var(--stone-on-ink); }

/* ---------- Statement: the large two-tone line that opens a section or page ---------- */
/* KOBU's big paragraph: ink first, the stone half after it. Measured to its capitals at the
   top, like every statement. The home sections still carry their own copies (intro,
   services, closing); they fold into this with the rest of the design system (BACKLOG id:02). */

.statement {
  font-size: var(--text-statement);
  font-weight: var(--weight-statement);
  letter-spacing: var(--tracking-statement);
  line-height: var(--leading-statement);
  text-wrap: pretty;
  text-box: trim-start cap alphabetic;
}
.statement__muted { color: var(--stone); }
.on-ink .statement__muted { color: var(--stone-on-ink); }

/* ---------- Page head: an eyebrow, then its statement ---------- */
/* Every page after home (portfolio, about, contact, the legal pages, 404) and the home's
   services band. The eyebrow, the eyebrow gap, then the statement on eight columns, the stone
   half on a line of its own (so a title never ends on a stray word), and the tile gap under
   its last baseline (the services band sets its own: --space-head to its list). */

.page-head {
  display: grid; grid-template-columns: repeat(12, minmax(0, 1fr)); column-gap: var(--gutter);
  margin-bottom: var(--space-tiles);
}
.page-head__eyebrow { grid-column: 1 / -1; margin-bottom: var(--space-eyebrow); text-box: trim-start cap alphabetic; }
.page-head__statement { grid-column: 1 / span 8; text-box: trim-both cap alphabetic; } /* ends on its baseline, so the gap under it is the token */
.page-head__statement .statement__muted { display: block; text-wrap: balance; }
@media (max-width: 989px) {
  .page-head__statement { grid-column: 1 / -1; }
}
@media (max-width: 600px) {
  .page-head__eyebrow { margin-bottom: var(--space-eyebrow-phone); }
}

/* ---------- Plain page: the legal pages and the 404 (tools/build-pages.py) ---------- */
/* The portfolio's shell (header, page top, page end), the page head, then plain text.
   Luke 10/12: no new text styles, so the legal text is the body text and its headings are
   the body size in the demi weight. One measure, six columns. */

.page-plain {
  padding: calc(var(--header-height, 88px) + var(--space-page-top)) var(--gutter)
    calc(var(--space-page-end) - var(--space-footer-top)); /* plus the footer's own paper */
  background: var(--paper);
  color: var(--ink);
}
/* The text is a .note: the rule, then the text at --space-note, on the measure (as About's bio) */
.legal { max-width: var(--measure); }
@media (max-width: 989px) { .legal { max-width: none; padding-right: max(0px, calc(100% - var(--measure))); } } /* its rule runs the page's width; the text keeps the measure */
.legal :is(p, li) {
  font-size: var(--text-body); font-weight: var(--weight-body);
  letter-spacing: var(--tracking-body); line-height: var(--leading-text); text-wrap: pretty;
}
/* A heading is a label for the text under it: the body size in demi, its baseline the cell
   label gap above that text's capitals. Sections are --space-near apart; paragraphs one line. */
.legal h2 {
  margin-top: var(--space-near); margin-bottom: calc(var(--space-cell-label) - var(--cap-gap) - var(--descent-gap));
  font-size: var(--text-body); font-weight: var(--weight-demi); letter-spacing: var(--tracking-body); line-height: var(--leading-text);
}
.legal :is(p, ul) + :is(p, ul) { margin-top: var(--space-paragraph); }
.legal ul { padding-left: 1.2em; list-style: disc; }
.legal li + li { margin-top: calc(var(--space-paragraph) / 4); } /* a list's items: closer than paragraphs */
.legal__date { margin-top: var(--space-near); color: var(--stone); }
.not-found { display: flex; flex-wrap: wrap; gap: var(--space-cell-label) var(--space-near); }
.not-found .arrow-link { margin-top: calc(-1 * var(--cap-gap)); } /* the title's last baseline to the links' capitals: the head's tile gap */

/* ---------- Arrow link: KOBU's "Explore ↗", with the hero caption's arrow ---------- */
/* The arrow slides on hover. One size, the body text's (the large version went 9/30). */

.arrow-link {
  display: inline-flex; align-items: center; gap: .4em;
  font-size: var(--text-body);
  font-weight: var(--weight-demi);
  letter-spacing: var(--tracking-body);
  line-height: var(--leading-text);
}
.arrow-link svg {
  width: .8em; height: auto; flex: none;
  transition: translate var(--duration) var(--ease-out);
}
.arrow-link:hover svg, .arrow-link:focus-visible svg { translate: .3em 0; }
/* Back: the arrow before the text, pointing left, and sliding left ("Back to home") */
.arrow-link--back svg { scale: -1 1; }
.arrow-link--back:hover svg, .arrow-link--back:focus-visible svg { translate: -.3em 0; }
.arrow-link:focus-visible { outline: 1px solid currentColor; outline-offset: var(--focus-offset); }
/* Small: a way around the site rather than a call to act ("Back to portfolio", "View all"),
   in the eyebrow's size, weight and stone, ink on hover (Luke 10/9: the body-size ones read
   large and black there) */
.arrow-link--small { font-size: var(--text-small); color: var(--stone); transition: color var(--duration-quick) var(--ease-out); }
.arrow-link--small:hover, .arrow-link--small:focus-visible { color: var(--ink); }
/* On a touch screen a small link, or a link alone in its cell, takes a finger-sized hit area
   around its letters (about 40px tall) without moving anything */
@media (pointer: coarse) {
  :is(.arrow-link--small, .contact__cell .text-link, .inquiry__or .text-link) { position: relative; }
  .arrow-link--small::after { content: ""; position: absolute; inset: -14px -10px; }
  :is(.contact__cell, .inquiry__or) .text-link::after { content: ""; position: absolute; inset: -10px -6px; }
}

/* ---------- Text link: a link inside running text ---------- */
/* An email address in a line of gray text, the contact details, the legal pages' links (Luke
   10/1: the address underlined, always). A 1px underline a fifth of the size below the text;
   stone on hover. (Brand pass 10/12: the legal pages had a 1.5px underline, the rest 1px.) */

:is(.text-link, .legal a) {
  color: var(--ink);
  text-decoration: underline; text-decoration-thickness: 1px; text-underline-offset: .2em;
  transition: color var(--duration-quick) var(--ease-out);
}
:is(.text-link, .legal a):hover { color: var(--stone); }
:is(.text-link, .legal a):focus-visible { outline: 1px solid currentColor; outline-offset: var(--focus-offset); }

/* ---------- Credit: who said it, then their title in stone ---------- */
/* The closing's client review and a project page's quote (Luke 10/1): the name in the body
   size and the demi weight, the title under it in the small size and stone, set close. The
   name's baseline to the title's capitals is --space-cite-role. */

.credit {
  display: block; /* a block, so a layout's text-box trims apply to its lines */
  font-size: var(--text-body); font-weight: var(--weight-body);
  letter-spacing: var(--tracking-body); line-height: var(--leading-text);
}
.credit__name, .credit__role { display: block; }
.credit__name { font-weight: var(--weight-demi); text-box: trim-end cap alphabetic; }
.credit__role {
  margin-top: var(--space-cite-role);
  font-size: var(--text-cite-role); line-height: var(--leading-cite-role);
  color: var(--stone);
  text-wrap: balance; /* a title, then a company: a line each (Luke 10/1) */
  text-box: trim-start cap alphabetic;
}

/* ---------- Caption: the one line under a photo ---------- */
/* Project tiles. One style for the whole line (Luke 9/30): the name at left, where it is at
   right, wrapping under it only when the line runs out of room. --space-caption is measured to
   the capitals, so the gap is what you see. (A project page's figure captions are the eyebrow
   style, the same gap: project.css .story-caption.) */

.caption {
  display: flex; flex-wrap: wrap; justify-content: space-between; align-items: baseline;
  gap: 0 var(--space-inline);
  /* Measured to the letters: the photo to the capitals, and the baseline is the bottom (a
     flex line with an arrow in it can't be trimmed, so the margins take the leading back) */
  margin-top: calc(var(--space-caption) - var(--cap-gap));
  margin-bottom: calc(-1 * var(--descent-gap));
  font-size: var(--text-caption);
  font-weight: var(--weight-caption);
  letter-spacing: var(--tracking-body);
  line-height: var(--leading-text);
}
.caption__name { display: inline-flex; align-items: center; gap: .4em; }
.caption__name svg { width: .8em; height: auto; flex: none; transition: translate var(--duration) var(--ease-out); }


/* ---------- Note: a short paragraph under a hairline, then its link or button ---------- */
/* Spenser's bio in the home intro and on About, the line under the home's selected works, a
   project page's feature and text notes (Luke 9/30: the same style for all). Sections place
   it; it never restyles. */

.note {
  padding-top: var(--space-note);
  border-top: var(--hairline-width) solid var(--hairline);
}
.note p {
  font-size: var(--text-body);
  font-weight: var(--weight-body);
  letter-spacing: var(--tracking-body);
  line-height: var(--leading-text);
  text-wrap: pretty;
}
/* Measured to the letters: the rule to the first capitals, the last baseline to the link's
   capitals, both --space-note; paragraphs a line apart */
.note p:first-child { text-box: trim-start cap alphabetic; }
.note p:last-of-type { text-box: trim-end cap alphabetic; }
.note p:only-of-type { text-box: trim-both cap alphabetic; }
.note p + p { margin-top: var(--space-paragraph); }
.note .arrow-link { margin-top: calc(var(--space-note) - var(--cap-gap)); }
.note .button { margin-top: var(--space-note); }

/* ---------- Field: one line of a form ---------- */
/* The inquiry form (the closing section; the contact page will use it too). KOBU's ruled list:
   a rule above each field and never under the last (Luke 9/30), the label in the eyebrow
   style, then what's typed in the body text. No boxes, no fills. Focus turns
   the label ink (Luke 10/1: not the rule); a missing answer turns the rule ink, with a line
   under it saying what's needed. The gaps are the cells' (tokens.css): the rule to the label's
   capitals, the label to the typed line, the typed line's baseline to the next rule.
   Layouts place fields and may draw the rule themselves (--field-rule: none). */

.field {
  --field-rule-color: var(--hairline);
  position: relative;
  display: flex; flex-direction: column;
  min-width: 0; margin: 0;
  padding: var(--field-top) 0 calc(var(--space-cell) - var(--field-descent));
  --field-top: var(--space-cell-top); /* the rule to the label's capitals; the closing's sheet adds the rule it draws itself */
  border: 0; border-top: var(--field-rule, var(--hairline-width) solid var(--field-rule-color));
  /* Where the typed line's text sits in its box, from the font's metrics (Switzer 10/14:
     content area 1.218em, ascent .97em, descent .248em, trimmed capitals .75em; tokens.css
     --cap-gap): the box runs --field-descent under the baseline and starts --field-cap-gap
     above the capitals. The padding and margin take them back off, so the gaps are measured
     to the letters. */
  --field-descent: calc((var(--leading-text) / 2 - .361) * var(--text-body));
  --field-cap-gap: calc((var(--leading-text) / 2 - .389) * var(--text-body));
  font-size: var(--text-body);
  transition: border-color var(--duration-quick) var(--ease-out);
}
.field.is-invalid { --field-rule-color: var(--ink); }
.field:focus-within .field__label { color: var(--ink); } /* the field being typed in (Luke 10/1: the label only, the rule stays) */
.field__label { transition: color var(--duration-quick) var(--ease-out); }

.field__label {
  float: left; width: 100%; padding: 0; /* lets a fieldset's legend sit in the flow like a label */
  display: block; /* a block, so the trim below applies */
  font-size: var(--text-small);
  font-weight: var(--weight-demi);
  letter-spacing: var(--tracking-body);
  line-height: var(--leading-text);
  color: var(--stone);
  text-box: trim-both cap alphabetic;
}
/* "Optional", at the label's far end, its capitals level with the label's */
.field__hint {
  position: absolute; top: var(--field-top); right: 0;
  font-weight: var(--weight-body);
  text-box: trim-both cap alphabetic;
}

/* What's typed: the body text in ink. The label's baseline to its capitals is
   --space-cell-label (the line's own leading and the font's ascent above the capitals are
   taken off). */
.field__input {
  display: block; width: 100%;
  margin: calc(var(--space-cell-label) - var(--field-cap-gap)) 0 0;
  padding: 0; border: 0; border-radius: 0; background: none; outline: none;
  -webkit-appearance: none; appearance: none;
  font: inherit;
  font-size: var(--text-body);
  font-weight: var(--weight-body);
  letter-spacing: var(--tracking-body);
  line-height: var(--leading-text);
  height: calc(var(--leading-text) * 1em);
  color: var(--ink);
  caret-color: var(--ink);
}
textarea.field__input {
  height: auto; min-height: calc(3 * var(--leading-text) * 1em); max-height: calc(10 * var(--leading-text) * 1em);
  resize: none; field-sizing: content; /* grows with what's typed, where supported */
}
/* Autofill keeps the paper and the ink (Chrome and Safari tint it otherwise) */
.field__input:-webkit-autofill, .field__input:autofill {
  -webkit-box-shadow: 0 0 0 100px var(--paper) inset;
  -webkit-text-fill-color: var(--ink);
}

/* Select: the native list, with the site's own chevron (an inline SVG after it, drawn like
   the arrows) at the line's right end. The chevron is pulled up into the select's line, its
   middle on the capitals' middle (.747em down the line at running leading), and gives the
   space back under it, so it stays with the line however tall the cell grows. */
.field--select select { padding-right: 28px; cursor: pointer; }
/* Its prompt ("Choose one") in stone until an answer is picked; the list itself stays ink */
.field--select select:invalid { color: var(--stone); }
.field--select option { color: var(--ink); }
.field__chevron {
  align-self: flex-end; flex: none;
  width: .7em; height: .4em; overflow: visible;
  margin: calc(.547em - var(--leading-text) * 1em) 1px calc(var(--leading-text) * 1em - .947em) 0;
  pointer-events: none;
  color: var(--ink);
}

/* A choice (radio buttons) in one line: a small ring, filled with a dot when picked */
.field__options {
  display: flex; flex-wrap: wrap; gap: 8px 28px;
  margin-top: calc(var(--space-cell-label) - var(--field-cap-gap));
}
.field__option {
  display: inline-flex; align-items: center; gap: 10px;
  font-size: var(--text-body); font-weight: var(--weight-body);
  letter-spacing: var(--tracking-body); line-height: var(--leading-text);
  color: var(--ink);
  cursor: pointer;
}
.field__option input {
  -webkit-appearance: none; appearance: none;
  flex: none; width: 15px; height: 15px; margin: 0;
  border-radius: 50%;
  background: var(--paper);
  box-shadow: inset 0 0 0 var(--hairline-width) var(--stone);
  cursor: pointer;
  transition: box-shadow var(--duration-quick) var(--ease-out), background-color var(--duration-quick) var(--ease-out);
}
/* Picked: an ink ring around an ink dot, the paper between them */
.field__option input:checked {
  background: var(--ink);
  box-shadow: inset 0 0 0 var(--hairline-width) var(--ink), inset 0 0 0 4px var(--paper);
}
.field__option input:focus-visible { outline: 1px solid var(--ink); outline-offset: var(--focus-offset); }

/* What's missing: a short line under the field, in the label's size, in ink */
.field__error {
  /* the typed line's baseline to these capitals, and this baseline to the next rule, are the
     same gaps as the label's */
  margin: calc(var(--space-cell-label) - var(--field-descent)) 0 var(--field-descent);
  font-size: var(--text-small);
  font-weight: var(--weight-demi);
  line-height: var(--leading-text);
  color: var(--ink);
  text-box: trim-both cap alphabetic;
}

/* ---------- Read-through: a statement's lead sentence fills in as you scroll ---------- */
/* Luke 9/30, from realfood.gov. js/read.js splits a statement's .read span into words
   (.read__w) and letters (.read__l) and lights the letters one by one as the page scrolls,
   both ways (Luke's pick: letter by letter). It starts in the statement's own muted gray, so
   the whole statement opens gray. Without JS the lead just shows lit. */

.read { --read-lit: var(--ink); --read-dim: var(--stone); }
.on-ink .read { --read-lit: var(--paper); --read-dim: var(--stone-on-ink); }
.read__w { white-space: nowrap; } /* a word's letters never split across lines */
.read__l:not(.is-lit) { color: var(--read-dim); }

/* ---------- Photo frame: a clipped window with the photo inside it ---------- */
/* Static: no scroll reveals or parallax below the hero (Luke 9/30: only the hero moves; the
   reveals may come back, designed fresh). .frame__shift holds the photo for the tile hover. */

.frame { position: relative; overflow: hidden; background: var(--photo-backdrop); } /* placeholder until the photo loads, and what a tile's faded photo shows */
.frame__shift { position: absolute; inset: 0; }
.frame__shift img {
  display: block; width: 100%; height: 100%;
  object-fit: cover; object-position: var(--focus, center);
}

/* ---------- Project tile: photo, then a caption (name with an arrow, where it is) ---------- */
/* The whole tile is the link (the old site hid that the photos were clickable). On hover
   the arrow slides and the photo does KOBU's gallery hover, copied exactly (Luke 9/30): it
   zooms to 1.05 over 1.5s and fades to .8 over .6s, both on the same curve (--ease-out is
   KOBU's), so the light backdrop shows through and lightens it. */

.tile { display: block; }
.tile__frame { aspect-ratio: var(--tile-ratio, 3 / 2); }
.tile .frame__shift img {
  transition: scale var(--hover-photo-zoom) var(--ease-out), opacity var(--hover-photo-fade) var(--ease-out);
}
.tile:focus-visible .frame__shift img {
  scale: var(--hover-photo-scale);
  opacity: var(--hover-photo-opacity);
}
.tile:focus-visible { outline: none; }
.tile:focus-visible .caption__name { outline: 1px solid currentColor; outline-offset: var(--focus-offset); }
.tile:focus-visible .caption__name svg { translate: .3em 0; }
/* Only with a mouse: a phone holds :hover after a tap, so the tile you opened came back faded
   when you stepped back to the page */
@media (hover: hover) {
  .tile:hover .frame__shift img { scale: var(--hover-photo-scale); opacity: var(--hover-photo-opacity); }
  .tile:hover .caption__name svg { translate: .3em 0; }
}

/* ---------- Tile grid: project tiles two to a row, every one the same 3:2 ---------- */
/* The portfolio (Luke 10/9) and a project page's "More projects". One column on phones. */

.tile-grid {
  display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); column-gap: var(--gutter);
  row-gap: var(--space-tiles);
  list-style: none;
}
@media (max-width: 600px) {
  .tile-grid { grid-template-columns: minmax(0, 1fr); }
}

/* ---------- More: a ruled head (eyebrow, "View all"), a statement, then tiles ---------- */
/* The home's and About's "Selected works", a project page's "More projects" (Luke 10/12: the
   eyebrow alone was easy to scroll past, so a statement title follows it; brand pass: the
   home's too, which had the eyebrow alone). The rule to the eyebrow's
   capitals and its baseline to the statement are the same gap; the statement to the tiles is
   the portfolio's statement-to-tiles gap. */

.more-head {
  --gap: var(--space-rule-eyebrow);
  display: flex; justify-content: space-between; align-items: baseline; gap: var(--space-inline);
  padding-top: var(--gap);
  border-top: var(--hairline-width) solid var(--hairline);
  margin-bottom: var(--gap);
}
.more-head .eyebrow { text-box: trim-both cap alphabetic; }
.more-head .arrow-link { margin-block: calc(-1 * var(--cap-gap)) calc(-1 * var(--descent-gap)); } /* its letters only, as the eyebrow's (it's a flex line: no trim) */
.more-title { margin-bottom: var(--space-tiles); text-box: trim-both cap alphabetic; }

/* ---------- Cell: a ruled cell in a title block ---------- */
/* The footer (Luke 9/30), the closing's sheet (10/1) and the contact details (10/12): cells on
   a grid whose rules run along their tops and meet in the gaps, with an upright between
   neighbors, like an architect's drawing title block. Each cell draws its own top rule,
   reaching half the gap past it where it meets a neighbor (--rule-l / --rule-r: 0 at the grid's
   edges), and with --vr: block the upright on its left, in the middle of the gap. The grid sets
   --half (minus half its column gap) and, on ink, --cell-line. --cell-rules: none turns the
   drawing off where a layout doesn't use it (the closing and contact below 990).
   Borders, not filled boxes: a browser rounds a border's width the same way wherever it lands,
   where a 1.5px box can come out 1px or 2px depending on where it sits (Luke 10/1: some lines
   looked thinner than others). Spacing inside a cell is the cell tokens (tokens.css). */

.cell { position: relative; }
.cell::before, .cell::after { content: ""; position: absolute; border: 0 solid var(--cell-line, var(--hairline)); }
.cell::before {
  display: var(--cell-rules, block);
  top: 0; left: var(--rule-l, var(--half)); right: var(--rule-r, var(--half)); border-top-width: var(--hairline-width);
}
.cell::after {
  display: var(--vr, none);
  top: 0; bottom: 0; left: calc(var(--half) - var(--hairline-width) / 2); border-left-width: var(--hairline-width);
}

/* ---------- Coming soon: every page not built yet (Crescent's placeholder) ---------- */
/* One line in the statement style and the way back, the same on every page (a project with no
   page yet). The pages are generated by tools/build-pages.py. The footer's ink band closes it, so it needs no rule
   of its own (it had one until the footer was rebuilt, 9/30). */

.coming-soon {
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: var(--space-near);
  min-height: 100vh; min-height: 100svh;
  padding: calc(var(--header-height, 88px) + var(--space-page-top)) var(--gutter) var(--space-page-end);
  background: var(--paper);
  color: var(--ink);
  text-align: center;
}
/* The title is a .statement */

/* ---------- Footer (every page) ---------- */
/* The title block of an architect's drawing (Luke 9/30 picked it from three; the old footer,
   four loose columns and the name across the bottom, read as a template). Every piece is a
   cell on the 12-column grid, in thirds: the logo | Studio over Pages | Contact over Follow,
   and the bottom line under the same thirds (copyright | Privacy Policy, Terms & Conditions |
   the website credit, in Crescent's wording). The last third starts on column 9, as the
   portrait and the right-hand tiles do. Luke 9/30, second pass: tighter cells and the first
   upright a column further left. Third: the same space above and below every cell's text.
   Fourth: the logo fills its cell top to bottom. Fifth (10/1): it fills it side to side too,
   at every width, with the same space above it, below it and beside it (Luke: "a block
   rectangle in its own cell"). So its upright is set by the logo, not the grid: the logo is
   as tall as the two rows of text beside it, as wide as that makes it, and its cell is that
   wide. The middle takes what's left. Sixth (10/1): the logo moved to the right. On the dials: cell padding, label to
   text, line spacing, logo stroke; the logo and its cell follow all four.

   One space through the whole block, --space-cell, measured from a line's inner edge: a
   rule to the capitals under it, the last baseline to the next rule, an upright to the text
   after it, and the logo's top, right and bottom. Each cell draws the rule along its top and,
   where it has a neighbor to its left, the upright between them, in the middle of the gap, so
   they meet; the uprights run down through the bottom line. --rule-l / --rule-r: how far a
   cell's top rule reaches past it (0 at the grid's edges, half the gap otherwise). --vr: block
   draws the upright. */

.site-footer {
  --half: calc(var(--footer-gap) / -2);
  /* The band's edge to the first row's cells: its edge to the titles' capitals, less the
     cells' own space above them. Under the last row: the last baseline to the band's bottom
     edge, less the cells' own space. */
  --band-pad: calc(var(--space-footer-band-top) - var(--space-cell-top) - var(--hairline-width));
  --band-pad-bottom: calc(var(--space-footer-band-bottom) - var(--space-cell));
  display: grid; grid-template-columns: var(--footer-cols); column-gap: var(--footer-gap); /* tokens.css */
  /* Ink, the brief's one dark band (Luke 10/1, picked on the dials over paper and linen). The
     page's paper runs on above it (a border, since the body under the page is ink). */
  border-top: var(--space-footer-top) solid var(--paper);
  padding: var(--band-pad) var(--gutter) var(--band-pad-bottom);
  background: var(--ink);
  color: var(--paper);
  font-size: var(--text-footer);
  font-weight: var(--weight-body);
  letter-spacing: var(--tracking-body);
  line-height: var(--leading-text);
}
.site-footer .eyebrow { color: var(--stone-on-ink); }
.site-footer { --cell-line: var(--hairline-on-ink); }
.site-footer a { transition: color var(--duration-quick) var(--ease-out); }
.site-footer a:focus-visible { outline: 1px solid currentColor; outline-offset: var(--focus-offset); }

/* Cells (.cell, above): the logo, the four groups and the three parts of the bottom line */
.site-footer__base { display: contents; }
.site-footer .cell {
  /* From the rules' inner edges, as text is trimmed to its capitals at the top and its
     baseline at the bottom: the rule to the title's capitals (--space-cell-top, tighter,
     Luke 10/1), and the last baseline to the next rule (the space) */
  padding: calc(var(--space-cell-top) + var(--hairline-width)) 0 var(--space-cell);
}
/* No rule along the band's top (Luke 10/1: tried with one, then back to none): its edge is the
   line. The first row (the logo, and on desktop Contact and Studio beside it) draws none, and
   its uprights run up to the edge; the bottom line's run down to the band's bottom edge, the
   window's (Luke 10/1), so they cross the band top to bottom. */
.site-footer .site-footer__brand::before { display: none; }
@media (min-width: 1040px) {
  .site-footer :is(.site-footer__group--contact, .site-footer__group--studio)::before { display: none; }
  .site-footer :is(.site-footer__group--contact, .site-footer__brand)::after { top: calc(-1 * var(--band-pad)); }
}
.site-footer .site-footer__base > *::after { bottom: calc(-1 * var(--band-pad-bottom)); }

/* Three stacks, each in its column (tokens.css --footer-cols): Studio over Pages over the
   legal links at left, on four of the site's columns; Contact over Follow over the credit in
   the middle (Luke 10/1: swapped, they were at left); the logo over the copyright at right
   (Luke 10/1). The left stack has no upright and its rules start at the edge; the right one
   stops its rules at the edge. */
.site-footer__group--studio { grid-column: 1; grid-row: 1; --rule-l: 0; }
.site-footer__group--pages { grid-column: 1; grid-row: 2; --rule-l: 0; }
.site-footer__legal { grid-column: 1; grid-row: 3; --rule-l: 0; }
.site-footer__group--contact { grid-column: 2; grid-row: 1; --vr: block; }
.site-footer__group--follow { grid-column: 2; grid-row: 2; --vr: block; }
.site-footer__credit { grid-column: 2; grid-row: 3; --vr: block; }
.site-footer__brand { grid-column: 3; grid-row: 1 / span 2; --vr: block; --rule-r: 0; }
.site-footer__copy { grid-column: 3; grid-row: 3; --vr: block; --rule-r: 0; }

/* The logo: the header's mark and stroke (Brand/SWI - Logo.svg). On desktop it fills its cell:
   its top on the labels' capitals, the foot of INTERIORS on the last line of Pages, its left
   on the grid's edge and its right the space short of the upright (Luke 10/1). Its viewBox
   takes in half the stroke on every side, so the box is the ink. On tablet and phone it's a
   row of its own at the foot, the full width between the gutters (below). */
.site-footer__logo { display: block; }
.site-footer__logo svg {
  display: block; width: 100%; height: auto; overflow: visible;
  fill: currentColor; stroke: currentColor; stroke-width: var(--footer-logo-stroke); stroke-linejoin: round;
}
.site-footer__logo:focus-visible { outline-offset: var(--focus-offset); }
@media (min-width: 1040px) {
  .site-footer__logo { position: absolute; inset: calc(var(--space-cell) + var(--hairline-width)) 0 var(--space-cell); width: auto; max-width: none; }
  .site-footer__logo svg { height: 100%; }
}

/* A group: its label, then one line per item. Every gap is what you see: the label is trimmed
   to its capitals and baseline, the first line to its capitals and the last to its baseline,
   so the rule to the label, the label to the first line, and the last line to the next rule
   are exactly the tokens. The lines' own spacing is --leading-footer (Luke 9/30: tighter than
   running text). */
.site-footer__group { display: flex; flex-direction: column; align-items: flex-start; }
.site-footer__group > * { line-height: var(--leading-footer); }
.site-footer__group .eyebrow { margin-bottom: var(--space-cell-label); text-box: trim-both cap alphabetic; }
.site-footer__group > :nth-child(2) { text-box: trim-start cap alphabetic; }
.site-footer__group > :last-child { text-box: trim-end cap alphabetic; }
.site-footer__group > :nth-child(2):last-child { text-box: trim-both cap alphabetic; }
.site-footer__group a:hover { color: var(--stone-on-ink); }

/* Copy links (Luke 10/1): "Email us" and "Call us" copy the address or number (js/site.js),
   and the words roll up to say so, then back. The confirmation sits on the label's own line
   (absolutely placed at its static spot, so the line's trims and baseline are untouched). */
.copy-link__done, .copy-link__label { display: inline-block; transition: translate var(--duration) var(--ease-in-out), opacity var(--duration) var(--ease-in-out); } /* in and out (Luke 10/12: on the ease-out most of the roll ran in its first frames and read as a jump) */
/* Placed at its link's top left and trimmed as its link's line is, so on a group's first line it
   sits where the label sat. (At its static spot, Safari and Chrome disagreed on the trim: a
   trim's height low in one or high in the other.) "Phone copied" fits a 320 phone's cell on one line. */
.copy-link { position: relative; }
.copy-link__done { position: absolute; top: 0; left: 0; white-space: nowrap; text-box: inherit; translate: 0 .7em; opacity: 0; pointer-events: none; }
.copy-link.is-copied .copy-link__label { translate: 0 -.7em; opacity: 0; }
.copy-link.is-copied .copy-link__done { translate: 0 0; opacity: 1; }
@media (prefers-reduced-motion: reduce) {
  .copy-link__done, .copy-link__label { translate: none !important; transition-property: opacity; }
}

/* Leaving the site (Instagram, Pinterest): a small up-right arrow that lifts on hover */
.site-footer__out {
  width: .5em; height: auto; margin-left: .05em; vertical-align: .08em; /* half the gap it had (Luke 10/1: was the word space and .3em) */
  transition: translate var(--duration) var(--ease-out);
}
.site-footer a:hover .site-footer__out, .site-footer a:focus-visible .site-footer__out { translate: .12em -.12em; }

/* The bottom line: the rows' own style (Luke 10/1: two text styles in the footer, the titles
   and the rows; it was smaller and gray). Two lines per cell; a line breaks between phrases
   ("…LLC." / "All rights reserved."), never inside one. The legal links are two lines of one
   block, so the trims take its first and last lines, level with its neighbors. */
.site-footer__base > * { line-height: var(--leading-footer); }
.site-footer__base a:hover { color: var(--stone-on-ink); }
.site-footer__copy, .site-footer__credit { text-box: trim-both cap alphabetic; }
:is(.site-footer__copy, .site-footer__credit) span { display: block; }
:is(.site-footer__copy, .site-footer__credit) > * { white-space: nowrap; }
/* The legal links are a group (its label shows below desktop, where they're a row of their own) */
@media (min-width: 1040px) { .site-footer__legal .eyebrow { display: none; } }

/* Tablet and phone (Luke 10/12: the stacked footer looked off on a phone): the same title
   block in halves, the two columns either side of one upright. Pages | Follow, Contact |
   Legal (Luke 10/12, third pass), then the copyright | the credit with no labels, and the logo across the whole width
   at the foot, the gutter from the rule above it, the page's edges and the band's foot.
   Studio goes (Dallas is in the lines above). (Under 1040 the three columns leave the middle
   one too narrow for "Terms & Conditions" in the rows' size, Luke 10/1.) */
@media (max-width: 1039px) {
  .site-footer { grid-template-columns: repeat(2, minmax(0, 1fr)); padding-bottom: var(--gutter); }
  .site-footer__group--studio { display: none; }
  .site-footer__group--pages { grid-column: 1; grid-row: 1; --rule-l: 0; }
  .site-footer__group--follow { grid-column: 2; grid-row: 1; --vr: block; --rule-l: initial; --rule-r: 0; }
  .site-footer__group--contact { grid-column: 1; grid-row: 2; --vr: none; --rule-l: 0; --rule-r: initial; }
  .site-footer__legal { grid-column: 2; grid-row: 2; --vr: block; --rule-l: initial; --rule-r: 0; }
  .site-footer__copy { grid-column: 1; grid-row: 3; --vr: none; --rule-l: 0; --rule-r: initial; }
  .site-footer__credit { grid-column: 2; grid-row: 3; --vr: block; --rule-r: 0; }
  .site-footer__brand { grid-column: 1 / -1; grid-row: 4; --vr: none; --rule-l: 0; --rule-r: 0; }
  /* The first row draws no rule (the band's edge is the line) and its upright runs up to the
     edge; the bottom line's stops at the logo's rule */
  .site-footer :is(.site-footer__group--pages, .site-footer__group--follow)::before { display: none; }
  .site-footer .site-footer__group--follow::after { top: calc(-1 * var(--band-pad)); }
  .site-footer .site-footer__base > *::after { bottom: 0; }
  .site-footer .site-footer__brand { padding: calc(var(--gutter) + var(--hairline-width)) 0 0; }
  .site-footer .site-footer__brand::before { display: block; }
  .site-footer__logo { width: 100%; }
  :is(.site-footer__copy, .site-footer__credit) > * { white-space: normal; } /* a narrow tablet's half wraps the long line */
}

/* Phone: the groups' lines a little further apart, so each link is a finger's height (28px).
   The bottom line keeps the rows' size and its own closer lines (Luke 10/12: one sentence, not
   a list), each half its phrases run on and balanced, never broken inside one ("Interiors
   LLC.", "All rights reserved.", "Luke Hults", "at DesignCloth"); its link a larger hit area
   than its letters */
@media (max-width: 600px) {
  .site-footer__group { --leading-footer: var(--leading-footer-phone); }
  .site-footer :is(.site-footer__copy, .site-footer__credit) { text-wrap: balance; }
  :is(.site-footer__copy, .site-footer__credit) span { display: inline; }
}
@media (max-width: 374px) {
  .site-footer { --footer-gap: calc(2 * var(--space-cell-tight) + var(--hairline-width)); --half: calc(var(--footer-gap) / -2); }
}
@media (max-width: 600px) and (pointer: coarse) {
  .site-footer__base a { position: relative; }
  .site-footer__base a::after { content: ""; position: absolute; inset: -10px -6px; }
}
