/* ═══════════════════════════════════════════════════════════
   Banavu Blog — style.css (brand restyle, 2026-09-29)
   ─────────────────────────────────────────────────────────
   The blog is "same brand, calmer layout": banavu.com's colours, fonts, top bar and footer, with a
   reading layout for long posts. Owner-approved as option B in the mockups at
   Research/blog-restyle-2026-09-28-v1..v3/ (v3 = the whole blog, clickable).

   Load order on every page (written by update_blog_categories.py, <!-- shared:blog-head -->):
     1. Google Fonts: Fraunces (headings), Figtree (body), Caveat + Nunito (download card)
     2. /site-f.css   banavu.com's shared styles: tokens (--navy, --paper, --orange ...), nav,
                      hamburger menu, section 4 (download card) and footer. COPIED from
                      web/home/public/site-f.css by update_blog_categories.py on every run:
                      never edit the blog's copy, edit the homepage's (see CLAUDE_WEB.md).
     3. /style.css    this file: blog-only styles. Uses site-f.css's tokens; defines no colours
                      of its own except code-block cream (#F3EBDD) and hover tints.

   Page types and their markup (generated or migrated by update_blog_categories.py + blog_chrome.py):
     index.html            .blog-home  > .blog-hero, .eyebrow, .cat-grid > a.cat-card.acc-*
     <category>.html       .cat-page.acc-* > .cat-head, ul.post-rows > a.post-row
     posts/<slug>/         article.post > header.post-head, .block > h2 + p ..., hr.block-divider
   Each category gets one of the four brand accents (.acc-orange/green/blue/purple) in the order of
   the cards on index.html; the card, the category page and the post's category pill share it.

   Deviation from the v3 mockup (2026-09-29): code blocks are light cream with navy text, not navy.
   15 posts colour diff lines red/green with inline spans inside <pre>; on navy those were hard to
   read. The section divider stays <hr class="block-divider">, drawn as a simple line (four dots until 2026-10-05).
═══════════════════════════════════════════════════════════ */

body{font-size:18px;line-height:1.7}

