/* Code blocks: framed, labelled with the language, line-numbered, copyable.
   Markup is built server-side in app/render.py (_frame_code_blocks).
   Loaded by both public/base.html and admin/base.html, so the editor preview
   looks exactly like the published page. Rules are scoped under `.cb` so they
   outrank the generic `.md pre` / `.md code` styles in those templates. */

figure.cb{
  margin:.9rem 0; border:1px solid #27272a; border-radius:10px;
  background:#0a0a0b; overflow:hidden;
}
.cb .cb-bar{
  display:flex; align-items:center; gap:.5rem;
  padding:.4rem .7rem; background:#131316; border-bottom:1px solid #27272a;
  font-family:ui-monospace,SFMono-Regular,Menlo,monospace; font-size:.68rem; line-height:1;
}
.cb .cb-dot{ width:7px; height:7px; border-radius:999px; background:#71717a; flex:none; }
.cb .cb-lang{ color:#a1a1aa; text-transform:uppercase; letter-spacing:.09em; }
.cb .cb-file{
  color:#52525b; overflow:hidden; text-overflow:ellipsis; white-space:nowrap;
}
.cb .cb-file::before{ content:"· "; }
.cb .cb-copy{
  margin-left:auto; flex:none; cursor:pointer;
  border:1px solid #27272a; border-radius:5px; background:transparent;
  padding:.2rem .45rem; color:#71717a; font:inherit; letter-spacing:.05em;
  transition:color .15s ease, border-color .15s ease;
}
.cb .cb-copy:hover{ color:#34d399; border-color:#34d39966; }
.cb .cb-copy.ok{ color:#34d399; border-color:#34d39966; }
.cb .cb-copy.fail{ color:#f87171; border-color:#f8717166; }

.cb .cb-pre{
  margin:0; padding:.75rem .85rem; background:transparent; border:0; border-radius:0;
  overflow-x:auto; font-size:.82rem; line-height:1.6;
  font-family:ui-monospace,SFMono-Regular,Menlo,monospace;
}
.cb .cb-code{ background:none; border:0; padding:0; font-size:inherit; color:#d4d4d8; }
.cb .cb-line{ display:inline; }
/* line-number gutter — pseudo-element content, so copying still yields clean code */
.cb-num .cb-pre{ counter-reset:cbline; }
.cb-num .cb-line::before{
  counter-increment:cbline; content:counter(cbline);
  display:inline-block; min-width:1.6ch; margin-right:1.1rem;
  text-align:right; color:#3f3f46; user-select:none; -webkit-user-select:none;
}

/* long blocks: show the first 15 lines, rest behind the button (app/render.py CLIP_LINES).
   The height is in `em`, so it stays exactly 15 lines when the font shrinks on mobile.
   Only the overflow is hidden — the full code is in the DOM, so Copy and find-in-page
   still see every line. */
.cb-clip:not(.cb-open) .cb-pre{ max-height:calc(15 * 1.6em + 1.5rem); overflow-y:auto; }
.cb .cb-pre:focus-visible{ outline:1px solid #34d39966; outline-offset:-1px; }
/* keep the inner window's scrollbar quiet, and stop a flick inside it from
   yanking the page once it hits the end */
.cb-clip .cb-pre{ overscroll-behavior-y:contain; scrollbar-width:thin; }
.cb-clip .cb-pre::-webkit-scrollbar{ width:8px; }
.cb-clip .cb-pre::-webkit-scrollbar-thumb{ background:#3f3f46; border-radius:4px; }
.cb-clip .cb-pre::-webkit-scrollbar-thumb:hover{ background:#52525b; }
html.light .cb-clip .cb-pre::-webkit-scrollbar-thumb{ background:#d4d4d8; }
html.light .cb-clip .cb-pre::-webkit-scrollbar-thumb:hover{ background:#a1a1aa; }
.cb .cb-more{
  display:block; width:100%; position:relative; cursor:pointer;
  border:0; border-top:1px solid #27272a; background:#131316; color:#a1a1aa;
  padding:.4rem .7rem; font-family:ui-monospace,SFMono-Regular,Menlo,monospace;
  font-size:.68rem; letter-spacing:.06em; transition:color .15s ease, background .15s ease;
}
.cb .cb-more:hover{ color:#34d399; background:#17171b; }
.cb .cb-more::after{ content:" ↓"; }
.cb-open .cb-more::after{ content:" ↑"; }
/* fade the last visible line so it reads as "there's more", not "it ends here".
   `.cb-end` (set by code.js once the reader has scrolled the window to its last line)
   drops it, so nothing sits over the code when there is nothing left below. */
.cb-clip:not(.cb-open):not(.cb-end) .cb-more::before{
  content:""; position:absolute; left:0; right:0; bottom:100%; height:2.5rem;
  pointer-events:none; background:linear-gradient(to bottom, rgba(10,10,11,0), #0a0a0b);
}

/* per-language accent on the dot */
.cb[data-lang="python"] .cb-dot{ background:#3b82f6; }
.cb[data-lang="bash"] .cb-dot,
.cb[data-lang="console"] .cb-dot,
.cb[data-lang="shell-session"] .cb-dot{ background:#22c55e; }
.cb[data-lang="powershell"] .cb-dot{ background:#5391fe; }
.cb[data-lang="javascript"] .cb-dot{ background:#eab308; }
.cb[data-lang="typescript"] .cb-dot{ background:#3178c6; }
.cb[data-lang="json"] .cb-dot{ background:#a1a1aa; }
.cb[data-lang="yaml"] .cb-dot{ background:#ef4444; }
.cb[data-lang="html"] .cb-dot,
.cb[data-lang="xml"] .cb-dot{ background:#f97316; }
.cb[data-lang="css"] .cb-dot{ background:#38bdf8; }
.cb[data-lang="sql"] .cb-dot{ background:#06b6d4; }
.cb[data-lang="docker"] .cb-dot{ background:#2496ed; }
.cb[data-lang="go"] .cb-dot{ background:#00add8; }
.cb[data-lang="rust"] .cb-dot{ background:#f97316; }
.cb[data-lang="java"] .cb-dot{ background:#f89820; }
.cb[data-lang="c"] .cb-dot,
.cb[data-lang="cpp"] .cb-dot{ background:#6b8fd4; }
.cb[data-lang="csharp"] .cb-dot{ background:#a855f7; }
.cb[data-lang="php"] .cb-dot{ background:#777bb3; }
.cb[data-lang="ruby"] .cb-dot{ background:#cc342d; }
.cb[data-lang="swift"] .cb-dot{ background:#f05138; }
.cb[data-lang="kotlin"] .cb-dot{ background:#a97bff; }
.cb[data-lang="nginx"] .cb-dot{ background:#009639; }
.cb[data-lang="ini"] .cb-dot,
.cb[data-lang="toml"] .cb-dot{ background:#94a3b8; }
.cb[data-lang="diff"] .cb-dot{ background:#f472b6; }
.cb[data-lang="markdown"] .cb-dot{ background:#a1a1aa; }

/* Pygments tokens (classprefix "tok-"), tuned to the zinc + emerald palette */
.cb .tok-c,.cb .tok-ch,.cb .tok-cm,.cb .tok-c1,.cb .tok-cs,.cb .tok-cp,.cb .tok-cpf{ color:#5f636b; font-style:italic; }
.cb .tok-k,.cb .tok-kc,.cb .tok-kd,.cb .tok-kn,.cb .tok-kp,.cb .tok-kr,.cb .tok-kt{ color:#c084fc; }
.cb .tok-o,.cb .tok-ow{ color:#94a3b8; }
.cb .tok-p{ color:#a1a1aa; }
.cb .tok-s,.cb .tok-sa,.cb .tok-sb,.cb .tok-sc,.cb .tok-dl,.cb .tok-sd,.cb .tok-s2,
.cb .tok-sh,.cb .tok-si,.cb .tok-sx,.cb .tok-s1,.cb .tok-ss,.cb .tok-sr{ color:#86efac; }
.cb .tok-se{ color:#fbbf24; }
.cb .tok-m,.cb .tok-mb,.cb .tok-mf,.cb .tok-mh,.cb .tok-mi,.cb .tok-mo,.cb .tok-il{ color:#fbbf24; }
.cb .tok-nf,.cb .tok-fm{ color:#60a5fa; }
.cb .tok-nc,.cb .tok-nn{ color:#67e8f9; }
.cb .tok-nb,.cb .tok-bp{ color:#22d3ee; }
.cb .tok-nd{ color:#f0abfc; }
.cb .tok-nt{ color:#f472b6; }
.cb .tok-na{ color:#a5b4fc; }
.cb .tok-no,.cb .tok-nl,.cb .tok-nv,.cb .tok-vc,.cb .tok-vg,.cb .tok-vi,.cb .tok-vm{ color:#e4e4e7; }
.cb .tok-ne,.cb .tok-nx,.cb .tok-n{ color:#e4e4e7; }
.cb .tok-err{ color:#f87171; }
.cb .tok-gd{ color:#f87171; }
.cb .tok-gi{ color:#4ade80; }
.cb .tok-gh,.cb .tok-gu{ color:#34d399; }
.cb .tok-gs{ font-weight:600; }
.cb .tok-ge{ font-style:italic; }
.cb .tok-w{ background:none; }

/* light mode */
html.light figure.cb{ background:#fbfbfc; border-color:#e4e4e7; }
html.light .cb .cb-bar{ background:#f4f4f5; border-color:#e4e4e7; }
html.light .cb .cb-lang{ color:#52525b; }
html.light .cb .cb-file{ color:#a1a1aa; }
html.light .cb .cb-copy{ color:#a1a1aa; border-color:#e4e4e7; }
html.light .cb .cb-copy:hover{ color:#059669; border-color:#05966966; }
html.light .cb .cb-code{ color:#27272a; }
html.light .cb-num .cb-line::before{ color:#c4c4c8; }
html.light .cb .tok-c,html.light .cb .tok-ch,html.light .cb .tok-cm,html.light .cb .tok-c1,
html.light .cb .tok-cs,html.light .cb .tok-cp,html.light .cb .tok-cpf{ color:#8b8f98; }
html.light .cb .tok-k,html.light .cb .tok-kc,html.light .cb .tok-kd,html.light .cb .tok-kn,
html.light .cb .tok-kp,html.light .cb .tok-kr,html.light .cb .tok-kt{ color:#7c3aed; }
html.light .cb .tok-o,html.light .cb .tok-ow,html.light .cb .tok-p{ color:#52525b; }
html.light .cb .tok-s,html.light .cb .tok-sa,html.light .cb .tok-sb,html.light .cb .tok-sc,
html.light .cb .tok-dl,html.light .cb .tok-sd,html.light .cb .tok-s2,html.light .cb .tok-sh,
html.light .cb .tok-si,html.light .cb .tok-sx,html.light .cb .tok-s1,html.light .cb .tok-ss,
html.light .cb .tok-sr{ color:#047857; }
html.light .cb .tok-m,html.light .cb .tok-mb,html.light .cb .tok-mf,html.light .cb .tok-mh,
html.light .cb .tok-mi,html.light .cb .tok-mo,html.light .cb .tok-il,
html.light .cb .tok-se{ color:#b45309; }
html.light .cb .tok-nf,html.light .cb .tok-fm{ color:#1d4ed8; }
html.light .cb .tok-nc,html.light .cb .tok-nn{ color:#0e7490; }
html.light .cb .tok-nb,html.light .cb .tok-bp{ color:#0891b2; }
html.light .cb .tok-nd{ color:#a21caf; }
html.light .cb .tok-nt{ color:#be185d; }
html.light .cb .tok-na{ color:#4338ca; }
html.light .cb .tok-no,html.light .cb .tok-nl,html.light .cb .tok-nv,html.light .cb .tok-vc,
html.light .cb .tok-vg,html.light .cb .tok-vi,html.light .cb .tok-vm,
html.light .cb .tok-ne,html.light .cb .tok-nx,html.light .cb .tok-n{ color:#27272a; }
html.light .cb .tok-gh,html.light .cb .tok-gu{ color:#047857; }
html.light .cb .tok-gd{ color:#b91c1c; }
html.light .cb .tok-gi{ color:#15803d; }
html.light .cb .cb-more{ background:#f4f4f5; border-color:#e4e4e7; color:#52525b; }
html.light .cb .cb-more:hover{ color:#059669; background:#eeeef0; }
html.light .cb-clip:not(.cb-open):not(.cb-end) .cb-more::before{
  background:linear-gradient(to bottom, rgba(251,251,252,0), #fbfbfc);
}

@media (max-width:640px){
  .cb .cb-pre{ font-size:.76rem; }
  .cb-num .cb-line::before{ margin-right:.7rem; }
  .cb .cb-file{ max-width:38vw; }
}
