/* ===============================================================
   RUSS RIZZO — shared styles for every page
   =============================================================== */

* { margin: 0; padding: 0; box-sizing: border-box; }

:root {
  --space: 20px;                          /* the site's one spacing unit: page edges, column padding, text-to-divider, between blocks */
  --gap: 5px;                             /* the thin gutter: between Grid images, between side-by-side images, and between a video title and its video */
  --line: 2px;                            /* every divider line: center line, project header/credits rules, wordmark top. Same weight as the underlines and pill outlines */
  --pill-line: 2px;                       /* Paragraph / List / Grid button outline */
  --rr-height: calc(16px * 0.71 * 166 / 82); /* height of the ®® logo and the view buttons beside it: sized so the logo's R (82 of its 166px height) matches the 16px type's cap height (0.71em) — about 23px */
  --leading: 1.375;                       /* 16px type on 22px lines */
  --trim-top: 0.29em;                     /* invisible space a 22px line adds above the capitals (4.64px) — subtracted so gaps measure to the letters */
  --trim-bottom: 0.375em;                 /* invisible space a 22px line adds below the baseline (6px) — subtracted likewise */
  --underline: 2px;                       /* link underline thickness: same weight as the divider lines */
  --underline-drop: calc(0.2em + var(--underline));   /* how far an underline's bottom edge sits below the baseline: 0.2em offset + its thickness */
  --home-width: calc(2 * var(--space) + var(--rr-height) * 362 / 166);   /* left column width when collapsed: the ®® plus --space each side (fallback; script.js sets the real value from the ®® button's measured width) */
  --ease: cubic-bezier(0.22, 1, 0.36, 1);
  --speed: 0.6s;
  --wm: calc((100vw - 2 * var(--space)) * 184 / 1774 + 2 * var(--space) + var(--line));   /* wordmark footer's real rendered height: image at 100vw minus side padding, 1774:184 ratio, + its top/bottom padding + its top line — used for bottom spacing. Recalculate if the wordmark image changes shape. */
}

/* For Chrome, Safari, and newer Edge */
::-webkit-scrollbar {
  display: none;
}

/* For Firefox */
html {
  scrollbar-width: none;
}

html, body { height: 100%; }

body {
  height: 100vh;
  background: #fff;
  color: #000;
  font-family: special-gothic-expanded, sans-serif;
  font-weight: 500;
  font-style: normal;
  font-size: 16px;
  line-height: var(--leading);
  -webkit-font-smoothing: antialiased;
  display: flex;
  flex-direction: column;                 /* [ columns ] over [ wordmark ] */
  overflow: hidden;                       /* page never scrolls; columns do */
  /* Site-wide custom cursor: Russ's round dot (images/other/cursor.png),
     sized to sit just inside one of the ®® logo's rings — the rings'
     opening is 134 of the logo's 166px, so at the logo's 23px height the
     dot is about 18.6px across. Rendered at 1x, 2x and 3x so it stays
     sharp: browsers draw a cursor image at CSS-pixel size, so a 1x-only
     image gets upscaled and soft on retina screens. The 10 10 is the
     hotspot: the centre of the 20x20 image. */
  cursor: url('images/other/cursor@1x.png') 10 10, auto;                                    /* fallback: no image-set support */
  cursor: -webkit-image-set(url('images/other/cursor@1x.png') 1x,
                            url('images/other/cursor@2x.png') 2x,
                            url('images/other/cursor@3x.png') 3x) 10 10, auto;             /* Safari */
  cursor: image-set(url('images/other/cursor@1x.png') 1x,
                    url('images/other/cursor@2x.png') 2x,
                    url('images/other/cursor@3x.png') 3x) 10 10, auto;
}

/* ---------------------------------------------------------------
   DOT CURSOR — with a mouse, script.js hides the system cursor
   (html.has-dot) and draws .rr-dot instead: a white dot in
   mix-blend-mode: difference, so it shows black on white, turns the
   black type under it white (it seems to sit behind the letters) and
   inverts pictures. 18.6px, like the cursor image above. The carousels
   keep their arrow cursors (the dot hides there, .is-away), and video
   players their own pointer.
   --------------------------------------------------------------- */

html.has-dot body {
    cursor: none;
  }

:where(html.has-dot) :where(:not(.phone-carousel, .phone-carousel *, .slides, .slides *, iframe)) {
    cursor: none;                           /* no specificity: anything with its own cursor (the carousels' arrows) keeps it */
  }

.rr-dot {
    position: fixed;
    top: 0;
    left: 0;
    z-index: 2147483647;
    width: 18.6px;
    height: 18.6px;
    margin: -9.3px 0 0 -9.3px;              /* centred on the pointer */
    border-radius: 50%;
    background: #fff;
    mix-blend-mode: difference;
    pointer-events: none;
    transform: translate3d(var(--dot-x, -100px), var(--dot-y, -100px), 0);
    visibility: hidden;
  }

.rr-dot.is-on {
    visibility: visible;
  }

.rr-dot.is-away,
body:has(iframe:hover) .rr-dot {
    visibility: hidden;                     /* over a carousel or a video player */
  }

/* ---------------------------------------------------------------
   HOME BUTTON — pinned top-left.
   Desktop: JS turns it into the collapse/expand toggle.
   Mobile: it stays a plain link to index.html (home).
   --------------------------------------------------------------- */

.RR {
  position: fixed;
  top: var(--space);
  left: var(--space);
  z-index: 10;
  display: block;
}

.RR img {
  display: block;
  height: var(--rr-height);
  width: calc(var(--rr-height) * 362 / 166);   /* the logo's own 362x166 shape, so its width is known before it loads */
}

/* ---------------------------------------------------------------
   TWO-COLUMN LAYOUT
   --------------------------------------------------------------- */
.layout { flex: 1; display: flex; min-height: 0; }   /* min-height:0 = scrollable panels */

.panel { height: 100%; overflow-y: auto; overflow-x: hidden; }

.panel--left {
  flex: 0 0 50%;
  border-right: var(--line) solid #000;   /* center line */
  transition: flex-basis var(--speed) var(--ease);
}
.panel--right { flex: 1 1 auto; }

.panel--left .inner {
  width: 50vw;
  padding: var(--space) var(--space) calc(var(--wm) + var(--space) - var(--trim-bottom));   /* bottom measured from the last baseline */
  transition: transform var(--speed) var(--ease);
}

.panel--right .inner {
  padding-bottom: var(--wm);
}

/* collapsed (desktop, toggled by the ®®) */
.rr-collapsed .panel--left { flex-basis: var(--home-width); }
.rr-collapsed .panel--left .inner { transform: translateX(-100%); }

/* When expanded, images and videos grow but text keeps the line length it
   has in the normal two-column view (half the window, minus the column's
   padding), so lines don't stretch across the screen or re-wrap. The
   header and credits still span the column. */
@media (min-width: 769px) {
  .rr-collapsed .project-idea p,
  .rr-collapsed .project-body > .caption {
    max-width: calc(50vw - 2 * var(--space));
  }
}

/* ---------------------------------------------------------------
   LEFT-COLUMN CONTENT
   --------------------------------------------------------------- */

p {
    margin-bottom: var(--space);                  /* between paragraphs (and credit groups): 20px between line boxes, which reads as ~30px from one baseline to the next capitals — deliberately roomier than the 20px-to-the-letters spacing elsewhere */
  }

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

/* A paragraph whose last line has an underlined link: the gap below is
   measured from the bottom of the underline, not the baseline (as for
   the List's Selected Work). script.js marks these, re-checking whenever
   the text re-wraps, since which line a link lands on depends on width. */
p.ends-underlined {
    padding-bottom: var(--underline-drop);
  }

.view--paragraph p:last-child {
    margin-bottom: var(--underline-drop);   /* the bio ends on an underlined link (LinkedIn), so the 20px to the footer line is measured from that underline */
  }

/* ---------------------------------------------------------------
   CURRENT PROJECT — on a project's own page, its name in the left column
   loses its underline (script.js marks it .is-current and removes its
   link, so it can't be clicked). In the List view an arrow sits before
   it, then a single space: the swipe cursor's arrow, drawn from this
   font's own em dash so it has the type's weight. The dash is 1.017em
   long and 0.1em thick; the mitered head's arms are 0.1em thick too,
   0.333em long at 45 degrees (in the SVG, 1000 units = 1em). It's centred
   0.335em above the baseline: the capitals' middle is 0.355em, but there
   it looked a touch high, so it sits 0.02em lower. A space in this font is 0.239em, and the dash's own side
   bearing is 0.05em, so the gaps match a typed "— ".

   Clicking a project in the List view (desktop) pushes its arrow in:
   every List link carries the arrow at zero width, and .is-arriving
   (added by script.js on the click; the page loads meanwhile) opens
   it to full width with the ®®'s --speed and --ease, revealing the arrow
   tip first so it slides in from the left and pushes the name over. The
   page being left lets its own arrow go the same way.
   --------------------------------------------------------------- */
