/* 2muchcoffee blog, on the 2MC Redesign 2026 design system (light half).
   Token values are the Figma export in 2muchcoffee_site/design-system/tokens.md.
   Plain CSS on purpose: Ghost serves this file directly, so there is no build
   step, no node_modules and no committed artifact that can drift from source. */

:root{
  --bg-base:#FFFFFF; --bg-subtle:#FAFAFA; --bg-elevated:#FFFFFF; --bg-inverse:#0E0E11;
  --text-primary:#17171B; --text-secondary:#55555E; --text-muted:#6F6F79; --text-inverse:#FFFFFF;
  --border-subtle:#E4E4E8; --border-strong:#C9C9D0; --border-interactive:#74747F;
  --accent:#6B35E0; --accent-hover:#5A28C4; --accent-subtle:#F4F0FE; --on-accent:#FFFFFF;
  --danger:#C5221F;
  /* the redesign's inverted pill: dark on the light ground, the mirror of the
     white pill it uses on the dark ground */
  --invert-bg:#17171B; --invert-ink:#FFFFFF;
  --glow:#6B35E0;
  --sans:'Instrument Sans',system-ui,-apple-system,'Segoe UI',Roboto,sans-serif;
  --mono:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;
  --measure:640px; --pad:20px; --rail:220px;
}
/* The redesign insets from the viewport edge and never caps a container, so the
   chrome is full-bleed and only the article keeps a measure. Scale copied from
   --pad-x in redesign-shared/_rd-shared.scss. */
@media (min-width:768px){ :root{ --pad:40px } }

/* ---------- dark theme ----------
   Light is the default and dark is strictly opt-in, because the marketing site
   is white: an OS-dark reader following prefers-color-scheme would get a dark
   blog hanging off a light site. So there is deliberately NO
   prefers-color-scheme block here. Dark applies only when the reader has
   chosen it and the choice is stamped on <html>. Values are Andrii's dark
   column, verbatim. */
:root[data-theme="dark"]{
  --bg-base:#0E0E11; --bg-subtle:#17171B; --bg-elevated:#1E1E23; --bg-inverse:#FFFFFF;
  --text-primary:#FFFFFF; --text-secondary:#9E9EA8; --text-muted:#8A8A94; --text-inverse:#0E0E11;
  --border-subtle:#26262C; --border-strong:#3C3C44; --border-interactive:#74747F;
  --accent:#7C4DEE; --accent-hover:#6B35E0; --accent-subtle:#250F4F; --on-accent:#FFFFFF;
  --danger:#FF6B6B;
  --invert-bg:#FFFFFF; --invert-ink:#17171B;
  --glow:#9979EE;
}
:root{color-scheme:light}
:root[data-theme="dark"]{color-scheme:dark}

/* the toggle itself */
.thm{display:inline-flex;align-items:center;justify-content:center;width:44px;height:44px;flex:none;
  padding:0;border:1px solid var(--border-subtle);border-radius:999px;background:transparent;
  color:var(--text-primary);cursor:pointer;transition:border-color .15s ease}
.thm:hover{border-color:var(--border-strong)}
.thm svg{width:18px;height:18px;fill:none;stroke:currentColor;stroke-width:1.7;stroke-linecap:round}
.thm .thm__moon{display:none}
:root[data-theme="dark"] .thm .thm__sun{display:none}
:root[data-theme="dark"] .thm .thm__moon{display:block}


@media (min-width:1024px){ :root{ --pad:60px } }
@media (min-width:1440px){ :root{ --pad:80px } }

*,*::before,*::after{box-sizing:border-box}
html{scroll-padding-top:88px}
body{margin:0;background:var(--bg-base);color:var(--text-primary);
  font:400 17px/25.5px var(--sans);-webkit-font-smoothing:antialiased}
img{max-width:100%;height:auto}
a{color:inherit;text-decoration-color:var(--border-strong);text-underline-offset:3px}
a:hover{text-decoration-color:var(--accent)}
:focus-visible{outline:2px solid var(--accent);outline-offset:3px}
.skip-link{position:absolute;left:-9999px}
.skip-link:focus{left:16px;top:16px;z-index:100;background:var(--bg-inverse);color:var(--text-inverse);
  padding:12px 18px;border-radius:8px;text-decoration:none}
.eyebrow{font:500 12px/14.6px var(--sans);letter-spacing:.72px;text-transform:uppercase;color:var(--text-muted)}

/* Button, mapped to the system's Primary / Accent / Secondary variants */
.btn{display:inline-flex;align-items:center;justify-content:center;gap:8px;padding:10px 18px;
  border:1px solid transparent;border-radius:8px;font:500 16px/24px var(--sans);text-decoration:none;
  white-space:nowrap;cursor:pointer;transition:background .15s ease,border-color .15s ease}