/* ── top bar: "Blog" pill beside the Banavu wordmark (the rest of the nav is site-f.css) ── */
.nav-left{display:flex;align-items:center;gap:10px}
.nav-tag{font-family:var(--body);font-weight:700;font-size:13px;letter-spacing:.02em;color:var(--blue-d);background:var(--blue-t);border-radius:999px;padding:5px 11px;line-height:1}
.nav-tag:hover{background:#c4dbf6}

/* ── per-category accent ── */
.acc-orange{--a:var(--orange);--a-t:var(--orange-t);--a-d:var(--orange-d)}
.acc-green {--a:var(--green); --a-t:var(--green-t); --a-d:var(--green-d)}
.acc-blue  {--a:var(--blue);  --a-t:var(--blue-t);  --a-d:var(--blue-d)}
.acc-purple{--a:var(--purple);--a-t:var(--purple-t);--a-d:var(--purple-d)}

/* ── crumbs (post category pill, category back link) ── */
.post-crumbs{display:flex;flex-wrap:wrap;align-items:center;gap:8px 12px;font-size:14px;font-weight:600;margin-bottom:22px}
.crumb-cat{background:var(--a-t,var(--blue-t));color:var(--a-d,var(--blue-d));border-radius:999px;padding:4px 12px}
.crumb-cat:hover{text-decoration:underline;text-underline-offset:3px}
.crumb-back{color:var(--ink)}
.crumb-back:hover{color:var(--navy);text-decoration:underline;text-underline-offset:3px}

/* the homepage's eyebrow label, with its dotted trail */
.eyebrow{display:inline-flex;align-items:center;gap:8px;font-size:14px;font-weight:700;letter-spacing:.08em;text-transform:uppercase;color:var(--green-d);margin-bottom:18px}
.eyebrow svg{width:26px;height:14px}

/* ══ blog home ══ */
.blog-home{max-width:calc(1040px + 2 * var(--gutter));margin:0 auto;padding:clamp(36px,7vw,84px) var(--gutter) clamp(48px,7vw,88px)}
.blog-hero{max-width:720px;margin-bottom:clamp(40px,6vw,64px)}
.blog-hero h1{font-family:var(--display);font-weight:800;font-variation-settings:"SOFT" 50,"WONK" 0;font-size:clamp(38px,6.4vw,64px);line-height:1.04;letter-spacing:-.025em;margin-bottom:20px;text-wrap:balance}
.blog-hero .lede{font-size:clamp(19px,2.3vw,22px);line-height:1.55;color:var(--ink);margin-bottom:18px}
.blog-hero .meta{font-size:14px;font-weight:600;color:var(--ink)}
.cat-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(290px,1fr));gap:18px}
.cat-card{display:flex;flex-direction:column;gap:8px;background:#fff;border:1px solid var(--line);border-radius:18px;padding:22px 22px 20px;transition:transform .15s,box-shadow .15s,border-color .15s}
.cat-card[hidden]{display:none}  /* a category with no live posts (update_blog_categories.py); .cat-card's display:flex would beat the browser's own [hidden] rule */
.cat-card:hover{transform:translateY(-2px);box-shadow:0 10px 26px rgba(10,22,40,.08);border-color:var(--a)}
.cat-emoji{display:grid;place-items:center;width:46px;height:46px;border-radius:14px;background:var(--a-t);font-size:24px;line-height:1;margin-bottom:6px}
.cat-title{font-family:var(--display);font-weight:700;font-variation-settings:"SOFT" 50,"WONK" 0;font-size:21px;line-height:1.2;letter-spacing:-.01em}
.cat-desc{font-size:15.5px;line-height:1.5;color:var(--ink);flex:1}
.cat-count{margin-top:8px;font-size:14px;font-weight:700;color:var(--a-d)}
.cat-count.empty{color:#9AA6B5;font-weight:600}

/* ══ category page ══ */
.cat-page{max-width:calc(760px + 2 * var(--gutter));margin:0 auto;padding:clamp(32px,6vw,72px) var(--gutter) clamp(48px,7vw,88px)}
.cat-head{padding-bottom:28px;border-bottom:1px solid var(--line)}
.cat-head .cat-emoji{width:60px;height:60px;border-radius:18px;font-size:32px;margin:6px 0 18px}
.cat-head h1{font-family:var(--display);font-weight:800;font-variation-settings:"SOFT" 50,"WONK" 0;font-size:clamp(34px,5.4vw,50px);line-height:1.08;letter-spacing:-.02em;margin-bottom:14px;text-wrap:balance}
.cat-head .lede{font-size:clamp(18px,2.1vw,20px);line-height:1.55;color:var(--ink);margin-bottom:16px}
.cat-head .meta{font-size:14px;font-weight:700;color:var(--a-d)}
.post-rows{list-style:none}
.post-row{display:flex;align-items:flex-start;gap:18px;padding:26px 0;border-bottom:1px solid var(--line)}
.post-row-body{flex:1}
.post-row-body > span{display:block}
.post-row-title{font-family:var(--display);font-weight:700;font-variation-settings:"SOFT" 50,"WONK" 0;font-size:clamp(21px,2.6vw,24px);line-height:1.22;letter-spacing:-.01em;margin-bottom:8px}
.post-row-sum{font-size:16px;line-height:1.55;color:var(--ink);margin-bottom:10px}
.post-row-meta{font-size:13.5px;font-weight:600;color:#6B7A8F}
.post-row-arrow{flex-shrink:0;margin-top:4px;font-size:22px;font-weight:700;color:var(--a-d);transition:transform .15s}
.post-row:hover .post-row-title{color:var(--a-d);text-decoration:underline;text-decoration-thickness:2px;text-underline-offset:4px}
.post-row:hover .post-row-arrow{transform:translateX(4px)}
.empty-note{padding:48px 0;text-align:center;color:var(--ink);font-size:17px}
.empty-note span{display:block;font-size:34px;margin-bottom:10px}

/* ══ post ══ */
.post{max-width:calc(680px + 2 * var(--gutter));margin:0 auto;padding:clamp(32px,6vw,72px) var(--gutter) clamp(40px,6vw,72px)}
.post-title{font-family:var(--display);font-weight:800;font-variation-settings:"SOFT" 50,"WONK" 0;font-size:clamp(34px,5.4vw,50px);line-height:1.08;letter-spacing:-.02em;margin-bottom:18px;text-wrap:balance}
.post-dek{font-size:clamp(19px,2.2vw,21px);line-height:1.55;color:var(--ink);margin-bottom:24px}
.post-meta{display:flex;align-items:center;gap:10px;font-size:14px;color:var(--ink);padding-bottom:28px;border-bottom:1px solid var(--line)}
.post-meta img{width:30px;height:30px;border-radius:24%}
.post-meta b{color:var(--navy);font-weight:600}

.post .block h2{font-family:var(--display);font-weight:700;font-variation-settings:"SOFT" 50,"WONK" 0;font-size:clamp(24px,3vw,28px);line-height:1.2;letter-spacing:-.01em;margin:0 0 14px}
.post-head + .block h2{margin-top:40px}
.post p{margin:0 0 18px}
.post strong{font-weight:700}
.post .block a{color:var(--blue-d);text-decoration:underline;text-decoration-thickness:1.5px;text-underline-offset:3px}
.post ul,.post ol{padding-left:1.3em;margin:0 0 20px}
.post li{margin-bottom:10px}
.post li::marker{color:var(--orange-d)}
.post details{margin:0 0 18px}
.post summary{cursor:pointer;font-weight:600}

/* section divider: a simple line (owner, 2026-10-05). Until then it was four brand-colour dots in a row, which on a
   phone looked like a carousel's page indicator and "makes it feel as if I need to side scroll". */
.post hr.block-divider{border:0;height:1px;margin:40px 0 34px;background:var(--line)}

/* code: light cream so inline red/green diff spans stay readable */
.post code{font-family:ui-monospace,"SF Mono",Menlo,monospace;font-size:.84em;background:#F3EBDD;color:var(--navy);border-radius:6px;padding:.12em .4em}
/* long paths / identifiers in running text wrap instead of widening the page on phones
   (2026-09-29: 9 posts overflowed a 390px screen by up to 219px) */
.post{overflow-wrap:break-word}
.post :not(pre) > code{overflow-wrap:anywhere}
/* TODO (seen 2026-10-07 on SIM9, android-coming-soon-recurring-fix, "The Fix That Actually Holds" table): inside a
   narrow table cell the rule above splits a short name mid-word ("deploy_ho" / "me.sh" at 402px). A table already
   sits in .table-scroll (overflow-x:auto), so it could keep code whole and scroll inside its own box instead:
     .table-scroll code{overflow-wrap:normal;white-space:nowrap}
   Trade-off for the owner: whole names vs a table that scrolls sideways on phones. If adopted, run
   node tests/blog-overflow-check.cjs (the page itself must still not scroll sideways) and look at every post with
   a table at 360, 375 and 390px. Rule: CLAUDE_WEB.md "Blog brand chrome rule" (measure every page after a CSS change). */
.post pre{background:#F3EBDD;color:var(--navy);border:1px solid var(--line);border-left:4px solid var(--orange);border-radius:12px;padding:16px 18px;overflow-x:auto;font-size:14px;line-height:1.65;margin:4px 0 22px}
.post pre code{background:none;color:inherit;padding:0;font-size:inherit;border-radius:0}
/* <pre class="diagram">: box-drawing art (│ ─ ▒). At the code line-height of 1.65 the vertical lines break into
   dashes; 1.2 lets each row touch the next. First used in debug-icon-watermark (2026-10-08). */
.post pre.diagram{line-height:1.2}

.post blockquote{margin:6px 0 24px;padding:2px 0 2px 22px;border-left:4px solid var(--orange);font-family:var(--display);font-weight:600;font-variation-settings:"SOFT" 100,"WONK" 0;font-size:21px;line-height:1.45;color:var(--ink)}

.table-scroll{overflow-x:auto;margin:6px 0 24px;border:1px solid var(--line);border-radius:14px;background:#fff}
.post table{border-collapse:collapse;width:100%;font-size:15.5px;line-height:1.5}
.post th{text-align:left;font-size:12.5px;font-weight:700;letter-spacing:.06em;text-transform:uppercase;color:var(--ink);background:var(--sand);padding:12px 16px;border-bottom:1px solid var(--line)}
.post td{padding:12px 16px;vertical-align:top;border-bottom:1px solid var(--line)}
.post tr:last-child td{border-bottom:none}
.post td:first-child{font-weight:600}
.table-scroll > table{margin:0}

.post .learnings-list{list-style:none;padding:22px 24px;background:var(--sand);border:1px solid var(--line);border-radius:18px}
.post .learnings-list li{position:relative;padding-left:28px;margin-bottom:14px}
.post .learnings-list li:last-child{margin-bottom:0}
.post .learnings-list li::before{content:"→";position:absolute;left:0;top:0;color:var(--orange-d);font-weight:700}

/* ══ components carried over from the old stylesheet (phone-frame carousels, video) ══ */
:root{--phone-pad-v:5px;--phone-pad-h:4px;--phone-radius:32px;--screen-radius:28px}
/* Centre a section heading only when a frame sits right under it (a phone section like "The Blank Map"). A frame at the
   end of a text section (android-coming-soon-recurring-fix, "The Setup", 2026-10-07) keeps its heading left-aligned
   like the text above it. Before 2026-10-07 this was .block:has(.ba-container) h2; all 12 frames then live sat right
   under their heading, so narrowing it changed no existing page. */
.post .block:has(> h2 + .ba-container) h2,.post .block:has(> h2 + .video-wrapper) h2{text-align:center}
.post .ba-container{margin:10px 0 24px}
.post img:not(.ba-slide){max-width:100%;height:auto;border-radius:12px}
.ba-container{display:flex;flex-direction:column;align-items:center;gap:6px}
.phone-mockup{position:relative;width:min(260px, calc(((100svh - 150px) * 0.462 + 18px) * 0.8123));max-width:100%;background:#1c1c1e;border-radius:var(--phone-radius);padding:var(--phone-pad-v) var(--phone-pad-h);box-shadow:0 0 0 1.5px #48484a, inset 0 0 0 1px #000, 0 28px 80px rgba(0,0,0,.30)}
.phone-mockup::before{content:"";position:absolute;left:-4px;top:19%;width:4px;height:40px;background:#3a3a3c;border-radius:2px 0 0 2px;box-shadow:0 57px 0 #3a3a3c, 0 108px 0 #3a3a3c}
.phone-mockup::after{content:"";position:absolute;right:-4px;top:25%;width:4px;height:74px;background:#3a3a3c;border-radius:0 2px 2px 0}
.phone-screen{position:relative;border-radius:var(--screen-radius);overflow:hidden;background:#1c1c1e;width:100%}
.phone-screen::before{content:"";position:absolute;top:1.4%;left:50%;transform:translateX(-50%);width:32%;aspect-ratio:3.4;background:#000;border-radius:50px;z-index:10;pointer-events:none}
.phone-screen::after{content:"";position:absolute;bottom:1.5%;left:50%;transform:translateX(-50%);width:34%;height:4px;background:rgba(255,255,255,.5);border-radius:4px;z-index:10;pointer-events:none}
.ba-strip{position:relative;width:100%;aspect-ratio:390 / 844;background:#000}
.ba-slide{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;object-position:top;display:block;transform:translateX(100%);transition:transform .38s cubic-bezier(.4,0,.2,1)}
.ba-slide.active{transform:translateX(0)}
.ba-slide.past{transform:translateX(-100%)}
.ba-toggle{display:flex;background:var(--line);border-radius:10px;padding:2px;gap:2px}
.ba-btn{font-family:var(--body);font-size:14px;font-weight:600;padding:7px 20px;border:none;border-radius:8px;background:transparent;color:var(--ink);cursor:pointer;transition:background .15s,color .15s,box-shadow .15s}
.ba-btn.active{background:#fff;color:var(--navy);box-shadow:0 1px 3px rgba(0,0,0,.15)}
.phone-screen--yt{aspect-ratio:390 / 844}
.phone-screen--yt iframe{width:100%;height:100%;border:none;display:block}
.video-wrapper{position:relative;padding-bottom:56.25%;height:0;border-radius:10px;overflow:hidden;background:#000}
.video-wrapper iframe{position:absolute;top:0;left:0;width:100%;height:100%;border:none}

/* ══ Pixel 10 Pro frame (added 2026-10-05) ══
   Use <div class="phone-mockup phone-mockup--pixel">. Everything inside works unchanged: phone-screen,
   ba-strip/ba-slide, phone-screen--yt, the ba-toggle buttons, and blog.js.
   Proportions follow Google's specs:
     - body 152.8 x 72 mm; 6.3" flat screen, 1280 x 2856 (20:9)
     - punch-hole camera centred at the top
     - power key above the volume rocker, both on the RIGHT edge; nothing on the left
     - colour: Obsidian
   Android screenshots already contain the status bar and the gesture bar, so this frame draws only the
   camera hole. A home-indicator pill like the iPhone's would show twice.
   Any 20:9 screenshot fits: 1080 x 2410 from a Pixel 10 Pro / 9 Pro, or 1280 x 2856 at full resolution.
   Other shapes are cropped from the top (object-fit:cover).
   Screen corner radius 19px is about 8.5% of the screen width. Keep it at 9% or less: Google Maps draws its logo
   18px (of 1080) from the left edge, and a rounder corner cut it off (it was 29px, about 13%, until 2026-10-05).
   Demo: Research/pixel-frame-2026-10-05-v1/. */
.phone-mockup--pixel{--phone-pad-v:9px;--phone-pad-h:9px;--phone-radius:30px;--screen-radius:19px;width:min(260px, calc(((100svh - 150px) * 0.4482 + 18px) * 0.8123));background:#0b0b0c;box-shadow:0 0 0 2.5px #3a3a3c, inset 0 0 0 1px #000, 0 28px 80px rgba(0,0,0,.30)}
.phone-mockup--pixel::before{left:auto;right:-5.5px;top:23%;width:3.5px;height:7%;border-radius:0 2px 2px 0;box-shadow:none}
.phone-mockup--pixel::after{right:-5.5px;top:33%;width:3.5px;height:14%}
.phone-mockup--pixel .phone-screen::before{top:2.5%;width:4.6%;aspect-ratio:1;border-radius:50%;box-shadow:0 0 0 1px rgba(255,255,255,.07)}
.phone-mockup--pixel .phone-screen::after{content:none}
.phone-mockup--pixel .ba-strip,.phone-mockup--pixel .phone-screen--yt{aspect-ratio:1280 / 2856}

/* TODO (parked by the owner 2026-10-05, to revisit): "every swipe is a section" on phones, like banavu.com/about.
   Trial: Research/blog-swipe-sections-2026-10-05-v1/ (README has the status and how to resume). Its rules would go here,
   in a block that applies only to phones in portrait and only to post pages, e.g.:
     @media (max-width:767px) and (orientation:portrait){
       html:has(article.post){scroll-snap-type:y proximity}             option B; option A = mandatory
       .post-head,.post .block{scroll-snap-align:start;scroll-margin-top:12px}
       main:has(.post) ~ .cta-wrap{scroll-snap-align:end} }
   Open question: the owner felt no difference between A and B on the simulators. 20 of 28 sections are taller than
   one screen, so A may only differ at section starts. Before shipping, measure the pages at 360, 375 and 390px
   (CLAUDE_WEB.md "Blog brand chrome rule"). Without the html:has(article.post) scope, the end-only snap point would
   pull the blog home and the category pages to the bottom. */

@media (max-width:520px){
  body{font-size:17px}
  .cat-grid{grid-template-columns:1fr;gap:14px}
  .cat-card{padding:18px 18px 16px}
  .post-row{gap:12px;padding:22px 0}
  .post .learnings-list{padding:18px}
  .post pre{border-radius:10px;font-size:13px}
  .post table{font-size:14px}
  .post th,.post td{padding:10px 12px}
}
@media (max-width:480px){
  :root{--phone-radius:30px;--screen-radius:26px}
  /* Phones (fixed 2026-10-05): 70vw, but never taller than the screen. A plain width:70vw overrode the fit-to-height
     width above, so the phone plus its buttons were 24-117px taller than the room below the top bar at 390x664,
     360x640 and 375x548 (screen sizes with the browser bars showing); no scroll position showed the whole phone.
     Room = 100svh - top bar (--navh) - 65px (6px gap + 35px buttons + 24px breathing room). The phone's height for a
     width W: iPhone (W - 8) * 844/390 + 10; Pixel (W - 18) * 2856/1280 + 18. These are solved for W below.
     blog.js centres a phone that comes to rest mostly on screen ("settle"), which needs the phone to fit. */
  .phone-mockup{width:min(70vw, calc((100svh - var(--navh) - 75px) * 0.4621 + 8px))}
  .phone-mockup--pixel{width:min(70vw, calc((100svh - var(--navh) - 83px) * 0.4482 + 18px))}
  /* A frame right under its section heading (h2 + .ba-container, e.g. "The Blank Map") settles together with that
     heading (blog.js), so it also leaves room for a one-line h2: 29px heading + 14px gap = 43px more (owner,
     2026-10-05: "starting with the blank map and ending with the last pixel of the screenshot toggle"). A heading that
     wraps to two lines doesn't fit; blog.js then settles the frame alone. */
  .block:has(> h2 + .ba-container) .phone-mockup{width:min(70vw, calc((100svh - var(--navh) - 118px) * 0.4621 + 8px))}
  .block:has(> h2 + .ba-container) .phone-mockup--pixel{width:min(70vw, calc((100svh - var(--navh) - 126px) * 0.4482 + 18px))}
}
@media (prefers-reduced-motion:reduce){
  .cat-card,.post-row-arrow,.ba-slide{transition:none}
  .cat-card:hover{transform:none}
}