a.is-current,
a.is-arriving {
    text-decoration: none;
  }

.view--list a.project-link::before {
    content: "";
    display: inline-block;
    width: 0;
    height: 0.808em;                        /* the head's full height */
    margin: 0;
    vertical-align: -0.069em;               /* centred just under the capitals' middle, where it looks centred: 0.335em - 0.808em / 2 */
    background-color: currentColor;
    -webkit-mask: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 -404 1017 808'><g fill='none' stroke='%23000' stroke-width='100' stroke-miterlimit='4'><path d='M0 0H946'/><path d='M613 -333L946 0L613 333'/></g></svg>") no-repeat right center / 1.017em 0.808em;
    mask: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 -404 1017 808'><g fill='none' stroke='%23000' stroke-width='100' stroke-miterlimit='4'><path d='M0 0H946'/><path d='M613 -333L946 0L613 333'/></g></svg>") no-repeat right center / 1.017em 0.808em;
    transition: width var(--speed) var(--ease), margin var(--speed) var(--ease);
  }

.view--list a.is-current::before,
.view--list a.is-arriving::before {
    width: 1.017em;
    margin-left: 0.05em;                    /* the em dash's side bearing, so it lines up with the letters above and below */
    margin-right: 0.29em;                   /* one space (0.239em) plus the dash's 0.05em bearing on that side */
  }

.view--list a.is-leaving::before {
    width: 0;
    margin: 0;
  }

.rr-instant .view--list a.project-link::before {
    transition: none;                       /* set on page load, so the open project's arrow is simply there */
  }

/* After a List click the new page carries the move on from where the old
   page got to: the same push, started --arrow-delay into it (a negative
   delay: the time since the click, set by script.js). */
@keyframes rr-arrow-in {
  from { width: 0; margin-left: 0; margin-right: 0; }
  to   { width: 1.017em; margin-left: 0.05em; margin-right: 0.29em; }
}

@keyframes rr-arrow-out {
  from { width: 1.017em; margin-left: 0.05em; margin-right: 0.29em; }
  to   { width: 0; margin-left: 0; margin-right: 0; }
}

.view--list a.is-continuing::before {
    animation: rr-arrow-in var(--speed) var(--ease) var(--arrow-delay, 0ms) both;
  }

.view--list a.is-left::before {
    animation: rr-arrow-out var(--speed) var(--ease) var(--arrow-delay, 0ms) both;
  }

a {
    color: #000;
    text-decoration: underline;
    text-decoration-thickness: var(--underline);   /* same weight as the divider lines and pill outlines */
    text-underline-offset: 0.2em;
    cursor: inherit;
  }

a:hover {
    text-decoration: none;
  }

/* Raises the hyphen in typed "RUSS-RIZZO" so it sits on the capitals'
   centre line. Measured in this font: the hyphen's middle is 0.255em above
   the baseline, the caps' middle 0.355em, so it goes up 0.1em. */
.rr-hyphen {
    position: relative;
    top: -0.1em;
  }

/* Never break RUSS-RIZZO at its hyphen. */
a:has(.rr-hyphen) {
    white-space: nowrap;
  }

/* Paragraph / List / Grid buttons — top right of the left column, level
   with the ®®. The current view is filled black; the others fill black
   on hover. */

.view-switch {
    display: flex;
    justify-content: flex-end;
    gap: 5px;
    height: var(--rr-height);
    margin-bottom: calc(var(--space) - var(--trim-top));   /* 20px from the pills to the top of the first capitals */
  }

.view-btn {
    display: flex;
    align-items: center;
    height: var(--rr-height);
    padding: 0.075em 9px 0;                /* nudges the label down 0.6px (half of 1.2px top padding, since the box height is fixed). Exact geometric centring was 1.2px but read as low; this splits the difference by eye */
    border: var(--pill-line) solid #000;
    border-radius: 999px;
    background: #fff;
    color: #000;
    font: inherit;
    line-height: 1;
    cursor: inherit;
  }

.view-btn:hover,
html:not([data-view]) .view-btn[data-show="paragraph"],
[data-view="paragraph"] .view-btn[data-show="paragraph"],
[data-view="list"] .view-btn[data-show="list"],
[data-view="grid"] .view-btn[data-show="grid"] {
    background: #000;
    color: #fff;
  }

/* Only one view shows at a time. The choice is a data-view attribute on
   <html>, set before first paint by the inline script in each <head>. */

.view--list,
.view--grid {
    display: none;
  }

[data-view="list"] .view--paragraph,
[data-view="grid"] .view--paragraph {
    display: none;
  }

[data-view="list"] .view--list,
[data-view="grid"] .view--grid {
    display: block;
  }

/* List view: plain stacked lines, groups spaced like paragraphs (~30px baseline to capitals) */

.view--list ul {
    list-style: none;
    margin-bottom: var(--space);
  }

/* Selected Work ends on an underlined link (Sonic Cup of Ice), so its gap
   is measured from the bottom of that underline instead of the baseline,
   giving the same visual gap as the other groups. */
.view--list ul.list-work {
    margin-bottom: calc(var(--space) + var(--underline-drop));
  }

.view--list ul:last-child {
    margin-bottom: var(--underline-drop);   /* the List ends on an underlined link (LinkedIn), so the 20px to the footer line is measured from that underline */
  }

/* Grid view: intro, then one full-width image per project */

.view--grid p {
    margin-bottom: calc(var(--space) - var(--trim-bottom));   /* 20px from the intro's last baseline to the first image */
  }

.grid-item {
    display: block;
    position: relative;                     /* holds the hover label */
    margin-bottom: var(--gap);
    text-decoration: none;
  }

.grid-item:last-child {
    margin-bottom: var(--trim-bottom);      /* the column's bottom padding assumes it ends on text; an image needs the full 20px */
  }

.grid-item > img {
    display: block;
    width: 100%;
    height: auto;
  }

/* Hover: the image hides and its place becomes white (the image's space
   is kept), with everything in the bottom left, --space in from the side:
   the project's name, brand over project on two lines as in a project
   page's header, then the awards / press strip as if it were the next
   line (its top where the next line's capitals would be) (images/grid/awards/), 1.6875em tall (27px at 16px type,
   measured from _design/dontcookbook-gridhover.jpg), --space from the
   bottom edge. Little Caesars and Sonic have no strip, so their name's
   baseline sits --space from the bottom. (The right column plays the teaser meanwhile.) The project
   you're already on doesn't change: its page is what the right column
   shows. */
.grid-label {
    position: absolute;
    inset: 0;
    display: flex;
    flex-direction: column;
    justify-content: flex-end;
    align-items: flex-start;
    padding: 0 var(--space) var(--space);
    color: #000;
    visibility: hidden;
  }

.grid-name {
    display: block;
    margin-bottom: var(--trim-top);                           /* baseline to the strip's top = baseline to the next line's capitals, as if the strip were a third line */
  }

.grid-name:last-child {
    margin-bottom: calc(-1 * var(--trim-bottom));             /* no strip: the baseline itself sits --space from the bottom */
  }

.grid-awards {
    display: block;
    width: auto;
    height: 1.6875em;
  }

.grid-item:not(.is-current):hover {
    background: #fff;
  }

.grid-item:not(.is-current):hover > img {
    visibility: hidden;
  }

.grid-item:not(.is-current):hover .grid-label {
    visibility: visible;
  }

/* ---------------------------------------------------------------
   RIGHT-COLUMN CONTENT (project pages)
   --------------------------------------------------------------- */

.project {
    animation: fade 0.2s var(--ease);
  }

@keyframes fade { from { opacity: 0; } to { opacity: 1; } }

/* Header: client / project on the left, agency / year on the right,
   with a rule across the column beneath. */

.project-head {
    display: flex;
    justify-content: space-between;
    gap: var(--space);
    padding: calc(var(--space) - var(--trim-top)) var(--space) calc(var(--space) - var(--trim-bottom));   /* 20px to the capitals above, 20px from the baseline below */
    border-bottom: var(--line) solid #000;
  }