.btn--primary{background:var(--invert-bg);color:var(--invert-ink)}
.btn--primary:hover{opacity:.88}
.btn--accent{background:var(--accent);color:var(--on-accent)}
.btn--accent:hover{background:var(--accent-hover)}
.btn--secondary{color:var(--text-primary);border-color:var(--border-interactive)}
.btn--s{font:600 14px/19.6px var(--sans);padding:8px 14px}

/* ---------- announce bar + header, ported from the redesign ---------- */
.an{display:flex;align-items:center;justify-content:center;gap:10px;flex-wrap:wrap;
  padding:16px var(--pad);font-size:14px}
@media (min-width:450px){.an{font-size:16px}}
.an__dot{color:var(--text-muted)}
.an__link{color:var(--text-secondary)}
.an__arrow{display:none;align-items:center;justify-content:center;width:67px;height:44px;
  border-radius:999px;background:var(--invert-bg);flex:none}
@media (min-width:450px){.an__arrow{display:inline-flex}}
.an__arrow svg{width:17px;height:17px;fill:none;stroke:var(--invert-ink);stroke-width:2;
  stroke-linecap:round;stroke-linejoin:round}
.an__arrow:hover{opacity:.88}
.an__glow{height:7px;margin:0 auto;max-width:826px;filter:blur(2px);
  background:linear-gradient(90deg,transparent,var(--glow),transparent)}

.hdr{display:flex;align-items:center;gap:8px;flex-wrap:wrap;padding:22px var(--pad)}
.hdr__brand{display:inline-flex;align-items:center;gap:9px;min-height:51px;padding:11px 16px;
  border:1px solid var(--border-subtle);border-radius:999px;text-decoration:none;
  color:var(--text-primary);transition:border-color .18s ease;flex:none}
.hdr__brand:hover{border-color:var(--border-strong)}
.hdr__mark{width:27px;height:20px;flex:none;color:var(--text-primary)}
.hdr__name{font-size:13px;letter-spacing:.02em}
.hdr__name b{font-weight:700}
.hdr__nav{display:none;align-items:center;gap:6px;min-height:51px;padding:5px 10px;
  border:1px solid var(--border-subtle);border-radius:999px}
@media (min-width:1280px){.hdr__nav{display:flex}}
.hdr__link{padding:7px 17px;border-radius:999px;font-size:16px;color:var(--text-secondary);text-decoration:none}
.hdr__link:hover{color:var(--text-primary);background:var(--bg-subtle)}
.hdr__link.is-current{color:var(--text-primary)}
.hdr__burger{display:inline-flex;align-items:center;justify-content:center;margin-left:auto;
  width:51px;height:51px;flex:none;padding:0;border:1px solid var(--border-subtle);
  border-radius:999px;background:transparent;color:var(--text-primary);cursor:pointer}
.hdr__burger:hover{border-color:var(--border-strong)}
@media (min-width:1280px){.hdr__burger{display:none}}
/* three bars from one element: the middle is the box, the outer two are pseudo-elements,
   so the X is a transform rather than a second icon */
.hdr__bars{position:relative;display:block;width:18px;height:1.5px;background:currentColor;
  transition:background-color .18s ease}
.hdr__bars::before,.hdr__bars::after{content:'';position:absolute;left:0;width:100%;height:1.5px;
  background:currentColor;transition:transform .22s cubic-bezier(.33,1,.68,1)}
.hdr__bars::before{top:-6px}
.hdr__bars::after{top:6px}
.hdr__bars.is-x{background:transparent}
.hdr__bars.is-x::before{transform:translateY(6px) rotate(45deg)}
.hdr__bars.is-x::after{transform:translateY(-6px) rotate(-45deg)}
.hdr__cta{display:none;min-height:44px}
@media (min-width:450px){.hdr__cta{display:inline-flex}}
@media (min-width:1280px){.hdr__cta{margin-left:auto}}
.hdr__menu{display:none;flex-basis:100%;flex-direction:column;margin-top:14px;padding:8px;
  border:1px solid var(--border-subtle);border-radius:20px;background:var(--bg-subtle)}
.hdr__menu.is-open{display:flex}
@media (min-width:1280px){.hdr__menu{display:none !important}}
.hdr__menu-link{padding:13px 16px;border-radius:12px;font-size:17px;
  color:var(--text-secondary);text-decoration:none}
.hdr__menu-link:hover{color:var(--text-primary);background:var(--bg-elevated)}
@media (min-width:450px){.hdr__menu-link--contact{display:none}}
@media (prefers-reduced-motion:reduce){.hdr__bars,.hdr__bars::before,.hdr__bars::after{transition:none}}

