/* ══════════════════════════════════════════════════════════════════
   BUILD LOG — the running record on case-studies/ai-products.html
   Added 2026-09-12.

   ⚑⚑ WHY THIS IS NOT CALLED A BACKLOG. Daniel used "live backlog" for
   months and changed it here on 2026-09-12. A backlog is work NOT YET
   DONE: a queue. This is the opposite, a dated record of what WAS done,
   including the parts that failed. Calling it a backlog mislabels it to
   the exact audience it exists for. "Build log" is the honest noun, it
   needs no explanation, and it sits inside the retro system for free:
   a ship's log, a flight log, a console readout.

   ⚑ NO NEW COMPONENTS. Everything here reuses the page's own tokens and
   type scale. Labels are 11px Roboto Slab at .01em, which is the site
   standing rule; body is the same 16px/1.65 as .study-body. If a rule
   below looks like it duplicates case-study.css, it is deliberate: this
   file is loaded ONLY by ai-products.html, so it cannot version-churn
   the six pages that share case-study.css.

   ⚑ ENTRIES ARE STATIC HTML, NOT FETCHED JSON. Chosen deliberately: the
   log is meant to be found, indexed and shared, and it must render with
   no JavaScript. A fetched log is invisible to a crawler and dies with a
   script error. The cost is that the file grows; at roughly 1KB an entry
   that is ~365KB a year, against the 24KB this page starts at.
   ══════════════════════════════════════════════════════════════════ */

/* ---------- the band ----------
   ⚑ DO NOT SET `padding` HERE. .bl rides on .study, which supplies the
   container padding (0 calc(var(--content-pad) + 44px)). This file loads
   AFTER case-study.css, so a `padding: 0` on .bl silently wins and drops
   the Build Log flush to the viewport edge while every neighbouring
   section stays inset. It did exactly that on the first build; the symptom
   is a computed paddingLeft of 0px on section.study.bl. Only top spacing
   belongs here. */
.bl {
  /* ⚑⚑ PADDING, NOT MARGIN, AND THE REASON IS MARGIN COLLAPSING. This was
     `margin-top: clamp(44px,6vw,76px)`. Once the Build Log became a
     .study-band, that margin collapsed straight THROUGH the band (the band
     is position:relative with no padding, border or overflow to stop it) and
     pushed the whole band down instead of insetting the content. The heading
     ended up flush against the frame's top edge and "No sessions logged yet"
     sat directly on the ribbon at the bottom.
     40 top / 60 bottom is what every band on uxresearch.html measures.
     ⚑ padding-BLOCK only. `.study` supplies the inline padding
     (0 calc(var(--content-pad) + 44px)) and this file loads after
     case-study.css, so a `padding` SHORTHAND here would zero the inline
     inset and drop the section flush to the viewport edge. */
  padding-block: 40px 60px;
}

/* ---------- the running list ---------- */
.bl-list {
  list-style: none;
  margin: 0;
  padding: 0;
}

