/* Kloudfuse blog -- article and index system.
   Layered on kf-design.css, which is not modified. Every blog page
   links this; nothing here is duplicated into a page. */

/* ============================================================ blog system
   Page-scoped. kf-design.css supplies tokens, nav, footer and .sec/.wrap;
   everything below is the blog itself. */

/* Section heading inside a .sec, at section scale rather than hero scale. */
.sec h2.b-h2{font-size:clamp(24px,2.2vw,31px);line-height:1.16;
  letter-spacing:-.028em;max-width:24ch}

.b-mast{padding:76px 0 0}
/* kf-design.css sizes h1 only under .hero / .page-hero, and this mast is a
   plain section, so the title fell back to the 33px UA default -- smaller
   than the h2 beneath it. Same fix compare.html already carries for .cmp-mast.
   Values are the canonical .page-hero h1 (kf-design.css). */
.b-mast h1{font-size:clamp(38px,4.6vw,58px);max-width:19ch;color:var(--kf-ink);
  line-height:1.02;letter-spacing:-.038em}
.b-mast .lead{margin-top:20px;max-width:60ch}

/* filter chips ------------------------------------------------------------ */
.b-filters{display:flex;flex-wrap:wrap;gap:9px;margin-top:34px}
.b-chip{display:inline-flex;align-items:center;height:34px;padding:0 15px;
  border:1px solid var(--line);border-radius:999px;background:var(--paper);
  font-size:13.5px;color:var(--fg-2);cursor:pointer;transition:.15s;
  font-family:inherit}