/* ---------- article ---------- */
.post{max-width:calc(var(--measure) + var(--pad) * 2);margin:0 auto;
  padding:48px var(--pad) 96px;position:relative}
.post__kicker{display:inline-block;text-decoration:none;margin-bottom:12px}
.post__title{margin:0 0 16px;font:400 40px/44px var(--sans);letter-spacing:-.01em;text-wrap:balance}
@media (min-width:768px){.post__title{font-size:48px;line-height:57.6px}}
.post__dek{margin:0 0 28px;font:400 20px/30px var(--sans);color:var(--text-secondary);max-width:34em}
.post__byline{display:flex;align-items:center;gap:14px;flex-wrap:wrap;
  padding:16px 0;border-top:1px solid var(--border-subtle);border-bottom:1px solid var(--border-subtle)}
.post__avatar{width:44px;height:44px;border-radius:50%;flex:none;object-fit:cover}
.post__avatar--initials{display:grid;place-items:center;background:var(--accent-subtle);
  color:var(--accent);font:600 14px var(--sans);text-transform:uppercase}
.post__who{display:grid;line-height:1.35}
.post__author{font:500 16px/24px var(--sans)}
.post__role{font:400 14px/21px var(--sans);color:var(--text-secondary)}
.post__dates{margin-left:auto;font:400 14px/21px var(--sans);color:var(--text-muted);display:grid;text-align:right}
.post__updated{color:var(--text-secondary)}
.post__hero{margin:28px 0 8px}
.post__hero img{display:block;width:100%;aspect-ratio:16/9;object-fit:cover;border-radius:12px;background:var(--bg-subtle)}
.post__hero figcaption{margin-top:10px;font:400 14px/21px var(--sans);color:var(--text-muted)}

/* ---------- post body: the .post-full-content contract ---------- */
.post-full-content{padding-top:20px}
.post-full-content > *{max-width:100%}
.post-full-content p{margin:0 0 18px;max-width:600px}
.post-full-content h2{margin:52px 0 14px;font:500 28px/36.4px var(--sans);text-wrap:balance;scroll-margin-top:88px}
.post-full-content h3{margin:36px 0 12px;font:500 22px/28.6px var(--sans)}
.post-full-content ul,.post-full-content ol{margin:0 0 18px;padding-left:22px;max-width:600px}
.post-full-content li{margin-bottom:8px}
.post-full-content a{color:var(--accent);text-decoration-color:var(--accent)}
.post-full-content strong{font-weight:500}
.post-full-content img{border-radius:12px}
.post-full-content figcaption{margin-top:10px;font:400 14px/21px var(--sans);color:var(--text-muted);text-align:center}
.post-full-content blockquote{margin:30px 0;padding:20px 24px;background:var(--accent-subtle);
  border-radius:12px;font:500 20px/30px var(--sans)}
.post-full-content blockquote p{margin:0;max-width:none}
.post-full-content hr{border:0;border-top:1px solid var(--border-subtle);margin:40px 0}
.post-full-content mark{background:var(--accent-subtle);color:var(--accent)}
.post-full-content code{font:14px/1.5 var(--mono);background:var(--bg-subtle);
  border:1px solid var(--border-subtle);border-radius:5px;padding:2px 6px}
.post-full-content pre{background:var(--bg-inverse);color:var(--text-inverse);padding:18px 20px;
  border-radius:12px;overflow-x:auto;font:13px/1.7 var(--mono)}
.post-full-content pre code{background:none;border:0;padding:0;color:inherit}

/* Archive embeds. 21 posts carry iframes. On prod today their stored HTML puts bare
   iframes as direct children of .post-full-content, so `> *{max-width:100%}` catches
   them; the moment anyone re-saves such a post Ghost re-renders it inside a
   .kg-embed-card figure and the cap stops applying. This is the latent-overflow guard.
   The 16/9 ratio is scoped to video/gif hosts on purpose: applying it to every
   .kg-embed-card iframe crushes the three SlideShare decks from 485px tall to 152px. */
.post-full-content iframe{max-width:100%}
.post-full-content .kg-embed-card iframe[src*="youtube.com"],
.post-full-content .kg-embed-card iframe[src*="youtube-nocookie.com"],
.post-full-content .kg-embed-card iframe[src*="giphy.com"]{width:100%;height:auto;aspect-ratio:16/9}

/* 60 pull-quotes across 42 posts (the whole 2026 cohort) carry an inline color:#1f2937,
   which lands on --accent-subtle #250F4F in dark at 1.14:1. Only the colour is forced:
   543 of the archive's 603 blockquotes carry no inline style and already read 16.67:1,
   so stripping the background would flatten 141 posts to rescue 42. */
:root[data-theme="dark"] .post-full-content blockquote{color:var(--text-primary)!important}

