/* Shared chrome for jaredhebb.com: palette, header, nav dropdowns, footer,
   cookie banner. Every page links this, then overrides in its own <style>.
   Derived from the rules that were already byte-identical across index, work,
   and claude-md, so linking it changes nothing visually.
   404.html deliberately does not use it: no header, no footer, own layout. */

:root{
  --paper:#F4F4EF; --ink:#20241F; --ink-soft:#4C544C; --line:#D9DAD0;
  --accent:#256B45; --accent-deep:#1B5335; --card:#FDFDFA;
  /* Text that sits ON an accent-filled button. Was a hardcoded #fff in six
     places; it has to be near-black in dark, where the accent is a bright
     green and white on it is unreadable. */
  --on-accent:#fff;
  --accent-wash:rgba(37,107,69,.08);
  --shadow:rgba(20,32,25,.16);
  --danger:#8A2C22;
}

/* Dark theme. Only the tokens change: every rule below and in tool.css,
   guide.css and the page <style> blocks already reads from them, which is what
   makes this a small edit rather than a second stylesheet.
   The palette is the terminal motif the site already uses, so dark mode looks
   like the thing on the homepage rather than an inverted copy of the light one.
   A first-time visitor with no stored choice gets LIGHT, not their OS setting.
   theme-init.js applies [data-theme="dark"] before paint only for a stored 'dark',
   or for a stored 'system' on a dark OS. Following the OS is the toggle's third
   state, stored as 'system', and it is opted into rather than assumed.
   Do not "fix" this back to prefers-color-scheme. It was that way on 2026-07-28
   and it was the bug: the hub went dark on a dark OS while every page clicked
   through to stayed white, because electricaltoolbox and the Build Log serve light
   unconditionally. Commit a7741d8, "Default to light, not to the OS setting", is
   the later decision and the current one. */
:root[data-theme="dark"]{
  --paper:#0F1310; --ink:#E4E8E1; --ink-soft:#9BA79E; --line:#2C332E;
  --accent:#5BD98F; --accent-deep:#8CE9B3; --card:#171C18;
  --on-accent:#0E1610;
  --accent-wash:rgba(91,217,143,.13);
  --shadow:rgba(0,0,0,.55);
  --danger:#FF9C8F;
}

/* reset and base */
*{box-sizing:border-box}
html{scroll-behavior:smooth;color-scheme:light}
/* Tracks the active theme, not just the light default: native scrollbars and form
   controls need to know dark is active or they render with light-theme chrome on a
   dark page. theme-init.js sets data-theme="dark" before paint whenever dark is the
   active theme, which means a stored 'dark' or a stored 'system' on a dark OS, so
   this selector alone covers all three states of the toggle. No stored choice is
   light and carries no attribute, which the html rule above already handles. */
html[data-theme="dark"]{color-scheme:dark}
a,button,input,select,textarea,summary,[role=button]{-webkit-tap-highlight-color:transparent}
button,a,summary,[role=button],input[type=submit],input[type=button]{touch-action:manipulation}
:focus-visible{outline:2px solid var(--accent);outline-offset:2px;border-radius:2px}
/* Offscreen until a keyboard user tabs to it. */
.skip-link{position:absolute;left:-9999px;top:0;z-index:100;padding:.6rem 1rem;
  border-radius:0 0 4px 0;text-decoration:none;background:var(--accent);color:var(--on-accent)}
.skip-link:focus{left:0}
@media (prefers-reduced-motion:reduce){
  *,*::before,*::after{animation-duration:.01ms!important;animation-iteration-count:1!important;transition-duration:.01ms!important;scroll-behavior:auto!important}
}
body{margin:0;background:var(--paper);color:var(--ink);min-height:100vh;
  display:flex;flex-direction:column;
  font:400 1.05rem/1.65 system-ui,-apple-system,"Segoe UI",Roboto,sans-serif}
.wrap{max-width:64rem;margin:0 auto;padding:0 1.25rem;width:100%}
a{color:var(--accent)}

/* headings */
h1{font-family:"Iowan Old Style","Palatino Linotype",Palatino,Georgia,serif;
  font-weight:600;line-height:1.12;letter-spacing:-0.01em;text-wrap:balance;
  font-size:clamp(2rem,4.5vw,2.9rem);margin:0 0 .4em}
.eyebrow{font-family:ui-monospace,"Cascadia Code",Consolas,Menlo,monospace;
  font-size:.72rem;letter-spacing:.14em;text-transform:uppercase;color:var(--accent);margin:0 0 1rem}