.bl-list > li {
  padding: 0 0 clamp(30px, 4.5vw, 48px);
  margin: 0 0 clamp(30px, 4.5vw, 48px);
  border-bottom: 1px solid var(--border, #c9c2bb);
}
.bl-list > li:last-child {
  border-bottom: 0;
  margin-bottom: 0;
}

/* ---------- entry head: the identity strip ----------
   ⚑⚑ STICKY, AND THAT IS THE WHOLE POINT. Added 2026-09-12 after looking at
   how other people solve this; a changelog pattern that pins the version and
   date while the entry scrolls past was the one idea in the reference set
   worth taking. This site's standing weakness is that a long page tells you
   nothing about where you are (its own design critique scored Visibility of
   System Status 2 of 4 on exactly that), and the Build Log is the one surface
   here guaranteed to grow past a hundred entries. Building the wayfinding into
   an empty log costs nothing; retrofitting it around a hundred entries is a
   different job.
   ⚑ It works here ONLY because body.study-page is `overflow-x: clip` rather
   than `hidden` (css/case-study.css:94). `clip` does not create a scroll
   container; `hidden` does, and it would pin this strip to the body instead of
   the viewport. If anyone ever "tidies" that clip back to hidden, this breaks
   silently and so does the site header, which is sticky for the same reason.
   ⚑ The background is load-bearing: without it the entry's own prose scrolls
   underneath the strip and collides with it. */
.bl-day {
  position: sticky;
  top: calc(var(--header-height, 72px) + 10px);
  z-index: 2;
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 10px;
  margin: 0 0 10px;
  padding: 7px 0 8px;
  background: var(--bg, #eeedeb);
  border-bottom: 1px solid var(--border, #c9c2bb);
  font-family: var(--font-slab, 'Roboto Slab', Georgia, serif);
  font-size: 11px;
  font-weight: 700;
  letter-spacing: .01em;
  color: var(--text-muted, #665b54);
}

/* ⚑ THE DAY LINE, once per day above that day's entries (Daniel, 14 Sep late: "way too
   small", it should earn more hierarchy). It borrows the site's own step pattern,
   "Step 1: Mapping the fragmented state", in the same bold serif, and sits between the
   Build Log heading (32px) and the entry headlines (19-23px) in size. A real <h3>; the
   entries under it are <h4>. The per-entry .bl-day strip below is RETIRED from the markup. */
.bl-date {
  font-family: var(--font-serif, 'Merriweather', Georgia, serif);
  font-weight: 700;
  font-size: clamp(22px, 2.8vw, 27px);
  line-height: 1.25;
  color: var(--text-primary, #2a2420);
  margin: 0 0 clamp(20px, 3vw, 30px);
  padding-bottom: 12px;
  border-bottom: 2px solid var(--study-accent, #076a7f);
}
.bl-date time { font-weight: 700; }
.bl-qa dd a { color: inherit; text-decoration: underline; text-underline-offset: 2px; }

/* Tags ride in the same strip, so the pinned bar answers three questions at
   once: which session, when, and what it was about.
   ⚑ Deliberately NOT coloured chips. Half the reference set uses typed badges
   (feature / fix / breaking), which is dashboard furniture for multi-actor
   systems; this is one person building two things. Coloured chips here would
   also fight the study theme ramps, which already carry meaning on this site. */
.bl-tags {
  margin-left: auto;
  font-weight: 500;
  color: var(--text-muted, #665b54);
}
.bl-tags span + span::before {
  content: " · ";
  color: var(--border, #c9c2bb);
}
.bl-n {
  color: var(--text-primary, #2a2420);
  font-variant-numeric: tabular-nums;
}
.bl-day time { font-weight: 500; }

/* The headline is the claim of the day. One sentence, and it is the only
   serif line in an entry, so the eye lands on what happened before it
   lands on how it happened. */
.bl-headline {
  font-family: var(--font-serif, 'Merriweather', Georgia, serif);
  font-size: clamp(19px, 2.2vw, 23px);
  font-weight: 700;
  line-height: 1.32;
  letter-spacing: -.005em;
  margin: 0 0 clamp(16px, 2.2vw, 22px);
  max-width: 34ch;
  color: var(--text-primary, #2a2420);
  text-wrap: balance;
}

/* ---------- media ----------
   ⚑ BARE ON THE PAGE. No card, no border, no background. That is the
   site-wide standing rule for visual support and it is not negotiable
   here just because the image is a rough screenshot. The roughness is
   the point of a build log; framing it would apologise for it. */
.bl-media {
  margin: 0 0 clamp(16px, 2.2vw, 22px);
}
.bl-media img,
.bl-media video {
  display: block;
  width: 100%;
  height: auto;
  max-width: 760px;
}
.bl-media figcaption {
  margin-top: 8px;
  font-size: 13px;
  line-height: 1.5;
  color: var(--text-muted, #665b54);
  max-width: 62ch;
}

/* ---------- the five answers ---------- */
.bl-qa {
  display: grid;
  grid-template-columns: minmax(140px, 168px) minmax(0, 1fr);
  gap: 12px 26px;
  margin: 0;
  align-items: start;
}
.bl-qa dt {
  font-family: var(--font-slab, 'Roboto Slab', Georgia, serif);
  font-size: 11px;
  font-weight: 700;
  letter-spacing: .01em;
  color: var(--text-muted, #665b54);
  padding-top: 4px;
}
.bl-qa dd {
  margin: 0;
  font-size: 16px;
  line-height: 1.65;
  color: var(--text-secondary, #4a3e38);
  max-width: 62ch;
}

/* Q2, the keystone: where AI came in and why that over doing it by hand.
   Marked with a NEUTRAL left rule so it reads as set apart without competing
   with .bl-cut below, which carries the accent. Two weights, one hierarchy.
   ⚑ Roboto Slab rather than a monospace: the site has no mono face, and
   introducing one for a single field would be a new voice for a small job. */
.bl-ai {
  font-family: var(--font-slab, 'Roboto Slab', Georgia, serif);
  font-size: 14.5px !important;
  line-height: 1.6 !important;
  color: var(--text-primary, #2a2420) !important;
  border-left: 3px solid var(--border, #c9c2bb);
  padding-left: 14px;
}

/* ⚑⚑ THE ONE THAT EARNS THE PAGE ITS PLACE. Anyone can post a screenshot.
   Almost nobody posts the option they rejected and why, and that is the
   only line in an entry that shows judgement rather than activity. It is
   marked in the theme colour so a skimmer's eye finds it, and it is the
   reason the question set asks for it every single day. */
.bl-cut {
  color: var(--text-primary, #2a2420) !important;
  border-left: 3px solid var(--study-accent, #076a7f);
  padding-left: 14px;
}

/* ---------- two columns: proof left, reasoning right ----------
   ⚑⚑ DANIEL'S LAYOUT, 2026-09-12, and he was right. The first build stacked
   the image above the answers, which ran a single entry to about two full
   screens. Side by side it fits one. Over a hundred entries that is the
   difference between a log a person reads and a log a person scrolls past,
   and it also puts the visual proof beside the reasoning it is proof OF
   rather than a screen above it.
   ⚑ THE STRIP AND THE CLAIM SPAN BOTH COLUMNS on purpose: the entry should
   read top-down (which session, what happened) before it reads left-right
   (proof, reasoning). Dropping the headline into the right column instead
   was tried and makes the image the first thing, which puts the artifact
   ahead of the decision. The decision leads.
   ⚑ SIDES DO NOT ALTERNATE. Flip-flopping proof and reasoning per entry was
   considered and rejected: it is a marketing-page rhythm, and in a dated list
   it forces the eye to re-find the text column on every entry. Changelog
   references that alternate are selling features; this one is being read.
   ⚑ The image is sticky WITHIN its own entry, so on a long answer the proof
   stays on screen while the reasoning scrolls. It releases at the entry
   boundary because the grid item is its containing block. */
@media (min-width: 900px) {
  .bl-list > li {
    display: grid;
    grid-template-columns: minmax(0, 40%) minmax(0, 1fr);
    column-gap: 40px;
    row-gap: 0;
    align-items: start;
  }
  .bl-headline { grid-column: 1 / -1; max-width: 46ch; }
  /* ⚑ NOT STICKY (Daniel, 14 Sep late: the pinned image riding down the entry was
     "stupid"). It sits at the top of its column and scrolls with the entry. */
  .bl-media    { grid-column: 1; margin-bottom: 0; }
  .bl-media img, .bl-media video { max-width: 100%; }
  .bl-qa       { grid-column: 2; grid-template-columns: 1fr; gap: 2px 0; }
  .bl-qa dt    { padding-top: 16px; }
  .bl-qa dt:first-of-type { padding-top: 0; }
  .bl-qa dd    { max-width: 54ch; }
}

/* ---------- phone ---------- */
@media (max-width: 720px) {
  /* No pinning on a phone: a strip that eats vertical space on a 390px screen
     costs more than the wayfinding it buys, and the entries are shorter here
     because everything is one column. */
  .bl-day { position: static; }
  .bl-tags { margin-left: 0; flex-basis: 100%; }
  .bl-qa {
    grid-template-columns: 1fr;
    gap: 4px 0;
  }
  .bl-qa dt { padding-top: 14px; }
  .bl-qa dt:first-of-type { padding-top: 0; }
  .bl-headline { max-width: none; }
}

/* ---------- empty state ----------
   No instructional text: it states a condition, it does not narrate how
   the page works. Standing rule. */
.bl-empty {
  font-size: 16px;
  line-height: 1.65;
  color: var(--text-muted, #665b54);
  max-width: 62ch;
  margin: 0;
}

@media (prefers-reduced-motion: reduce) {
  .bl-list > li { transition: none !important; }
}

/* ── Hero motion. Transitions only; js/dreadfuls-hero.js drives them.
     §8.2: slow fades, subtle, NOTHING BOUNCY, so every easing decelerates
     into place and none overshoot.

     ⚑⚑ NOTHING HERE HIDES ANYTHING AT REST. An earlier version parked the
     phones at opacity 0 until an observer undid it; the observer did not
     fire in a background tab and the hero rendered empty. The finished
     state is the authored state, and the script only borrows it. ── */
.dfs-prog i{ transition: inline-size .9s cubic-bezier(.22,.61,.36,1); }

/* ⚑ the drops are <svg> now, not <i>; selector updated with them */
.dfs-drops svg{ transition: background-color .34s ease-out,
                          border-color .34s ease-out,
                          transform .34s cubic-bezier(.22,.61,.36,1); }
.dfs-drops svg{ transform: scale(1); }


.dfs-vc{ transition: border-color .45s ease-out, opacity .45s ease-out; }
.dfs-vc-picked{ border-color: var(--p-accent, #7FFFD4); }
.dfs-vc-passed{ opacity: .45; }

@media (prefers-reduced-motion: reduce){
  .dfs-prog i, .dfs-drops svg, .dfs-vc{ transition: none !important; }

}

/* ══════════════════════════════════════════════════════════════════
   REDESIGN, 15 Sep 2026 (Daniel: the heading, its sentence, the day line
   and the first headline were all competing; this section is allowed to be
   its own thing, not a case study band).
   Researched on Mobbin: Plain and Dub put the date in a narrow LEFT column so
   it never fights the headlines; Vercel runs a vertical rail with a node per
   entry. This takes both, in the site's own type:
     1. Header row: "Build Log" and a quiet one-line intro BESIDE it, one rule under.
     2. Left rail: the day as a big teal numeral, the date beneath. It is the
        loudest thing after the heading because the day is what groups the work.
     3. Right: a 2px timeline rule with a ringed node per entry, so four entries
        read as four moments in one day instead of four equal slabs.
   ⚑ Nothing here is sticky. Daniel ruled that out on 14 Sep.
   ══════════════════════════════════════════════════════════════════ */
/* ⚑⚑ .bl-head AND .bl-intro ARE GONE (2026-09-16). They were the pre-15-Sep
   section head: h2 left, a one-liner floated right. The redesign replaced it
   with .bl-mast, but only Dreadfuls was converted, so FindaFan kept rendering
   the old head and the two logs read as two different features. Removing the
   rules rather than leaving them dead is deliberate: while they existed, the
   markup they styled kept looking correct in a browser, which is exactly why
   the drift survived a whole day of review. The /build-log skill was carrying
   the same stale instruction and has been corrected too. */

.bl-group {
  display: grid;
  grid-template-columns: clamp(104px, 12vw, 150px) minmax(0, 1fr);
  column-gap: clamp(20px, 3vw, 44px);
  align-items: start;
}
.bl-rail {
  margin: 0;
  display: flex;
  flex-direction: column;
  font-family: var(--font-serif, 'Merriweather', Georgia, serif);
  color: var(--text-primary, #2a2420);
}
.bl-rail-k {
  font-family: var(--font-slab, 'Roboto Slab', Georgia, serif);
  font-size: 13px;
  font-weight: 700;
  letter-spacing: .01em;
  color: var(--text-muted, #665b54);
}
.bl-rail-n {
  font-size: clamp(64px, 7.5vw, 96px);
  font-weight: 700;
  line-height: .92;
  color: var(--study-accent, #076a7f);
  font-variant-numeric: tabular-nums;
}
.bl-rail time {
  margin-top: 12px;
  font-family: var(--font-slab, 'Roboto Slab', Georgia, serif);
  font-size: 14px;
  font-weight: 500;
  line-height: 1.35;
  color: var(--text-secondary, #4a3e38);
}

.bl-group .bl-list {
  --bl-gutter: clamp(22px, 3vw, 40px);
  border-left: 2px solid var(--border, #c9c2bb);
  padding-left: var(--bl-gutter);
}
.bl-group .bl-list > li { position: relative; }
.bl-group .bl-list > li::before {
  content: "";
  position: absolute;
  left: calc(-1 * var(--bl-gutter) - 8px);
  top: .5em;
  width: 14px;
  height: 14px;
  border-radius: 50%;
  background: var(--bg, #eeedeb);
  border: 3px solid var(--study-accent, #076a7f);
  box-sizing: border-box;
}
.bl-group .bl-headline { font-size: clamp(20px, 2.3vw, 25px); }

@media (max-width: 720px) {
  .bl-group { grid-template-columns: 1fr; row-gap: 22px; }
  .bl-rail { flex-direction: row; align-items: baseline; gap: 10px; }
  .bl-rail-n { font-size: 48px; }
  .bl-rail time { margin-top: 0; }
  .bl-group .bl-list { --bl-gutter: 20px; }
}
.bl-rail-c {
  margin-top: 4px;
  font-family: var(--font-slab, 'Roboto Slab', Georgia, serif);
  font-size: 13px;
  font-weight: 400;
  color: var(--text-muted, #665b54);
}
/* the four question labels were 11px and read as footnotes under a 25px headline */
.bl-group .bl-qa dt { font-size: 13px; }
@media (max-width: 720px) { .bl-rail-c { margin-top: 0; } }

/* ══════════════════════════════════════════════════════════════════
   ROUND 2, 15 Sep 2026: masthead, numbered entries, a balanced Day block.
   ⚑⚑ PORTFOLIO LANGUAGE ONLY (Daniel, 15 Sep): no app fonts or app colours anywhere in the
   log's chrome, or every app redesign would force a rebuild here. Colour blocking pulled back
   to the theme ribbon and the metric dividers.
   Mobbin: Campsite (big title left, follow actions right: the future home of
   the social links Daniel wants), Linear (a dark, cinematic header that makes
   a build story feel like an event).
   ══════════════════════════════════════════════════════════════════ */
.bl-mast {
  position: relative;
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: 0;
  align-items: end;
  margin: 0 0 clamp(36px, 5vw, 60px);
  padding: 0 0 clamp(22px, 2.6vw, 30px);
  border-bottom: 1px solid var(--border, #c9c2bb);
}

.bl-mast-k {
  margin: 0 0 8px;
  font-family: var(--font-slab, 'Roboto Slab', Georgia, serif);
  font-size: 15px;
  font-weight: 700;
  letter-spacing: .01em;
  color: var(--study-1);
}
.bl-mast-h.study-h2 {
  margin: 0;
  font-size: clamp(44px, 5.6vw, 64px);
  font-weight: 700;
  line-height: 1.05;
}
.bl-mast-p {
  margin: 14px 0 0;
  max-width: 62ch;
  font-size: 16px;
  line-height: 1.6;
  color: var(--text-secondary, #4a3e38);
}
.bl-mast-stats { justify-content: flex-end; min-height: 0; gap: 14px; }
.bl-mast-stats .study-metric { flex: 0 0 auto; max-width: none; }
/* the intro holds one line on desktop (Daniel, 15 Sep); the stats give up space for it */

.bl-mast-stats .study-metric-value { font-size: clamp(28px, 3vw, 36px); color: var(--study-1); }
.bl-mast-stats .study-metric-label { font-size: 12px; }

/* ── the Day block, rebalanced: "Day 1" reads as one phrase on one baseline, and the
   date is a real line of text, not a caption ── */
.bl-rail-dn { display: flex; align-items: baseline; gap: 8px; }
.bl-group .bl-rail-k {
  font-family: var(--font-serif, 'Merriweather', Georgia, serif);
  font-size: clamp(22px, 2.4vw, 28px);
  font-weight: 700;
  color: var(--text-primary, #2a2420);
}
.bl-group .bl-rail-n { font-size: clamp(48px, 5.4vw, 64px); line-height: 1; }
.bl-group .bl-rail time {
  margin-top: 10px;
  font-family: var(--font-serif, 'Merriweather', Georgia, serif);
  font-size: 17px;
  font-weight: 700;
  color: var(--text-primary, #2a2420);
}
.bl-group .bl-rail-c { margin-top: 4px; font-size: 15px; }
.bl-group { grid-template-columns: clamp(150px, 16vw, 190px) minmax(0, 1fr); }

/* ── numbered entries: the node on the timeline carries the entry number ── */
.bl-group .bl-list { --bl-gutter: clamp(30px, 3.6vw, 48px); }
.bl-group .bl-list > li::before {
  content: attr(data-n);
  left: calc(-1 * var(--bl-gutter) - 19px);
  top: .1em;
  width: 36px;
  height: 36px;
  display: flex;
  align-items: center;
  justify-content: center;
  font-family: var(--font-slab, 'Roboto Slab', Georgia, serif);
  font-size: 13px;
  font-weight: 700;
  color: var(--study-1);
  background: var(--bg, #eeedeb);
  border: 2px solid var(--study-accent, #076a7f);
}

@media (max-width: 720px) {
  .bl-mast { grid-template-columns: 1fr; align-items: start; }
  .bl-mast-stats { justify-content: flex-start; }
  /* must repeat here: the desktop column width above is declared later than round 1's phone rule */
  .bl-group { grid-template-columns: 1fr; row-gap: 22px; }
  .bl-group .bl-rail { flex-wrap: wrap; column-gap: 12px; }
  .bl-group .bl-rail-n { font-size: 44px; }
  .bl-group .bl-list { --bl-gutter: 28px; margin-left: 18px; }
}

/* ══════════════════════════════════════════════════════════════════
   THE NEWEST ENTRY IS FEATURED (15 Sep). Every entry used to get the same
   treatment, so nothing said "this is the latest". The first entry of the
   first day now runs its image across the full width of the timeline column,
   with its answers underneath; older entries keep proof-left, reasoning-right.
   ⚑ Pure :first-child, no class: a new entry becomes the feature just by being
   added at the top, and the old one drops back on its own.
   ══════════════════════════════════════════════════════════════════ */
@media (min-width: 900px) {
  .bl-group:first-of-type .bl-list > li:first-child { grid-template-columns: minmax(0, 1fr); }
  .bl-group:first-of-type .bl-list > li:first-child .bl-media { grid-column: 1; margin-bottom: clamp(18px, 2.4vw, 26px); }
  /* ⚑⚑ NO FORCED ASPECT RATIO ON THE FEATURED IMAGE (fixed 2026-09-16).
     This was `aspect-ratio: 16/10; object-fit: cover`, written while the only
     featured image was dreadfuls-5.jpg at 1600x999 — which IS 16:10, so the
     crop removed nothing and looked deliberate. The moment FindaFan led with
     a 1600x316 strip (5.06:1) the same rule scaled it ~3.2x and cropped two
     of the four team cards clean off, along with the contrast numbers that
     were the entire point of the picture.
     ⚑ THE LOG'S IMAGES ARE EVIDENCE, AND CROPPING EVIDENCE IS THE DEFECT.
     Their shapes are all over the place by nature: this log already holds
     0.94:1 portraits and a 5.06:1 strip, and any entry can become the
     feature just by being newest. So the feature gets its own WIDTH, never
     its own SHAPE. If a future entry is so tall it dominates the column,
     crop the source file to say what it means; do not crop it in CSS, where
     the caption keeps describing the parts that got cut. */
  .bl-group:first-of-type .bl-list > li:first-child .bl-media img,
  .bl-group:first-of-type .bl-list > li:first-child .bl-media video {
    width: 100%; max-width: none; height: auto;
  }
  .bl-group:first-of-type .bl-list > li:first-child .bl-qa { grid-column: 1; }
  .bl-group:first-of-type .bl-list > li:first-child .bl-qa dd { max-width: 66ch; }
  .bl-group:first-of-type .bl-list > li:first-child .bl-headline { font-size: clamp(24px, 2.8vw, 32px); max-width: 30ch; }
}
/* ⚑ HEADLINES RUN THE FULL WIDTH (Daniel, 15 Sep: they stacked and broke oddly). The 30ch/46ch
   caps plus text-wrap:balance were splitting one-line headlines into two even halves. */
.bl-group .bl-headline,
.bl-group:first-of-type .bl-list > li:first-child .bl-headline { max-width: none; text-wrap: pretty; }