/* Tables: solved HERE so post-level cards stop paying 32 !important each to
   escape a theme rule. The wrapper scrolls, so the page body never does. */
.post-full-content table{width:100%;border-collapse:collapse;font-size:16px;display:block;overflow-x:auto}
.post-full-content table th,.post-full-content table td{
  padding:12px 14px;text-align:left;vertical-align:top;white-space:normal;
  border-bottom:1px solid var(--border-subtle)}
.post-full-content table th{font-weight:500;background:var(--bg-subtle)}

/* FAQ: ships open so the answer is in the rendered text, not behind a toggle */
/* ---------- neutralising the site-wide code injection ----------
   Production's Ghost code injection carries casper-era CSS, and default.hbs puts
   {{ghost_head}} (line 23) AFTER this stylesheet (line 19) by Ghost's own
   convention, so on equal specificity the injection wins. Casper set
   html{font-size:62.5%}, making 1rem = 10px; this theme does not, so those rem
   values inflate by 1.6x the moment the theme is activated. Measured on a local
   copy of the live injection: h2 renders 49.6px inside a 36.4px line box, which
   overlaps glyphs on every two-line heading in all 283 archive posts.

   Each selector below is the injection's own selector plus a `body` prefix, which
   raises it by one element and wins without !important. Nothing here changes what
   the theme looks like on its own; remove the injection and every one of these is
   a no-op restating the rule directly above it.

   DELIBERATELY NOT NEUTRALISED, do not "finish" this list:
     h3 u, h3 u:hover, .post-full-content li u, .post-full-content li a:hover
       78% of sampled posts use <u> inside headings and list items and rely on
       these to suppress the underline. Overriding them restores browser-default
       underlines across most of the archive.
     .post-header .post-title, .post-content p, .post-full-content .nft img
       dead casper selectors that match nothing this theme renders.

   Rejected alternative 1: html{font-size:62.5%}. The theme uses zero rem (checked:
   0 occurrences), so it would be side-effect free and would restore 3.1rem to
   31px. It also hands the archive's whole type scale back to the injection, which
   is the casper scale, not this one.
   Rejected alternative 2: moving the stylesheet below {{ghost_head}}. That wins by
   source order with no specificity games, but it silently disables ALL future code
   injection CSS site-wide, not just these eight stale declarations. */
body .post-full-content h2{font-size:28px;line-height:36.4px;padding-bottom:0}
body .post-full-content h3{font-size:22px;line-height:28.6px;font-weight:500;padding-top:0}
body .post-full-content blockquote{font-size:20px;line-height:30px}
body .post-full-content{padding-bottom:0}
body .post-full-content ul{padding-left:22px}
body .post-full-content a:hover{color:var(--accent)}

.post-full-content details{border-bottom:1px solid var(--border-subtle)}
.post-full-content summary{list-style:none;cursor:pointer;display:flex;justify-content:space-between;
  align-items:center;gap:16px;padding:16px 0;font:500 20px/30px var(--sans)}
.post-full-content summary::-webkit-details-marker{display:none}
.post-full-content summary::after{content:"+";flex:none;width:28px;height:28px;border-radius:50%;
  border:1px solid var(--border-interactive);display:grid;place-items:center;font:400 18px/1 var(--sans);color:var(--text-muted)}
.post-full-content details[open] summary::after{content:"\2212";background:var(--accent-subtle);
  color:var(--accent);border-color:transparent}

/* ---------- sticky scroll-spy rail ---------- */
.post__rail{display:none}
@media (min-width:1320px){
  /* Raised from 1180: .post is now 640 + 2*--pad wide, so below 1320 the rail's
     -260px offset ran off the left edge. The rail is an inert duplicate of the
     in-body list, so hiding it early costs nothing.
     top:48px matched .post's padding-top, but height:100% then measured the FULL
     container from that offset, so the rail overhung its own article by 48px and
     the sticky child rode down over the footer. Anchoring top AND bottom lets the
     height compute, so the rail can never outlast the article it belongs to. */
  .post__rail{display:block;position:absolute;top:48px;bottom:96px;
    left:calc(var(--rail) * -1 - 40px);width:var(--rail)}
  .post__rail-in{position:sticky;top:88px}
  .post .toc{display:grid;gap:2px;margin-top:12px;border-left:2px solid var(--border-subtle)}
  .post .toc a{display:block;padding:5px 0 5px 12px;margin-left:-2px;border-left:2px solid transparent;
    font:400 14px/19.6px var(--sans);color:var(--text-secondary);text-decoration:none}
  .post .toc a:hover{color:var(--text-primary)}
  .post .toc a.is-current{color:var(--accent);font-weight:500;border-left-color:var(--accent)}
}