.b-chip:hover{border-color:var(--fg-3);color:var(--fg)}
.b-chip[aria-pressed="true"]{background:var(--kf-ink);border-color:var(--kf-ink);
  color:#fff}

/* covers ------------------------------------------------------------------
   One component, two fills: a real image, or a typographic panel for posts
   that have no cover art yet. The panel is a design, not a placeholder --
   it does not pretend to be a screenshot. */
 /* Every post cover on kloudfuse.com is a 2400x2400 title card -- artwork
    with the headline, the author and a "Read the Blog" button drawn into it.
    Two consequences drive this component:

    1. The frame is SQUARE. A 16/9 frame cropped the top and bottom clean off
       the artwork, taking the kicker and the button with them.
    2. object-fit is CONTAIN, not cover. Square art in a square frame fills it
       exactly, and any cover that is not perfectly square letterboxes on the
       card's own ground rather than losing its edges. Nothing is ever cut.

    The topic label is NOT overlaid on the art either. These covers carry their
    own typography, and a label dropped on top lands in the middle of it. */
.b-cover{position:relative;overflow:hidden;border-radius:var(--r-card);
  background:var(--kf-ink);border:1px solid var(--line);display:block}
.b-cover img{width:100%;height:100%;object-fit:contain;display:block}
 /* A cover for a post with no art. It carries the topic and a motif, never
    the headline -- the headline is already directly underneath it, and a cover
    that reads the title back to you is just the title twice. */
.b-cover.is-type{display:grid;align-content:end;padding:22px;
  background:linear-gradient(150deg,var(--kf-ink) 0%,#331C25 100%)}
.b-cover.is-type .bt-k{position:relative;z-index:1;font-family:var(--font-mono);
  font-size:11px;letter-spacing:.16em;text-transform:uppercase;color:var(--signal)}
.b-cover.is-type .bt-k::before{content:"// "}
/* motif: a quiet ruled field, the same grammar as the section rules */
.b-cover.is-type::before{content:"";position:absolute;inset:0;
  background-image:repeating-linear-gradient(115deg,rgba(255,255,255,.055) 0 1px,
    transparent 1px 13px);
  -webkit-mask-image:linear-gradient(200deg,#000 10%,transparent 72%);
  mask-image:linear-gradient(200deg,#000 10%,transparent 72%)}
.b-cover.is-feat{display:grid;place-items:center;
  background:linear-gradient(150deg,var(--kf-ink) 0%,#3A2029 55%,#2A1A22 100%)}
.b-cover.is-feat .bf-mark{width:86px;opacity:.92}
.b-cover.is-feat .bf-k{position:absolute;left:24px;bottom:20px;
  font-family:var(--font-mono);font-size:11px;letter-spacing:.16em;
  text-transform:uppercase;color:var(--signal)}
.b-cover.is-feat::after{content:"";position:absolute;inset:auto -50px -70px auto;
  width:230px;height:230px;border-radius:50%;
  background:radial-gradient(circle,rgba(87,184,197,.20),transparent 70%)}
.b-cover.is-type::after{content:"";position:absolute;inset:auto -30px -46px auto;
  width:150px;height:150px;border-radius:50%;
  background:radial-gradient(circle,rgba(87,184,197,.22),transparent 70%)}

/* featured ---------------------------------------------------------------- */
.b-feat{display:grid;grid-template-columns:minmax(0,.85fr) minmax(0,1fr);
  gap:52px;align-items:center;margin-top:14px}
.b-feat .b-cover{max-width:440px}
.b-feat .b-cover{aspect-ratio:1/1}
.b-feat h2{margin:14px 0 0;font-size:clamp(26px,2.5vw,36px);line-height:1.12;
  letter-spacing:-.032em;max-width:20ch}
.b-feat h2 a{color:inherit}
.b-feat h2 a:hover{color:var(--link)}
.b-feat .b-deck{margin-top:16px;font-size:16.5px;line-height:1.62;
  color:var(--fg-2);max-width:52ch}

/* meta line --------------------------------------------------------------- */
.b-meta{display:flex;flex-wrap:wrap;align-items:center;gap:10px;
  font-family:var(--font-mono);font-size:11.5px;letter-spacing:.06em;
  text-transform:uppercase;color:var(--fg-3)}
.b-meta .b-tag{color:var(--action);font-weight:600}
.b-meta .b-dot{width:3px;height:3px;border-radius:50%;background:var(--line);
  flex:none}

/* card grid --------------------------------------------------------------- */
.b-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:26px;margin-top:34px}
.b-card{display:flex;flex-direction:column;gap:14px}
/* The index filters and Load more toggle the hidden attribute. A class that
   sets display beats the UA [hidden]{display:none} rule (0,1,0 vs 0,0,0), so
   without these the cards and the Load more button stay on screen and only
   the counter changes. Same reason kf-design.css carries .panel[hidden]. */
.b-card[hidden],.b-more[hidden],.b-lead-card[hidden]{display:none}
.b-card .b-cover{aspect-ratio:1/1}
.b-card h3{margin:0;font-size:18.5px;line-height:1.3;letter-spacing:-.02em}
.b-card h3 a{color:inherit}
.b-card h3 a:hover{color:var(--link)}
.b-card .b-deck{margin:0;font-size:14.5px;line-height:1.6;color:var(--fg-2)}
.b-card a.b-cover:hover img{transform:scale(1.02)}
.b-card .b-cover img{transition:transform .4s ease}

.b-head{display:flex;flex-wrap:wrap;align-items:baseline;gap:16px;
  justify-content:space-between}
.b-count{margin:0;font-family:var(--font-mono);font-size:11.5px;
  letter-spacing:.08em;text-transform:uppercase;color:var(--fg-3)}
.b-more{display:flex;justify-content:center;margin-top:46px}
.b-more button{height:46px;padding:0 26px;border:1px solid var(--line);
  border-radius:var(--r-btn,8px);background:var(--paper);font:inherit;
  font-weight:600;font-size:14.5px;color:var(--fg);cursor:pointer}
.b-more button:hover{border-color:var(--fg-3)}
.b-empty{margin-top:34px;color:var(--fg-3);font-size:15px}

@media (max-width:1000px){
  .b-feat{grid-template-columns:1fr;gap:24px}
  .b-grid{grid-template-columns:repeat(2,1fr)}
}
@media (max-width:640px){.b-grid{grid-template-columns:1fr}}

/* ======================================================== article template */

/* reading progress */
.b-prog{position:fixed;top:0;left:0;height:2px;width:0;background:var(--action);
  z-index:60;transition:width .1s linear}

.a-head{padding-top:64px}
.a-crumbs{font-family:var(--font-mono);font-size:11.5px;letter-spacing:.08em;
  text-transform:uppercase;color:var(--fg-3)}
.a-crumbs a{color:var(--fg-3)}
.a-crumbs a:hover{color:var(--link)}
.a-head h1{margin-top:20px;font-size:clamp(30px,3.4vw,46px);line-height:1.07;
  letter-spacing:-.036em;max-width:18ch}
.a-deck{margin-top:22px;font-size:18px;line-height:1.55;color:var(--fg-2);
  max-width:46ch}
/* the title column already sets the measure, so the ch caps only get in the
   way inside it */
.a-open .a-title h1,.a-open .a-title .a-deck{max-width:none}
.a-by{display:flex;flex-wrap:wrap;align-items:center;gap:12px;margin-top:28px;
  padding-top:22px;border-top:1px solid var(--line)}
.a-av{width:38px;height:38px;border-radius:50%;flex:none;display:grid;
  place-items:center;background:var(--kf-ink);color:#fff;font-weight:600;
  font-size:13px;letter-spacing:.02em}
.a-by .who{font-weight:600;font-size:14.5px;color:var(--fg)}
.a-by .when{font-family:var(--font-mono);font-size:11.5px;letter-spacing:.06em;
  text-transform:uppercase;color:var(--fg-3)}

/* Article hero, two columns: the title block reads left, the square title card
   sits right, vertically centred against it.
   Stacked, the 480px square sat under a full-width text block and left an
   empty half-page beside it -- the artwork is square, so a full-width row can
   never be its row. Pairing it with the title uses that width instead. */
.a-open{display:grid;grid-template-columns:minmax(0,58fr) minmax(0,38fr);
  gap:clamp(28px,4vw,64px);align-items:center;margin-top:4px}
.a-hero{width:100%;aspect-ratio:1/1;margin:0;
  border-radius:var(--r-panel);border:1px solid var(--line);
  background:var(--kf-ink);overflow:hidden}
.a-hero img{width:100%;height:100%;object-fit:contain;display:block}
@media (max-width:900px){
  /* one column, and the artwork follows the title rather than leading it --
     the headline should still be the first thing on a phone. */
  .a-open{grid-template-columns:minmax(0,1fr);gap:30px}
  .a-hero{max-width:420px}
}

/* layout: sticky contents rail + one measured column */
.a-layout{display:grid;grid-template-columns:210px minmax(0,1fr);gap:64px;
  margin-top:54px;align-items:start}
.a-toc{position:sticky;top:96px}
.a-toc .t-lab{font-family:var(--font-mono);font-size:11px;letter-spacing:.14em;
  text-transform:uppercase;color:var(--fg-3);padding-bottom:12px;
  border-bottom:1px solid var(--line)}
.a-toc ol{list-style:none;margin:14px 0 0;padding:0;display:flex;
  flex-direction:column;gap:2px}
.a-toc a{display:block;padding:7px 0 7px 13px;font-size:13.5px;line-height:1.4;
  color:var(--fg-3);border-left:2px solid transparent}
.a-toc a:hover{color:var(--fg)}
.a-toc a.on{color:var(--kf-ink);border-left-color:var(--action);font-weight:600}

/* prose ------------------------------------------------------------------- */
.prose{max-width:68ch;min-width:0;font-size:17px;line-height:1.72;color:var(--fg-2)}
.prose > p{margin:0 0 22px}
/* A long unbroken token -- a typed rule of em dashes, a URL, a hash --
   must wrap rather than widen the page. */
.prose,.prose p,.prose li{overflow-wrap:break-word}

/* No lede treatment. The title and the deck have already introduced the piece;
   stepping the first paragraph up to 19.5px and darkening it only read as the
   font changing mid-page. Emphasis is the .a-note callout underneath, which is
   a deliberate component rather than a silent type change. */
/* kf-design.css sets `.sec h2{font-size:clamp(28px,3.2vw,42px);max-width:21ch}`
   twice, plus a :nth-of-type(even) variant. Those are display headings for
   marketing folds; inside an article they render a body heading at 42px. This
   selector is (0,3,1) so it wins on specificity, not on load order. */
.sec .a-layout .prose h2{margin:52px 0 16px;font-size:clamp(23px,2vw,28px);
  line-height:1.22;letter-spacing:-.026em;color:var(--kf-ink);
  scroll-margin-top:96px;max-width:30ch}
.prose h3{margin:34px 0 12px;font-size:19px;line-height:1.3;
  letter-spacing:-.02em;color:var(--kf-ink);scroll-margin-top:96px}
.prose strong{color:var(--fg);font-weight:600}
.prose a{color:var(--link);border-bottom:1px solid rgba(23,107,105,.35)}
.prose a:hover{border-bottom-color:var(--link)}
.prose ul,.prose ol{margin:0 0 22px;padding-left:0;list-style:none}
.prose ul > li,.prose ol > li{position:relative;padding-left:26px;margin-bottom:11px}
.prose ul > li::before{content:"";position:absolute;left:6px;top:11px;width:6px;
  height:6px;border-radius:50%;background:var(--action)}
.prose ol{counter-reset:n}
.prose ol > li{counter-increment:n}
.prose ol > li::before{content:counter(n);position:absolute;left:0;top:0;
  font-family:var(--font-mono);font-size:12px;color:var(--action);
  line-height:1.85}

/* inline code */
.prose code{font-family:var(--font-mono);font-size:.88em;
  background:var(--stone);border:1px solid var(--line-soft);border-radius:5px;
  padding:.12em .4em;color:var(--kf-ink)}

/* code block: label rail, no copy affordance that lies about what it does */
.a-code{margin:0 0 26px;border:1px solid var(--line);border-radius:var(--r-card);
  overflow:hidden;background:var(--kf-ink)}
.a-code .ac-bar{display:flex;align-items:center;justify-content:space-between;
  gap:14px;padding:10px 16px;background:rgba(255,255,255,.05);
  border-bottom:1px solid rgba(255,255,255,.10)}
.a-code .ac-lang{font-family:var(--font-mono);font-size:11px;letter-spacing:.12em;
  text-transform:uppercase;color:var(--signal)}
.a-code .ac-copy{font-family:var(--font-mono);font-size:11px;letter-spacing:.06em;
  text-transform:uppercase;color:rgba(255,255,255,.55);background:none;border:0;
  cursor:pointer;padding:0}
.a-code .ac-copy:hover{color:#fff}
.a-code pre{margin:0;padding:18px 18px 20px;overflow-x:auto;
  font-family:var(--font-mono);font-size:13.5px;line-height:1.7;color:#E9E2DF}
.a-code pre code{background:none;border:0;padding:0;color:inherit;font-size:inherit}
.a-code .k{color:#7FD1DC}
.a-code .s{color:#F2A98F}
.a-code .c{color:#8E827F}

/* figure: contain, never crop -- these are diagrams */
.a-fig{margin:0 0 28px}
.a-fig .f-frame{background:var(--stone);border:1px solid var(--line);
  border-radius:var(--r-card);padding:16px;display:grid;place-items:center;
  /* a floor, so a figure whose source is slow or missing holds its space
     instead of collapsing into a hairline */
  min-height:140px}
.a-fig img,.a-fig video{max-width:100%;max-height:520px;height:auto;object-fit:contain;
  display:block;border-radius:6px}
.a-fig figcaption{margin-top:11px;font-size:13.5px;line-height:1.55;
  color:var(--fg-3)}

/* pull quote */
.a-quote{margin:34px 0;padding:4px 0 4px 24px;border-left:3px solid var(--action)}
.a-quote p{margin:0;font-family:var(--font-display);font-size:21px;
  line-height:1.42;letter-spacing:-.02em;color:var(--kf-ink)}
.a-quote cite{display:block;margin-top:12px;font-style:normal;
  font-family:var(--font-mono);font-size:11.5px;letter-spacing:.06em;
  text-transform:uppercase;color:var(--fg-3)}

/* callout */
.a-note{margin:0 0 26px;padding:20px 22px;background:var(--stone);
  border-radius:var(--r-card);border:1px solid var(--line)}
.a-note .n-lab{font-family:var(--font-mono);font-size:11px;letter-spacing:.13em;
  text-transform:uppercase;color:var(--action)}
.a-note p{margin:9px 0 0;font-size:15.5px;line-height:1.62}
.a-note p:last-child{margin-bottom:0}

/* table */
.a-table{margin:0 0 28px;border:1px solid var(--line);border-radius:var(--r-card);
  overflow:hidden;background:var(--paper)}
.a-table table{width:100%;border-collapse:collapse;font-size:14.5px}
.a-table th{text-align:left;padding:12px 16px;background:var(--stone);
  font-family:var(--font-mono);font-size:11px;letter-spacing:.1em;
  text-transform:uppercase;color:var(--fg-3);border-bottom:1px solid var(--line)}
.a-table td{padding:12px 16px;border-top:1px solid var(--line-soft);
  vertical-align:top;line-height:1.55}
.a-table td:first-child{font-weight:600;color:var(--fg)}
@media (max-width:620px){.a-table{overflow-x:auto}.a-table table{min-width:520px}}

/* article foot */
.a-foot{margin-top:56px;padding-top:26px;border-top:1px solid var(--line);
  display:flex;flex-wrap:wrap;gap:18px;align-items:center;
  justify-content:space-between}
.a-share{display:flex;gap:10px;align-items:center}
.a-share span{font-family:var(--font-mono);font-size:11px;letter-spacing:.1em;
  text-transform:uppercase;color:var(--fg-3)}
.a-share a{width:32px;height:32px;border:1px solid var(--line);border-radius:8px;
  display:grid;place-items:center;color:var(--fg-2)}
.a-share a:hover{border-color:var(--fg-3);color:var(--kf-ink)}
.a-share svg{width:15px;height:15px;fill:currentColor}
.a-back{font-weight:600;font-size:14.5px;color:var(--link)}

@media (max-width:1000px){
  /* minmax(0,1fr), not 1fr: a grid item defaults to min-width:auto, so the
     table's min-width:520px would otherwise widen the whole track and push the
     page into horizontal scroll on a phone. */
  .a-layout{grid-template-columns:minmax(0,1fr);gap:0}
  .a-toc{position:static;margin-bottom:34px;padding-bottom:8px}
  .a-toc ol{flex-direction:row;flex-wrap:wrap;gap:0 18px}
  .a-toc a{border-left:0;padding-left:0}
  .a-toc a.on{border-left:0}
}

/* lead ---------------------------------------------------------------------
   Two featured posts, equal weight: same column width, same cover size, same
   type scale. Nothing distinguishes one from the other, because nothing is
   meant to -- both are the lead. The old single-featured .b-feat rules are
   unused once this ships. */
.b-lead{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));
  gap:56px;align-items:start;margin-top:16px}
@media(max-width:900px){.b-lead{grid-template-columns:1fr;gap:44px}}

.b-lead-card{min-width:0;display:block}
/* Covers here are 2400x2400 title cards and the component is
   object-fit:contain, so the frame stays square. Capped rather than filling
   the column: at half of the wrap a full-width square is taller than the
   text beside it and swamps the fold. */
/* The cover fills its column. A capped cover left visible dead space to the
   right of each card, which reads as unresolved on a symmetrical lead. */
.b-lead-card .b-cover{aspect-ratio:1/1;margin-bottom:24px}
@media(max-width:900px){.b-lead-card .b-cover{max-width:420px}}
.b-lead-card h2{margin:13px 0 0;font-size:clamp(21px,1.9vw,26px);line-height:1.2;
  letter-spacing:-.026em;max-width:22ch}
.b-lead-card h2 a{color:inherit}
.b-lead-card h2 a:hover{color:var(--link)}
.b-lead-card .b-deck{margin-top:13px;font-size:15.5px;line-height:1.62;
  color:var(--fg-2);max-width:48ch}
.b-lead-card a.b-cover img{transition:transform .4s ease}
.b-lead-card a.b-cover:hover img{transform:scale(1.02)}