/* The project's awards / press strip (the same file as its Grid hover's,
   images/grid/awards/) on the header's right, where the agency used to be
   (until 2026-10-25), centred on the name's letters: halfway between the
   top of its capitals and its last baseline (the line box has more room
   below the baseline than above the capitals, so the bottom margin lifts
   it by half the difference). 1.6875em (27px) tall; it shrinks to fit,
   keeping its proportions, on a narrow column. If beside the name it would
   make the name wrap onto more lines than it needs on its own, script.js
   ("AWARDS BESIDE OR BELOW") adds .awards-below and it sits under the name
   instead, as in the Grid hover: its top where the next line's capitals
   would be, --space from its bottom to the rule. Little Caesars and Sonic
   have none. */
.project-title {
    min-width: 0;
  }

.project-awards {
    flex: 0 1 auto;
    min-width: 0;
    align-self: center;
    display: block;
    width: auto;
    height: 1.6875em;
    margin-bottom: calc(var(--trim-bottom) - var(--trim-top));
    object-fit: contain;
    object-position: right center;
  }

.project-head.awards-below {
    flex-wrap: wrap;
    row-gap: 0;
  }

.awards-below .project-awards {
    flex-basis: 100%;
    align-self: flex-start;
    margin: var(--trim-top) 0 var(--trim-bottom);
    object-position: left center;
  }

/* Idea line: the project's one-sentence summary, in its own section
   between the header rule and a second rule, 20px to the letters. */
.project-idea {
    padding: calc(var(--space) - var(--trim-top)) var(--space) calc(var(--space) - var(--trim-bottom));
    border-bottom: var(--line) solid #000;
  }

/* A picture inside the idea section (Bijan's opening image): --space from
   the paragraph's baseline above and to the next paragraph's capitals
   below, as between a caption and an image anywhere else. */
.project-idea > p:has(+ .media) {
    margin-bottom: calc(var(--space) - var(--trim-bottom));
  }

.project-idea > .media {
    margin-bottom: calc(var(--space) - var(--trim-top));
  }

/* The line introducing Bijan's jump list sits closer to it than two
   paragraphs would: --space from its baseline to the list's capitals, like
   two captions in a row (18px on phones, below). */
.project-idea > p:has(+ .jump-list) {
    margin-bottom: calc(var(--space) - var(--trim-bottom) - var(--trim-top));
  }

/* Body: captions and media, 20px apart, measured to the letters of any
   caption (its capitals above, its baseline below). The exception is a
   video or radio spot's title ("Name" :30), which sits just the thin
   gutter above its player. */

.project-body {
    padding: var(--space);
  }

.project-body > * + * {
    margin-top: var(--space);
  }

.project-body > .caption {
    margin-bottom: 0;                       /* spacing here comes from the next block's margin-top; a bottom margin would swallow the small title gap below */
  }

.project-body > * + .caption {
    margin-top: calc(var(--space) - var(--trim-top));
  }

.project-body > .caption + * {
    margin-top: calc(var(--space) - var(--trim-bottom));
  }

.project-body > .caption + .caption {
    margin-top: calc(var(--space) - var(--trim-top) - var(--trim-bottom));
  }

.project-body > .caption:first-child {
    margin-top: calc(-1 * var(--trim-top));
  }

.project-body > .caption:last-child {
    margin-bottom: calc(-1 * var(--trim-bottom));
  }

/* Pictures and videos with no text between them sit the thin gutter
   apart, anywhere in a project (2026-10-25): the bigger --space only
   comes with a caption between. The phone carousel keeps --space above
   it, since it opens with its counter (text). */
.project-body > .media + .media:not(.phone-carousel) {
    margin-top: var(--gap);
  }

.media img {
    display: block;
    width: 100%;
    height: auto;
  }

.media--pair {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: var(--gap);                        /* side-by-side images: the same thin gutter as between Grid images */
  }

/* ---------------------------------------------------------------
   VIDEO PLAYER — every video is a Vimeo embed with Vimeo's own controls
   off (controls=0, Vimeo Plus) under our own: the video's thumbnail
   (saved from Vimeo, images/projects/<project>/<name>-thumb.jpg) until
   it first plays; a bar along the bottom (play/pause, timeline, volume,
   fullscreen) and the name and length bottom left, --gap above it (until
   2026-10-25 the name sat top left), each on a solid
   black box, square corners, white type and icons, 2px lines. Type is
   0.75em, the size of the Don't Cookbook slide counters (12px; 9px on
   phones), and every other size is in em, so the whole player scales
   with it. While playing, the boxes switch straight off after 2s
   without the mouse moving (no fade). The iframe takes no mouse events,
   so the dot cursor works over videos too. Behaviour: script.js
   "VIDEO PLAYER".
   --------------------------------------------------------------- */

.media--video {
    position: relative;
    aspect-ratio: 16 / 9;                   /* every video is 16:9 */
    overflow: hidden;
    background: #000;
    user-select: none;
    -webkit-user-select: none;
  }

.media--video:fullscreen {
    aspect-ratio: auto;
  }

.media--video:-webkit-full-screen {
    aspect-ratio: auto;
  }

.media--video iframe {
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    border: 0;
    pointer-events: none;                   /* the mouse never enters Vimeo's frame: our controls get it, and the dot can follow */
  }

.media .vp-thumb {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    object-fit: cover;
  }

.vp.has-played .vp-thumb {
    visibility: hidden;
  }

.vp {
    --vp-ink: #fff;                         /* the player's type, icons and lines... */
    --vp-box: #000;                         /* ...on its boxes (black on white was tried, 2026-10-25, and not taken) */
  }

.vp-ui {
    position: absolute;
    inset: 0;
    color: var(--vp-ink);
    font-size: 0.75em;
  }

.vp.is-playing.is-idle .vp-ui {
    visibility: hidden;                     /* straight off, no fade */
  }

.vp-title {
    position: absolute;
    bottom: calc(0.625em + 1.25em + 1.375em - var(--trim-top) - var(--trim-bottom) + var(--gap));   /* bottom left, --gap above the control bar: the bar's inset and height, then the gap */
    left: 0.625em;
    margin: 0;
    padding: calc(0.625em - var(--trim-top)) 0.625em calc(0.625em - var(--trim-bottom));
    background: var(--vp-box);
  }

.vp-bottom {
    position: absolute;
    left: 0.625em;
    right: 0.625em;
    bottom: 0.625em;
    display: flex;
    align-items: center;
    gap: 1em;
    height: calc(1.25em + 1.375em - var(--trim-top) - var(--trim-bottom));   /* exactly the title box's height: its padding (2 x 0.625em) + its line, less the trims */
    padding: 0 0.625em;
    background: var(--vp-box);
  }

.vp button {
    display: block;
    flex: none;
    font-size: inherit;                     /* buttons don't inherit it by default: 1em is the player's 12px */
    color: inherit;                         /* nor the colour: without this the icons take the default black */
    width: 1em;
    height: 1em;
    padding: 0;
    border: 0;
    background: currentColor;
    -webkit-mask: var(--icon) center / contain no-repeat;
    mask: var(--icon) center / contain no-repeat;
  }

.vp-play {
    --icon: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M3.5 1.8v12.4L14 8z'/%3E%3C/svg%3E");
  }

.vp.is-playing .vp-play {
    --icon: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M3 2h3.5v12H3zM9.5 2H13v12H9.5z'/%3E%3C/svg%3E");
  }

.vp .vp-mute {
    width: 1.25em;
    --icon: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 16'%3E%3Cpath d='M1 5.5h3.5L9 2v12l-4.5-3.5H1z'/%3E%3Cpath d='M12 5a4 4 0 0 1 0 6M14.5 2.5a7.5 7.5 0 0 1 0 11' fill='none' stroke='black' stroke-width='2' stroke-linecap='round'/%3E%3C/svg%3E");
  }

.vp.is-muted .vp-mute {
    --icon: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 16'%3E%3Cpath d='M1 5.5h3.5L9 2v12l-4.5-3.5H1z'/%3E%3Cpath d='M12.5 5.5l5 5M17.5 5.5l-5 5' fill='none' stroke='black' stroke-width='2' stroke-linecap='round'/%3E%3C/svg%3E");
  }

.vp-fs {
    --icon: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M1 6V1h5M10 1h5v5M15 10v5h-5M6 15H1v-5' fill='none' stroke='black' stroke-width='2'/%3E%3C/svg%3E");
  }

.vp:fullscreen .vp-fs {
    --icon: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M6 1v5H1M15 6h-5V1M10 15v-5h5M1 10h5v5' fill='none' stroke='black' stroke-width='2'/%3E%3C/svg%3E");
  }

.vp:-webkit-full-screen .vp-fs {
    --icon: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M6 1v5H1M15 6h-5V1M10 15v-5h5M1 10h5v5' fill='none' stroke='black' stroke-width='2'/%3E%3C/svg%3E");                             /* a rule of its own: a browser that doesn't know the prefix drops the whole rule */
  }