/* ---------- author card ---------- */
.au{display:grid;grid-template-columns:56px 1fr;gap:16px;align-items:start;margin-top:48px;
  padding:22px;background:var(--bg-subtle);border:1px solid var(--border-subtle);border-radius:12px}
.au__avatar{width:56px;height:56px;border-radius:50%;object-fit:cover}
.au__avatar--initials{display:grid;place-items:center;background:var(--accent-subtle);
  color:var(--accent);font:600 16px var(--sans);text-transform:uppercase}
.au__name{font:500 20px/30px var(--sans)}
.au__role{margin:2px 0 8px}
.au__link{font-size:14px;color:var(--accent)}

/* 38 of 282 published posts close with their own inline person card (.tmc-au,
   authored into the body). On those the theme's card is a duplicate; on the
   other 244 it is the only author attribution there is. :has() suppresses the
   theme card only where the body already provides one, and degrades to showing
   both, which is untidy rather than broken. */
.post-full-content:has(.tmc-au) + .au{display:none}

/* ---------- reveal on scroll ----------
   Guard 1: nothing hides until the script confirms it runs, so a stripped or
   failed script leaves a plain readable article. Guard 2: reduced motion wins.
   Guard 3: opacity and transform only, so nothing reflows. */
html.js-ready .rv{opacity:0;transform:translateY(18px);
  /* was .5s ease, which decelerates too late and reads as a pop. This curve is
     nearly all deceleration, so the block arrives rather than snaps. The script
     adds a small per-item delay so a batch crossing together does not flash. */
  transition:opacity .62s cubic-bezier(.22,1,.36,1),transform .62s cubic-bezier(.22,1,.36,1)}
html.js-ready .rv.in{opacity:1;transform:none}
/* cards carry a touch more travel, since they are larger objects */
html.js-ready .card.rv{transform:translateY(24px) scale(.995)}
html.js-ready .card.rv.in{transform:none}
@media (prefers-reduced-motion:reduce){
  .rv{opacity:1 !important;transform:none !important;transition:none !important}
}

/* ---------- index ---------- */
.feed{padding:48px var(--pad) 80px;
  display:grid;gap:24px;grid-template-columns:repeat(auto-fill,minmax(300px,1fr))}
.card{display:grid;gap:10px;text-decoration:none;padding:20px;border:1px solid var(--border-subtle);
  border-radius:12px;background:var(--bg-elevated);transition:border-color .15s ease}
.card:hover{border-color:var(--border-interactive)}
.card__img{width:100%;aspect-ratio:16/9;object-fit:cover;border-radius:8px;background:var(--bg-subtle)}
.card__title{font:500 20px/30px var(--sans);text-wrap:balance}
.card__excerpt{font:400 16px/24px var(--sans);color:var(--text-secondary)}
.card__meta{font:500 12px/14.6px var(--sans);letter-spacing:.72px;text-transform:uppercase;color:var(--text-muted)}

/* ---------- contact form, matched to the redesign's .rd__form-embed ----------
   Same shape as the Angular one: a 40px-radius bordered card sitting on the
   subtle band, the form on the left and "What's next" on the right behind a
   hairline divider, 12px fields on --bg-subtle, and the inverted pill submit.

   One deliberate divergence. The redesign keeps a floating faux-placeholder
   label because the Angular component is shared and JS-driven, so they could
   only restyle it. Nothing constrains us here, and a placeholder standing in
   for a label disappears the moment someone types, so these are real <label>s
   above their fields. Visual weight matches; the mechanism is better. */
.cf{padding:64px var(--pad) 72px;background:var(--bg-subtle);border-top:1px solid var(--border-subtle);margin-top:64px}
.cf__in{border:1px solid var(--border-subtle);border-radius:40px;overflow:hidden;
  background:var(--bg-base);display:grid}
@media (min-width:1024px){.cf__in{grid-template-columns:minmax(0,1.2fr) minmax(0,.8fr)}}
.cf__form{padding:40px 28px}
@media (min-width:768px){.cf__form{padding:48px 40px}}
.cf__title{margin:0;font:400 28px/1.15 var(--sans);letter-spacing:-.01em;text-wrap:balance}
@media (min-width:768px){.cf__title{font-size:34px}}
.cf__sub{margin:14px 0 28px;max-width:52ch;font:400 15px/1.6 var(--sans);color:var(--text-secondary)}
.cf__fields{display:grid;gap:16px}
.cf__row{display:grid;gap:16px}
@media (min-width:600px){.cf__row{grid-template-columns:1fr 1fr}}
.cf__cell{display:grid;gap:6px}
.cf__label{font:400 13px/1.4 var(--sans);color:var(--text-muted)}
.cf__input,.cf__textarea{width:100%;padding:15px 17px;border:1px solid var(--border-subtle);
  border-radius:12px;background:var(--bg-subtle);color:var(--text-primary);
  font:400 15px/1.5 var(--sans);transition:border-color .18s ease}