/* header */
header.site{border-bottom:1px solid var(--line)}
header.site .wrap{display:flex;justify-content:space-between;align-items:center;padding:1.1rem 1.25rem}
.brand{font-family:ui-monospace,"Cascadia Code",Consolas,monospace;font-size:.9rem;color:var(--ink);text-decoration:none;letter-spacing:.02em}
header nav{display:flex;gap:1.5rem;align-items:center;flex-wrap:wrap}
header nav > a{color:var(--ink-soft);text-decoration:none;font-size:.9rem}
header nav > a:hover{color:var(--accent)}

/* Visible theme control. A real button (not a link), so it is a control an
   accessible-name test and a keyboard user both find. Its text comes from
   theme-toggle.js, which also owns the click; this is just the resting look.
   Lives in the footer markup (still inside the footer CHROME_BLOCKS boundary,
   so chrome.test.js still catches drift across all 18 pages) but is pinned to
   the bottom-right corner of the viewport, not the footer's own position: it
   is a small, understated control, not part of the primary nav (moved out of
   the header 2026-07-30, per Jared: "it's not meant to be a prominent
   feature, just a feature"). */
.theme-toggle{position:fixed;right:1rem;bottom:1rem;z-index:41;
  font:inherit;color:var(--ink-soft);background:var(--card);
  border:1px solid var(--line);border-radius:5px;padding:.5rem;cursor:pointer;
  display:inline-flex;align-items:center;justify-content:center;
  min-width:44px;min-height:44px;box-shadow:0 4px 14px var(--shadow)}
.theme-toggle:hover{color:var(--accent);border-color:var(--accent)}
.theme-toggle svg{width:1.1rem;height:1.1rem;display:block}

/* Keep clear of the cookie consent banner, which also anchors to the bottom
   of the viewport: while it's visible, lift the toggle above it so neither
   ever covers the other.
   ponytail: fixed safe offsets rather than a measured banner height (no JS
   added for this). 5rem/7.5rem clear the banner's one-line desktop layout and
   its wrapped two-line mobile layout with room to spare. If the banner's copy
   ever grows enough to need more, bump these two numbers. */
