/* Reskin design system — ported from the approved React demo (danieliwugo-blog-demo).
   Faithful copy of the demo's CSS; asset URLs rewritten to Ghost /assets paths. */

  /* Graphik LC Web — the exact typeface Varonis uses (self-hosted woff2). */
  @font-face{font-family:'Graphik LC Web';font-weight:300;font-style:normal;font-display:swap;src:url('../fonts/Graphik-Light-Cy-Web.woff2') format('woff2');}
  @font-face{font-family:'Graphik LC Web';font-weight:400;font-style:normal;font-display:swap;src:url('../fonts/Graphik-Regular-Cy-Web.woff2') format('woff2');}
  @font-face{font-family:'Graphik LC Web';font-weight:400;font-style:italic;font-display:swap;src:url('../fonts/Graphik-RegularItalic-Cy-Web.woff2') format('woff2');}
  @font-face{font-family:'Graphik LC Web';font-weight:500;font-style:normal;font-display:swap;src:url('../fonts/Graphik-Medium-Cy-Web.woff2') format('woff2');}
  @font-face{font-family:'Graphik LC Web';font-weight:600;font-style:normal;font-display:swap;src:url('../fonts/Graphik-Semibold-Cy-Web.woff2') format('woff2');}
  @font-face{font-family:'Graphik LC Web';font-weight:600;font-style:italic;font-display:swap;src:url('../fonts/Graphik-SemiboldItalic-Cy-Web.woff2') format('woff2');}
  @font-face{font-family:'Graphik LC Web';font-weight:700;font-style:normal;font-display:swap;src:url('../fonts/Graphik-Bold-Cy-Web.woff2') format('woff2');}
  /* Graphik XCond — the extra-condensed cut Varonis uses for tags/labels. */
  @font-face{font-family:'Graphik XCond';font-weight:500;font-style:normal;font-display:swap;src:url('../fonts/GraphikXCondensed-Medium-Web.woff2') format('woff2');}
  @font-face{font-family:'Graphik XCond';font-weight:600;font-style:normal;font-display:swap;src:url('../fonts/GraphikXCondensed-Semibold-Web.woff2') format('woff2');}
  @font-face{font-family:'Graphik XCond';font-weight:700;font-style:normal;font-display:swap;src:url('../fonts/GraphikXCondensed-Bold-Web.woff2') format('woff2');}
  /* Graphik Cond — the condensed cut Varonis uses for display headers ("Featured content"). */
  @font-face{font-family:'Graphik Cond';font-weight:500;font-style:normal;font-display:swap;src:url('../fonts/GraphikCondensed-Medium-Web.woff2') format('woff2');}
  @font-face{font-family:'Graphik Cond';font-weight:600;font-style:normal;font-display:swap;src:url('../fonts/GraphikCondensed-Semibold-Web.woff2') format('woff2');}
  @font-face{font-family:'Graphik Cond';font-weight:700;font-style:normal;font-display:swap;src:url('../fonts/GraphikCondensed-Bold-Web.woff2') format('woff2');}
  /* Reset the browser's default body margin (8px each side = the 16px the whole
     shell was sitting inside, so nothing ever reached the true page edge). */
  html,body{margin:0;padding:0;width:100%;}
  /* Always reserve the scrollbar gutter. During a transition the view goes
     position:fixed (and Lenis's freeze sets overflow:hidden), so the document
     briefly has no scrollbar — without a reserved gutter the page would jog
     ~12px wider then snap back. scrollbar-gutter keeps the width rock steady
     regardless of overflow, so the scrollbar is part of the frozen frame. */
  html{scrollbar-gutter:stable;}
  /* Lenis smooth-scroll required styles (it virtualises scroll; lenis-stopped
     sets overflow:hidden to enforce the freeze). */
  html.lenis,html.lenis body{height:auto;}
  .lenis.lenis-smooth{scroll-behavior:auto !important;}
  .lenis.lenis-smooth [data-lenis-prevent]{overscroll-behavior:contain;}
  .lenis.lenis-stopped{overflow:hidden;}
  .lenis.lenis-smooth iframe{pointer-events:none;}
  /* Custom crimson scrollbar (SOHub-style, in the brand red). */
  html{scrollbar-width:thin;scrollbar-color:var(--red) rgba(0,0,0,0.06);}
  ::-webkit-scrollbar{width:12px;height:12px;}
  ::-webkit-scrollbar-track{background:rgba(0,0,0,0.05);}
  ::-webkit-scrollbar-thumb{background:var(--red);border-radius:100px;border:3px solid transparent;background-clip:padding-box;}
  ::-webkit-scrollbar-thumb:hover{background:#b91c1c;border:3px solid transparent;background-clip:padding-box;}
  :root{
    --black:#0a0a0a; --white:#ffffff; --text:#111318; --text-dim:#6b7280;
    --border:#e5e7eb; --grey-bg:#f7f7f8; --blue:#2f5eff; --blue-tint:#e8edff;
    --grey-tag-text:#374151; --red:#dc2626; --nav-h:80px;
    --font-sans:'Graphik LC Web',-apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif;
  }
  .site{background:var(--white);color:var(--text);font-family:var(--font-sans);line-height:1.5;-webkit-font-smoothing:antialiased;}
  .site *{box-sizing:border-box;}
  .site button,.site input,.site select,.site textarea{font-family:inherit;}
  .site a{color:inherit;text-decoration:none;}
  html{scroll-behavior:smooth;}
  /* Premium view crossfade — the content region eases in on every view change
     (SOHub feel: fast start, gentle settle). Nav + footer stay put, so nothing
     ever reads as a fresh page load. */
  @keyframes viewIn{from{opacity:0;transform:translateY(18px) scale(0.992);}to{opacity:1;transform:none;}}
  /* Base view is always opaque, so a settled article can never read as
     translucent even if the behind layer lingers a frame. */
  .view-fade{position:relative;z-index:1;background:var(--white);}
  .view-fade[data-anim="fade"]{animation:viewIn .55s cubic-bezier(.16,1,.3,1) both;}
  /* SOHub page transition: the NEW page (nav + content) rises from the bottom
     (rounded top, flattening) while the PREVIOUS page blurs and scales behind
     it. Clean strong ease, no spring; ~1s so it doesn't feel rushed. */
  /* z-index 0: the receding old page sits BELOW the settled article (.view-fade
     z-index 1) and only shows through the gap under the rising panel (z-index
     260) during the transition. This makes it structurally impossible for the
     blurred layer to paint over a settled page/nav, no matter the timing or how
     the navigation was triggered — the whole "translucent" class of glitch. */
  .page-behind{position:fixed;inset:0;z-index:0;overflow:hidden;background:var(--white);pointer-events:none;transform-origin:center;animation:behindRecede 1.05s cubic-bezier(.62,0,.2,1) forwards;}
  /* The old page starts clear and recedes/blurs gradually as the new one rises,
     rather than the blur slapping on instantly. */
  @keyframes behindRecede{0%{filter:blur(0);transform:scale(1);opacity:0.92;}30%{filter:blur(1px);transform:scale(0.995);opacity:0.9;}100%{filter:blur(9px);transform:scale(0.93);opacity:0.45;}}
  .view-fade.rising{position:fixed;inset:0;z-index:260;overflow:hidden;background:var(--white);will-change:transform;animation:pageRise 1.05s cubic-bezier(.62,0,.2,1) forwards;box-shadow:0 -30px 70px rgba(0,0,0,0.28);}
  /* Slower, intentional start (ease-in-out): the panel holds a beat, then rises. */
  @keyframes pageRise{0%{transform:translateY(100%);border-radius:52px 52px 0 0;}12%{transform:translateY(97%);border-radius:52px 52px 0 0;}100%{transform:translateY(0);border-radius:0;}}
  /* Staggered content reveal — the signature SOHub feel: content flows in
     block by block as the page settles, rather than the whole slab appearing. */
  @keyframes contentRise{from{opacity:0;transform:translateY(34px);}to{opacity:1;transform:none;}}
  /* No per-element entrance on load. Under a native View Transition the real DOM is hidden
     while the snapshot rises, so an opacity:0 -> 1 stagger is captured BLANK: the empty page
     rides up and the content only mounts once the transition ends, which is exactly the
     "page rises then rebuilds itself" remount. The page-rise is the entrance now; the content
     is present in the snapshot and rides up with it, fully formed. */
  .stagger-in{opacity:1;}
  .stagger-children > *{opacity:1;}
  .stagger-children > *:nth-child(1){animation-delay:.06s;}
  .stagger-children > *:nth-child(2){animation-delay:.13s;}
  .stagger-children > *:nth-child(3){animation-delay:.2s;}
  .stagger-children > *:nth-child(4){animation-delay:.27s;}
  .stagger-children > *:nth-child(5){animation-delay:.34s;}
  .stagger-children > *:nth-child(6){animation-delay:.41s;}
  .stagger-children > *:nth-child(n+7){animation-delay:.48s;}
  .page-behind-inner{position:absolute;top:0;left:0;width:100%;will-change:transform;}
  /* Next-article handoff backdrop — the next cover, full-bleed, on a dark base. */
  .handoff-cover{position:absolute;inset:0;background:#0e0d12;background-size:cover;background-position:center;}
  /* The outgoing page is a frozen snapshot — it must not replay its entrance
     animations (stagger, glitch) as it blurs away. */
  .page-behind .stagger-in, .page-behind .stagger-children > *{opacity:1;animation:none;}
  /* Blanket freeze: nothing inside the outgoing snapshot may run an entrance or
     loop animation (ticker marquee, glitch, results fade) — it's a still frame
     that blurs away, so it never flashes or re-animates. (The recede/blur lives
     on .page-behind itself, so this doesn't touch it. Stagger opacity is forced
     back to 1 above so frozen content stays visible.) */
  .page-behind *, .page-behind *::before, .page-behind *::after{animation:none !important;}
  /* Note: prefers-reduced-motion is deliberately NOT honoured — the site runs
     the full transitions and smooth scroll at all times (operator decision). */

  /* Hero entrance (initial load + navigation back home): the name leads the eye, rising and fading
     in first, then the tagline, then the CTA. One-shot and gentle; matches the always-on transition
     convention above. The outgoing snapshot freeze (.page-behind *) already stops it re-playing. */
  @keyframes heroIn{from{opacity:0;transform:translateY(28px);}to{opacity:1;transform:none;}}
  .cover-hero .name-line-1{animation:heroIn .74s cubic-bezier(.16,1,.3,1) both;animation-delay:.10s;}
  .cover-hero .name-line-2{animation:heroIn .74s cubic-bezier(.16,1,.3,1) both;animation-delay:.20s;}
  .cover-hero .cover-tagline{animation:heroIn .74s cubic-bezier(.16,1,.3,1) both;animation-delay:.36s;}
  .cover-hero .btn-pill{animation:heroIn .74s cubic-bezier(.16,1,.3,1) both;animation-delay:.48s;}
  .cover-hero .scroll-hint{animation:heroIn .74s cubic-bezier(.16,1,.3,1) both;animation-delay:.56s;}

  /* Accessibility: a visible keyboard-focus ring. :focus-visible keeps it to keyboard users, so
     mouse/touch interaction stays clean. On-brand red, offset so it never clips the element. */
  a:focus-visible,button:focus-visible,input:focus-visible,textarea:focus-visible,[tabindex]:focus-visible,
  .nav-item:focus-visible,.nav-subscribe:focus-visible,.btn-pill:focus-visible,.nav-toggle:focus-visible,
  .share-btn:focus-visible,.icon-btn:focus-visible,.crumb-pill:focus-visible,.next-post-card:focus-visible,
  .post-card:focus-visible,.article-cell:focus-visible{outline:2px solid var(--red);outline-offset:3px;border-radius:6px;}

  .wrap{max-width:1920px;margin:0 auto;padding:0 32px;}
  .narrow-wrap{max-width:720px;margin:0 auto;padding:0 32px;}
  /* Opaque surfaces so the cover never bleeds through behind content.
     Real breathing room before the dark footer on every view (home + blog);
     the last grid row was jamming straight into it. */
  /* scroll-margin so "scroll down" settles the grid a touch below the sticky
     header instead of jamming the first row under it. */
  .blog-surface{position:relative;z-index:2;background:var(--white);padding-bottom:104px;scroll-margin-top:88px;}
  .page{position:relative;z-index:2;background:var(--white);}

  /* Dynamic navbar (Varonis-style): dark at the top of the page, flips to white
     once scrolled a little; text and icons invert with it. */
  header.top{position:sticky;top:0;z-index:500;background:var(--black);border-bottom:1px solid transparent;transition:background .35s ease,border-color .35s ease;}
  header.top.scrolled{background:var(--white);border-bottom-color:var(--border);}
  .top .brand-word{color:#fff;}
  .top .icon-btn{color:#fff;opacity:0.92;}
  .top .nav-links .nav-item{color:#fff;}
  .top .nav-links .nav-item.active{color:var(--red);}
  .top .nav-toggle .bar{background:#fff;}
  .top.scrolled .brand-word{color:var(--black);}
  .top.scrolled .icon-btn{color:var(--black);opacity:0.75;}
  .top.scrolled .nav-links .nav-item{color:var(--text);}
  .top.scrolled .nav-links .nav-item.active{color:var(--red);}
  .top.scrolled .nav-toggle .bar{background:var(--text);}
  /* Navbar search — an input that expands from the icon; colour follows the nav
     state, and the icon flips to a close (X) while open. */
  .nav-search{display:flex;align-items:center;gap:4px;}
  .nav-search-input{width:0;opacity:0;padding:0;border:none;outline:none;background:transparent;font-family:inherit;font-size:14px;color:inherit;border-bottom:1px solid transparent;transition:width .4s cubic-bezier(.22,.68,0,1),opacity .3s ease,padding .3s ease,border-color .3s ease;}
  .nav-search.open .nav-search-input{width:220px;opacity:1;padding:6px 4px;}
  .nav-search-btn{background:none;border:none;cursor:pointer;padding:0;display:inline-flex;align-items:center;color:var(--black);opacity:0.75;transition:color .35s ease,opacity .35s ease,transform .3s cubic-bezier(.22,.68,0,1);}
  .nav-search-btn:hover{transform:scale(1.08);}
  .nav-search-btn svg{width:20px;height:20px;}
  .top .nav-search-btn{color:#fff;opacity:0.92;}
  .top .nav-search-input{color:#fff;}
  .top .nav-search-input::placeholder{color:rgba(255,255,255,0.6);}
  .top .nav-search.open .nav-search-input{border-bottom-color:rgba(255,255,255,0.45);}
  .top.scrolled .nav-search-btn{color:var(--black);opacity:0.75;}
  .top.scrolled .nav-search-input{color:var(--text);}
  .top.scrolled .nav-search-input::placeholder{color:var(--text-dim);}
  .top.scrolled .nav-search.open .nav-search-input{border-bottom-color:var(--border);}
  /* Search/filter results ease in instead of hard cutting. */
  @keyframes resultsIn{from{opacity:0;transform:translateY(10px);}to{opacity:1;transform:translateY(0);}}
  /* The blog grid renders every post server-side (for client-side filtering); JS then shows
     one page. Pre-hide the overflow BEFORE JS runs, via the .prehide class that initBlog
     removes on its first render, so the first paint already shows one page (9 desktop / 6
     mobile) rather than all 13 cards collapsing a beat later — that collapse reads as a
     rebuild under View Transitions. No opacity:0 entrance either, so the grid is present in
     the VT snapshot, not faded in after the page lands. JS uses inline display for paging. */
  .article-grid.prehide .article-cell:nth-child(n+10){display:none;}
  @media (max-width:640px){ .article-grid.prehide .article-cell:nth-child(n+7){display:none;} }
  nav.main{display:flex;align-items:center;justify-content:space-between;padding:18px 32px;max-width:1920px;margin:0 auto;}
  .brand{display:inline-flex;align-items:center;gap:11px;color:var(--black);text-decoration:none;}
  /* Two icon variants crossfade with the nav state: light-field mark on the
     dark navbar (top), dark-field mark on the white navbar (scrolled). */
  .brand-mark{position:relative;width:34px;height:34px;flex-shrink:0;}
  .brand-mark img{position:absolute;inset:0;width:100%;height:100%;border-radius:8px;transition:opacity .35s ease;}
  .brand-mark .m-light{opacity:0;}
  .brand-mark .m-dark{opacity:1;}
  .top:not(.scrolled) .brand-mark .m-light{opacity:1;}
  .top:not(.scrolled) .brand-mark .m-dark{opacity:0;}
  .brand-word{font-family:var(--font-sans);font-weight:700;font-size:20px;letter-spacing:-0.4px;line-height:1;transition:color .35s ease;}
  .brand-accent{color:var(--red);}
  .foot-mark{width:52px;height:52px;border-radius:12px;display:block;}
  .nav-links{display:flex;gap:34px;align-items:center;}
  .nav-links .nav-item{font-size:13.5px;font-weight:500;letter-spacing:0.04em;text-transform:uppercase;color:var(--text);transition:color .3s ease;}
  .nav-links .nav-item.active{color:var(--red);font-weight:600;}
  .nav-right{display:flex;align-items:center;gap:20px;}
  .icon-btn{width:20px;height:20px;color:var(--black);opacity:0.75;transition:color .35s ease,opacity .35s ease;}
  .site .btn-pill{background:var(--red);color:#fff;font-weight:700;font-size:14px;padding:11px 22px;border-radius:100px;display:inline-block;border:none;font-family:var(--font-sans);}
  .btn-pill.dark{background:var(--black);color:#fff;padding:14px 30px;font-size:15px;}

  /* Cover hero — a normal full-height opening section. The blog surface below
     is a solid opaque block that scrolls up over it cleanly (no sticky pin, so
     nothing bleeds through at the edges as you scroll). */
  .cover-hero{
    position:relative;height:calc(100vh - 65px);z-index:0;
    background:var(--grey-bg);display:flex;align-items:center;justify-content:center;
    overflow:hidden;border-bottom:1px solid var(--border);
  }
  .cover-inner{position:relative;z-index:1;max-width:1100px;width:100%;padding:0 40px;display:flex;flex-direction:column;align-items:stretch;gap:0;}
  .cover-top{align-self:flex-end;max-width:420px;text-align:right;margin-bottom:6px;}
  .cover-tagline{font-size:15px;color:var(--text-dim);line-height:1.55;margin:0;}
  .cover-name{width:100%;margin:4px 0 34px;}
  .name-line{font-size:7.2vw;line-height:0.94;font-weight:300;letter-spacing:-1px;color:var(--black);white-space:nowrap;}
  .name-line-1{text-align:left;}
  .name-line-2{text-align:left;margin-left:11%;}
  .cover-bottom{display:flex;flex-direction:column;align-items:center;gap:16px;}
  .scroll-hint{display:flex;flex-direction:column;align-items:center;gap:4px;font-size:13px;color:var(--text-dim);cursor:pointer;}
  .scroll-arrow{font-size:16px;animation:bob 1.8s ease-in-out infinite;}
  @keyframes bob{0%,100%{transform:translateY(0);}50%{transform:translateY(5px);}}
  /* Demo switchers — compare the background + glitch variations live */
  .cover-controls{position:absolute;bottom:16px;right:20px;z-index:3;display:flex;flex-direction:column;gap:7px;align-items:flex-end;max-width:min(560px,80vw);}
  .switch-row{display:flex;gap:6px;align-items:center;flex-wrap:wrap;justify-content:flex-end;}
  .switch-tag{font-size:10px;font-weight:700;letter-spacing:0.08em;text-transform:uppercase;color:var(--text-dim);margin-right:4px;}
  .mesh-swatch{font-family:inherit;font-size:11px;font-weight:600;letter-spacing:0.04em;text-transform:lowercase;color:var(--text-dim);background:rgba(255,255,255,0.72);backdrop-filter:blur(6px);border:1px solid var(--border);border-radius:100px;padding:5px 12px;cursor:pointer;transition:all .2s ease;}
  .mesh-swatch:hover{color:var(--text);}
  .mesh-swatch.on{color:#fff;background:var(--red);border-color:var(--red);}
  /* Glitch treatment for the IWUGO line (slice) */
  .glitch{position:relative;display:inline-block;color:var(--black);}
  .glitch-slice::before,.glitch-slice::after{content:attr(data-text);position:absolute;left:0;top:0;width:100%;color:var(--red);opacity:0;pointer-events:none;}
  .glitch-slice::before{clip-path:inset(0 0 55% 0);animation:gSliceA 3.4s infinite steps(1);}
  .glitch-slice::after{clip-path:inset(52% 0 0 0);animation:gSliceB 3.4s infinite steps(1);}
  @keyframes gSliceA{0%,85%,100%{opacity:0;transform:translateX(0);}86%{opacity:.9;transform:translateX(-12px);}89%{opacity:.9;transform:translateX(7px);}92%{opacity:.9;transform:translateX(-3px);}93%{opacity:0;}}
  @keyframes gSliceB{0%,85%,100%{opacity:0;transform:translateX(0);}86%{opacity:.9;transform:translateX(12px);}89%{opacity:.9;transform:translateX(-7px);}92%{opacity:.9;transform:translateX(3px);}93%{opacity:0;}}

  /* Featured band (Blog page) — Varonis hero on BLACK */
  .featured-band{background:#000;color:#fff;position:relative;z-index:2;padding:40px 0 4px;}
  .feat-wrap{max-width:1920px;margin:0 auto;padding:0 32px;}
  .feat-head{border-bottom:1px solid #4c4d4f;padding-bottom:14px;margin-bottom:28px;}
  .feat-head h2{font-family:'Graphik Cond',var(--font-sans);font-size:32px;font-weight:600;color:#fff;margin:0;letter-spacing:-0.005em;}
  .feat-grid{display:grid;grid-template-columns:1fr 1fr;gap:0;align-items:stretch;}
  .feat-hero{cursor:pointer;max-width:600px;padding-right:40px;}
  .feat-hero-image{aspect-ratio:16/9;overflow:hidden;border:1px solid #4c4d4f;position:relative;}
  .feat-hero:hover .thumb-inner{transform:scale(1.04);}
  .feat-tag{display:inline-block;font-family:'Graphik XCond',var(--font-sans);font-size:14px;font-weight:600;letter-spacing:0.07em;text-transform:uppercase;color:#fff;background:#1c2ae0;padding:7px 12px;position:relative;z-index:2;}
  .feat-hero .feat-tag{margin:-16px 0 0 16px;}
  .feat-hero h3{font-size:22px;font-weight:600;line-height:1.3;color:#fff;margin:20px 0 16px;}
  .feat-side{display:flex;flex-direction:column;border-left:1px solid #4c4d4f;padding-left:40px;}
  .feat-side-item{cursor:pointer;flex:1;display:flex;flex-direction:column;justify-content:space-between;padding-bottom:24px;margin-bottom:24px;border-bottom:1px solid #4c4d4f;}
  .feat-side-item:last-child{border-bottom:none;margin-bottom:0;padding-bottom:0;}
  .feat-side-top .feat-tag{margin:0 0 14px;}
  .feat-side-item h4{font-size:19px;font-weight:600;line-height:1.32;color:#fff;margin:0;}
  .feat-meta{display:flex;align-items:center;justify-content:space-between;gap:12px;font-size:13px;color:#dadbe1;margin-top:16px;}
  .feat-meta .who{display:flex;align-items:center;gap:9px;font-weight:600;color:#fff;}
  .feat-read{display:flex;align-items:center;gap:6px;color:#dadbe1;white-space:nowrap;}
  .feat-ticker{display:flex;align-items:center;gap:16px;border-top:1px solid #4c4d4f;margin-top:28px;padding:18px 0 4px;overflow:hidden;white-space:nowrap;font-size:15px;font-weight:600;}
  .feat-tick-item{display:inline-flex;align-items:center;gap:12px;color:#fff;cursor:pointer;flex-shrink:0;}
  .feat-tick-date{color:#9aa0ad;font-weight:500;}
  .feat-plus{color:#4b4f58;flex-shrink:0;}
  .new-badge{background:#1d3a29;color:#d1f399;font-size:12px;font-weight:700;padding:3px 9px;letter-spacing:0.5px;flex-shrink:0;}
  .avatar{width:30px;height:30px;border-radius:50%;background:var(--red);color:#fff;display:flex;align-items:center;justify-content:center;font-size:11px;font-weight:700;flex-shrink:0;}
  @keyframes scroll{from{transform:translateX(0);}to{transform:translateX(-50%);}}

  .filter-section{padding:40px 32px 0;max-width:1920px;margin:0 auto;position:relative;z-index:2;background:var(--white);}
  .filter-row{display:flex;align-items:center;justify-content:space-between;gap:20px;flex-wrap:wrap;margin-bottom:18px;}
  .filter-pills{display:flex;gap:10px;flex-wrap:wrap;}
  .fpill{font-size:14px;font-weight:500;padding:9px 18px;border-radius:100px;border:1px solid var(--border);color:var(--text);}
  .fpill.active{background:#fdeaea;color:var(--red);border-color:#fdeaea;}
  .search-box{display:flex;align-items:center;gap:10px;background:var(--grey-bg);border:1px solid var(--border);border-radius:10px;padding:10px 16px;min-width:220px;color:var(--text-dim);font-size:14px;}
  .search-input{border:none;background:transparent;outline:none;font-size:14px;color:var(--text);font-family:inherit;width:100%;}
  .results-count{font-size:14px;color:var(--text-dim);}

  /* Grid — the EXACT Varonis mechanism.
     Not per-cell nth-child border math (fragile the moment the last row isn't
     full). Instead: a gapped grid, and every cell paints two divider lines as
     pseudo-elements — a 100vw horizontal line 32px below it, and a 100vh
     vertical line 32px to its right (both centred in the 64px gap). The grid
     then does overflow:clip + clip-path:inset(-2px), so the parts of those
     lines that fall outside the grid box (the whole outer perimeter, and every
     line past the last row/column) get clipped away. Result: internal dividers
     only, no perimeter, no stray edges, whatever the item count — one
     continuous interlaced grid, not a set of floating cards. */
  .article-grid{
    display:grid;grid-template-columns:repeat(3,1fr);gap:64px;
    width:min(1920px, calc(100% - 64px));margin:0 auto;padding:0;
    min-height:60px;position:relative;z-index:2;background:var(--white);
    margin-top:44px;margin-bottom:8px;overflow:clip;clip-path:inset(-2px);
  }
  .empty-state{grid-column:1/-1;padding:48px 20px;text-align:center;color:var(--text-dim);font-size:14px;}
  .article-cell{display:flex;flex-direction:column;cursor:pointer;position:relative;}
  .article-cell::before{content:"";position:absolute;inset:auto auto -32px auto;height:1px;width:100vw;background:var(--border);}
  .article-cell::after{content:"";position:absolute;inset:0 -32px auto auto;width:1px;height:100vh;background:var(--border);}
  .article-thumb, .related-thumb{aspect-ratio:16/9;overflow:hidden;}
  .thumb-inner{width:100%;height:100%;background:radial-gradient(circle at 70% 30%, rgba(229,72,77,0.22), transparent 55%),repeating-linear-gradient(135deg, #17171a 0 2px, #101012 2px 4px);background-size:cover;background-position:center;transition:transform .5s cubic-bezier(.22,.68,0,1);}
  .article-cell:hover .thumb-inner, .related-cell:hover .thumb-inner{transform:scale(1.05);}
  /* Subtle premium hover: title warms to red, no layout shift (dividers stay put). */
  .article-body h3{transition:color .2s ease-out;}
  .article-cell:hover .article-body h3, .related-cell:hover .related-body h3{color:var(--red);}
  .article-body{padding:20px 0 0;display:flex;flex-direction:column;flex-grow:1;}
  .tag-row{display:flex;flex-wrap:wrap;gap:10px;margin-bottom:16px;}
  .tag-text{display:inline-block;font-family:'Graphik XCond',var(--font-sans);font-size:14px;font-weight:600;letter-spacing:0.1em;text-transform:uppercase;padding:6px 8px;border-radius:4px;color:#dc2626;background:#fdeaea;line-height:14px;}

  /* ── Category colour system ────────────────────────────────────────────────
     Five canonical tags, each keyed by its Ghost slug. The primary (first) tag on
     a card and every filter pill wear their category colour; any second tag on a
     card is neutral grey. Keyed on slug, so reclassifying a post in Ghost admin is
     all it takes for the colour to follow.
       offensive  red    · defensive blue · fundamentals amber
       ai         violet · engineering teal                                     */
  .tag-text[data-tag="offensive"]{color:#dc2626;background:#fdeaea;}
  .tag-text[data-tag="defensive"]{color:#2f5eff;background:#e8edff;}
  .tag-text[data-tag="fundamentals"]{color:#b26a00;background:#fbeed6;}
  .tag-text[data-tag="ai"]{color:#6b4bd8;background:#ece7fb;}
  .tag-text[data-tag="engineering"]{color:#0f8a6a;background:#d9f2ea;}
  /* Any tag beyond the first on a card is neutral grey, whatever its slug. */
  .tag-text.tag-secondary{color:#5b6472;background:#eef0f3;}

  /* Filter pills carry the same category colour: soft-tinted text + border when
     idle, filled tint when active. The "All" pill stays neutral. */
  .fpill[data-tag="offensive"]{color:#dc2626;border-color:#f3c9c9;}
  .fpill[data-tag="defensive"]{color:#2f5eff;border-color:#c9d5f7;}
  .fpill[data-tag="fundamentals"]{color:#b26a00;border-color:#eeddbc;}
  .fpill[data-tag="ai"]{color:#6b4bd8;border-color:#d9cef4;}
  .fpill[data-tag="engineering"]{color:#0f8a6a;border-color:#bfe6d8;}
  .fpill.active[data-tag="offensive"]{background:#fdeaea;border-color:#fdeaea;color:#dc2626;}
  .fpill.active[data-tag="defensive"]{background:#e8edff;border-color:#e8edff;color:#2f5eff;}
  .fpill.active[data-tag="fundamentals"]{background:#fbeed6;border-color:#fbeed6;color:#b26a00;}
  .fpill.active[data-tag="ai"]{background:#ece7fb;border-color:#ece7fb;color:#6b4bd8;}
  .fpill.active[data-tag="engineering"]{background:#d9f2ea;border-color:#d9f2ea;color:#0f8a6a;}
  .article-date{font-size:13px;color:var(--text-dim);margin-bottom:8px;font-weight:600;}
  .article-body h3{font-size:19px;font-weight:600;line-height:1.35;margin:0 0 12px;color:var(--text);}
  .article-body p.desc{font-size:14px;color:var(--text-dim);margin:0 0 20px;flex-grow:1;line-height:1.55;}
  .article-meta{display:flex;align-items:center;justify-content:space-between;margin-top:auto;font-size:13px;}
  .article-meta .who{display:flex;align-items:center;gap:10px;font-weight:700;color:var(--text);}
  .article-meta .read-time{color:var(--text-dim);}
  /* + crosses at internal grid intersections (Varonis decorations); edge ones clip away */
  .xhair{position:absolute;right:-32px;bottom:-32px;width:11px;height:11px;transform:translate(50%,50%);z-index:1;pointer-events:none;}
  .xhair::before{content:"";position:absolute;top:50%;left:0;width:100%;height:1.5px;background:#c4c7cf;transform:translateY(-50%);}
  .xhair::after{content:"";position:absolute;left:50%;top:0;height:100%;width:1.5px;background:#c4c7cf;transform:translateX(-50%);}

  .pagination-section{display:flex;align-items:center;justify-content:center;flex-wrap:wrap;gap:16px;padding:36px 32px 0;max-width:1920px;margin:0 auto;position:relative;z-index:2;background:var(--white);}
  .per-page{display:flex;align-items:center;gap:10px;font-size:13px;color:var(--text-dim);}
  .per-page select{border:1px solid var(--border);border-radius:8px;padding:6px 10px;font-size:13px;color:var(--text);background:var(--white);font-family:inherit;}
  .pager{display:flex;align-items:center;gap:6px;}
  .pager span{display:inline-flex;align-items:center;justify-content:center;min-width:30px;height:30px;font-size:14px;font-weight:700;border-radius:6px;color:var(--red);}
  .pager .current{color:var(--text);background:var(--grey-bg);}
  .pager .arrow{color:var(--text-dim);}

  .newsletter{background:var(--black);color:#fff;padding:64px 32px;text-align:center;position:relative;z-index:2;}
  .newsletter .eyebrow{font-size:12px;font-weight:700;letter-spacing:1.4px;text-transform:uppercase;color:var(--red);}
  .newsletter h2{font-size:30px;font-weight:700;margin:14px 0 10px;}
  .newsletter p{color:#9aa0ad;max-width:480px;margin:0 auto 26px;}
  .signup-form{display:flex;gap:10px;justify-content:center;flex-wrap:wrap;}
  .signup-form input{background:#141416;border:1px solid #26262a;border-radius:8px;padding:12px 16px;color:#fff;font-size:14px;width:280px;max-width:80vw;font-family:inherit;}
  .signup-form input::placeholder{color:#6b7280;}
  .signup-note{font-size:12px;color:#6b7280;margin-top:14px;}
  .subscribed-note{font-size:14px;font-weight:700;color:var(--blue);background:var(--blue-tint);display:inline-block;padding:12px 20px;border-radius:8px;}
  .subscribed-note.dark{background:rgba(47,94,255,0.15);color:#8fa4ff;}

  /* About */
  .about-section{padding:80px 32px;background:var(--grey-bg);border-top:1px solid var(--border);position:relative;z-index:2;}
  .eyebrow-dark{font-size:12px;font-weight:700;letter-spacing:1.4px;text-transform:uppercase;color:var(--blue);}
  .about-section h2{font-size:32px;font-weight:700;margin:12px 0 24px;}
  .about-lead{font-size:16px;color:#333844;line-height:1.7;margin:0 0 20px;}
  .topic-cloud{display:flex;flex-wrap:wrap;gap:10px;margin:28px 0 32px;}
  .topic-chip{font-size:12.5px;font-weight:700;color:var(--text);background:var(--white);border:1px solid var(--border);padding:7px 14px;border-radius:100px;}
  .about-links{display:flex;flex-wrap:wrap;gap:10px 24px;}
  .about-links a{font-size:14px;font-weight:700;color:var(--red);}
  .about-links a:hover{text-decoration:underline;}

  .site-footer{padding:56px 32px 32px;position:relative;z-index:2;background:var(--black);color:#e6e9f2;}
  .site-footer .brand{color:#fff;}
  .foot-grid{display:grid;grid-template-columns:2fr 1fr 1fr;gap:40px;margin-bottom:40px;max-width:1920px;margin-left:auto;margin-right:auto;}
  .foot-brand p{color:#8b93a7;font-size:14px;max-width:280px;}
  .foot-col h4{font-size:13px;text-transform:uppercase;letter-spacing:1px;color:#8b93a7;margin:0 0 16px;}
  .foot-col a{display:block;font-size:14px;color:#cfd4dd;margin-bottom:12px;}
  .foot-col a:hover{color:#fff;}
  .foot-bottom{display:flex;justify-content:space-between;align-items:center;border-top:1px solid #26303f;padding-top:24px;font-size:13px;color:#8b93a7;max-width:1920px;margin:0 auto;flex-wrap:wrap;gap:12px;}
  .social-row{display:flex;gap:16px;}
  .site-footer .social-row a{color:#cfd4dd;}
  .site-footer .social-row a:hover{color:#fff;}

  .breadcrumb{padding:24px 32px 0;max-width:760px;margin:0 auto;font-size:13px;font-weight:700;}
  .breadcrumb .sep{color:var(--text-dim);margin:0 6px;}
  .breadcrumb .tag-crumb{color:var(--red);}
  .breadcrumb a{color:var(--text-dim);}
  .article-head{padding:20px 32px 32px;max-width:760px;margin:0 auto;}
  .article-head h1{font-size:36px;font-weight:700;line-height:1.2;margin:0 0 16px;}
  .article-head .subtitle{font-size:18px;color:var(--text-dim);margin:0 0 28px;}

  /* Centered (Varonis) treatment for the article header. */
  .article-head-center{text-align:center;padding-top:36px;}
  .article-head-center h1{font-size:46px;line-height:1.12;letter-spacing:-0.02em;margin:20px 0 18px;}
  .article-head-center .subtitle{max-width:60ch;margin:0 auto 24px;}
  .article-head-center .byline{justify-content:center;}
  .crumb-pills{display:flex;gap:10px;justify-content:center;flex-wrap:wrap;}
  .crumb-pill{display:inline-block;font-family:'Graphik XCond',var(--font-sans);font-size:13px;font-weight:600;letter-spacing:0.08em;text-transform:uppercase;padding:7px 16px;border-radius:100px;border:1px solid var(--border);color:var(--text-dim);transition:border-color .2s,color .2s;}
  .crumb-pill:hover{color:var(--text);border-color:var(--text-dim);}
  .crumb-tag[data-tag="offensive"]{color:#dc2626;border-color:#f3c9c9;}
  .crumb-tag[data-tag="defensive"]{color:#2f5eff;border-color:#c9d5f7;}
  .crumb-tag[data-tag="fundamentals"]{color:#b26a00;border-color:#eeddbc;}
  .crumb-tag[data-tag="ai"]{color:#6b4bd8;border-color:#d9cef4;}
  .crumb-tag[data-tag="engineering"]{color:#0f8a6a;border-color:#bfe6d8;}
  .byline{display:flex;align-items:center;gap:12px;font-size:14px;color:var(--text-dim);flex-wrap:wrap;}
  .byline .name{color:var(--text);font-weight:700;}
  .byline .dot{color:var(--border);}
  /* Ghost content images fit the reading column and keep their aspect. This is the base
     rule the reskin dropped when it stopped loading the base stylesheet; without it a
     default image renders at its intrinsic width and overflows the column, pushing a
     horizontal scrollbar onto the page. kg-width-wide / kg-width-full override this with
     higher specificity, so the deliberate wide/full-bleed cards still work. */
  .gh-content img{max-width:100%;height:auto;}
  /* ── Ghost article content elements ───────────────────────────────────────────
     Restored from the base stylesheet the reskin stopped loading. The reskin was
     built from the demo's hand-written HTML, which never had <pre><code>, tables,
     blockquotes or lists, so real articles rendered these unstyled: bare code with
     no box, long lines pushing a horizontal scrollbar onto the page, and faint
     tables. Written in the reskin's own tokens (the base file's tokens aren't
     loaded). */
  .gh-content{--code-font:ui-monospace,'SF Mono','JetBrains Mono','Cascadia Code',Menlo,Consolas,monospace;}
  .gh-content :not(pre) > code{font-family:var(--code-font);font-size:0.88em;background:var(--grey-bg);border:1px solid var(--border);border-radius:4px;padding:0.12em 0.4em;color:var(--text);}
  /* The `pre[class*="language-"]` variant is here on purpose: prism-dracula.css loads AFTER
     this file and sets a background from an undefined token (transparent -> white) on
     language-tagged code blocks, at equal specificity, so it would otherwise win. Naming the
     language selector here raises specificity so the dark box always holds, whichever cut of
     code markup Ghost emits. */
  .gh-content pre,.gh-content pre[class*="language-"]{background:#15161c;color:#e6e9f2;border:none;border-radius:10px;padding:18px 20px;margin:26px 0;overflow-x:auto;line-height:1.55;font-family:var(--code-font);font-size:14px;-webkit-overflow-scrolling:touch;}
  .gh-content pre code{background:none;border:none;padding:0;color:inherit;font-size:inherit;white-space:pre;}
  /* Malformed content sometimes nests a <p><code> inside a <pre> (Ghost markdown can emit this);
     the code's parent is then <p>, so the inline-code rule would paint it dark-on-grey inside the
     dark box (the faint lines). Force anything inside a code box to read as light code text. */
  .gh-content pre code,.gh-content pre p code,.gh-content pre p{color:#e6e9f2 !important;background:none !important;}
  .gh-content pre p{margin:0;}
  .gh-content table{width:100%;border-collapse:collapse;margin:28px 0;font-size:15px;color:var(--text);}
  .gh-content .kg-table-card,.gh-content figure.kg-card table{overflow-x:auto;}
  /* Force every cell dark like body text. !important because some pasted tables carry inline
     light-grey colour on the cells (the faint left column); the operator wants them all black. */
  /* Premium editorial table (Option A): no cell grid, uppercase header with a single
     rule, hairline row separators, airy padding. !important on border kills the white
     inline borders that pasted tables (e.g. a dark VS Code preview) bake onto cells. */
  .gh-content th,.gh-content td{padding:14px 18px;border:0 !important;vertical-align:top;color:var(--text) !important;}
  .gh-content thead th{text-transform:uppercase;letter-spacing:0.05em;font-size:12.5px;font-weight:700;color:var(--black) !important;border-bottom:2px solid #cfd4dd !important;}
  .gh-content tbody tr{border-bottom:1px solid #eceef1 !important;}
  .gh-content tbody td:first-child{font-weight:500;color:var(--black) !important;}

  /* In-content links: brand red + underline, like the old theme. Scoped to prose links
     so it never touches card / nav / button anchors. */
  .gh-content a{color:var(--red);text-decoration:underline;text-underline-offset:2px;text-decoration-thickness:1px;transition:color .15s ease;}
  .gh-content a:hover{color:var(--red-deep,#b42318);text-decoration-thickness:2px;}
  .gh-content blockquote{border-left:3px solid var(--red);padding-left:20px;margin:28px 0;font-style:italic;color:var(--text);}
  .gh-content hr{border:none;border-top:1px solid var(--border);margin:40px 0;}
  .gh-content ul,.gh-content ol{padding-left:24px;margin:0 0 18px;}
  .gh-content li{margin-bottom:8px;}
  .gh-content li::marker{color:var(--text-dim);}
  .hero-image{max-width:760px;margin:0 auto 8px;padding:0 32px;}
  .hero-image-inner{aspect-ratio:16/9;border-radius:8px;overflow:hidden;background:radial-gradient(circle at 30% 25%, rgba(229,72,77,0.18), transparent 60%),repeating-linear-gradient(135deg, #17171a 0 2px, #101012 2px 4px);background-size:cover;background-position:center;}
  article.body{padding:40px 32px 0;max-width:760px;margin:0 auto;}
  article.body p{font-size:17px;color:#1f2430;margin:0 0 22px;line-height:1.75;}
  article.body h2{font-size:23px;font-weight:700;margin:44px 0 18px;}
  .author-card{max-width:760px;margin:56px auto 0;padding:32px;display:flex;gap:16px;align-items:flex-start;border-top:1px solid var(--border);}
  .author-card .name{font-weight:700;font-size:15px;color:var(--text);margin-bottom:4px;}
  .author-card .bio{font-size:14px;color:var(--text-dim);}
  .cta-band{background:var(--grey-bg);border-top:1px solid var(--border);border-bottom:1px solid var(--border);padding:48px 32px;text-align:center;margin-top:56px;}
  .cta-band h2{font-size:24px;font-weight:700;margin:0 0 10px;}
  .cta-band p{color:var(--text-dim);font-size:14px;margin:0 0 22px;}
  .signup-form-inline{display:flex;gap:10px;justify-content:center;flex-wrap:wrap;}
  .signup-input-light{border:1px solid var(--border);border-radius:8px;padding:12px 16px;font-size:14px;width:280px;max-width:80vw;font-family:inherit;}
  .related-section{padding:56px 32px 20px;max-width:1920px;margin:0 auto;}
  .related-section h2{font-size:24px;font-weight:700;margin:0 0 24px;}
  .related-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:64px;width:min(1920px,calc(100% - 64px));margin:0 auto;overflow:clip;clip-path:inset(-2px);}
  .related-cell{cursor:pointer;position:relative;display:flex;flex-direction:column;}
  .related-cell::before{content:"";position:absolute;inset:auto auto -32px auto;height:1px;width:100vw;background:var(--border);}
  .related-cell::after{content:"";position:absolute;inset:0 -32px auto auto;width:1px;height:100vh;background:var(--border);}
  .related-body{padding:20px 0 0;}
  .related-body h3{font-size:16px;font-weight:600;line-height:1.35;margin:0 0 10px;}
  .related-meta{font-size:12.5px;color:var(--text-dim);}

  /* Pergamon + About pages */
  .page-head{max-width:720px;margin:0 auto;padding:80px 32px 8px;}
  .page-head h1{font-size:clamp(2.4rem,1.8rem + 2vw,3.4rem);font-weight:700;line-height:1.08;letter-spacing:-0.02em;margin:0 0 20px;color:var(--text);}
  .page-lead{font-size:19px;line-height:1.6;color:var(--text-dim);max-width:600px;margin:0 0 28px;}
  .page-body{max-width:720px;margin:0 auto;padding:8px 32px 88px;}
  .page-body .about-lead{font-size:16px;color:#333844;line-height:1.75;margin:0 0 22px;}
  /* Editable prose migrated into a Ghost page body (About bio, Pergamon intro): match the old
     .about-lead look for top-level paragraphs, and restore section-heading styling for h2/h3 so the
     Ghost rich-text renders identically to the previous hardcoded markup. Direct-child selectors so
     the Offensive/Defensive .lab-cell contents (an inline HTML card) keep their own styling. */
  .editable-prose > p{font-size:16px;color:#333844;line-height:1.75;margin:0 0 22px;}
  .editable-prose > h2{font-size:22px;font-weight:700;letter-spacing:-0.01em;margin:48px 0 18px;color:var(--text);}
  .editable-prose > h3{font-size:18px;font-weight:700;margin:28px 0 8px;color:var(--text);}
  .section-title{font-size:22px;font-weight:700;letter-spacing:-0.01em;margin:48px 0 18px;color:var(--text);}
  .pub-card{border:1px solid var(--border);border-radius:14px;padding:28px;background:var(--grey-bg);}
  .pub-label{display:inline-block;font-family:'Graphik XCond',var(--font-sans);font-size:13px;font-weight:600;letter-spacing:0.1em;text-transform:uppercase;color:var(--red);margin-bottom:10px;}
  .pub-card p{color:var(--text-dim);font-size:15px;line-height:1.65;margin:0 0 16px;}
  .pub-link{font-weight:600;color:var(--red);font-size:15px;}
  .lab-grid{display:grid;grid-template-columns:1fr 1fr;gap:20px;margin:8px 0;}
  .lab-cell{border:1px solid var(--border);border-radius:14px;padding:26px;}
  .lab-cell h3{font-size:18px;font-weight:600;margin:12px 0 10px;color:var(--text);}
  .lab-cell p{font-size:14.5px;line-height:1.6;color:var(--text-dim);margin:0;}
  .lab-tag{display:inline-block;font-family:'Graphik XCond',var(--font-sans);font-size:13px;font-weight:600;letter-spacing:0.1em;text-transform:uppercase;padding:5px 9px;border-radius:4px;}
  .lab-tag.red{color:#b42318;background:#fef0ee;}
  .lab-tag.blue{color:#0c20e7;background:#daf2ff;}
  /* Pergamon artefacts — premium cards. A hand-authored .artefact and a Ghost
     bookmark card (added from the editor) share one refined card shell: soft
     border, a red accent bar that lights on hover, a lift, and a sliding arrow. */
  .artefact-list{display:flex;flex-direction:column;gap:14px;}
  .artefact,
  .artefact-list .kg-bookmark-container{
    position:relative;display:flex;align-items:center;justify-content:space-between;gap:22px;
    padding:22px 26px 22px 28px;text-decoration:none;background:var(--white);
    border:1px solid var(--border);border-radius:14px;overflow:hidden;
    transition:border-color .22s ease,box-shadow .28s ease,transform .28s ease;}
  .artefact::before,
  .artefact-list .kg-bookmark-container::before{
    content:"";position:absolute;left:0;top:0;bottom:0;width:3px;background:var(--red);
    opacity:0;transform:scaleY(.35);transition:opacity .25s ease,transform .25s ease;}
  .artefact:hover,
  .artefact-list .kg-bookmark-container:hover{
    border-color:#f1c9c5;box-shadow:0 12px 34px rgba(17,19,24,.09);transform:translateY(-3px);}
  .artefact:hover::before,
  .artefact-list .kg-bookmark-container:hover::before{opacity:1;transform:scaleY(1);}
  /* strip Ghost's default bookmark box so only our card shell shows */
  .artefact-list .kg-card{margin:0;}
  .artefact-list .kg-bookmark-card{border:0;box-shadow:none;border-radius:0;background:transparent;}
  .artefact-list .kg-bookmark-content{display:flex;flex-direction:column;order:1;flex:1;min-width:0;}
  .artefact-name,
  .artefact-list .kg-bookmark-title{font-size:19px;font-weight:700;letter-spacing:-.01em;color:var(--text);margin:0 0 5px;transition:color .2s ease;}
  .artefact:hover .artefact-name,
  .artefact-list .kg-bookmark-container:hover .kg-bookmark-title{color:var(--red);}
  .artefact-desc,
  .artefact-list .kg-bookmark-description{font-size:15px;line-height:1.5;color:var(--text-dim);margin:0;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden;}
  /* source domain kept as a refined uppercase label (icon dropped) */
  .artefact-list .kg-bookmark-metadata{order:3;display:flex;align-items:center;margin-top:10px;font-size:11.5px;font-weight:600;letter-spacing:.08em;text-transform:uppercase;color:var(--text-dim);}
  .artefact-list .kg-bookmark-icon,.artefact-list .kg-bookmark-thumbnail{display:none;}
  .artefact-arrow,
  .artefact-list .kg-bookmark-container::after{content:"\2192";order:2;font-size:20px;line-height:1;color:var(--red);flex-shrink:0;transition:transform .25s ease;}
  .artefact:hover .artefact-arrow,
  .artefact-list .kg-bookmark-container:hover::after{transform:translateX(6px);}

  /* Table of contents — sticky left rail on wide screens, scroll-spy active */
  article.body h2{scroll-margin-top:90px;}
  .article-toc{display:none;}
  @media(min-width:1200px){
    .article-toc{display:block;position:fixed;top:130px;left:max(24px,calc((100vw - 760px)/2 - 210px));width:190px;max-height:66vh;overflow:auto;z-index:5;transition:opacity .25s ease;}
    /* Fades out once the reader leaves the article body (related / next-post / footer
       scroll in), so the fixed rail never overlaps the post-article sections. */
    .article-toc.toc-hidden{opacity:0;pointer-events:none;}
    /* Subtler scrollbar for the rail than the loud page-wide crimson one. */
    .article-toc{scrollbar-width:thin;scrollbar-color:var(--border) transparent;}
    .article-toc::-webkit-scrollbar{width:6px;}
    .article-toc::-webkit-scrollbar-thumb{background:var(--border);border:0;border-radius:100px;}
    .article-toc::-webkit-scrollbar-track{background:transparent;}
    .toc-label{display:block;font-size:11px;font-weight:700;letter-spacing:0.08em;text-transform:uppercase;color:var(--text-dim);margin-bottom:12px;}
    .article-toc ul{list-style:none;margin:0;padding:0;border-left:2px solid var(--border);}
    .article-toc li a{display:block;padding:6px 0 6px 14px;margin-left:-2px;border-left:2px solid transparent;font-size:13px;color:var(--text-dim);line-height:1.4;transition:color .2s,border-color .2s;}
    .article-toc li a:hover{color:var(--text);}
    .article-toc li.on a{color:var(--red);border-left-color:var(--red);font-weight:600;}
  }

  /* Continuous "next post" band — a tall, near-full-screen runway so flowing
     into the next article is a deliberate scroll, not an instant snap. The
     inner reveals as you scroll in (JS-driven opacity/rise); on completion the
     whole band eases out and the next article's crossfade carries it in. */
  .next-post{background:var(--black);position:relative;z-index:2;height:190vh;transition:opacity .5s ease;margin-top:clamp(40px,5vw,64px);}
  .next-post.leaving{opacity:0;}
  .next-post-sticky{position:sticky;top:0;height:100vh;display:flex;align-items:center;justify-content:center;padding:0;}
  /* Card fills the viewport; the inline scale (0.90 -> 1.00) provides the inset
     margin at low progress and the full-bleed at 100%, expanding in step with
     the image zoom (SOHub's paired card+image reveal). */
  .next-post-card{position:relative;width:100%;max-width:none;height:calc(100vh - 44px);border-radius:20px;overflow:hidden;background:#17181d;transform-origin:center;will-change:transform;transition:transform .1s linear,border-radius .1s linear;}
  .next-post-image{position:absolute;inset:0;transform-origin:center;will-change:transform;transition:transform .1s linear;}
  /* A full, rich placeholder hero (a real Ghost hero image drops in here later),
     so the card reads as a complete picture, never empty background. */
  .next-post-image .thumb-inner{background:radial-gradient(130% 120% at 24% 12%, rgba(220,38,38,0.42), rgba(220,38,38,0.08) 44%, transparent 68%),radial-gradient(120% 120% at 86% 96%, rgba(220,38,38,0.3), transparent 55%),linear-gradient(140deg, #251524 0%, #14111a 46%, #0e0d12 100%);background-size:cover;background-position:center;transition:none;}
  /* When a real cover image is set inline, darken it a touch so the overlay text
     stays legible (the inline background-image sits above this tint layer). */
  .next-post-image .thumb-inner{box-shadow:inset 0 0 0 2000px rgba(10,9,13,0.18);}
  .next-post-overlay{position:absolute;inset:0;display:flex;align-items:flex-end;justify-content:space-between;gap:24px;padding:clamp(28px,4vw,56px);color:#fff;background:linear-gradient(to top, rgba(0,0,0,0.55), rgba(0,0,0,0.05) 46%, transparent 70%);}
  .next-post-head{max-width:64%;}
  .next-post-label{font-family:'Graphik XCond',var(--font-sans);font-size:15px;font-weight:600;letter-spacing:0.12em;text-transform:uppercase;color:rgba(255,255,255,0.72);}
  .next-post-head h2{font-size:clamp(2rem,4vw,3.6rem);font-weight:700;line-height:1.04;letter-spacing:-0.01em;margin:10px 0 0;color:#fff;}
  .next-post-pctbig{font-size:clamp(2rem,4vw,3.6rem);font-weight:700;color:#fff;line-height:1;flex-shrink:0;}
  .next-post-fill{position:absolute;left:0;right:0;bottom:0;height:6px;background:rgba(255,255,255,0.16);}
  .next-post-fill>div{height:100%;background:var(--red);transition:width .1s linear;}
  /* Mobile next-article: SOHub's tap-to-continue landscape card (no scroll
     runway). Rendered only under 640px via the isMobile branch. */
  /* SOHub-style: a full-bleed, near-square image filling the width, with the title big at the
     bottom-left and the VIEW pill at the bottom-right. No scroll runway, no side padding, no
     rounded corners; the section IS the card. Drops the 100vh sticky scaffold (that void was the
     misplacement) so the card sizes to its own square aspect-ratio. */
  .next-post-mobile{height:auto;padding:0;display:flex;justify-content:center;}
  .next-post-mobile .next-post-sticky{position:static;height:auto;width:100%;display:flex;justify-content:center;padding:0;}
  .next-post-mobile .next-post-card{height:auto;width:100%;max-width:none;flex:1 1 100%;aspect-ratio:1/1;border-radius:0;cursor:pointer;}
  .next-post-mobile .next-post-image{transform:none;}
  .next-post-mobile .next-post-overlay{flex-direction:row;align-items:flex-end;justify-content:space-between;gap:14px;padding:clamp(22px,6vw,34px);background:linear-gradient(to top, rgba(0,0,0,0.74), rgba(0,0,0,0.12) 52%, transparent 100%);}
  .next-post-mobile .next-post-head{max-width:60%;}
  .next-post-mobile .next-post-head h2{font-size:clamp(1.9rem,8.5vw,2.7rem);line-height:1.02;margin:6px 0 0;}
  .next-post-mobile .next-post-fill{display:none;}   /* the runway progress bar is desktop-only */
  /* Desktop navigates by scrolling the runway (the big % counter is the cue), so
     the tap target is mobile-only; mobile in turn hides the static % counter that
     never advances there. */
  .next-post-mobile .next-post-pctbig{display:none;}
  /* VIEW pill, SOHub-style: text left, arrow in a filled circle at the right edge. */
  .next-post-mobile .next-post-view{display:inline-flex;align-items:center;gap:12px;padding:7px 7px 7px 22px;font-size:15px;}
  .next-post-mobile .next-post-view svg{width:38px;height:38px;padding:10px;box-sizing:border-box;border-radius:50%;background:#0a0a0a;color:#fff;}
  .next-post-view{display:none;align-items:center;gap:9px;padding:11px 20px 11px 22px;border:none;border-radius:100px;background:#fff;color:#0a0a0a;font-family:'Graphik XCond',var(--font-sans);font-weight:700;font-size:14px;letter-spacing:0.06em;text-transform:uppercase;cursor:pointer;transition:transform .2s ease,background .2s ease;}
  .next-post-view:active{transform:scale(0.96);}
  .next-post-view svg{width:18px;height:18px;}

  /* Reading progress bar (fixed, fills as you scroll the article) */
  /* Sits directly under the (sticky, opaque) navbar. At top:0 with z-index below the
     nav it was painted behind the black bar and invisible; anchored to --nav-h and
     lifted above the nav it reads as a red progress line under the header. */
  /* Touch feedback: kill the default browser tap-highlight (a translucent blue/grey box) and
     replace it with a subtle, premium press-down. Scoped to touch devices (hover:none) so the
     desktop hover states are untouched; every tappable element depresses briefly, which reads as
     tactile and intentional rather than as a flat flash. */
  html{-webkit-tap-highlight-color:transparent;}
  @media(hover:none){
    a,button,.nav-item,.nav-subscribe,.nav-toggle,.btn-pill,.crumb-pill,.share-btn,.icon-btn,
    .scroll-hint,.article-cell,.post-card,.next-post-card,.pill,.tag-pill,.filter-pill{
      -webkit-tap-highlight-color:transparent;
    }
    .btn-pill:active,.nav-item:active,.nav-subscribe:active,.nav-toggle:active,.crumb-pill:active,
    .share-btn:active,.icon-btn:active,.scroll-hint:active,.article-cell:active,.post-card:active,
    .next-post-card:active,.pill:active,.tag-pill:active,.filter-pill:active{
      transform:scale(0.97);transition:transform .13s cubic-bezier(.22,.68,0,1);
    }
  }

  .reading-progress{position:fixed;top:var(--nav-h);left:0;right:0;height:3px;z-index:600;pointer-events:none;background:transparent;}
  .reading-progress>div{height:100%;background:var(--red);width:0;transition:width .08s linear;}
  /* Share row */
  .article-share{max-width:760px;margin:40px auto 0;padding:22px 32px 0;display:flex;align-items:center;gap:14px;border-top:1px solid var(--border);}
  .share-label{font-family:'Graphik XCond',var(--font-sans);font-size:13px;font-weight:600;letter-spacing:0.1em;text-transform:uppercase;color:var(--text-dim);margin-right:2px;}
  .share-btn{width:38px;height:38px;border-radius:50%;border:1px solid var(--border);background:var(--white);color:var(--text);display:inline-flex;align-items:center;justify-content:center;cursor:pointer;transition:background .2s ease,color .2s ease,border-color .2s ease,transform .2s ease;}
  .share-btn:hover{background:var(--red);color:#fff;border-color:var(--red);transform:translateY(-2px);}
  .share-copied{font-size:13px;color:var(--red);font-weight:600;}

  /* Demo-only red picker */
  .red-picker{position:fixed;bottom:16px;left:16px;z-index:60;display:flex;align-items:center;gap:7px;background:rgba(255,255,255,0.82);backdrop-filter:blur(8px);border:1px solid var(--border);border-radius:100px;padding:7px 12px;}
  .red-swatch{width:20px;height:20px;border-radius:50%;border:2px solid transparent;cursor:pointer;padding:0;transition:transform .15s;}
  .red-swatch:hover{transform:scale(1.12);}
  .red-swatch.on{border-color:#111;box-shadow:0 0 0 2px #fff inset;}

  /* ── Responsive ───────────────────────────────────────────────────────── */
  /* Animated hamburger: three bars that morph into an X on open. */
  .nav-toggle{display:none;background:none;border:none;cursor:pointer;padding:0;width:40px;height:40px;position:relative;}
  .nav-toggle .bar{position:absolute;left:9px;right:9px;height:2px;border-radius:2px;background:var(--text);transition:transform .34s cubic-bezier(.22,.68,0,1),opacity .18s ease,background .35s ease;}
  .nav-toggle .bar:nth-child(1){top:14px;}
  .nav-toggle .bar:nth-child(2){top:20px;}
  .nav-toggle .bar:nth-child(3){top:26px;}
  .nav-toggle.open .bar:nth-child(1){transform:translateY(6px) rotate(45deg);}
  .nav-toggle.open .bar:nth-child(2){opacity:0;}
  .nav-toggle.open .bar:nth-child(3){transform:translateY(-6px) rotate(-45deg);}
  /* Subscribe as a menu item — only rendered inside the mobile dropdown. */
  .nav-subscribe{display:none;}
  @keyframes navItemIn{from{opacity:0;transform:translateX(-10px);}to{opacity:1;transform:translateX(0);}}

  /* Tablet: 2-column grids, stacked featured */
  @media(max-width:992px){
    .article-grid, .related-grid{grid-template-columns:repeat(2,1fr);}
    .feat-grid{grid-template-columns:1fr;}
    .feat-hero{max-width:none;padding-right:0;padding-bottom:24px;margin-bottom:24px;border-bottom:1px solid #4c4d4f;}
    .feat-side{border-left:none;padding-left:0;}
    .feat-ticker{display:none;}
    .lab-grid{grid-template-columns:1fr;}
  }

  /* Hamburger nav below the desktop layout — animated slide + fade, staggered
     items, and Subscribe moved in as the last item. */
  @media(max-width:860px){
    .nav-links{
      display:flex;flex-direction:column;gap:0;align-items:stretch;
      position:absolute;top:100%;left:0;right:0;
      background:var(--white);border-bottom:1px solid var(--border);
      box-shadow:0 14px 30px rgba(0,0,0,0.08);
      max-height:0;opacity:0;overflow:hidden;pointer-events:none;
      transform:translateY(-8px);
      transition:max-height .4s cubic-bezier(.22,.68,0,1),opacity .26s ease,transform .4s cubic-bezier(.22,.68,0,1);
    }
    .nav-links.open{max-height:360px;opacity:1;transform:translateY(0);pointer-events:auto;padding:8px 0 14px;}
    .nav-links .nav-item{padding:14px 32px;font-size:15px;}
    /* The dropdown panel is always light, so the items need dark text regardless of the header's
       dark/top state; without this they inherit the white hero-nav colour and vanish (white on
       white). Same specificity as the global `.top .nav-links .nav-item` rule but later, so it wins;
       the scrolled state keeps its own darker rule. */
    .top .nav-links .nav-item{color:var(--text);}
    .top .nav-links .nav-item.active{color:var(--red);}
    .nav-links .nav-subscribe{display:block;margin:12px 32px 2px;padding:13px 22px;background:var(--red);color:#fff;font-weight:700;border-radius:100px;text-align:center;font-size:15px;}
    .nav-links.open .nav-item,.nav-links.open .nav-subscribe{animation:navItemIn .42s cubic-bezier(.22,.68,0,1) both;}
    .nav-links.open .nav-item:nth-child(1){animation-delay:.05s;}
    .nav-links.open .nav-item:nth-child(2){animation-delay:.10s;}
    .nav-links.open .nav-item:nth-child(3){animation-delay:.15s;}
    .nav-links.open .nav-subscribe{animation-delay:.20s;}
    .nav-toggle{display:inline-flex;align-items:center;}
    .nav-right .nav-subscribe-top{display:none;}

    /* Hero across the whole touch/hamburger range (phone + tablet): lead with the NAME (identity
       first), tagline supporting below it, CTA last; anchored to the upper area rather than floating
       mid-screen with dead space above. Sizes with clamp so it reads well from 320px up to 860px. */
    .cover-hero{align-items:flex-start;}
    .cover-inner{padding:clamp(60px,12vh,140px) clamp(24px,5vw,48px) 44px;justify-content:flex-start;text-align:center;align-items:center;}
    .cover-name{order:1;width:100%;margin:0 0 26px;text-align:center;}
    .name-line{font-size:clamp(3.4rem,16vw,7rem);}
    .name-line-1,.name-line-2{text-align:center;margin-left:0;}
    .cover-top{order:2;align-self:center;text-align:center;max-width:min(90%,440px);margin:0 0 36px;}
    .cover-tagline{font-size:clamp(15px,2vw,17px);line-height:1.6;}
    .cover-bottom{order:3;gap:22px;}
    .scroll-hint{margin-top:6px;}
  }

  /* Mobile: single column, tighter type, stacked footer */
  @media(max-width:640px){
    nav.main{padding:16px 20px;}
    /* The tighter mobile nav is ~73px tall, so the reading-progress bar (top:var(--nav-h)) must
       track it or it floats in a gap below the bar. */
    :root{--nav-h:73px;}
    .brand-word{display:none;}  /* logo alone on mobile — the wordmark made it busy */
    .nav-search.open .nav-search-input{width:42vw;}
    .article-grid, .related-grid{grid-template-columns:1fr;gap:40px;width:min(1920px,calc(100% - 40px));}
    .article-cell::after, .related-cell::after{display:none;}
    .xhair{display:none;}
    .foot-grid{grid-template-columns:1fr;gap:26px;}
    .foot-bottom{flex-direction:column;align-items:flex-start;}
    /* (Hero name-first layout is handled in the <=860px block above so tablets share it.) */
    .filter-row{flex-direction:column;align-items:stretch;gap:14px;}
    .search-box{min-width:0;}
    .cover-controls{bottom:12px;right:12px;max-width:72vw;}
    .switch-row{flex-wrap:wrap;justify-content:flex-end;}
    .switch-tag{display:none;}
    .article-head h1{font-size:29px;}
    .article-head-center h1{font-size:31px;}
    .article-head-center{padding-top:20px;}
    .filter-section,.pagination-section{padding-left:20px;padding-right:20px;}
    .page-head,.page-body{padding-left:20px;padding-right:20px;}
  }

/* ── Ghost-port additions ──────────────────────────────────────────────────
   The mesh canvas was styled inline in the React demo; give it the same look
   here (blur is set per-variant by the JS). */
.mesh-canvas{position:absolute;inset:0;width:100%;height:100%;z-index:0;opacity:0.9;pointer-events:none;}
.article-toc .toc-label{font-family:'Graphik XCond',var(--font-sans);font-size:12px;font-weight:600;letter-spacing:0.12em;text-transform:uppercase;color:var(--text-dim);margin-bottom:12px;}
.article-toc li.toc-sub{padding-left:14px;}

/* Native cross-document View Transitions (replaces swup): the whole new page
   rises from the bottom over the blurred, receding old page — the SOHub feel,
   no library. Browsers without support just navigate normally. */
html,body{background:#ffffff;}  /* solid base so a page swap never flashes an unstyled background */
@view-transition{navigation:auto;}
::view-transition-old(root){animation:rvtOld .6s cubic-bezier(.62,0,.2,1) both;}
::view-transition-new(root){animation:rvtNew 1s cubic-bezier(.62,0,.2,1) both;z-index:2;}
/* Receding page stays OPAQUE (blur + scale only) — no opacity fade means the new
   page never shows a see-through gap while it rises (that was the flash). */
@keyframes rvtOld{to{filter:blur(8px);transform:scale(.96);}}
@keyframes rvtNew{0%{transform:translateY(100%);border-radius:44px 44px 0 0;}12%{transform:translateY(97%);}100%{transform:translateY(0);border-radius:0;}}
/* prefers-reduced-motion deliberately NOT honoured — full transitions at all times (operator decision). */