.vp-bar,
.vp-volbar {
    position: relative;
    height: 1.25em;                         /* an easy target around the 2px line */
    touch-action: none;
  }

.vp-bar {
    flex: 1;
  }

.vp-vol {
    display: flex;
    align-items: center;
  }

.vp-volbar {
    width: 0;
    overflow: hidden;
    transition: width 0.25s var(--ease);
  }

.vp-vol:hover .vp-volbar,
.vp-vol.is-dragging .vp-volbar {
    width: 4em;                             /* shown on hover, as on Vimeo */
  }

.vp-bar::before,
.vp-fill,
.vp-volbar::before,
.vp-volfill {
    content: "";
    position: absolute;
    top: 50%;
    height: var(--line);
    margin-top: calc(var(--line) / -2);
    background: currentColor;
  }

.vp-bar::before,
.vp-fill {
    left: 0;
  }

.vp-volbar::before,
.vp-volfill {
    left: 0.5em;
  }

.vp-bar::before,
.vp-volbar::before {
    right: 0;
    opacity: 0.35;
  }

.vp-fill {
    width: calc(var(--p, 0) * 100%);
  }

.vp-volfill {
    width: calc(var(--v, 1) * (100% - 0.5em));
  }

/* Radio spots (Other Ads): the same player as the videos, as one black
   rectangle in two rows: the name and length across the top, then play,
   timeline and volume (no fullscreen). Type 0.75em like the videos; 1em
   (12px) from the box's edges to the letters and icons, 1em from the
   name's baseline down to the controls, 1.5em between play, timeline and
   volume (chosen 2026-10-25 from the layouts in
   _design/audio-player-options.html). The <audio> itself is never shown.
   Spots without a caption between them sit --gap apart like any media. */
.media--audio audio {
    display: none;
  }

.vp-strip {
    display: grid;
    grid-template-columns: auto 1fr auto;
    grid-template-rows: auto 1.25em;
    align-items: center;
    gap: calc(1em - var(--trim-bottom) - 0.125em) 1.5em;   /* the icons sit 0.125em inside their 1.25em row */
    padding: calc(1em - var(--trim-top)) 1em calc(1em - 0.125em);
    background: var(--vp-box);
    color: var(--vp-ink);
    font-size: 0.75em;
  }

.vp-strip .vp-title {
    position: static;
    grid-row: 1;
    grid-column: 1 / -1;
    padding: 0;
    background: none;
  }

.vp-strip .vp-play {
    grid-row: 2;
    grid-column: 1;
  }

.vp-strip .vp-bar {
    grid-row: 2;
    grid-column: 2;
  }

.vp-strip .vp-vol {
    grid-row: 2;
    grid-column: 3;
  }

/* Sub-projects (e.g. Bijan Mustardson): each is its own .project-body with
   a rule above, its name, then the description 20px below it (to the
   letters, like any two captions), then captions and media as usual. A .media--pair with four images makes a
   2x2 block with the thin gutter both ways. */
.subproject {
    border-top: var(--line) solid #000;
  }

.project-idea + .subproject {
    border-top: none;                       /* the idea section above already draws this line */
  }

/* A sub-project whose images all touch: stacked images get the same thin
   gutter as side-by-side ones (Bijan's Packaging). */
.subproject--tight > .media + .media {
    margin-top: var(--gap);
  }

/* A set of images that belong together: inside a .media--group they sit
   the thin gutter apart; the group as a whole keeps the normal spacing. */
.media--group > .media + .media {
    margin-top: var(--gap);
  }

/* Phone carousel (Bijan's @bijanmustardson): one row of phone slices you
   step through one at a time. Each slice is one phone plus its share of the
   gap, cut from the old three-across images (1108px slices of a 3451px-wide
   row, the first starting 64px in), so three slices sit exactly where the
   three phones used to and the spacing is unchanged. The row runs past the
   column's padding to the screen edge on both sides, so the 4th phone peeks
   in at the right and stepped-past phones slide off to the left.
   --i (the step, set by script.js) moves the row; the slide uses the ®®'s
   own --speed and --ease. The cursor is an em-dash arrow: right to go on
   (looping back to the start at the end), left on the left half once you've
   moved forward. */
.phone-carousel {
    container-type: inline-size;
    margin-left: calc(-1 * var(--space));
    margin-right: calc(-1 * var(--space));
    overflow: hidden;
    touch-action: pan-y;                   /* vertical scrolling still works on touch; sideways swipes step the row */
    outline: none;
  }

/* Both carousel kinds (phones, and the 16:9 .slides below): an em-dash
   arrow cursor showing which way a click goes. */
.phone-carousel,
.slides {
    cursor: url('images/other/cursor-arrow-right@1x.png') 14 10, e-resize;
    cursor: -webkit-image-set(url('images/other/cursor-arrow-right@1x.png') 1x,
                              url('images/other/cursor-arrow-right@2x.png') 2x,
                              url('images/other/cursor-arrow-right@3x.png') 3x) 14 10, e-resize;
    cursor: image-set(url('images/other/cursor-arrow-right@1x.png') 1x,
                      url('images/other/cursor-arrow-right@2x.png') 2x,
                      url('images/other/cursor-arrow-right@3x.png') 3x) 14 10, e-resize;
  }

.phone-carousel[data-dir="prev"],
.slides[data-dir="prev"] {
    cursor: url('images/other/cursor-arrow-left@1x.png') 14 10, w-resize;
    cursor: -webkit-image-set(url('images/other/cursor-arrow-left@1x.png') 1x,
                              url('images/other/cursor-arrow-left@2x.png') 2x,
                              url('images/other/cursor-arrow-left@3x.png') 3x) 14 10, w-resize;
    cursor: image-set(url('images/other/cursor-arrow-left@1x.png') 1x,
                      url('images/other/cursor-arrow-left@2x.png') 2x,
                      url('images/other/cursor-arrow-left@3x.png') 3x) 14 10, w-resize;
  }

.phone-track {
    --row: calc(100cqw - 2 * var(--space));          /* the column's content width */
    --cell: calc(var(--row) * 1108 / 3451);           /* one phone + its share of the gap */
    display: flex;
    padding-left: calc(var(--space) + var(--row) * 64 / 3451);
    transform: translateX(calc(var(--i, 0) * -1 * var(--cell)));
    transition: transform var(--speed) var(--ease);
  }

/* The phones' counter: which phone is leftmost (1/18...), above it, in
   black at the same size as the Don't Cookbook counters (0.75em),
   --space in from the column's edge, --gap above the phones (from the
   baseline); the gap above it is measured to its numbers. */
.phone-carousel .slides-count {
    margin: calc(-1 * var(--trim-top)) 0 calc(var(--gap) - var(--trim-bottom));
    padding-left: var(--space);             /* 20px in from the column's edge, like the text */
    font-size: 0.75em;
    user-select: none;
  }

.phone-carousel .phone-track img {
    flex: 0 0 var(--cell);
    width: var(--cell);
    height: auto;
    display: block;
    -webkit-user-drag: none;
    user-select: none;
  }

/* Slides (Don't Cookbook's activity book and World's Thinnest Burger):
   a set of spreads stepped one at a time, with the same clicks, swipes,
   arrow keys and arrow cursor as the phones (script.js "CAROUSELS"). Each
   spread is the full content width, the same size as every other image
   on the page (Chow Roulette's); the next one waits just out of sight
   (until 2026-10-25 the row ran to the column's right edge and the next
   spread peeked in by ~15%). In the
   image's top-left corner, in white at 0.75 of the text size (12px on
   desktop), a counter reads 1/10, 2/10...: 10px from the image's top edge
   to the top of the numbers and 10px in from its left edge. It stays put
   while the images slide under it. */
.slides {
    position: relative;                     /* holds the counter over the image */
    container-type: inline-size;
    overflow: hidden;
    touch-action: pan-y;                   /* vertical scrolling still works on touch; sideways swipes step the set */
    outline: none;
  }

.slides-track {
    --slide: 100cqw;                        /* one spread, the full content width */
    display: flex;
    gap: var(--gap);
    transform: translateX(calc(var(--i, 0) * -1 * (var(--slide) + var(--gap))));
    transition: transform var(--speed) var(--ease);
  }

.slides .slides-track img {
    flex: 0 0 var(--slide);
    width: var(--slide);
    height: auto;
    display: block;
    -webkit-user-drag: none;
    user-select: none;
  }