body:has(#cookie-consent:not([hidden])) .theme-toggle{bottom:5rem}
@media(max-width:640px){
  body:has(#cookie-consent:not([hidden])) .theme-toggle{bottom:7.5rem}
}

/* nav dropdowns (native <details>, no JS; name="nav" keeps them exclusive) */
.menu{position:relative}
.menu summary{list-style:none;cursor:pointer;color:var(--ink-soft);font-size:.9rem;
  display:inline-flex;align-items:center;gap:.3rem}
.menu summary::-webkit-details-marker{display:none}
.menu summary:hover,.menu[open] summary{color:var(--accent)}
.menu summary:focus-visible{outline:2px solid var(--accent);outline-offset:3px;border-radius:3px}
.menu .drop{position:absolute;top:calc(100% + .8rem);right:0;min-width:17rem;
  background:var(--card);border:1px solid var(--line);border-radius:8px;
  box-shadow:0 14px 34px var(--shadow);padding:.5rem;z-index:20}
.menu .drop a{display:block;text-decoration:none;padding:.55rem .65rem;border-radius:5px;color:var(--ink)}
.menu .drop a:hover{background:var(--accent-wash)}
.menu .drop .t{display:block;font-size:.9rem;font-weight:600}
.menu .drop .s{display:block;font-size:.78rem;color:var(--ink-soft);line-height:1.4;margin-top:.1rem}
.menu .drop .sep{height:1px;background:var(--line);margin:.4rem .3rem}
.menu .drop a.all{font-size:.82rem;color:var(--accent-deep);font-weight:600}

/* Phone header. Without this the brand and the six nav links share one
   space-between row, which squeezes the brand until "jared hebb ~ %" wraps to two
   lines and the nav rows collide with it. Stack them: brand on its own line, nav a
   full-width wrapping row underneath. */
@media(max-width:640px){
  header.site .wrap{flex-direction:column;align-items:flex-start;gap:.75rem}
  header nav{width:100%;position:relative;gap:.55rem 1.1rem}

  /* The dropdown panels open leftward (right:0), which is right on desktop and runs
     off the left edge of a phone. Anchor them to the nav's left edge instead and
     cap the width to the viewport, so neither menu can spill off either side
     regardless of where it lands in the wrap. .menu goes static so the panel
     positions against nav, not against its own summary. */
  .menu{position:static}
  .menu .drop{top:calc(100% + .5rem);left:0;right:auto;
    width:min(20rem,calc(100vw - 2.5rem));min-width:0}
}

/* Entry cards. Shared by /work and /workflows, which list the same shape: a kind
   tag, a name, a plain blurb, and one link out. Lived in work.html until the second
   page needed it. */
.projects{display:flex;flex-direction:column;border-top:1px solid var(--line)}
.proj{display:grid;grid-template-columns:1fr auto;gap:1.4rem;align-items:start;
  padding:1.6rem 0;border-bottom:1px solid var(--line)}
@media(max-width:640px){.proj{grid-template-columns:1fr;gap:.9rem}}
.proj .kind{font-family:ui-monospace,Consolas,monospace;font-size:.68rem;letter-spacing:.1em;
  text-transform:uppercase;color:var(--accent);margin:0 0 .5rem}
.proj h2{font-family:"Iowan Old Style","Palatino Linotype",Palatino,Georgia,serif;
  font-weight:600;font-size:1.4rem;margin:0 0 .4rem}
.proj p{margin:0;color:var(--ink-soft);max-width:38rem;font-size:.98rem}
.proj .go{white-space:nowrap;text-decoration:none;font-size:.85rem;font-weight:600;
  color:var(--accent-deep);border:1px solid var(--line);background:var(--card);
  padding:.5rem .95rem;border-radius:5px}
.proj .go:hover{border-color:var(--accent)}

/* Shop cards. A store shelf, not a list: cover first, one Buy button per card, and the
   grid drops to two columns then one on its own via auto-fill rather than breakpoints.
   The covers are not all the same shape (600x900 and 600x960 today), so the frame owns
   the aspect ratio and the image is contained inside it. Cropping a book cover cuts the
   title off, which is the one thing a cover is for. */
.shop{display:grid;gap:1.6rem;grid-template-columns:repeat(auto-fill,minmax(15rem,1fr))}
.buy{display:flex;flex-direction:column;border:1px solid var(--line);border-radius:10px;
  background:var(--card);overflow:hidden}
.buy:hover{border-color:var(--accent)}
.buy .shot{display:block;aspect-ratio:2/3;background:var(--paper);border-bottom:1px solid var(--line)}
.buy .shot img{width:100%;height:100%;object-fit:contain;display:block}
.buy .text{padding:1.1rem 1.1rem .4rem}
.buy .kind{font-family:ui-monospace,Consolas,monospace;font-size:.68rem;letter-spacing:.1em;
  text-transform:uppercase;color:var(--accent);margin:0 0 .4rem}
.buy h2{font-family:"Iowan Old Style","Palatino Linotype",Palatino,Georgia,serif;
  font-weight:600;font-size:1.2rem;margin:0 0 .45rem;line-height:1.25}
.buy h2 a{color:inherit;text-decoration:none}
.buy h2 a:hover{text-decoration:underline}
.buy .text p{margin:0;color:var(--ink-soft);font-size:.9rem}
/* margin-top:auto pins the price row to the bottom, so buttons line up across a row of
   cards whose blurbs are different lengths. */
.buy .foot{margin:auto 0 0;padding:1.1rem;display:flex;align-items:center;
  justify-content:space-between;gap:.75rem}
.buy .price{font-weight:600;font-size:.95rem}
.buy .go{text-decoration:none;font-size:.85rem;font-weight:600;color:var(--on-accent);
  background:var(--accent-deep);padding:.5rem 1.1rem;border-radius:5px}
.buy .go:hover{background:var(--accent)}

/* footer */
footer.site{border-top:1px solid var(--line)}
footer.site .wrap{padding:1.6rem 1.25rem;color:var(--ink-soft);font-size:.85rem;
  display:flex;justify-content:space-between;flex-wrap:wrap;gap:.6rem}
footer.site a{color:var(--ink-soft)}
footer.site a:hover{color:var(--accent)}

/* cookie consent banner (GA4 loads only after Accept, see ga-consent.js) */
#cookie-consent{position:fixed;left:0;right:0;bottom:0;z-index:50;
  background:var(--paper);border-top:1px solid var(--line);padding:1rem 1.25rem}
#cookie-consent .inner{max-width:64rem;margin:0 auto;display:flex;gap:.8rem;
  flex-wrap:wrap;align-items:center;justify-content:space-between}
#cookie-consent p{margin:0;font-size:.9rem;color:var(--ink-soft)}
#cookie-consent .buttons{display:flex;gap:.6rem}
#cookie-consent button{font:600 .85rem system-ui,sans-serif;padding:.5rem 1rem;
  border-radius:4px;cursor:pointer}
#cookie-consent .accept{background:var(--accent);color:var(--on-accent);border:0}
#cookie-consent .accept:hover{background:var(--accent-deep)}
#cookie-consent .reject{background:transparent;color:var(--ink-soft);border:1px solid var(--line)}