.cf__input:focus,.cf__textarea:focus{outline:none;border-color:var(--border-strong)}
.cf__input:focus-visible,.cf__textarea:focus-visible{outline:2px solid var(--accent);outline-offset:2px}
.cf__textarea{resize:none;min-height:120px}
/* the ported script appends this into the field's PARENT on a failed check */
.form-row-error{font:400 13px/1.5 var(--sans);color:var(--danger)}
.cf__check{display:flex;align-items:center;gap:11px;font:400 14px/1.5 var(--sans);
  color:var(--text-secondary);cursor:pointer;position:relative}
.cf__check input{position:absolute;left:0;width:18px;height:18px;margin:0;opacity:0;cursor:pointer}
.cf__box{width:18px;height:18px;flex:none;border:1px solid var(--border-strong);border-radius:4px;
  background:transparent;transition:background .15s ease,border-color .15s ease}
.cf__check input:checked + .cf__box{background:var(--text-primary);border-color:var(--text-primary);
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M3.5 8.5l3 3 6-7' fill='none' stroke='%230E0E11' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
  background-size:15px;background-position:center;background-repeat:no-repeat}
:root[data-theme="dark"] .cf__check input:checked + .cf__box{
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M3.5 8.5l3 3 6-7' fill='none' stroke='%230E0E11' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E")}
.cf__check input:focus-visible + .cf__box{outline:2px solid var(--accent);outline-offset:3px}
/* the redesign's submit is the inverted pill, not the accent one */
.cf__submit{justify-self:start;margin-top:8px;min-width:190px;padding:13px 22px;border-radius:999px;
  background:var(--invert-bg);color:var(--invert-ink);border:0;
  font:500 15px/1.4 var(--sans);cursor:pointer;transition:opacity .15s ease}
.cf__submit:hover{opacity:.88}
.cf__submit:focus-visible{outline:2px solid var(--accent);outline-offset:3px}
/* the What's next panel, behind the hairline the redesign draws */
.cf__next{padding:40px 28px;border-top:1px solid var(--border-subtle)}
@media (min-width:768px){.cf__next{padding:48px 40px}}
@media (min-width:1024px){.cf__next{border-top:0;border-left:1px solid var(--border-subtle)}}
.cf__next-title{margin:0 0 26px;font:500 24px/1.2 var(--sans)}
.cf__next ul{margin:0;padding:0;list-style:none;display:grid;gap:22px}
.cf__next li{display:grid;grid-template-columns:20px 1fr;gap:14px;align-items:start;
  font:400 15px/1.55 var(--sans);color:var(--text-secondary)}
.cf__next li::before{content:'';width:20px;height:20px;margin-top:2px;opacity:.75;
  background-color:var(--text-primary);-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;
  -webkit-mask-position:center;mask-position:center;-webkit-mask-size:contain;mask-size:contain}
.cf__next li:nth-child(1)::before{-webkit-mask-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='1.5' stroke-linecap='round'%3E%3Ccircle cx='12' cy='12' r='9'/%3E%3Cpath d='M12 7v5l3 2'/%3E%3C/svg%3E");mask-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='1.5' stroke-linecap='round'%3E%3Ccircle cx='12' cy='12' r='9'/%3E%3Cpath d='M12 7v5l3 2'/%3E%3C/svg%3E")}
.cf__next li:nth-child(2)::before{-webkit-mask-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='1.5' stroke-linecap='round'%3E%3Cpath d='M9 6h11M9 12h11M9 18h11'/%3E%3Cpath d='M4 6h.01M4 12h.01M4 18h.01'/%3E%3C/svg%3E");mask-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='1.5' stroke-linecap='round'%3E%3Cpath d='M9 6h11M9 12h11M9 18h11'/%3E%3Cpath d='M4 6h.01M4 12h.01M4 18h.01'/%3E%3C/svg%3E")}
.cf__next li:nth-child(3)::before{-webkit-mask-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M14 3H7a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2V8z'/%3E%3Cpath d='M14 3v5h5M9 13h6M9 17h4'/%3E%3C/svg%3E");mask-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M14 3H7a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2V8z'/%3E%3Cpath d='M14 3v5h5M9 13h6M9 17h4'/%3E%3C/svg%3E")}
.cf__next li:nth-child(4)::before{-webkit-mask-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='4' y='10' width='16' height='11' rx='2'/%3E%3Cpath d='M8 10V7a4 4 0 0 1 8 0v3'/%3E%3C/svg%3E");mask-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='4' y='10' width='16' height='11' rx='2'/%3E%3Cpath d='M8 10V7a4 4 0 0 1 8 0v3'/%3E%3C/svg%3E")}
/* the panels the ported script injects into #contact-form-wrap on send */
.form-thanks{margin:0 28px 40px;padding:24px;border:1px solid var(--border-subtle);border-radius:16px;
  background:var(--bg-subtle);display:grid;gap:8px;justify-items:start}