.slides .slides-count {
    position: absolute;
    top: calc(10px - var(--trim-top));      /* 10px to the top of the numbers (trim-top is in this text's own em) */
    left: 10px;
    z-index: 1;
    margin: 0;
    color: #fff;
    font-size: 0.75em;
    pointer-events: none;                   /* clicks go to the set underneath */
    user-select: none;
  }

/* ---------------------------------------------------------------
   INSTAGRAM PROFILE PREVIEW — shown in the right column while the
   left column's Instagram link is hovered (built by script.js from
   INSTAGRAM, then zoomed: "ZOOMED PROFILES"). Deliberately NOT in the
   site's style: it imitates Instagram's mobile profile as a visitor sees
   it, in Instagram's light mode (back from a dark-mode trial; the dark
   values are noted beside the --ig-* colours) — system font,
   Instagram's sizes, icon tabs.
   --------------------------------------------------------------- */

.ig {
    --ig-bg: #fff;                          /* Instagram light mode (dark mode was #000, #f5f5f5, #a8a8a8, #262626, #e0f1ff, #363636) */
    --ig-text: #000;
    --ig-muted: #737373;
    --ig-line: #dbdbdb;
    --ig-blue: #0095f6;
    --ig-link: #4150f7;
    --ig-button: #efefef;
    --ig-side: var(--space);                /* one side margin for everything (the site's 20px): avatar, bio and buttons */
    min-height: 100vh;                      /* black to the bottom of the column... */
    margin-bottom: calc(-1 * var(--wm));    /* ...including under the footer wordmark's space */
    padding-bottom: var(--wm);
    background: var(--ig-bg);
    color: var(--ig-text);
    font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
    font-weight: 400;
    font-size: 15px;
    line-height: 20px;
    letter-spacing: normal;
    -webkit-font-smoothing: antialiased;
  }

.ig-top {
    display: flex;
    align-items: center;
    gap: 28px;
    padding: 24px var(--ig-side) 14px;
  }

