:root{
  --surface:#0a0908;
  --card:#15130f;
  --ink:#f5f3ef;
  --muted:#a8a29a;
  --line:#1a1714;
  --brass:#D0A254;
  --brass-line:#C79A4B;
  --maxw:720px;
  /* Capped at 56px, not 80px. .wrap is border-box at 720px, so the gutter eats
     the reading column: an 80px gutter left only 560px of text on wide screens,
     meaning the column got narrower the wider the display was. */
  --gutter:clamp(20px,5vw,56px);
}

*{box-sizing:border-box}
html{-webkit-text-size-adjust:100%;color-scheme:dark}
::selection{background:rgba(208,162,84,0.28)}
body{
  margin:0;
  background:var(--surface);
  color:var(--ink);
  font-family:"Geist",system-ui,-apple-system,sans-serif;
  font-size:16px;
  line-height:1.65;
  -webkit-font-smoothing:antialiased;
  overflow-x:hidden;
}
.mono{font-family:"Geist Mono",ui-monospace,monospace}
/* Base link treatment: underline only, colour never shifts on hover. Anything
   that needs a different look (nav, buttons, cards) sets its own color and
   text-decoration and wins on specificity. */
a{color:var(--ink);text-decoration:underline;text-decoration-color:#4a4336;text-underline-offset:3px;transition:text-decoration-color .15s}
a:hover{text-decoration-color:var(--brass)}
img{max-width:100%;display:block}

.wrap{max-width:var(--maxw);margin:0 auto;padding-inline:var(--gutter)}

/* ---- nav ----
   One line, plain flow (not sticky), the same on every page: a plumb-line
   mark plus name back to home, and a few anchors. */
.skip-link{
  position:absolute;left:-9999px;top:16px;z-index:100;
  background:var(--ink);color:var(--surface);font:600 13px "Geist",sans-serif;
  padding:10px 16px;border-radius:7px;text-decoration:none;
}
.skip-link:focus{left:16px}
/* padding-block-start, not the `padding` shorthand: this element is also
   .wrap, and the shorthand was resetting .wrap's padding-inline to 0, which
   put the name and the Email button flush against the window edge. */
.nav-inner{display:flex;align-items:center;justify-content:space-between;gap:1rem;padding-block-start:44px}
.nav-name{font-size:15px;font-weight:500;letter-spacing:-.01em;color:var(--ink);text-decoration:none}
.nav-name:hover{text-decoration:none}
.nav-links{display:flex;align-items:center;gap:22px}
.nav-links a{font-size:14px;color:var(--muted);text-decoration:none}
.nav-links a:hover{color:var(--ink)}
/* Same ghost treatment as the hero's secondary action. Negative block margin
   keeps the border from growing the nav's height. */
.nav-cta{
  font-weight:500;color:#c8c8ce;
  border:1px solid #2c2c32;border-radius:6px;padding:5px 12px;margin-block:-6px;
  transition:border-color .15s,color .15s;
}
.nav-cta:hover{border-color:#4a4336;color:#f5f3ef}
.nav-cta:focus-visible{outline:2px solid #d0a254;outline-offset:2px}

/* ---- labels ----
   One rule holds this stylesheet together: monospace, uppercase and wide
   letter-spacing are reserved for literal code and data. They are not
   decoration. Labels are plain sentence case in the body face, and hierarchy
   comes from size, weight and space instead. */
.eyebrow{
  font-size:.92rem;
  color:var(--muted);
  font-weight:400;
}

.sec-lede{margin:.7rem 0 0;font-size:1rem;color:var(--muted);max-width:52ch}

/* ---- hero ----
   Three descending text steps plus a link row. Size, weight and colour step
   down together; flattening any one of them collapses the ladder. Step 2 is
   deliberately brighter than the lines either side of it, because it carries
   the argument and must not read as body filler. */
.hero{padding:96px 0 0}
.hero .wrap{max-width:680px;padding-inline:24px}

@keyframes rise{from{opacity:0;transform:translateY(14px)}to{opacity:1;transform:none}}
.hero .hero-eyebrow,.hero h1,.hero .hero-turn,.hero .hero-plumb{animation:rise .6s cubic-bezier(.22,1,.36,1) both}
.hero h1{animation-delay:.06s}
.hero .hero-turn{animation-delay:.12s}
.hero .hero-plumb{animation-delay:.18s}

.hero-eyebrow{margin:0 0 20px;font-size:15px;font-weight:400;line-height:1.6;color:#a8a29a}

h1{
  margin:0;
  font-size:clamp(30px,4.6vw,42px);
  font-weight:600;
  line-height:1.2;
  letter-spacing:-.024em;
  color:#f5f3ef;
  max-width:17ch;
  text-wrap:balance;
}

.hero-turn{margin:20px 0 0;font-size:17px;font-weight:400;line-height:1.65;color:#c8c8ce;max-width:52ch;text-wrap:pretty}

.hero-plumb,.lede-muted{margin:14px 0 0;font-size:16px;font-weight:400;line-height:1.7;color:#a8a29a;max-width:52ch;text-wrap:pretty}
/* A proper-noun lift, not a bold shout. Both properties set explicitly to
   beat the UA stylesheet's 700. */
.hero-plumb strong{color:#f5f3ef;font-weight:500}

/* "and stops" is the whole thesis in three words, so it gets the page's one
   flourish: a rule that fades up from nothing into full gold as it travels,
   so the emphasis arrives with the phrase rather than sitting under it evenly.
   inline-block keeps the phrase and its line from ever breaking across two
   lines. */
.stops{position:relative;display:inline-block;color:#f5f3ef}
.stops::after{
  content:"";position:absolute;left:0;right:0;bottom:2px;height:1.5px;
  background:linear-gradient(90deg,rgba(208,162,84,0),var(--brass));
  border-radius:1px;
}

/* The primary action in a section carries the brass, the way .btn-lead does
   for Plumb. Secondary links stay ink and pick it up on hover. */
.link-lead{
  font-size:15px;font-weight:500;color:var(--brass);
  text-decoration-color:color-mix(in srgb,var(--brass) 45%,transparent);
}

/* ---- Plumb queue, a working rebuild ----
   The frame is the hero's evidence, not a separate section further down the
   page, so it sits right under the 80px section padding with no heading
   between. Everything below reflows live off one threshold value; nothing
   here is a screenshot. */
.work-queue{padding-block:80px 100px}

.queue-frame,.triage-frame{
  background:linear-gradient(180deg,#131316,#0d0d10);
  border:1px solid #26262a;
  border-radius:12px;
  overflow:hidden;
}

/* Plumb is a web app, so it breaks out past the 680px reading column to 880px
   and reads at something like its real size. Centered against the column via
   negative margin: the column is narrower than the frame, so this side falls
   negative on purpose. */
.queue-frame{
  width:min(880px,calc(100vw - 48px));
  margin-left:calc((100% - min(880px,calc(100vw - 48px))) / 2);
  /* Plumb's own accent, the same rule --tg-accent applies to Triage. Sage is
     its --primary/--ring in dark mode, so it is what every control there
     actually is; plum is its --accent-foreground, for emphasis and wayfinding
     only. Keeping the two apart means colour still carries meaning inside the
     panel instead of just being decoration. */
  --pl-accent:#9fb86e;
  --pl-emphasis:#d9a3c2;
}

/* Triage is a Figma plugin. src/code.ts calls showUI at 400x640, so that is
   the width it actually has. Shown any wider it stops being a plugin panel
   and starts being a lie about the medium. Narrower than its container, so
   plain auto margins centre it. The shadow reads as floating over a canvas. */
.triage-frame{
  width:min(400px,calc(100vw - 48px));
  margin-inline:auto;
  box-shadow:0 18px 50px rgba(0,0,0,.45);
  /* Triage's own accent, not the site's. Lifted from the plugin's dark theme
     (src/ui.html --accent). Painting it gold would collapse two products with
     separate identities into one, which is the opposite of what a portfolio
     should show. */
  --tg-accent:#a3a0f3;
  --tg-accent-contrast:#141327;
}

/* ---- "Try it" affordance ----
   Both frames look enough like screenshots that people scroll past without
   touching them. This sits above the frame rather than on it, because the
   frames clip their own overflow and because it is the site talking, not the
   product. It takes each frame's accent so it still reads as belonging to the
   object below it. aria-hidden: the controls inside are already properly
   labelled, so to a screen reader this would be a floating "Try it" with no
   referent. */
.frame-badge-row{
  width:min(880px,calc(100vw - 48px));
  margin-left:calc((100% - min(880px,calc(100vw - 48px))) / 2);
  margin-bottom:10px;
  text-align:right;
}
.try-badge{
  display:inline-flex;align-items:center;gap:6px;
  font-size:11px;font-weight:500;letter-spacing:.01em;
  color:var(--try-accent,var(--brass));
  border:1px solid color-mix(in srgb,var(--try-accent,var(--brass)) 38%,transparent);
  background:color-mix(in srgb,var(--try-accent,var(--brass)) 9%,transparent);
  padding:4px 10px;border-radius:16px;
}
.try-badge svg{flex-shrink:0;display:block}
/* Triage's panel column is only 400px, so the badge follows the column. It
   stays the site's gold rather than taking Triage's indigo: this badge is the
   site talking to the visitor, and the same affordance in two colours implies
   a difference in behaviour that does not exist. Product accents stay inside
   the frames, where they actually distinguish the two products. */
.triage-panel-col .frame-badge-row{width:auto;margin-left:0}

/* Shared by any sub-section heading smaller than the page's section h2s:
   Triage, Now, Work. Case studies keeps the larger clamp() h2 untouched. */
.mini-h2{margin:0;font-size:16px;font-weight:500;letter-spacing:-.01em;color:#f5f3ef}

.triage-section{padding-block:88px 120px}
.triage-section h2{margin-top:.35rem}
.triage-section .lede-muted{max-width:none}

/* Text left, panel right. In Figma the plugin floats beside the canvas, so
   this mirrors the real arrangement, and it stops a 400px panel from sitting
   alone in a 720px column with dead air either side. Breaks out to the same
   880px as the Plumb frame so the two sections share an outer edge. */
.triage-layout{
  width:min(880px,calc(100vw - 48px));
  margin-left:calc((100% - min(880px,calc(100vw - 48px))) / 2);
  display:grid;
  grid-template-columns:1fr 400px;
  gap:56px;
  align-items:start;
}
/* A light artifact on a dark page. It stays bright rather than being tinted
   down to match, because the whole reason it earns a place here is that it
   is a different register from this site. The border keeps it from bleeding
   into the background at its own edges. */
.triage-cta{margin:20px 0 0}
.cover-shot{margin:28px 0 0}
.cover-shot img{
  width:100%;height:auto;border-radius:10px;
  border:1px solid rgba(255,255,255,.09);
}
.cover-shot figcaption,.craft-video figcaption{margin-top:10px;font-size:12px;line-height:1.6;color:#82828c}

@media(max-width:840px){
  .triage-layout{grid-template-columns:1fr;gap:32px}
}

.now-section .mini-h2,.work-history-h,.craft-section .mini-h2{margin-bottom:40px}
.now-list,.craft-copy{display:flex;flex-direction:column;gap:18px;font-size:15px;line-height:1.7;color:var(--muted)}
.now-list p,.craft-copy p{margin:0}
.now-list strong,.craft-copy strong{color:var(--ink);font-weight:500}

/* The one joke on the page gets the one bit of play. Hovering anywhere on the
   line tilts his head, which is the thing an actual terrier does when you talk
   to him. Overshoot easing so it lands with a bit of spring. */
/* Tied to the last word so the two wrap as one. On a phone the icon was
   falling to a line of its own, which read as a stray graphic rather than the
   end of the joke. */
.terrier-tail{white-space:nowrap}
.terrier{display:inline-block;vertical-align:-6px;margin-left:8px;transform-origin:50% 80%;transition:transform .28s cubic-bezier(.34,1.56,.64,1)}
.terrier-line:hover .terrier{transform:rotate(-12deg)}
@media(prefers-reduced-motion:reduce){
  .terrier{transition:none}
  .terrier-line:hover .terrier{transform:none}
}

.work-history{display:flex;flex-direction:column}
.wh-row{display:flex;gap:28px;padding:20px 0;border-bottom:1px solid var(--line)}
.wh-row:last-child{border-bottom:none}
.wh-yr{font-size:13px;color:#82828c;width:80px;flex-shrink:0}
.wh-yr.current{color:var(--brass)}
.wh-role{font-size:15px;font-weight:500;color:var(--ink)}
.wh-row p{margin:6px 0 0;font-size:14px;line-height:1.6;color:var(--muted)}

/* A fixed 80px date column plus its 28px gap was taking 32% of the row on a
   phone, leaving the description 227px to wrap in. The date reads fine as a
   line of its own above the role. */
@media(max-width:600px){
  .wh-row{flex-direction:column;gap:4px}
  .wh-yr{width:auto}
}

/* Each frame wears its product's identity in its own chrome, the way the real
   apps do. Without it the panels read as unlabelled screenshots. */
.queue-chrome{display:flex;align-items:center;gap:8px;padding:12px 14px;border-bottom:1px solid #1e1e22}
.brand-mark{flex:none;border-radius:5px}
.brand-name{font-size:14px;font-weight:600;letter-spacing:-.01em;color:#f5f3ef}
/* Plumb's panel is a rebuild of an app you can go and use, so it says so.
   Triage has no pill: that plugin is live in Community and "Demo" next to it
   would undercut the install link two lines below. */
.demo-pill{font-size:11px;font-weight:500;color:#a8a29a;background:#1e1e22;border-radius:999px;padding:2px 8px}
.queue-chrome-label{font-size:12px;font-weight:400;color:#82828c}
.queue-chrome-status{margin-left:auto;font-size:12px;font-weight:400;color:#82828c;white-space:nowrap}

.queue-header{padding:20px 18px 18px;display:flex;align-items:baseline;justify-content:space-between;gap:12px;flex-wrap:wrap}

/* Each step gets a rule above it and a numbered label, so the three regions
   (sources, bar, results) read as separate things at a glance instead of one
   continuous column. */
.queue-step{border-top:1px solid #1e1e22;padding:16px 18px 18px}
.step-label{display:flex;align-items:center;gap:9px;margin:0 0 12px;font-size:12px;font-weight:500;color:#a1a1aa}
.step-label .n{
  display:flex;align-items:center;justify-content:center;flex-shrink:0;
  width:18px;height:18px;border-radius:50%;
  background:color-mix(in srgb,var(--pl-emphasis) 10%,transparent);
  border:1px solid color-mix(in srgb,var(--pl-emphasis) 35%,transparent);
  color:var(--pl-emphasis);font-size:10px;font-weight:500;
}
.queue-header .queue-title{margin:0;font-size:16px;letter-spacing:-.01em;font-weight:500;color:#f5f3ef}
.queue-note{font-size:12px;font-weight:400;color:#82828c;white-space:nowrap}

/* The source registry. This is the part of Plumb people miss: it isn't
   diffing two files, it's reconciling every place a component is described
   and letting a person name the winner. */
/* Deliberately not a boxed panel. The slider below is the only framed control
   in here, so it reads as the thing you operate; when both were boxes they
   competed and the slider got lost. */
.queue-sources{margin:0;padding:0}
.qs-intro{margin:0 0 12px;font-size:12px;line-height:1.5;color:#c8c8ce}
.qs-intro strong{color:#f5f3ef;font-weight:500}
.qs-list{list-style:none;margin:0;padding:0}

/* One row of three, not three stacked rows. Which document is which is the
   fact worth showing; the URL of whichever one you have chosen sits under it
   as the evidence, instead of repeating a URL per line. */
.qs-chips{display:flex;flex-wrap:wrap;gap:8px}
.qs-chip{
  font:500 12px "Geist",sans-serif;color:#a1a1aa;cursor:pointer;
  background:none;border:1px solid #26262a;border-radius:16px;padding:5px 12px;
  transition:color .15s,border-color .15s,background .15s;
}
.qs-chip:hover{color:#f5f3ef;border-color:#4a4336}
.qs-chip:focus-visible{outline:2px solid var(--pl-accent);outline-offset:2px}
.qs-chip.is-truth{
  color:var(--pl-accent);
  border-color:color-mix(in srgb,var(--pl-accent) 45%,transparent);
  background:color-mix(in srgb,var(--pl-accent) 8%,transparent);
}
.qs-current{display:flex;align-items:baseline;gap:8px;margin-top:10px;flex-wrap:wrap}
.qs-current-label{font-size:10px;font-weight:500;color:var(--pl-emphasis);white-space:nowrap}
.qs-current .mono{font-size:10.5px;color:#6e6e76;overflow-wrap:anywhere}

.queue-slider{margin:0;background:#0c0c0f;border:1px solid #1e1e22;border-radius:9px;padding:14px 16px}
.queue-slider-row{display:flex;align-items:center;gap:12px}
.queue-slider-label{font-size:12px;font-weight:500;color:var(--pl-accent);white-space:nowrap}
.queue-track-wrap{flex:1;height:24px;display:flex;align-items:center;cursor:pointer;touch-action:none;position:relative}
.queue-track{flex:1;height:2px;background:#26262a;border-radius:1px;position:relative}
.queue-track-fill{position:absolute;left:0;top:0;bottom:0;background:var(--pl-accent);border-radius:1px}
.queue-thumb{
  position:absolute;top:50%;width:14px;height:14px;border-radius:50%;
  background:var(--pl-accent);transform:translate(-50%,-50%);cursor:grab;
  box-shadow:0 0 0 4px color-mix(in srgb,var(--pl-accent) 16%,transparent),
             0 0 12px color-mix(in srgb,var(--pl-accent) 30%,transparent);
}
.queue-thumb:focus-visible{outline:2px solid #f5f3ef;outline-offset:3px}
.queue-slider-range{font-size:12px;font-weight:400;color:#82828c;white-space:nowrap}
.queue-slider-caption{margin:10px 0 0;font-size:12px;line-height:1.5;color:#c8c8ce}

.queue-groups{padding:0;display:flex;flex-direction:column;gap:18px}
.queue-group-head{display:flex;align-items:center;gap:7px;margin-bottom:8px}
.queue-group-head .dot{width:5px;height:5px;border-radius:50%}
.queue-group-head .label{font-size:12px;font-weight:500}
.queue-group.auto .dot{background:var(--pl-accent)}
.queue-group.auto .label{color:var(--pl-accent)}
.queue-group.wait .dot{background:#6e6e76}
.queue-group.wait .label{color:#a1a1aa}

.queue-cards{display:flex;flex-direction:column;gap:8px}

.qc-auto{display:flex;align-items:center;gap:10px;border:1px solid color-mix(in srgb,var(--pl-accent) 25%,transparent);background:color-mix(in srgb,var(--pl-accent) 5%,transparent);border-radius:8px;padding:9px 12px}
.qc-auto .check{color:var(--pl-accent)}
.qc-auto .title{font-size:12px;font-weight:500;color:#e8e8ea}
.qc-auto .conf{margin-left:auto;font-size:11px;font-weight:400;color:var(--pl-accent);white-space:nowrap;flex-shrink:0}

.qc-empty{border:1px dashed #26262a;border-radius:8px;padding:12px 14px;font-size:12px;color:#82828c}

/* Opening a card animates its height, not a 4px nudge. The card grows by
   about 220px on a phone, so a small fade was invisible next to the jump it
   was meant to soften. Animating grid-template-rows from 0fr lets the real
   height ease open without measuring anything in JS. Fired by a one-shot
   class so the slider's constant re-renders don't retrigger it. */
.qc-reveal,.tg-reveal{display:grid;grid-template-rows:1fr}
.qc-body{display:flex;flex-direction:column;gap:9px}
/* The reveal animates the grid row's height, and .qc-body is a flex column, so
   its children were shrinking to fit instead of holding their size and being
   clipped. The diff block sat squashed at 18px for a third of the animation
   and then snapped open. Holding their size turns that into a clean wipe. */
.qc-body > *{flex-shrink:0}

@keyframes revealOpen{
  from{grid-template-rows:0fr;opacity:0}
  to{grid-template-rows:1fr;opacity:1}
}
.qc-reveal.is-opening,.tg-reveal.is-opening{animation:revealOpen .24s cubic-bezier(.22,1,.36,1)}
/* Only while it plays. JS drops the class on animationend so this can never
   outlive the animation and clip a focus ring on the buttons inside. */
.qc-reveal.is-opening > *,.tg-reveal.is-opening > *{overflow:hidden}
/* The diff block is a scroll container. Its width is still settling while the
   card eases open, which was enough for the browser to paint a horizontal
   scrollbar for a frame or two and flash it. Nothing scrolls mid-reveal;
   overflow-x:auto comes back when the class is dropped. */
.qc-reveal.is-opening .qc-diff{overflow:hidden}

@media(prefers-reduced-motion:reduce){
  .qc-reveal.is-opening,.tg-reveal.is-opening{animation:none}
  .qc-reveal.is-opening > *,.tg-reveal.is-opening > *{overflow:visible}
}

.qc-pending{border-radius:8px;background:#101014;padding:12px 14px;display:flex;flex-direction:column;gap:9px;border:1px solid #26262a}
.qc-pending.collapsed{padding:9px 12px;gap:0}
.qc-pending.kind-fp{border-style:dashed;border-color:#3a3a42}
.qc-pending.kind-high{border-color:rgba(224,138,138,.35)}

.qc-row-toggle{display:flex;align-items:center;gap:10px;width:100%;background:none;border:none;padding:0;margin:0;font:inherit;color:inherit;text-align:left;cursor:pointer}
.qc-row-toggle:focus-visible{outline:2px solid #f5f3ef;outline-offset:2px;border-radius:4px}
.qc-row-toggle .qc-pill{flex-shrink:0}
.qc-row-toggle .conf{margin-left:auto;font-size:11px;font-weight:400;color:#82828c;white-space:nowrap;flex-shrink:0}
.qc-row-toggle .chev{color:#6e6e76;font-size:13px;width:1em;text-align:center;flex-shrink:0}
.qc-pending.collapsed .qc-held{margin-top:6px}

.qc-title{font-size:13px;line-height:1.3;font-weight:500;color:#f4f4f5;text-wrap:pretty}
.qc-pill{font-size:10px;font-weight:500;padding:3px 8px;border-radius:16px;white-space:nowrap;background:none}
.qc-pill.low{color:#a1a1aa;border:1px solid #2c2c32}
.qc-pill.medium{color:#c8c8ce;border:1px solid #3a3a42}
.qc-pill.high{color:#e08a8a;border:1px solid rgba(224,138,138,.4)}
.qc-pill.fp{color:#82828c;border:1px solid #3a3a42}

.qc-diff{background:#0a0a0d;border:1px solid #1a1a1e;border-radius:6px;padding:8px 11px;font-size:11px;line-height:1.5;overflow-x:auto}
.qc-diff-row{display:flex;gap:8px;white-space:nowrap}
/* Wide enough for the longest source name, so the two values stack in a
   column you can actually compare down. */
.qc-diff-row .k{flex:0 0 86px;color:#6e6e76}
.qc-diff-row .v{color:#c8c8ce}

.qc-reason{font-size:12px;line-height:1.5;color:#a1a1aa;text-wrap:pretty}

.qc-held{font-size:12px;line-height:1.5;font-weight:400;color:var(--pl-emphasis)}

.qc-ack{display:flex;align-items:flex-start;gap:8px;background:rgba(224,138,138,.05);border:1px solid rgba(224,138,138,.25);border-radius:6px;padding:8px 10px;cursor:pointer}
.qc-ack input{accent-color:var(--pl-accent);width:14px;height:14px;margin-top:1px}
.qc-ack input:focus-visible{outline:2px solid #f5f3ef;outline-offset:2px}
.qc-ack span{font-size:12px;line-height:1.4;color:#c8c8ce}

.qc-fp-banner{font-size:12px;font-weight:500;color:#82828c;border:1px dashed #3a3a42;padding:7px 10px;border-radius:6px}

.qc-actions{display:flex;align-items:center;gap:7px;flex-wrap:wrap}
/* Bordered and left-aligned, next to the finding they act on. As underlined
   text pushed to the far right of an 810px card they read as footnotes and
   got missed. This also matches the buttons in the Triage panel, so the two
   demos ask for a decision the same way. */
.qc-btn{
  background:none;border:1px solid #2c2c32;border-radius:6px;
  font:500 11.5px "Geist",sans-serif;padding:5px 12px;cursor:pointer;
  color:#a1a1aa;transition:border-color .15s,color .15s;
}
.qc-btn:hover{border-color:#4a4336;color:#f5f3ef}
.qc-btn:focus-visible{outline:2px solid #f5f3ef;outline-offset:2px}
.qc-btn:disabled{opacity:.45;cursor:not-allowed}
.qc-btn.approve{color:#f4f4f5}
.qc-btn.approve:hover{border-color:color-mix(in srgb,var(--pl-accent) 60%,transparent);color:var(--pl-accent)}

.qc-edit textarea{width:100%;background:#0a0a0d;border:1px solid #26262a;border-radius:6px;color:#e8e8ea;font-family:inherit;font-size:12px;line-height:1.5;padding:8px 10px;resize:vertical}
.qc-edit textarea:focus-visible{outline:2px solid #f5f3ef;outline-offset:2px}
.qc-edit .qc-actions{margin-top:8px}

.qc-resolved{display:flex;align-items:center;gap:10px;border:1px solid #1e1e22;background:#0c0c0f;border-radius:8px;padding:9px 12px}
.qc-resolved .mark{color:#82828c;width:14px;text-align:center;flex-shrink:0}
.qc-resolved .title{font-size:12px;color:#82828c;text-decoration:line-through;text-decoration-color:#3a3a42}
.qc-resolved .status{margin-left:auto;font-size:11px;color:#82828c;display:flex;align-items:center;gap:8px;white-space:nowrap}

/* Same breakout math as the frame it captions. `margin:auto` cannot centre an
   element wider than its container (both autos resolve to 0), which left this
   flush with the text column and visibly off-centre under the frame. */
.queue-caption{
  width:min(880px,calc(100vw - 48px));
  margin:12px 0 0;
  margin-left:calc((100% - min(880px,calc(100vw - 48px))) / 2);
  font-size:12px;line-height:1.6;color:#82828c;text-align:center;
}
/* The Triage panel is only 400px, so its caption follows the reading column
   instead of the 880px breakout its sibling uses. */
.triage-section .queue-caption{width:auto;margin-left:0;max-width:52ch;margin-inline:auto}

/* ---- narrow screens ----
   These rows are all built as single flex lines, which is right on a wide
   frame and wrong on a phone: the slider label alone is 234px of nowrap text,
   which left the track literally 0px and undraggable. Everything here gives
   the long thing its own line instead of letting it crush its neighbours. */
@media(max-width:600px){
  /* The bar has to stay usable before it has to stay on one line. */
  .queue-slider-row{flex-wrap:wrap;gap:8px 12px}
  .queue-slider-label{white-space:normal;flex:1 1 100%}
  .queue-track-wrap{flex:1 1 auto;min-width:0}

  /* Title gets the full width; pill, score and chevron share the line below. */
  .qc-row-toggle{flex-wrap:wrap;gap:7px 10px}
  .qc-row-toggle .qc-title{flex:1 1 100%}
  .qc-row-toggle .conf{margin-left:0}
  .qc-row-toggle .chev{margin-left:auto}

  /* Source name above its value, so the value gets the whole width rather
     than 188px and a scrollbar. */
  .qc-diff-row{flex-direction:column;gap:1px}
  .qc-diff-row .k{flex:0 0 auto}

  /* The mark stays with the title it belongs to; only the score drops. */
  .qc-auto{flex-wrap:wrap;gap:4px 10px}
  .qc-auto .check{flex:0 0 auto}
  .qc-auto .title{flex:1 1 0;min-width:0}
  .qc-auto .conf{margin-left:auto}
  .qc-resolved{flex-wrap:wrap;gap:4px 10px}
  .qc-resolved .mark{flex:0 0 auto}
  .qc-resolved .title{flex:1 1 0;min-width:0}
  .qc-resolved .status{margin-left:auto;flex-wrap:wrap}

  .queue-header{gap:6px 12px}
  .queue-note{white-space:normal}

  /* Window title on one line, what it's looking at on the next, rather than
     both breaking mid-phrase against each other. */
  .queue-chrome{flex-wrap:wrap;gap:2px 6px}
  .queue-chrome-status{margin-left:0;width:100%;white-space:normal}

  /* 17ch is a good measure at 42px and a straitjacket at 30px. Even released,
     "Design systems disagree" overshot 342px by a hair at 30px and stranded
     "disagree" alone on its own line, so the type steps down to hold the
     two-line break it was written for. */
  h1{max-width:none;font-size:clamp(22px,7.2vw,30px)}
}

/* The nav had 334px of content in 350px of room, which is why it read as
   cramped. Name on one line, links on the next. */
@media(max-width:560px){
  .nav-inner{flex-wrap:wrap;gap:14px;padding-block-start:32px}
  .nav-links{width:100%;gap:18px;flex-wrap:wrap}
}

@media(prefers-reduced-motion:reduce){.queue-frame,.queue-parallax{transform:none!important}}

/* ---- Triage, a working rebuild of the Figma plugin's review queue ----
   Real scan output, grouped by what it found. Approve/reject/edit toggle a
   proposal's status; nothing is final until Apply, which swaps the whole
   panel for a receipt. */

/* The claim the panel exists to prove, stated before you touch anything,
   the way Plumb's numbered steps narrate its flow. */
.tg-thesis{margin:0;padding:16px 18px 4px;font-size:12.5px;line-height:1.55;color:#c8c8ce}
.tg-thesis strong{color:var(--tg-accent);font-weight:500}

.tg-body{padding:12px 18px 18px;display:flex;flex-direction:column;gap:10px}

/* Kind of drift on the left, how sure it is on the right. The 100% one is
   picked out because it is the proof: certainty still does not grant
   permission. */
.tg-card-head{display:flex;align-items:baseline;justify-content:space-between;gap:10px}
.tg-label{font-size:12px;font-weight:500;color:#e8e8ea}
.tg-conf.is-certain{color:var(--tg-accent)}
.tg-needs-note{margin:0;font-size:11px;line-height:1.5;color:#82828c}
.tg-status-note{margin:0;font-size:11px;font-weight:500;color:var(--tg-accent)}

.tg-card{border-radius:8px;padding:12px 13px;display:flex;flex-direction:column;gap:9px;transition:border-color .15s,background .15s}
.tg-swatch-row{display:flex;align-items:center;gap:9px}
.tg-swatch{width:18px;height:18px;border-radius:4px;border:1px solid rgba(255,255,255,.12);flex-shrink:0}
.tg-arrow{color:#6e6e76;font-size:11px}
.tg-swatch-labels{display:flex;flex-direction:column;gap:1px;font-size:10px;line-height:1.4}
.tg-swatch-labels .before{color:#82828c}
.tg-swatch-labels .after{color:#c8c8ce}
.tg-inst-before{flex:1;border:1px dashed #3a3a42;border-radius:5px;padding:7px 10px;font-size:10px;color:#82828c;text-align:center}
.tg-inst-after{flex:1;border:1px solid rgba(163,160,243,.3);border-radius:5px;padding:7px 10px;font-size:10px;color:#c8c8ce;text-align:center}
.tg-desc-before{margin:0;font-size:11px;color:#82828c}
.tg-desc-before strong{color:#f4f4f5;font-weight:500}
.tg-desc-after{background:#0a0a0d;border:1px solid #1a1a1e;border-radius:5px;padding:7px 10px;font-size:10px;line-height:1.5;color:#c8c8ce}

.tg-rationale{margin:0;font-size:12px;line-height:1.5;color:#c8c8ce;text-wrap:pretty}
.tg-conf{font-size:10px;color:#82828c}

.tg-actions{display:flex;gap:7px;flex-wrap:wrap}
.tg-btn{background:none;font:500 11px "Geist",sans-serif;padding:5px 12px;border-radius:6px;cursor:pointer;transition:border-color .15s,color .15s;border:1px solid #2c2c32;color:#a1a1aa}
.tg-btn:hover{border-color:#4a4336;color:#f5f3ef}
.tg-btn:focus-visible{outline:2px solid var(--tg-accent);outline-offset:2px}
/* Resting colour comes from a custom property (set inline per item's state)
   so the :hover rule below, which targets the same properties by class, can
   still win. A literal inline color/border-color would beat any stylesheet
   hover rule outright. */
.tg-btn.approve{color:var(--ac,#c8c8ce);border-color:var(--ab,#2c2c32)}
.tg-btn.approve:hover{border-color:rgba(163,160,243,.6);color:var(--tg-accent)}
.tg-btn.reject{color:var(--rc,#a1a1aa);border-color:var(--rb,#2c2c32)}
.tg-btn.reject:hover{border-color:rgba(224,138,138,.5);color:#e08a8a}
.tg-btn.save{border-color:rgba(163,160,243,.5);color:var(--tg-accent)}
.tg-btn.save:hover{background:rgba(163,160,243,.08);border-color:rgba(163,160,243,.5);color:var(--tg-accent)}
.tg-btn.cancel{border:none;font-weight:400;color:#a1a1aa;text-decoration:underline;text-decoration-color:#4a4336;padding:5px 8px}
.tg-btn.cancel:hover{color:#f5f3ef;border:none}

.tg-edit{display:flex;flex-direction:column;gap:8px}
.tg-edit textarea{width:100%;box-sizing:border-box;background:#0a0a0d;border:1px solid #2c2c32;border-radius:6px;padding:9px 11px;color:#f4f4f5;font-family:"Geist Mono",monospace;font-size:11px;line-height:1.6;resize:vertical}
.tg-edit textarea:focus-visible{outline:2px solid var(--tg-accent);outline-offset:1px}

.tg-footer{border-top:1px solid #1e1e22;padding:14px 18px;display:flex;flex-direction:column;gap:8px}
.tg-footer-row{display:flex;align-items:center;gap:12px;flex-wrap:wrap}
.tg-apply{border:none;font:500 12px "Geist",sans-serif;padding:9px 18px;border-radius:7px;cursor:pointer}
.tg-apply:focus-visible{outline:2px solid #f5f3ef;outline-offset:2px}
.tg-blast{font-size:11px;color:var(--tg-accent)}
.tg-footnote{margin:0;font-size:11px;line-height:1.55;color:#82828c;max-width:62ch}

.tg-receipt{padding:28px 18px;display:flex;flex-direction:column;gap:12px;align-items:flex-start}
.tg-receipt-label{font-size:11px;font-weight:500;color:var(--tg-accent)}
.tg-receipt-line{margin:0;font-size:15px;line-height:1.5;color:#f4f4f5;font-weight:500}
.tg-receipt-sub{margin:0;font-size:12px;line-height:1.6;color:#a1a1aa;max-width:46ch}
.tg-scan-again{margin-top:4px;background:none;border:1px solid #2c2c32;color:#c8c8ce;font:500 11px "Geist",sans-serif;padding:7px 13px;border-radius:6px;cursor:pointer}
.tg-scan-again:hover{border-color:#4a4336;color:#f5f3ef}
.tg-scan-again:focus-visible{outline:2px solid var(--tg-accent);outline-offset:2px}

/* ---- Before design systems ----
   Same two-column shape and 880px breakout the Triage section already uses,
   and the same 840px collapse. Copy reuses the Now list's type tokens and the
   caption reuses the cover shot's, so nothing new is invented except the
   facade itself. */
.craft-layout{
  width:min(880px,calc(100vw - 48px));
  margin-left:calc((100% - min(880px,calc(100vw - 48px))) / 2);
  display:grid;
  grid-template-columns:1fr 1fr;
  gap:56px;
  align-items:start;
}
.craft-video{margin:0}

/* Click to load. The thumbnail is served from this domain and the iframe is
   only injected on click, so the page makes no request to YouTube and sets no
   cookie unless somebody actually plays it. */
.video-facade{
  position:relative;display:block;width:100%;padding:0;
  background:none;border:1px solid var(--line);border-radius:12px;
  overflow:hidden;cursor:pointer;line-height:0;
}
.video-facade img{width:100%;height:auto}
.video-facade:hover{border-color:var(--brass-line)}
.video-play{
  position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);
  display:flex;align-items:center;justify-content:center;
  width:56px;height:56px;border-radius:50%;
  background:rgba(10,9,8,.72);border:1px solid rgba(245,243,239,.35);
  color:var(--ink);
  transition:background .15s,border-color .15s;
}
.video-facade:hover .video-play{background:rgba(10,9,8,.9);border-color:var(--brass-line)}
.video-play svg{margin-left:2px}
.craft-video iframe{display:block;width:100%;aspect-ratio:16/9;height:auto;border:0;border-radius:12px}

@media(max-width:840px){
  .craft-layout{grid-template-columns:1fr;gap:32px}
}

/* ---- section frame ---- */
section{padding-block:clamp(52px,7vh,88px)}
.divider{border-top:1px solid var(--line)}
.sec-head{display:flex;align-items:baseline;justify-content:space-between;gap:1rem;margin-bottom:1.6rem;flex-wrap:wrap}
/* section headings do the work the removed labels used to do, so they scale up */
/* No text-wrap:balance here. Balance is for headings meant to run to several
   lines; on a heading that fits, it can decide to split it anyway, which is why
   this one broke at 1600px wide but not at 1440. */
h2,.sec-head h2{
  font-size:clamp(1.45rem,2.5vw,2rem);
  letter-spacing:-.025em;
  font-weight:500;
  margin:.45rem 0 0;
  line-height:1.2;
}

.prose p{margin:0 0 1.15rem;max-width:58ch}
.prose p:last-child{margin-bottom:0}
.prose strong{font-weight:600}

/* pull points */
.points{display:grid;gap:1.8rem;margin-top:2rem}
.point{min-width:0}
.point .k{font-size:.88rem;color:var(--muted);margin-bottom:.45rem}
.point h3{margin:0 0 .5rem;font-size:1.14rem;letter-spacing:-.015em;font-weight:500;line-height:1.3}
.point p{margin:0;font-size:.95rem;color:color-mix(in srgb,var(--ink) 76%,var(--muted));line-height:1.6;max-width:58ch}

.shot{margin-top:2rem;border:1px solid var(--line);border-radius:12px;overflow:hidden}
.shot video{width:100%;height:auto;display:block}
/* Never upscale a still. These come off 1x screen recordings, and stretching one
   past its pixel width gives back the legibility the breakout was for. */
.shot img{width:auto;max-width:100%;display:block;margin-inline:auto}

/* Media breaks out of the 720px reading column. These are screenshots of dense
   product UI: at column width every label inside them is about 6px tall, which
   makes the work unreadable at exactly the moment someone is judging it.
   Subtracting the gutter from 100vw keeps the scrollbar from causing overflow,
   and below ~760px the min() collapses to the column width anyway. */
.wide{
  width:min(1120px,calc(100vw - 2 * var(--gutter)));
  /* figure carries a 40px UA margin, which offsets the centring below */
  margin-inline:0;
  position:relative;
  left:50%;
  transform:translateX(-50%);
}
.shot .demo-still{display:none}
@media(prefers-reduced-motion:reduce){
  .shot .demo-motion{display:none}
  .shot .demo-still{display:block}
}

.stack{margin-top:1.5rem;font-size:.88rem;color:var(--muted)}
.stack b{color:var(--ink);font-weight:500}

.hero-sub{margin:1.3rem 0 0;font-size:1.05rem;line-height:1.5;color:var(--muted);max-width:54ch}
.hero-sub b{color:var(--ink);font-weight:500}

.actions{margin-top:1.5rem;display:flex;gap:1.8rem;flex-wrap:wrap}
.btn{font-size:.9rem;font-weight:500;text-decoration:none;color:var(--ink);display:inline-flex;align-items:center;gap:.45em;padding-bottom:2px;border-bottom:1px solid color-mix(in srgb,var(--ink) 30%,transparent);transition:border-color .15s}
.btn:hover{border-color:var(--brass-line)}
.btn .arw{color:var(--muted);font-weight:600}
/* the working demo outranks the repo link, so it carries the brass */
.btn-lead{color:var(--brass);border-bottom-color:color-mix(in srgb,var(--brass) 45%,transparent)}
.btn-lead .arw{color:var(--brass)}

/* background */
.bg-grid{display:grid;grid-template-columns:1fr;gap:1.2rem}
.bg-list{list-style:none;padding:0;margin:3rem 0 0}
.bg-list li{border-top:1px solid var(--line);padding:1.4rem 0;display:flex;gap:clamp(1.2rem,4vw,3rem);align-items:flex-start}
.bg-list li:last-child{border-bottom:1px solid var(--line)}
/* dates are data, so they get tabular figures for alignment, not monospace */
.bg-list .yr{font-size:.92rem;font-variant-numeric:tabular-nums;color:var(--muted);white-space:nowrap;padding-top:.15rem;min-width:9.5ch}
.bg-list .t{font-size:1rem;line-height:1.5}
.bg-list .t b{font-weight:500}
.bg-list .t span{color:var(--muted)}

/* footer */
footer{border-top:1px solid var(--line);padding-block:clamp(52px,7vh,96px)}
.foot-h{
  font-size:clamp(1.35rem,2.4vw,1.95rem);letter-spacing:-.025em;font-weight:500;
  line-height:1.2;margin:0;max-width:18ch;text-wrap:balance;
}
/* The call to action is its own element rather than a link tucked into the end
   of a sentence, so it can never break across two lines mid-phrase. */
.foot-cta{
  display:inline-flex;align-items:center;gap:.5em;margin-top:1.5rem;
  font-size:1.05rem;font-weight:500;color:var(--brass);text-decoration:none;
  padding-bottom:3px;border-bottom:1px solid color-mix(in srgb,var(--brass) 45%,transparent);
  transition:border-color .15s ease,gap .15s ease;
}
.foot-cta:hover{border-color:var(--brass);gap:.75em}
@media(prefers-reduced-motion:reduce){.foot-cta{transition:border-color .15s ease}.foot-cta:hover{gap:.5em}}
.foot-h a{color:var(--brass);text-decoration:none;border-bottom:2px solid color-mix(in srgb,var(--brass-line) 45%,transparent);transition:border-color .15s}
.foot-h a:hover{border-color:var(--brass-line)}
.foot-links{margin-top:2.4rem;display:flex;flex-wrap:wrap;gap:1.6rem;font-size:.92rem}
@media(max-width:600px){.foot-links{flex-direction:column;gap:.85rem}}
.foot-links a{color:var(--muted);text-decoration:none;display:inline-flex;gap:.5em;align-items:center;transition:color .15s}
.foot-links a:hover{color:var(--ink)}
.colophon{margin-top:3rem;font-size:.82rem;color:var(--muted);line-height:1.6}

:focus-visible{outline:2px solid var(--brass-line);outline-offset:3px;border-radius:3px}

@media(prefers-reduced-motion:reduce){
  .hero .hero-eyebrow,.hero h1,.hero .hero-turn,.hero .hero-plumb{animation:none}
  *{scroll-behavior:auto}
}
html{scroll-behavior:smooth}

/* ---- case studies ---- */
.cs-hero{padding-top:clamp(64px,10vh,110px);padding-bottom:clamp(36px,5vh,56px)}
/* The whole case study in one paragraph, for anyone who reads only one. */
.cs-lede{font-size:1.12rem;line-height:1.55;color:var(--ink);max-width:54ch}
.cs-meta{margin-top:1.2rem;font-size:.88rem;color:var(--muted);line-height:1.6}
.cs-meta b{color:var(--ink);font-weight:500}
.shot figcaption{font-size:.82rem;color:var(--muted);padding:.7rem 1rem;border-top:1px solid var(--line)}
.cs-cards{display:grid;gap:1.2rem;margin-top:2rem}
@media(min-width:640px){.cs-cards{grid-template-columns:1fr 1fr}}
.cs-card{display:block;border:1px solid var(--line);border-radius:12px;padding:1.4rem 1.5rem;text-decoration:none;transition:border-color .15s}
.cs-card:hover{border-color:var(--brass-line)}
.cs-card .k{font-size:.88rem;font-variant-numeric:tabular-nums;color:var(--muted);margin-bottom:.5rem}
/* slightly tighter than other h3s: these sit in half-width cards, and the
   longer of the two titles ran to three lines against the shorter one's one */
.cs-card h3{margin:0 0 .45rem;font-size:1.05rem;line-height:1.3;font-weight:500;letter-spacing:-.015em;color:var(--ink);max-width:none;text-wrap:balance}
.cs-card p{margin:0;font-size:.92rem;color:var(--muted);line-height:1.55}

/* pattern grid */
.pattern-grid{display:grid;gap:1.6rem;margin-top:2rem}
@media(min-width:640px){.pattern-grid{grid-template-columns:1fr 1fr}.pattern-grid .span2{grid-column:1/-1}}
.pattern-grid .point .shot{margin-top:.9rem}

.eyebrow{font-weight:400}
h2.eyebrow{margin-block:1em}

/* working pattern demos */
.demo{position:relative;background:var(--surface);min-height:300px;display:flex;align-items:center;justify-content:center;padding:2.5rem 2rem;font-size:.85rem}
.demo .menu{background:var(--card);border:1px solid var(--line);border-radius:10px;padding:6px 0;width:210px}
.demo .menu > button{display:block;width:100%;text-align:left;background:none;border:0;color:var(--ink);font:inherit;padding:9px 14px;cursor:pointer}
.demo .menu > button:hover{background:color-mix(in srgb,var(--ink) 7%,transparent)}
.demo .menu > button.danger{color:#E5484D}
.demo .confirm-row{padding:4px 14px 10px;display:flex;gap:8px}
.demo .btn-sm{font:inherit;font-size:.8rem;padding:5px 11px;border-radius:6px;border:1px solid var(--line);background:none;color:var(--muted);cursor:pointer}
.demo button.btn-sm.danger{background:#E5484D;border-color:#E5484D;color:#fff}
.demo .dim{position:absolute;inset:0;background:rgba(0,0,0,.5);display:flex;align-items:center;justify-content:center}
.demo .modal{background:var(--card);border:1px solid var(--line);border-radius:12px;padding:20px 22px;width:min(300px,80%);box-shadow:0 8px 32px rgba(0,0,0,.4)}
.demo .modal h4{margin:0 0 6px;font-size:.95rem;font-weight:600;color:var(--ink)}
.demo .modal p{margin:0 0 14px;color:var(--muted);line-height:1.45}
.demo .modal .confirm-row{padding:0;justify-content:flex-end}
.demo .rows{width:100%;max-width:380px;display:flex;flex-direction:column;gap:8px}
.demo .row{display:flex;justify-content:space-between;align-items:center;background:var(--card);border:1px solid var(--line);border-radius:8px;padding:9px 12px;color:var(--ink)}
.demo .row button{background:none;border:0;color:var(--muted);cursor:pointer;font-size:1rem;line-height:1;padding:2px 6px}
.demo .row button:hover{color:#E5484D}
.demo .ghost{border:1px dashed var(--line);border-radius:8px;height:38px}
.demo .toast{position:absolute;bottom:18px;left:50%;transform:translateX(-50%);background:#212630;border-radius:10px;padding:10px 16px;display:flex;gap:14px;align-items:center;color:var(--ink);box-shadow:0 6px 24px rgba(0,0,0,.35);white-space:nowrap}
.demo .toast button{background:none;border:0;color:var(--brass);font:inherit;font-weight:500;cursor:pointer}
/* Positioning only. The pill's look comes from .try-badge, so the affordance
   is identical to the one on the home page demos. Keeps the `hint` class the
   demo reset JS checks for. */
.demo .hint{position:absolute;top:12px;right:14px}
.demo .done{color:var(--muted);text-align:center}
.demo .done .replay{font:inherit;background:none;border:0;color:var(--ink);cursor:pointer;text-decoration:underline;padding:0;margin-left:.4em}

.pattern-grid .point{display:flex;flex-direction:column}
.pattern-grid .point .shot{margin-top:auto}
.pattern-grid .point p{margin-bottom:.9rem}