@media (min-width:768px){.form-thanks{margin:0 40px 48px}}
.form-thanks img{width:40px;height:40px}
.form-thanks-title,.form-error-title{margin:0;font:500 20px/1.3 var(--sans);color:var(--text-primary)}
.form-thanks-text{font:400 15px/1.6 var(--sans);color:var(--text-secondary)}
.form-thanks-link{font:500 15px/1.5 var(--sans);color:var(--accent);cursor:pointer}

/* ---------- footer, ported from the redesign ---------- */
.ftr{border-top:1px solid var(--border-subtle);background:var(--bg-subtle)}
.ftr__in{padding:52px var(--pad) 28px}
.ftr__nav{display:grid;grid-template-columns:1fr 1fr;gap:28px 24px}
@media (min-width:768px){.ftr__nav{grid-template-columns:repeat(4,1fr)}}
.ftr__col ul{list-style:none;margin:0;padding:0}
.ftr__title{display:block;width:fit-content;margin-bottom:14px;font:500 15px/1.4 var(--sans);
  color:var(--text-primary);text-decoration:none;transition:color .15s ease}
.ftr__title:hover{color:var(--accent)}
.ftr__link{font:400 14px/2 var(--sans);color:var(--text-secondary);text-decoration:none;transition:color .15s ease}
.ftr__link:hover{color:var(--text-primary)}
.ftr__company{display:flex;flex-direction:column;gap:20px;margin-top:36px;padding-top:24px;
  border-top:1px solid var(--border-subtle)}
@media (min-width:768px){.ftr__company{flex-direction:row;align-items:center;justify-content:space-between}}
.ftr__wordmark{display:inline-flex;align-items:center;color:var(--text-primary);text-decoration:none}
/* The sprite's own mark, 150x27 in its viewBox. An inline <svg> with only a
   viewBox has no intrinsic size in flex, so it needs an explicit one or it
   collapses to nothing, which is exactly what it did. */
.ftr__logo{display:block;width:150px;height:27px;fill:currentColor}
.ftr__contact{display:flex;flex-wrap:wrap;align-items:center;gap:8px}
.ftr__meta{font:400 13px/1.6 var(--sans);color:var(--text-muted);text-decoration:none}
a.ftr__meta:hover{color:var(--text-primary)}
.ftr__rights{display:flex;flex-wrap:wrap;gap:16px;margin-top:24px}

/* Tag and author heads. They used an inline max-width:640px;margin:0 auto, which centred
   a 640px box above a full-bleed feed, so the h1 sat left of the cards beneath it and
   drifted further out as the viewport grew. Full-bleed on the --pad rail instead, which
   is what .idx-head and .feed both use, so the three line up. */
.list-head{padding:48px var(--pad) 0}

/* ---------- index head: title, search trigger, topic row ---------- */
.idx-head{position:relative;padding:56px var(--pad) 8px}
/* Sits behind the head content in the top-right, the corner the old theme used.
   It is decoration, so it never takes pointer events.

   right:0, NOT var(--pad). The asset carries 53 rows of opaque pixels on its own
   right frame edge: the model's forearm is cut by the image boundary. Inset by a
   gutter, that cut floated in whitespace with white on both sides and read as a
   broken image. Flush to the viewport edge, the same cut reads as a deliberate
   bleed. This is the whole reason the rule does not use the gutter token.

   top:0 puts it at .idx-head's own top edge, which is the header's bottom edge.
   The old -8px/-20px slid it up UNDER the nav, which also dropped the asset's
   purple accent marks directly beneath the "Get a quote" button, close enough
   that they read as chrome belonging to the CTA.

   The earlier note here claimed the text column's own cap meant the two "cannot
   collide at any viewport". That was wrong: at 1024 the description's second
   line reached x=749 against ink starting at x=732, so "RAG," sat on the laptop.
   Moving the picture out by a full gutter clears it there too. Below 1024 there
   is no room beside the text, so it is gone rather than shrunk into a smudge. */