.ig-avatar {
    flex: none;
    padding: 3px;                           /* ring thickness */
    border-radius: 50%;
    background: linear-gradient(45deg, #feda75 0%, #fa7e1e 30%, #d62976 62%, #962fbf 100%);
  }

/* The photo inside the unwatched-story ring, as visitors see it on
   Instagram: the brand gradient (yellow at the bottom left, through orange
   and pink, to purple at the top right) as a thin outer ring, then a white
   gap, then the photo. */
.ig-avatar img,
.ig-avatar:empty {
    display: block;
    box-sizing: content-box;
    width: 120px;
    height: 120px;
    border: 3px solid var(--ig-bg);         /* gap between ring and photo, in the background colour */
    border-radius: 50%;
    object-fit: cover;
    background: var(--ig-button);
  }

.ig-summary {
    flex: 1;
    min-width: 0;
  }

.ig-name {
    font-size: 17px;
    line-height: 22px;
    font-weight: 600;
    margin-bottom: 5.6px;                   /* with .ig-handle's margin-top, keeps both letter-to-letter gaps at 14.6px */
    overflow-wrap: anywhere;
  }

.ig-handle {
    display: block;                         /* on its own line under the name */
    margin-top: 8.9px;                      /* letter-to-letter: top line's underscore bottom to this line's capitals = this line's baseline to the counts' digit tops (14.6px each) */
    font-size: 15px;
    line-height: 20px;
    font-weight: 600;                       /* bold, like the name */
  }

.ig-stats {
    display: flex;
    gap: 40px;
    list-style: none;
    margin: 0;
    padding: 0;
  }

.ig-stats li {
    display: flex;
    flex-direction: column;
  }

.ig-stat-num {
    font-size: 15px;                        /* same size as the name above (Russ Rizzo); bold, the labels regular */
    line-height: 20px;
    font-weight: 600;
  }

.ig-stat-label {
    font-size: 15px;
    line-height: 20px;
  }

.ig-bio {
    display: flex;
    flex-direction: column;
    padding: 0 var(--ig-side);
  }

.ig-category {
    color: var(--ig-muted);
  }

.ig-bio-text {
    white-space: pre-line;
    overflow-wrap: anywhere;
  }

.ig-site {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    margin-top: 2px;
    color: var(--ig-link);
  }

.ig-site svg {
    width: 16px;
    height: 16px;
    flex: none;
  }

.ig-actions {
    display: flex;
    gap: 6px;
    padding: 16px var(--ig-side) 0;
  }

.ig-btn {
    flex: 1;
    display: flex;
    align-items: center;
    justify-content: center;
    height: 34px;
    border-radius: 8px;
    background: var(--ig-button);
    color: var(--ig-text);
    font-size: 14px;
    font-weight: 600;
  }

.ig-btn--follow {
    background: var(--ig-blue);
    color: #fff;
  }

.ig-tabs {
    display: flex;
    margin: 20px var(--ig-side) 0;          /* inset like everything else, so the gray line stops at the 20px margins */
    border-bottom: 1px solid var(--ig-line);
  }

.ig-tab {
    flex: 1;
    position: relative;
    display: flex;
    align-items: center;
    justify-content: center;
    height: 48px;
    color: var(--ig-muted);
  }

.ig-tab svg {
    width: 24px;
    height: 24px;
  }

.ig-tab.is-active {
    color: var(--ig-text);
  }

.ig-tab.is-active::after {
    content: "";
    position: absolute;
    left: 50%;
    bottom: -1px;
    width: 64px;
    height: 2px;
    transform: translateX(-50%);
    background: var(--ig-text);
  }

/* ---------------------------------------------------------------
   TEASER — a project's looping 3:4 video (or still 3:4 image), shown in
   the right column while its link is hovered (desktop). It fills the
   whole visible column edge to edge, centred, cropping whatever doesn't
   fit the column's shape. While
   the footer wordmark is up, the area ends where the wordmark starts,
   sliding with it.
   --------------------------------------------------------------- */

.panel--right.is-teaser {
    overflow: hidden;                       /* nothing to scroll while a teaser shows */
  }

.panel--right.is-teaser .inner {
    padding-bottom: 0;
  }

.teaser {
    display: flex;
    align-items: center;
    justify-content: center;
    height: 100vh;
    transition: height 0.4s var(--ease);    /* same move as the wordmark */
  }

body:has(.wordmark.is-visible) .teaser {
    height: calc(100vh - var(--wm));
  }

.teaser video,
.teaser img {
    /* The teaser fills the whole column, cropping what doesn't fit, from
       the centre. (For a smaller, uncropped teaser instead: e.g.
       --teaser-size: 50% and --teaser-fit: contain.) */
    --teaser-size: 100%;                    /* the teaser's box: this share of the column's width and of its height */
    --teaser-fit: cover;                    /* cover = fill the box, cropping; contain = whole frame, as large as fits */
    display: block;
    width: var(--teaser-size);
    height: var(--teaser-size);
    object-fit: var(--teaser-fit);
    background: transparent;
  }

/* ---------------------------------------------------------------
   ZOOMED PROFILES — the Instagram and LinkedIn replicas, drawn at their
   real size in .zoom-stage and scaled up a long way, pinned top left, so
   most of each is cut off by the column's edges (nothing scrolls).
   script.js (fitZoom) sets --zoom and the offsets so both profile
   pictures land the same size in the same place. The frame ends where
   the footer wordmark starts, like a teaser.
   --------------------------------------------------------------- */

.panel--right.is-zoom {
    overflow: hidden;
  }

.panel--right.is-zoom .inner {
    padding-bottom: 0;
  }

.zoom {
    position: relative;
    overflow: hidden;
    height: 100vh;
    transition: height 0.4s var(--ease);    /* same move as the wordmark */
  }

body:has(.wordmark.is-visible) .zoom {
    height: calc(100vh - var(--wm));
  }

.zoom--ig {
    background: #fff;
  }

.zoom--li {
    background: #f4f2ee;
  }

.zoom-stage {
    position: absolute;
    top: 0;
    left: 0;
    transform-origin: 0 0;
    transform: translate(var(--zoom-x, 0), var(--zoom-y, 0)) scale(var(--zoom, 1));
  }

.zoom--ig .zoom-stage {
    width: 600px;                           /* real size: wide enough that the counts sit in one row */
  }

.zoom--li .zoom-stage {
    width: 680px;                           /* real size: the card as wide as it was in the old preview, so the banner shows as much as the reference */
  }

.zoom .ig,
.zoom .li {
    min-height: 0;
    margin: 0;
    padding: 0;
  }

.zoom .ig {
    padding-bottom: 40px;
  }

.zoom img {
    color: transparent;                     /* no alt text over a picture that hasn't loaded */
  }

/* ---------------------------------------------------------------
   AMUSEMENT STUDIO STILLS — hovering RUSS RIZZO (the amusement studio
   link) flips through the studio's stills exactly as the right column of
   amusementstudio.russrizzo.com does: each at most 40% of the column's
   width and height, centred on white, one at a time (script.js, "The
   amusement studio's stills"). Like a teaser, the area ends where the
   footer wordmark starts.
   --------------------------------------------------------------- */

.amuse {
    display: grid;
    grid-template-rows: minmax(0, 1fr);     /* the row is the box's own height, so the stills' 40% has something to resolve against */
    place-items: center;
    height: 100vh;
    transition: height 0.4s var(--ease);    /* same move as the wordmark */
  }

body:has(.wordmark.is-visible) .amuse {
    height: calc(100vh - var(--wm));
  }

.amuse-still {
    grid-area: 1 / 1;
    min-width: 0;
    min-height: 0;
    max-width: 40%;
    max-height: 40%;
    width: auto;
    height: auto;
    visibility: hidden;
  }

.amuse-still.is-on {
    visibility: visible;
  }

/* ---------------------------------------------------------------
   SITE SCREENSHOT PREVIEW — a link marked data-preview="screenshot"
   (Casey's site) shows its data-src picture across the whole right
   column, edge to edge, with no 20px margin.
   --------------------------------------------------------------- */

.site-shot img {
    display: block;
    width: 100%;
    height: auto;
  }

/* ---------------------------------------------------------------
   LINKEDIN PROFILE PREVIEW — shown in the right column while a
   LinkedIn link in the left column is hovered (built by script.js from
   LINKEDIN). Deliberately NOT in the site's style: it imitates
   LinkedIn's desktop profile as a visitor sees it — system font,
   LinkedIn's sizes, greys, blue and beige, at its real size (measured
   from screenshots). The cards sit 20px in from the column's edges.
   --------------------------------------------------------------- */

.li {
    --li-text: rgba(0, 0, 0, 0.9);
    --li-muted: rgba(0, 0, 0, 0.6);
    --li-icon: rgba(0, 0, 0, 0.75);
    --li-blue: #0a66c2;
    --li-green: #01754f;
    --li-line: #dfdeda;
    --li-inner-line: #e8e8e8;
    box-sizing: border-box;
    min-height: 100vh;
    margin-bottom: calc(-1 * var(--wm));    /* the beige runs on under the footer wordmark... */
    padding: var(--space) var(--space) calc(var(--space) + var(--wm));   /* ...and the cards stop 20px above it */
    background: #f4f2ee;
    color: var(--li-text);
    font-family: -apple-system, system-ui, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Helvetica, Arial, sans-serif;
    font-weight: 400;
    font-size: 14px;
    line-height: 20px;
    letter-spacing: normal;
    -webkit-font-smoothing: antialiased;
  }

.li p {
    margin: 0;
  }

.li-card {
    position: relative;
    overflow: hidden;
    border: 1px solid var(--li-line);
    border-radius: 8px;
    background: #fff;
  }

.li-card + .li-card {
    margin-top: 8px;
  }

.li-link {
    color: var(--li-blue);
    font-weight: 600;
  }

.li-shield {
    display: inline-flex;
    margin-left: 5px;
    color: var(--li-icon);
  }

/* ---- intro card ---- */
.li-banner {
    display: block;
    width: 100%;
    aspect-ratio: 4 / 1;
    object-fit: cover;
  }

.li-photo {
    display: block;
    box-sizing: content-box;
    width: 152px;
    height: 152px;
    margin: -96px 0 0 24px;
    border: 4px solid #fff;
    border-radius: 50%;
    object-fit: cover;
    position: relative;
  }

.li-details {
    display: flex;
    gap: 24px;
    padding: 31px 24px 24px;
  }

.li-main {
    flex: 1;
    min-width: 0;
  }

.li-name {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    font-size: 24px;
    line-height: 30px;
    font-weight: 600;
  }

.li-shield--big {
    margin: 0 7px 0 7.5px;
  }

.li-shield--big svg {
    width: 20.6px;
    height: 22px;
  }

.li-pronouns {
    color: var(--li-muted);
    font-size: 14px;
    font-weight: 400;
  }

.li-headline {
    margin-top: 4px;
    font-size: 16px;
    line-height: 20px;
  }

.li-location {
    margin-top: 2px;
    color: var(--li-muted);
  }

.li-sep {
    padding: 0 6px;
  }

.li-website {
    display: flex;
    align-items: center;
    gap: 6px;
    margin-top: 6px;
    color: var(--li-blue);
  }

.li-school {
    flex: 0 0 244px;
    display: flex;
    align-items: center;
    gap: 8px;
    align-self: flex-start;
    margin-top: 5px;
    font-weight: 600;
  }

.li-school-logo {
    width: 32px;
    height: 32px;
    border-radius: 4px;
  }

/* In a narrow column the school would squeeze the name and headline, so
   it's dropped, as LinkedIn does on narrow screens. */
.li-intro {
    container: li-intro / inline-size;
  }

@container li-intro (max-width: 600px) {
  .li-school {
      display: none;
    }
}

/* ---- Activity card ---- */
.li-activity {
    padding: 19px 0 23px 24px;              /* no right padding: the posts run to the card's edge */
  }

.li-title {
    font-size: 20px;
    line-height: 25px;
    font-weight: 600;
  }

.li-pills {
    display: flex;
    gap: 8px;
    margin-top: 9px;
  }

.li-pill {
    box-sizing: border-box;
    height: 32px;
    padding: 0 11px;
    border: 1px solid rgba(0, 0, 0, 0.3);
    border-radius: 16px;
    color: var(--li-icon);
    font-weight: 600;
    line-height: 30px;
  }

.li-pill.is-active {
    border-color: var(--li-green);
    background: var(--li-green);
    color: #fff;
  }

/* Posts in a row; the next one is cut off at the card's edge with an
   arrow over it, as on LinkedIn. When there's room for both, no arrow. */
.li-carousel {
    position: relative;
    margin-top: 24px;
    container: li-posts / inline-size;
  }

.li-track {
    display: flex;
    gap: 16px;
    overflow: hidden;
  }

.li-next {
    position: absolute;
    top: 50%;
    right: 16px;
    display: flex;
    align-items: center;
    justify-content: center;
    width: 32px;
    height: 32px;
    margin-top: -16px;
    border-radius: 50%;
    background: #fff;
    box-shadow: 0 0 0 1px rgba(140, 140, 140, 0.2), 0 2px 6px rgba(0, 0, 0, 0.25);
    color: var(--li-icon);
  }

@container li-posts (min-width: 728px) {
  .li-next {
      display: none;
    }
}

.li-post {
    flex: 0 0 356px;
    box-sizing: border-box;
    display: flex;
    flex-direction: column;
    position: relative;
    overflow: hidden;
    border: 1px solid var(--li-inner-line);
    border-radius: 8px;
  }

.li-actor {
    display: flex;
    gap: 8px;
    padding: 12px 40px 0 12px;
  }

.li-actor-photo {
    flex: none;
    width: 48px;
    height: 48px;
    border-radius: 50%;
  }

.li-actor-text {
    display: flex;
    flex-direction: column;
    min-width: 0;
  }

.li-actor-name,
.li-company-name {
    display: flex;
    align-items: center;
    font-size: 16px;
    line-height: 19px;
    font-weight: 600;
  }

.li-actor-sub {
    overflow: hidden;
    color: var(--li-muted);
    font-size: 12px;
    line-height: 15px;
    white-space: nowrap;
    text-overflow: ellipsis;
  }

.li-post-menu {
    position: absolute;
    top: 23px;
    right: 15px;
    display: flex;
    color: var(--li-icon);
  }

.li-post-text {
    padding: 8px 12px 0;
  }

.li-more {
    color: var(--li-muted);
  }

.li-reshare {
    margin: 9px 12px 0;
    overflow: hidden;
    border: 1px solid var(--li-inner-line);
    border-radius: 8px;
  }

.li-actor--company {
    padding: 8px 8px 0;
  }

.li-company-logo {
    flex: none;
    width: 40px;
    height: 40px;
  }

.li-company-name {
    line-height: 20px;
  }

.li-actor--company .li-actor-sub {
    line-height: 16px;
  }

.li-reshare-text {
    padding: 5px 8px 10px;
  }

.li-post-image {
    display: block;
    width: 100%;
  }

.li-post-image--full {
    margin-top: 8px;
  }

.li-social {
    display: flex;
    align-items: center;
    gap: 23px;
    height: 70px;                           /* 22 above, a 40px row, 8 below */
    margin-top: auto;
    padding: 22px 13px 8px 21.5px;
    color: var(--li-icon);
  }

.li-act {
    display: flex;
    align-items: center;
    gap: 6px;
  }

.li-act:first-child {
    margin-right: -4px;                     /* the like count sits a little closer to Comment */
  }

.li-count {
    color: var(--li-muted);
    font-weight: 600;
  }

.li-reactions {
    width: 38.5px;
    height: 16px;
    margin-left: auto;
  }

/* ---------------------------------------------------------------
   GMAIL COMPOSE PREVIEW — shown in the right column while an email
   link in the left column is hovered, and pinned there (live: type,
   send) when it's clicked (script.js + contact.php). Imitates Gmail's
   iPhone compose screen with the iOS keyboard, filling the column top
   to bottom; measurements are from an iPhone screenshot (pt). Not in
   the site's style, on purpose.
   --------------------------------------------------------------- */

.gm {
    --gm-text: #1f1f1f;
    --gm-muted: #5f6368;
    --gm-line: #c7c7c7;
    --gm-blue: #0b57d0;
    --gm-inset: var(--space);                 /* everything sits 20px in from the edges; the divider lines still run edge to edge */
    --gm-kb: #d1d3d9;
    --gm-fn: #abb0ba;
    position: relative;
    display: flex;
    flex-direction: column;
    height: 100vh;
    background: #fff;
    color: var(--gm-text);
    font-family: "Google Sans Text", "Google Sans", Roboto, -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif;
    font-size: 17px;
    font-weight: 400;                         /* Gmail's regular weight, for everything on this screen */
    line-height: 22px;
    letter-spacing: normal;
    -webkit-font-smoothing: antialiased;
  }

.gm svg {
    display: block;
  }

.gm button {
    margin: 0;
    padding: 0;
    border: 0;
    background: none;
    color: inherit;
    font: inherit;
    cursor: inherit;
  }

.gm-bar {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: var(--gm-inset) var(--gm-inset) 14px;
    flex: none;
  }

.gm .gm-close {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 30px;
    height: 30px;
    border-radius: 50%;
    background: #ebebeb;
    color: #6f6f72;
  }

.gm-actions {
    display: flex;
    align-items: center;
  }

.gm-icon {
    display: flex;
    color: #c4c7c5;                         /* light grey: these aren't clickable (Send turns blue when it is) */
  }

.gm-actions > .gm-icon:nth-child(1),
.gm-actions > .gm-icon:nth-child(2) {
    margin-right: 33px;
  }

.gm-actions > .gm-icon:nth-child(3) {
    margin-right: 23px;
  }

/* Help me write and the paperclip: blue on hover */
.gm .gm-write,
.gm .gm-attach {
    transition: color 0.15s;
  }

.gm .gm-write:hover,
.gm .gm-attach:hover {
    color: var(--gm-blue);
  }

.gm .gm-send {
    color: #c4c7c5;                           /* light grey outline until From and a message are filled in */
  }

.gm-send-fill {
    display: none;
    color: var(--gm-blue);                    /* then a solid blue arrow, the sliver cut out in white */
  }

.gm .gm-send.is-ready .gm-send-outline {
    display: none;
  }

.gm .gm-send.is-ready .gm-send-fill {
    display: flex;
  }

.gm-field {
    display: flex;
    align-items: center;
    gap: 14px;
    height: 48px;
    padding: 0 var(--gm-inset);
    border-bottom: 1px solid var(--gm-line);
    flex: none;
  }

.gm-label {
    color: var(--gm-muted);
  }

.gm-value {
    flex: 1;
    display: flex;
    align-items: center;
    min-width: 0;
  }

.gm-chevron {
    display: flex;
    color: #c4c7c5;                         /* light grey like the other unclickable icons: it's decorative */
  }

.gm-input,
.gm-body {
    width: 100%;
    margin: 0;
    padding: 0;
    border: 0;
    outline: none;
    background: none;
    color: var(--gm-text);
    font: inherit;
    caret-color: transparent;                 /* the drawn 2px caret (.gm-caret--live) is shown instead */
  }

.gm-input::placeholder,
.gm-body::placeholder {
    color: var(--gm-muted);
    opacity: 1;
  }

.gm-from {
    position: relative;
  }

/* The blinking cursor in From until someone clicks in (then the real one
   takes over, in the same blue). Hidden once From has text. */
.gm-caret {
    position: absolute;
    left: 0;
    top: 50%;
    width: 2px;
    height: 22px;
    margin-top: -11px;
    border-radius: 1px;
    background: var(--gm-blue);
    pointer-events: none;
    animation: gm-blink 1.06s steps(1) infinite;
  }

.gm-input:focus ~ .gm-caret,
.gm-input:not(:placeholder-shown) ~ .gm-caret,
.gm:focus-within .gm-from .gm-caret {
    display: none;
  }

/* The cursor while typing: same 2px blue bar, moved to the insertion
   point by script.js. Solid while typing, blinking when idle. */
.gm-caret--live {
    position: absolute;
    left: 0;
    top: 0;
    margin-top: 0;
    display: none;
    animation: gm-blink 1.06s steps(1) infinite;
  }

.gm-caret--live.is-on {
    display: block;
  }

.gm-caret--live.is-typing {
    animation: none;
  }

@keyframes gm-blink {
    0%   { opacity: 1; }
    50%  { opacity: 0; }
  }

.gm-body {
    flex: 1;
    min-height: 0;
    padding: var(--gm-inset);
    resize: none;
    font-size: 17px;                          /* same as the fields */
    line-height: 22px;
  }

/* Attached files: a card per file under the message, like Gmail's */
.gm-attachments {
    display: none;
    flex-direction: column;
    gap: 8px;
    padding: 0 var(--gm-inset) var(--gm-inset);
    flex: none;
    max-height: 40%;
    overflow-y: auto;
  }

.gm.has-files .gm-attachments {
    display: flex;
  }

.gm-att {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 8px 8px 8px 10px;
    border: 1px solid var(--gm-line);
    border-radius: 8px;
  }

.gm-att-icon {
    display: flex;
    align-items: center;
    justify-content: center;
    flex: none;
    width: 34px;
    height: 34px;
    border-radius: 4px;
    background: #f1f3f4;
    color: var(--gm-muted);
    font-size: 10px;
    letter-spacing: 0.02em;
  }

.gm-att-text {
    display: flex;
    flex-direction: column;
    flex: 1;
    min-width: 0;
  }

.gm-att-name {
    overflow: hidden;
    white-space: nowrap;
    text-overflow: ellipsis;
    color: var(--gm-text);
    font-size: 15px;
    line-height: 20px;
  }

.gm-att-size {
    color: var(--gm-muted);
    font-size: 13px;
    line-height: 18px;
  }

.gm .gm-att-remove {
    display: flex;
    align-items: center;
    justify-content: center;
    flex: none;
    width: 32px;
    height: 32px;
    color: var(--gm-muted);
    transition: color 0.15s;
  }

.gm .gm-att-remove:hover {
    color: var(--gm-text);
  }

/* The file picker itself is never shown; the paperclip opens it */
.gm-file {
    display: none;
  }

/* Hidden trap field for bots (people never see or reach it) */
.gm-trap {
    position: absolute;
    left: -9999px;
    width: 1px;
    height: 1px;
    opacity: 0;
  }

/* "Sending…" / "Message sent" bar, like Gmail's */
.gm-snack {
    position: absolute;
    left: var(--gm-inset);
    right: var(--gm-inset);
    bottom: calc(var(--gm-kb-h, 300px) + 12px);
    padding: 14px var(--gm-inset);
    border-radius: 8px;
    background: #303030;
    color: #f2f2f2;
    font-size: 15px;
    line-height: 20px;
    opacity: 0;
    transform: translateY(8px);
    transition: opacity 0.2s, transform 0.2s;
    pointer-events: none;
  }

.gm-snack.is-shown {
    opacity: 1;
    transform: none;
  }

/* Help me write's dialog: a small Gmail-style card centred in the
   column, over a light scrim. */
.gm-scrim {
    position: absolute;
    inset: 0;
    z-index: 3;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: var(--gm-inset);
    background: rgba(0, 0, 0, 0.32);
    opacity: 0;
    visibility: hidden;
    transition: opacity 0.15s, visibility 0s 0.15s;
  }

.gm.is-dialog .gm-scrim {
    opacity: 1;
    visibility: visible;
    transition: opacity 0.15s;
  }

.gm-dialog {
    width: 100%;
    max-width: 312px;
    padding: 24px 24px 18px;
    border-radius: 8px;                       /* same as the keyboard */
    background: #fff;
    box-shadow: 0 4px 24px rgba(0, 0, 0, 0.18);
    transform: scale(0.96);
    transition: transform 0.15s;
  }

.gm.is-dialog .gm-dialog {
    transform: none;
  }

.gm-dialog-text {
    margin: 0 0 20px;
    color: var(--gm-text);
    font-size: 17px;
    line-height: 24px;
  }

.gm .gm-dialog-ok {
    display: block;
    margin-left: auto;
    padding: 10px 12px;
    border-radius: 20px;
    color: var(--gm-blue);
    font-size: 15px;
  }

.gm .gm-dialog-ok:hover,
.gm .gm-dialog-ok:focus-visible {
    background: rgba(11, 87, 208, 0.08);
    outline: none;
  }

/* When the footer wordmark is showing (short pages, or scrolled to the
   bottom), end the screen where the footer starts so the keyboard isn't
   hidden behind it. */
body:has(.wordmark.is-visible) .gm {
    height: calc(100vh - var(--wm));
  }

/* iOS keyboard */
.gm-kb {
    flex: none;
    margin: 0 var(--gm-inset) var(--gm-inset);   /* inset like everything else */
    padding: 10px 10px 0;                     /* grey margin around the keys */
    border-radius: 8px;
    background: var(--gm-kb);
  }

.gm {
    --gm-kb-h: 236px;                         /* keyboard height + its bottom inset, for placing the sent bar above it */
  }

.gm-row {
    display: flex;
    gap: 5px;
    margin-bottom: 8px;
  }

.gm-row--mid {
    /* nine keys, inset by half a key each side, as on iOS */
    padding: 0 calc((100% - 45px) / 20 + 2.5px);   /* half a key + half a gap */
  }

.gm-key {
    flex: 1;
    display: flex;
    align-items: center;
    justify-content: center;
    height: 34px;
    border-radius: 5px;
    background: #fff;
    box-shadow: 0 1px 0 rgba(0, 0, 0, 0.3);
    color: var(--gm-text);
    font-size: 18px;                          /* the fields' font and weight (inherited), a touch larger */
    line-height: 1;
    transition: background-color 0.08s;
  }

.gm-key--fn {
    background: var(--gm-fn);
  }

/* Lit while the matching key is held on the real keyboard */
.gm-key.is-pressed {
    background: #b4b8c0;
  }

/* Shift while it's on: a white key, as on iOS */
.gm-kb.is-shift .gm-key[data-action="shift"] {
    background: #fff;
  }

/* "#+=" / "123" on the left of the third row */
.gm-key--label {
    font-size: 15px;
  }

.gm-key--fn.is-pressed {
    background: #fff;
  }

.gm-key--side {
    flex: 1.36;
  }

.gm-key svg {
    width: auto;
    height: 15px;                             /* shift and delete icons, scaled with the smaller keys */
  }

.gm-gap {
    flex: 0.12;
  }

.gm-key--wide {
    flex: 2.7;
    font-size: 15px;
  }

.gm-key--sym {
    flex: 1.25;
    font-size: 18px;
  }

.gm-kb-foot {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    height: 40px;
    padding: 4px 22px 0;
    color: #50555c;
  }

.gm-kb-foot svg {
    width: auto;
    height: 22px;
  }

/* A list of jump links that ends its section: measure the 20px below from
   the last link's underline, as in the List view. */
.project-idea .jump-list:last-child {
    margin-bottom: var(--underline-drop);
  }

/* Credits: rule above, "Role: Name" lines, blank line between groups */

.project-credits {
    padding: calc(var(--space) - var(--trim-top)) var(--space) calc(var(--space) - var(--trim-bottom));   /* 20px to the capitals above, 20px from the baseline below */
    border-top: var(--line) solid #000;
  }

/* ---------------------------------------------------------------
   WORDMARK FOOTER — also a link home
   --------------------------------------------------------------- */

.wordmark {
  position: fixed;
  left: 0;
  right: 0;
  bottom: 0;                               /* the top line runs the full screen width */
  z-index: 5;
  background: #fff;                         /* opaque so content doesn't show through it */
  border-top: var(--line) solid #000;
  padding: var(--space);                    /* same spacing on all sides at every window size */
  display: block;
  transform: translateY(100%);             /* tucked below the viewport by default */
  transition: transform 0.4s var(--ease);
}
.wordmark.is-visible { transform: translateY(0); }   /* revealed at the bottom of the scroll */

/* Wordmark image: always stretches to the full available width at any
   browser size (its height scales with that width). */

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

/* ---------------------------------------------------------------
   MOBILE / VERTICAL  ≤ 768px
   The same two columns and center line, used differently:
   - Home (Paragraph / List / Grid): the left column fills the screen
     except a --peek strip on the right, where the center line peeks in.
   - A project (body.is-project): the line slides left to the ®®'s
     column (--home-width, as in the desktop collapse) and the project
     fills the rest. The ®® slides it back. script.js swaps between the
     two in place (no page load), so the slide plays both ways and the
     left column comes back exactly as it was left (view and scroll).
   Both columns' contents keep a fixed width while the line moves, so
   nothing re-wraps mid-slide: the project rides in with the line.
   --------------------------------------------------------------- */

@media (max-width: 768px) {
  :root {
    --space: 10px;                        /* phones: every spacing is 10px */
    --gap: 2px;                           /* ...and tight images (Grid, side-by-side, grouped) sit 2px apart */
    --rr-height: calc(12px * 0.71 * 166 / 82);   /* ®® and view buttons sized to the 12px type's cap height, as on desktop */
    --peek: 20px;                         /* how far in from the right edge the center line sits on Home */
    --line: 1.5px;                        /* phones: lines, underlines and pill outlines scale with the type (2px x 12/16) */
    --underline: 1.5px;
    --pill-line: 1.5px;
  }

  body {
    font-size: 12px;                      /* phones: 12px type (desktop 16px); lines stay at --leading (16.5px) */
  }

  .view-btn {
    padding: 0.075em 7px 0;               /* desktop's 9px side padding, scaled to the smaller type */
  }

  /* Home gets more air than the 10px elsewhere: 20px from the ®® and the
     pills down to the first letters, and 20px from the Grid intro's last
     baseline to the first image. */
  .view-switch {
    margin-bottom: calc(20px - var(--trim-top));
  }

  .view--grid p {
    margin-bottom: calc(20px - var(--trim-bottom));
  }

  /* Paragraphs, List groups and credit groups: 18px from one baseline
     (or underline) to the next capitals, instead of desktop's 20px
     between line boxes. */
  p,
  .view--list ul {
    margin-bottom: calc(18px - var(--trim-bottom) - var(--trim-top));
  }

  .view--list ul.list-work {
    margin-bottom: calc(18px - var(--trim-bottom) - var(--trim-top) + var(--underline-drop));
  }

  /* Two captions in a row on a project page (a caption then a video
     title, a sub-project name then its description): the same 18px as
     between paragraphs, so they don't run together at 10px. */
  .project-idea > p:has(+ .jump-list) {
    margin-bottom: calc(18px - var(--trim-bottom) - var(--trim-top));
  }

  .project-body > .caption + .caption {
    margin-top: calc(18px - var(--trim-top) - var(--trim-bottom));
  }

  .panel--left {
    flex-basis: calc(100% - var(--peek));
  }

  .panel--left .inner {
    width: calc(100vw - var(--peek) - var(--line));
  }

  .panel--right {
    min-width: 0;
  }

  .panel--right .inner {
    width: calc(100vw - var(--home-width));
    visibility: hidden;                   /* nothing in the peek strip on Home... */
    transition: visibility 0s linear var(--speed);   /* ...hidden only once the line has slid back */
  }

  /* a project is showing */
  body.is-project .panel--left {
    flex-basis: var(--home-width);
  }

  body.is-project .panel--left .inner {
    transform: translateX(-100%);
  }

  body.is-project .panel--right .inner {
    visibility: visible;
    transition: none;
  }

  /* the desktop collapse (#big) doesn't apply on mobile */
  .rr-collapsed .panel--left {
    flex-basis: calc(100% - var(--peek));
  }

  .rr-collapsed .panel--left .inner {
    transform: none;
  }
}

/* Tiny screens only (under 300px): at 12px type the ®® and the three
   buttons fit on one line even at 320px; below that the buttons drop to
   their own row beneath the ®®. */
@media (max-width: 300px) {
  .view-switch {
    justify-content: flex-start;
    margin-top: calc(var(--rr-height) + var(--space));
  }
}

@media (prefers-reduced-motion: reduce) {
  * { transition: none !important; animation: none !important; }
}