.idx-pic{display:none}
@media (min-width:1024px){
  /* 1024-1279 is the tight band. The text column ends at x=762 and the picture is
     at its smallest, so it needs every pixel of the gutter to clear: on the rail
     here its ink would start at x=732, 30px INSIDE the copy. right:0 buys exactly
     the gutter that clearance costs. Both frame cuts are dissolved now, so the
     edge is a choice rather than somewhere to hide a slice. */
  .idx-pic{display:block;position:absolute;right:0;top:0;
    width:clamp(240px,26vw,420px);pointer-events:none;user-select:none;z-index:0}
  .idx-pic img{display:block;width:100%;height:auto}
  .idx-head__in{position:relative;z-index:1;max-width:min(100%,62ch)}
}
/* From 1280 there is room to rejoin the --pad rail that the header CTA, the
   section titles, the feed and the footer all terminate on, so the picture stops
   being the one element in the theme whose ink touches the viewport edge.

   The max() is the wide-screen brake. Right-anchored decoration beside a text
   column capped at 62ch drifts away 1:1 with viewport width: the gap from the
   copy to the first opaque pixel was 772px at 1920 and 1412px at 2560, which
   reads as two unrelated islands rather than one composition. calc(50% - 720px)
   holds the picture's right edge inside a 1440-wide band centred on the page, so
   past 1600 it stops chasing the edge. Continuous at the handover: at 1600 both
   arms of the max() are 80px. Gap falls to 532px at 1920 and 852px at 2560.

   The picture is NOT grown to close the rest of that gap. It is square and starts
   at the head's top edge, so height tracks width, and 420px already lands its
   bottom at y=599 against a head that ends at ~603. Anything larger would hang
   into the Popular section below and paint over the heading. */
@media (min-width:1280px){
  .idx-pic{right:max(var(--pad),calc(50% - 720px))}
}
/* Dark is a chosen theme here, and on #0E0E11 the cream lid is the brightest
   object on the page: it outranked the h1 it is meant to sit behind. Decoration
   recedes rather than competes. */
:root[data-theme="dark"] .idx-pic{opacity:.55}
.idx-title{margin:0;font:400 40px/44px var(--sans);letter-spacing:-.01em;text-wrap:balance}
@media (min-width:768px){.idx-title{font-size:48px;line-height:57.6px}}
.idx-desc{margin:14px 0 0;max-width:60ch;font:400 20px/30px var(--sans);color:var(--text-secondary)}
.idx-search{display:inline-flex;align-items:center;gap:10px;margin-top:26px;padding:12px 20px 12px 16px;
  border:1px solid var(--border-interactive);border-radius:999px;background:var(--bg-base);
  font:400 16px/24px var(--sans);color:var(--text-secondary);cursor:pointer;
  transition:border-color .15s ease,color .15s ease}
.idx-search:hover{border-color:var(--text-primary);color:var(--text-primary)}
.idx-search svg{width:18px;height:18px;fill:none;stroke:currentColor;stroke-width:1.8;stroke-linecap:round}
.idx-tags{display:flex;flex-wrap:wrap;gap:8px;margin-top:26px}
.idx-tag{padding:7px 15px;border:1px solid var(--border-subtle);border-radius:999px;
  font:400 15px/1.4 var(--sans);color:var(--text-secondary);text-decoration:none;
  transition:border-color .15s ease,color .15s ease}
.idx-tag:hover{border-color:var(--border-interactive);color:var(--text-primary)}
.idx-tag__n{margin-left:7px;font-size:13px;color:var(--text-muted);font-variant-numeric:tabular-nums}

/* ---------- sections on the index ---------- */
.sec{padding:40px 0 0}
.sec__title{margin:0;padding:0 var(--pad);font:500 24px/31.2px var(--sans)}
.feed--flush{padding-top:20px}

/* ---------- pagination ---------- */
/* Ghost emits <nav class="pagination"> with .page-number plus optional
   .older-posts / .newer-posts anchors. */
.pagination{padding:8px var(--pad) 72px;
  display:flex;align-items:center;justify-content:center;gap:20px;
  font:400 15px/1.4 var(--sans);color:var(--text-secondary)}
.pagination a{display:inline-flex;align-items:center;gap:8px;padding:10px 18px;
  border:1px solid var(--border-interactive);border-radius:8px;text-decoration:none;
  color:var(--text-primary);font-weight:500;transition:border-color .15s ease}
.pagination a:hover{border-color:var(--text-primary)}
.page-number{order:0}
.older-posts{order:1}
.newer-posts{order:-1}

/* ---------- Ghost editor card widths (required by gscan) ---------- */
.post-full-content .kg-width-wide{max-width:min(1040px,92vw);margin-left:50%;transform:translateX(-50%)}
.post-full-content .kg-width-full{max-width:100vw;margin-left:50%;transform:translateX(-50%)}
.post-full-content .kg-width-full img{border-radius:0}

/* Ghost 6 lets the site owner pick fonts in admin; when they do, these custom
   properties are set and must win over the theme default. */
:root{--gh-font-heading:var(--sans);--gh-font-body:var(--sans)}
body{font-family:var(--gh-font-body,var(--sans))}
.post__title,.post-full-content h2,.post-full-content h3,.card__title{font-family:var(--gh-font-heading,var(--sans))}
