/* app_shell: every selector of the frame, ported 2026-09-09 from the prototype's styles.css (the
   shell block, the base components) and engine.css (palette, help, scrim). Every colour is a token or
   an rgba(); the tokens live in app_brand/brand.css. Classes as the prototype has them: .win, .bar,
   .tabs, .st-*, .sb-*, .fbl-*. One selector lives in one file: a rule here is not repeated in a page's
   stylesheet. */

*{box-sizing:border-box;margin:0}
html,body{height:100%}
body{overflow:hidden;background:var(--desk-a);font-family:var(--fd);color:var(--ink);-webkit-font-smoothing:antialiased}
button{font-family:inherit;color:inherit}
/* a link nothing else colours is the accent, never the browser's blue or visited purple (Erik 2026-10-02, 0683) */
a{color:var(--acc);text-underline-offset:3px}
::selection{background:var(--acc);color:var(--acc-ink)}
/* a field the browser filled in keeps the site's look: Chrome paints it white with dark text (Erik, the
   sign-in door, 2026-09-27), a paint no background rule reaches; a transition that never starts holds it off */
:is(input,textarea,select):-webkit-autofill,:is(input,textarea,select):-webkit-autofill:is(:hover,:focus){
  -webkit-text-fill-color:var(--ink);caret-color:var(--acc);transition:background-color 0s 100000s,color 0s 100000s}
:focus-visible{outline:2px solid var(--acc);outline-offset:2px}

/* ---------- the FAQ (0605, 0635, 0650): .faqs > .faq > button + .fa-a, a list slot of question and
   answer pairs, the site's one FAQ: /join-ai-lab/, the RVTDocs MCP page, the pyRevit MCP guide. The
   question gray (a step under white), the answer darker and smaller, a gap between; shell.js keeps one
   open per list. Here at the top, as when pages.css carried it: every other rule of this file still
   wins a tie (.card p). ---------- */
.faqs{border:1px solid var(--hl1);border-radius:var(--rc);overflow:hidden}
.faq{border-bottom:1px solid var(--hl1)}
.faq:last-child{border-bottom:none}
.faq > button{width:100%;display:flex;align-items:center;justify-content:space-between;gap:16px;
  background:none;border:none;padding:18px 22px;cursor:pointer;text-align:left;
  font:600 .95rem/1.4 var(--fd);color:var(--dim2)}
.faq > button:hover{background:var(--s1);color:var(--ink)}
.fa-x{font:400 16px/1 var(--fm);color:var(--faint);flex:none;transition:transform .16s}
.faq.open .fa-x{transform:rotate(45deg);color:var(--acc)}
.fa-a{display:none;padding:0 22px 20px;border-radius:0}
.faq.open .fa-a{display:block}
.fa-a p{color:var(--dim);line-height:1.6;max-width:40rem}
.faq .fa-a{padding:6px 22px 22px}
.faq .fa-a p{font-size:.875rem;line-height:1.65}
img,svg{max-width:100%}
[x-cloak]{display:none !important}

/* ---------- desk (Erik 2026-08-27: Lighter color, stripe 16, visibility .1, drift 80s loop) ---------- */
.desk{position:fixed;inset:0;padding:var(--inset);
  background:repeating-linear-gradient(135deg,
    rgba(0,0,0,calc(var(--desk-o,.1) / 2)) 0, transparent .75px calc(var(--desk-s,16px) - .75px),
    rgba(0,0,0,var(--desk-o,.1)) calc(var(--desk-s,16px) + .75px) calc(var(--desk-s,16px) * 2 - .75px),
    rgba(0,0,0,calc(var(--desk-o,.1) / 2)) calc(var(--desk-s,16px) * 2))
    0 0 / calc(100% + var(--desk-s,16px) * 28.2843) 100% no-repeat var(--desk-a);
  animation:var(--desk-anim,deskdrift 80s linear infinite)}
/* seamless (Erik 2026-09-29): one period is exactly 2 stripes, the picture is wider than the desk by the drift so no tile edge ever shows,
   and the drift is 10 periods along the 135deg axis (2s * sqrt 2 * 10), so the loop jump lands on the same picture */
@keyframes deskdrift{from{background-position:calc(var(--desk-s,16px) * -28.2843) 0}to{background-position:0 0}}

/* ---------- window ---------- */
.win{height:100%;display:flex;flex-direction:column;overflow:hidden;
  background:var(--canvas);border-radius:var(--r);border:1px solid rgba(0,0,0,.55);
  box-shadow:0 40px 90px rgba(46,20,0,.55),0 14px 34px rgba(46,20,0,.45)}

/* ---------- titlebar ---------- */
.bar{flex:none;display:flex;align-items:center;gap:14px;height:50px;padding:0 14px;
  background:var(--s1);border-bottom:1px solid var(--hl1);min-width:0}
.dots{display:flex;gap:6px;flex:none}
.dots i{width:10px;height:10px;border-radius:50%;background:rgb(36,36,36);border:1px solid var(--hl2)}
.brand{flex:none;font:600 12px/1 var(--fm);letter-spacing:.14em;color:var(--ink);display:flex;align-items:baseline;gap:8px;text-decoration:none}
.bar-grow{flex:1}
/* the wordmark, lockup 65 (final 2026-09-03): bar-scale sizing keeps the 50px titlebar untouched */
.brand-logo{display:inline-flex;align-items:center;min-width:0}
.brand-logo .lg{display:inline-flex;align-items:baseline;white-space:nowrap;position:relative;line-height:1;
  font:400 15px/1 var(--fl);letter-spacing:0;color:var(--ink)}
.brand-logo .lg .o{color:var(--acc)}
.brand-logo .lg .f{color:var(--faint)}
.brand-logo .lg .d{color:var(--dim)}
.lg-pre{font-family:var(--fm);font-weight:500;margin-right:.1em}
.lg-com{font-family:var(--fm);font-weight:500;font-size:.84em}
.bar-acts{flex:none;display:flex;gap:6px;position:relative}
.bar-acts button{background:none;border:1px solid var(--hl1);border-radius:7px;padding:5px 7px;cursor:pointer;transition:border-color .16s}
.bar-acts button:hover{border-color:var(--hl3)}
kbd{font:500 10px/1 var(--fm);color:var(--faint);background:var(--s2);border:1px solid var(--hl2);border-radius:5px;padding:2px 5px;letter-spacing:.06em}

/* ---------- tabs bar ---------- */
.tabbar2{flex:none;display:flex;align-items:stretch;height:40px;padding:0 14px;
  background:var(--s2);border-bottom:1px solid var(--hl1)}
.tabs{flex:1;display:flex;align-items:stretch;gap:2px;height:100%;overflow-x:auto;scrollbar-width:none;min-width:0}
.tabs::-webkit-scrollbar{display:none}
.tn{font:600 9px/1 var(--fm);color:var(--faint);margin-right:7px;font-variant-numeric:tabular-nums}
.tabs a.act .tn{color:var(--acc)}
.tabs a{flex:none;display:inline-flex;align-items:center;height:100%;padding:0 11px;background:none;border:none;cursor:pointer;
  font:500 13.5px/1 var(--fd);color:var(--faint);position:relative;text-decoration:none}
/* a tab with no digit is one the public cannot reach: staff only, so it reads fainter than the numbered
   ones and Erik sees at a glance which addresses are still his alone. The digit IS the marker, so the
   two can never drift apart (0139). */
.tabs a:not(:has(.tn)){opacity:.55}
.tabs a:hover{color:var(--dim)}
.tabs a.act{color:var(--ink)}
.tabs a.act::before{content:"";position:absolute;left:10px;right:10px;bottom:0;height:2px;background:var(--acc)}
/* tab focus: the global outline gets clipped by the tabbar overflow (Erik bug 2026-08-27); an unclippable indicator instead */
.tabs a:focus-visible{outline:none;background:rgba(255,255,255,.07);color:var(--ink)}
/* the admin pulldown (0139): the tab still navigates, the caret beside it opens the rows. The menu is
   fixed and placed by shell.js, because the tabs bar scrolls and an absolute one would be clipped. Row
   height, gap, chip and ground are the prototype's (styles.css L1736 to L1751). */
.pullwrap{display:flex;align-items:stretch;flex:none}
.pullwrap:not([data-pull-menu]) > a{padding-right:4px}
.pt-car{flex:none;background:none;border:none;cursor:pointer;padding:0 11px 0 6px;color:var(--faint);font:500 9px/1 var(--fd)}
.pt-car:hover{color:var(--dim)}
.pullmenu{position:fixed;z-index:60;min-width:236px;background:var(--s1);border:1px solid var(--hl3);
  border-radius:var(--rb);box-shadow:0 18px 44px rgba(0,0,0,.55);padding:5px;display:flex;flex-direction:column;
  gap:2px;max-height:calc(100vh - 92px);overflow-y:auto}
.pullmenu[hidden]{display:none}
.tabs .pullmenu a{display:flex;align-items:center;gap:9px;width:100%;height:36px;padding:0 10px;border-radius:7px;
  box-sizing:border-box;font:500 12.5px/1 var(--fd);color:var(--dim);text-align:left;flex:none}
.tabs .pullmenu a::before{content:none}
.tabs .pullmenu a:hover{background:var(--s2);color:var(--ink)}
.tabs .pullmenu a:hover .tn{color:var(--acc)}
.tabs .pullmenu .badge{margin-left:auto}
.tabs .pullmenu a.memrow .badge{color:var(--acc);border-color:rgb(from var(--acc) r g b / .35)}
/* hide/show (` key + chevrons). Hiding is INSTANT: tab geometry never animates. */
.tb-toggle{flex:none;align-self:center;display:grid;place-items:center;width:24px;height:24px;
  background:none;border:1px solid transparent;border-radius:6px;cursor:pointer;color:var(--faint);padding:0;margin-left:8px}
.tb-toggle:hover{color:var(--dim);border-color:var(--hl2)}
.tb-toggle svg,.tb-reopen svg{width:14px;height:14px;fill:none;stroke:currentColor;stroke-width:2;stroke-linecap:round;stroke-linejoin:round}
body[data-tabsbar="hidden"] .tabbar2{display:none}
.tb-reopen{display:none;position:absolute;top:calc(var(--inset) + 50px);right:calc(var(--inset) + 14px);z-index:30;
  align-items:center;justify-content:center;width:28px;height:15px;padding:0;
  background:var(--s1);border:1px solid var(--hl1);border-top:none;border-radius:0 0 7px 7px;
  color:var(--faint);cursor:pointer}
.tb-reopen:hover{color:var(--dim);border-color:var(--hl3)}
body[data-tabsbar="hidden"] .tb-reopen{display:flex}

/* ---------- canvas: the responsive CONTAINER of the whole site (round Q). Every site breakpoint is a
   @container page query and every fluid clamp uses cqw, so the page responds to the width of the
   site WINDOW, not the browser. The chrome outside it (status bar, wordmark) keeps media queries. ---------- */
.canvas{flex:1;overflow-y:auto;overflow-x:hidden;position:relative;min-width:0;
  container-type:inline-size;container-name:page}
/* canvas pattern LOCKED = dots 24px at .08 + top grid 48px at .07 (Erik 2026-08-28); attachment:local covers the whole scroll */
.canvas{
  background-image:radial-gradient(rgba(255,255,255,var(--pat-dot-a,.08)) 1px,transparent 1.5px),
    radial-gradient(1100px 720px at 50% 0%,transparent 30%,var(--canvas) 78%),
    linear-gradient(rgba(255,255,255,var(--pat-grid-a,.07)) 1px,transparent 1px),
    linear-gradient(90deg,rgba(255,255,255,var(--pat-grid-a,.07)) 1px,transparent 1px);
  background-size:var(--pat-dot-s,24px) var(--pat-dot-s,24px),100% 100%,var(--pat-grid-s,48px) var(--pat-grid-s,48px),var(--pat-grid-s,48px) var(--pat-grid-s,48px);background-attachment:local}

/* ---------- the page frame: Framed-55 "55 ticks", solid outline + ember tint, LOCKED (Erik 2026-08-27).
   The frame wraps the page's top-level section only; a rail or a sidebar stays outside it. ---------- */
main > section{position:relative;border-radius:16px;margin:18px auto;max-width:1272px;width:calc(100% - clamp(28px,4cqw,56px));
  background-color:color-mix(in srgb,rgb(12,10,8) 40%,transparent);
  border:1px solid rgba(255,255,255,.14);
  box-shadow:0 18px 50px rgba(0,0,0,.4)}
/* the 4 orange corner brackets + edge ticks: one rounded border, masked. Top and bottom keep a single
   centred tick, the sides repeat every --f55-gap (brand.css). --f55-mask is declared HERE, not on
   :root: a nested var() is substituted at the element that declares it. */
main > section::before{content:"";position:absolute;inset:-1px;pointer-events:none;z-index:2;
  border:2px solid rgb(from var(--acc) r g b / .65);border-radius:inherit;
  --f55-mask:
    linear-gradient(rgb(0,0,0) 0 0) left  0px top    0px / 18px 18px no-repeat,
    linear-gradient(rgb(0,0,0) 0 0) right 0px top    0px / 18px 18px no-repeat,
    linear-gradient(rgb(0,0,0) 0 0) left  0px bottom 0px / 18px 18px no-repeat,
    linear-gradient(rgb(0,0,0) 0 0) right 0px bottom 0px / 18px 18px no-repeat,
    linear-gradient(rgb(0,0,0) 0 0) center top    / 14px 6px no-repeat,
    linear-gradient(rgb(0,0,0) 0 0) center bottom / 14px 6px no-repeat,
    var(--f55-side) left  center / var(--f55-side-sz) no-repeat,
    var(--f55-side) right center / var(--f55-side-sz) no-repeat;
  -webkit-mask:var(--f55-mask);
  mask:var(--f55-mask)}

/* ---------- sections and type ---------- */
.sec{position:relative;padding:var(--secpad) 0;border-top:1px solid var(--hl1)}
.sec:first-child,main > section > .sec:first-child{border-top:none}
main > section > .sec:first-of-type{padding-top:calc(var(--secpad) * .42)}
.sec[data-no]::before{content:attr(data-no);position:absolute;top:34px;right:clamp(16px,4cqw,56px);
  font:700 clamp(4rem,9cqw,8rem)/1 var(--fm);letter-spacing:-.04em;color:rgba(255,255,255,.035);pointer-events:none}
.sec-sub{font-size:1rem;color:var(--dim);margin:-22px 0 32px}
.sec-tight{padding:calc(var(--secpad) * .45) 0}
/* a gated page's staff switch, Gate | Inside (gate_switch.html, Erik 2026-09-30): top right of its holder, the view-as
   menu's look, the open side ink on the accent line */
.gate-sw{position:absolute;top:12px;right:16px;z-index:6;display:flex;border:1px solid var(--hl1);border-radius:6px;
  background:var(--canvas);font:500 10px/1 var(--fm);letter-spacing:.12em;text-transform:uppercase}
.gate-sw a{padding:6px 9px;color:var(--faint);text-decoration:none}
.gate-sw a:hover{color:var(--dim)}
.gate-sw a.on{color:var(--ink);text-decoration:underline 2px var(--acc);text-underline-offset:5px}
.gate-sw a:focus-visible{outline:1px solid var(--acc);outline-offset:-2px}
.wrap{max-width:1240px;margin:0 auto;padding:0 clamp(20px,4cqw,56px);min-width:0}
.wrap.narrow{max-width:780px}

p,li,figcaption,dd,blockquote{text-wrap:pretty}
h3,h4{text-wrap:balance}
h1{font:600 var(--h1)/1.08 var(--fd);letter-spacing:-.03em;text-wrap:balance}
h2{font:600 1.75rem/1.15 var(--fd);letter-spacing:-.02em;margin-bottom:34px;text-wrap:balance}
h3{font:600 1.125rem/1.3 var(--fd);letter-spacing:-.01em}
/* Nunito is a soft face: body sits at 500 and headings at 800, or the whole page reads lighter than the
   Geist original did. Must follow the rules above: the `font` shorthand in each of them sets a weight. */
body{font-weight:500}
:is(h1,h2,h3,.chead-h,.hub-hey,.lesson-h,.hc-tx h3){font-weight:800}
.sub{font-size:1.125rem;color:var(--dim);margin-top:18px;max-width:34rem}
.lead{font-size:1.05rem;line-height:1.65;color:var(--dim)}
p{min-width:0}
.hl{color:var(--acc)}
.eyebrow{display:block;font:500 11px/1 var(--fm);letter-spacing:.22em;color:var(--faint);margin-bottom:14px}
.hero{position:relative;padding-top:calc(var(--secpad) * .95)}
.hero .wrap{position:relative}
.hero-c{position:relative;max-width:var(--hero-w);margin:0 auto;text-align:center}
.hero-c .sub{margin-left:auto;margin-right:auto}

/* pill: badge LOCKED = Outline (Erik 2026-08-27); never orange-filled */
.pill{display:inline-flex;align-items:center;gap:8px;background:transparent;border:1px solid var(--hl3);border-radius:8px;
  color:var(--dim);font:500 11px/1 var(--fm);letter-spacing:.14em;padding:8px 12px;margin-bottom:22px}
.pill-acc{color:var(--acc);border-color:rgb(from var(--acc) r g b / .45)}
.pill-acc .pdot{background:var(--acc)}
.badge{display:inline-block;background:var(--s2);border:1px solid var(--hl2);border-radius:6px;color:var(--dim);
  font:500 10px/1 var(--fm);letter-spacing:.16em;padding:5px 8px}

/* pulser + category dots */
.pdot{width:6px;height:6px;border-radius:50%;background:var(--acc);position:relative;flex:none;display:inline-block}
.pdot::after{content:"";position:absolute;inset:-4px;border:1px solid var(--acc);border-radius:50%;animation:ping 2.2s ease-out infinite}
@keyframes ping{0%{transform:scale(.35);opacity:.9}70%,100%{transform:scale(1.3);opacity:0}}
.cdot{width:6px;height:6px;border-radius:50%;flex:none;display:inline-block}
.dot-video{background:var(--c-video)} .dot-docs{background:var(--c-docs)} .dot-blog{background:var(--c-blog)}
.dot-forum{background:var(--c-forum)} .dot-ai{background:var(--c-ai)}

/* ---------- buttons ---------- */
.btn{font:600 12px/1 var(--fm);letter-spacing:.12em;text-transform:uppercase;padding:14px 22px;
  background:transparent;border:1px solid var(--hl3);border-radius:var(--rb);color:var(--ink);cursor:pointer;text-decoration:none;
  transition:background .16s,border-color .16s,color .16s}
.btn:hover{background:var(--acc);border-color:var(--acc);color:var(--acc-ink)}
.btn:active{transform:translateY(1px)}
.btn:disabled{opacity:.45;cursor:default;pointer-events:none}
.btn-acc{background:var(--acc);border-color:var(--acc);color:var(--acc-ink)}
.btn-acc:hover{background:var(--acc2);border-color:var(--acc2)}
.btn-row{display:flex;gap:12px;flex-wrap:wrap;margin-top:30px}
.btn-row.center{justify-content:center}

/* ---------- cards: "Glass" (glassweave, Erik 2026-08-27), flat since 0440: the diagonals went with every other
   hatch on the site. The hover outline is the .ctrace SVG below at constant perimeter speed. ---------- */
.card{position:relative;border:1px solid rgba(255,255,255,.09);border-radius:var(--rc);padding:24px;min-width:0;
  background-color:rgba(255,255,255,.03);
  box-shadow:inset 0 1px 0 rgba(255,255,255,.07),inset 0 -14px 30px rgba(0,0,0,.35);
  transition:border-color .18s,background-color .18s,box-shadow .18s,transform .18s}
.card::before{content:"";position:absolute;top:-1px;left:0;right:0;height:1px;background:var(--acc);border-radius:var(--rc) var(--rc) 0 0;
  transform:scaleX(0);transform-origin:left;transition:transform .18s}
.card.callout{border-left:1px solid rgba(255,255,255,.09);background:rgba(255,255,255,.03)}
.clabel{display:flex;align-items:center;gap:8px;font:500 11px/1 var(--fm);letter-spacing:.18em;color:var(--faint);margin-bottom:14px}
.card p{font-size:.9rem;line-height:1.55;color:var(--dim)}
.card h3{margin-bottom:8px}
a.card{display:block;text-decoration:none;color:inherit}
.card:hover{background-color:var(--s2)}
/* tracing outline: an SVG rect with pathLength 100, so an animated dashoffset moves a constant ARC LENGTH
   per unit time at any aspect ratio (a conic gradient snapped round the corners, Erik 2026-08-28).
   ONE shared overlay, moved into the hovered card by shell.js. */
.ctrace{position:absolute;inset:0;width:100%;height:100%;overflow:visible;
  pointer-events:none;z-index:2;border-radius:inherit}
.ctrace rect{x:.8px;y:.8px;width:calc(100% - 1.6px);height:calc(100% - 1.6px);
  fill:none;stroke:rgb(from var(--acc) r g b / .8);stroke-width:1.6;stroke-linecap:round;
  stroke-dasharray:16 84;stroke-dashoffset:0;
  animation:cardtrace 4.6s linear infinite}
@keyframes cardtrace{to{stroke-dashoffset:-100}}

/* ---------- the (i) and the one tooltip (0440). `class="sh-info" data-tip="words"` is every help icon of the
   site; shell.js shows any [data-tip]'s words in .sh-tip at once, a manual popover in the top layer, so
   the box is narrow, grows in height and is never clipped by a scrolling table. Never a title= on it. ---------- */
.sh-info{display:inline-flex;align-items:center;justify-content:center;flex:none;width:16px;height:16px;margin-left:4px;
  vertical-align:middle;border:1px solid var(--hl2);border-radius:50%;font:600 10px/1 var(--fm);font-style:normal;
  letter-spacing:0;text-transform:none;color:var(--faint);cursor:help;transition:color .12s,border-color .12s}
.sh-info:is(:hover,:focus-visible){color:var(--ink);border-color:var(--hl3)}
.sh-tip{position:fixed;inset:auto;top:0;left:0;margin:0;width:max-content;max-width:min(260px,calc(100vw - 16px));
  padding:7px 10px;border:1px solid var(--hl3);border-radius:7px;background:var(--s3);color:var(--dim2);
  font:500 11.5px/1.45 var(--fd);letter-spacing:0;text-transform:none;text-align:left;white-space:normal;
  overflow-wrap:anywhere;box-shadow:0 10px 26px rgba(0,0,0,.55);pointer-events:none;overflow:visible}

/* ---------- the row sort (0540): a [data-sort-grip] is grabbed, the carried row fades while it moves ---------- */
[data-sort-grip]{cursor:grab}
.sh-sorting{opacity:.45}

/* ---------- the click-to-load player (0513): shell.js marks a pressed [data-play] poster data-state and
   loads the frame under it; the poster covers the frame and spins until YouTube plays. An app draws its
   own poster and glyph; the shell adds the cover and the spinner and nothing else. A picture slot's
   wrapper (.tt-img, staff in edit mode) is the picture, so it stays. ---------- */
:has(> [data-play][data-state]){position:relative}
[data-play][data-state]{position:absolute;inset:0;z-index:1;cursor:progress}
[data-play][data-state] > :not(img, .tt-img){visibility:hidden}
[data-play][data-state] img{filter:brightness(.55)}
[data-play][data-state]::after{content:"";position:absolute;inset:0;margin:auto;width:56px;height:56px;border-radius:50%;
  background:rgb(from var(--canvas) r g b / .7);border:4px solid rgb(from var(--ink) r g b / .25);border-top-color:var(--acc);
  animation:shspin .8s linear infinite}
@keyframes shspin{to{transform:rotate(1turn)}}

/* ---------- the sign-off (0432): app_shell/sign.html, one look on every page that signs off ---------- */
.sh-sign{margin:44px 0 0;font:600 26px/1.35 var(--fh);color:var(--acc);text-align:center;transform:rotate(-2deg)}
.sh-sign span{display:block;font-size:20px;color:var(--dim)}

/* ---------- caret, reveal, dimension lines ---------- */
.sh-cur{display:inline-block;width:7px;height:12px;background:var(--acc);margin-left:4px;animation:blink 1.1s steps(2,start) infinite}
@keyframes blink{to{visibility:hidden}}
.rv{opacity:0;transform:translateY(20px);transition:opacity .5s ease,transform .5s ease}
.rv.in{opacity:1;transform:none}
.sh-dim{display:flex;align-items:center;min-width:0;color:var(--dim)}
.sh-dim i{flex:1;height:1px;background:var(--hl3);position:relative;min-width:14px}
.sh-dim i::after{content:"";position:absolute;top:-5px;width:1px;height:11px;background:var(--hl3);transform:rotate(45deg)}
.sh-dim i:first-child::before,.sh-dim i:last-child::before{content:"";position:absolute;top:-4px;width:1px;height:9px;background:var(--hl3)}
.sh-dim i:first-child::before{left:0}.sh-dim i:first-child::after{left:0}
.sh-dim i:last-child::before{right:0}.sh-dim i:last-child::after{right:0}
.sh-dim b{flex:none;font:500 9.5px/1 var(--fm);letter-spacing:.18em;color:var(--faint);padding:0 12px;white-space:nowrap}
.rv .sh-dim i,.sh-dim.rv i{transform:scaleX(0);transition:transform .8s ease .25s}
.rv .sh-dim i:first-child{transform-origin:right}.rv .sh-dim i:last-child{transform-origin:left}
.rv.in .sh-dim i,.sh-dim.rv.in i{transform:scaleX(1)}

/* ---------- capture forms (form[data-capture]): the fields box, the sent state, the beam ---------- */
/* centred: the form in its section, the fields, the button and the words in the form (Erik, 2026-09-24).
   A page that sets a form in a left column (the home hero) says so in its own sheet. The radius is
   what the beam reads when a bare form is its own card. The full width keeps a form in a flex column as
   wide as before (auto margins there shrink a box to its content). A field takes its form's alignment,
   so a form that reads from the left (the podcast pitch) says it once. */
.cap{width:100%;max-width:30rem;min-width:0;margin-left:auto;margin-right:auto;text-align:center;border-radius:var(--rb)}
.cap :is(input,textarea){text-align:inherit}
/* the honeypot every capture fragment draws (0375): off screen for a person, filled by a bot */
.cap-hp{position:absolute;left:-9999px;width:1px;height:1px;overflow:hidden}
.cap-fields{position:relative;display:flex;gap:0;border:1px solid var(--hl3);border-radius:var(--rb);overflow:hidden}
.cap-fields input{flex:1;min-width:0;background:var(--s1);border:none;color:var(--ink);
  font:400 14px/1 var(--fd);padding:14px 16px;outline:none}
.cap-fields input::placeholder{color:var(--faint)}
.cap-fields .btn{border:none;border-left:1px solid var(--hl3);border-radius:0;flex:none}
/* a second row above the inline one: the name field, when a form asks for a name. Its own row, never
   beside the address and the button, which packed three things into one line (Erik, 0255) */
.cap-fields+.cap-fields{margin-top:10px}
/* stacked: the field row becomes a column and the button takes its own line. The same three rules
   a narrow window has always used, so a form Erik flips in the Studio needs no second fragment
   and no page stylesheet of its own (0269). The button keeps the auto width it has when stacked. */
.cap-stack .cap-fields{flex-direction:column;border:none;gap:10px}
.cap-stack .cap-fields input{border:1px solid var(--hl3);border-radius:var(--rb)}
/* the focus (birthdayscan FT Q9): the field's own line one step lighter than --hl3; never orange, never
   thicker. The box draws it where it has a line, the input where the fields stack */
.cap-fields:focus-within,.cap-fields input:focus{border-color:rgba(255,255,255,.28)}
.cap-stack .cap-fields .btn{border:1px solid var(--acc);border-radius:var(--rb);align-self:center}
.cap-note{font:400 11px/1 var(--fm);color:var(--dim);margin-top:16px;letter-spacing:.06em}
.cap .cap-note{line-height:1.5}
/* every form's box is a .card (0619), and `.card p` would draw the note at body size */
.card .cap-note{font-size:11px;line-height:1.5}
.cap-ok{display:none;align-items:center;justify-content:center;gap:10px;border:1px solid var(--hl3);border-radius:var(--rb);background:var(--s1);
  padding:16px;font:500 13px/1.4 var(--fd);color:var(--ink)}
.cap.ok .cap-fields{display:none}
.cap.ok .cap-ok{display:flex}
.cap.err .cap-fields{border-color:var(--red)}
/* in flight (shell.js data-cap-busy, set on the press): the button spins at once, a pitch's mails can
   hold the answer a second or two and a still button read as a dead one (Erik, 2026-09-26) */
form[data-cap-busy] [type=submit]{position:relative;color:transparent;cursor:progress;pointer-events:none}
form[data-cap-busy] [type=submit]::after{content:"";position:absolute;inset:0;margin:auto;width:14px;height:14px;border-radius:50%;
  border:2px solid rgb(from var(--acc-ink) r g b / .3);border-top-color:var(--acc-ink);animation:shspin .7s linear infinite}
/* the labelled field parts of the forms that ask more than an address (prototype styles.css L923-931):
   the Academy waitlist and the podcast pitch. Here beside .cap, the one sheet every fragment can read,
   instead of a copy in each page's stylesheet (0369) */
.kf-label{display:block;font:500 10px/1 var(--fm);letter-spacing:.16em;color:var(--faint);text-transform:uppercase;margin:0 0 8px}
.kf-label span{text-transform:none;letter-spacing:.04em}
.kf-input{display:block;width:100%;box-sizing:border-box;background:var(--canvas);border:1px solid var(--hl2);border-radius:var(--rb);
  color:var(--ink);font:400 14px/1 var(--fd);padding:13px 14px;outline:none;margin-bottom:16px}
.kf-input::placeholder{color:var(--faint)}
.kf-input:focus{border-color:var(--hl3)}
/* auto width, never a full width bar: a button is as wide as its words (Erik, 0255) */
/* centred under the fields, with the note that is already centred under it (Erik, 0298) */
.kf-btn{width:auto;align-self:center;margin-top:4px}
/* the beam (Erik 2026-08-28), ONE per form and on its card, never on a field (Erik 2026-09-24): shell.js
   puts it in the card the form sits in, or on the form itself when it stands bare, and marks that box
   [data-cap-host]; the rect's rx is the box's own radius. The cards' constant-speed trace; two rects
   share one dash cycle, a wide faint body and a short bright head, so it reads as a comet. It goes
   with the fields once the form is sent. */
.captrace{position:absolute;inset:0;width:100%;height:100%;overflow:visible;pointer-events:none;z-index:2}
.captrace rect{x:1px;y:1px;width:calc(100% - 2px);height:calc(100% - 2px);
  fill:none;stroke-linecap:round;stroke-dashoffset:0;animation:cardtrace 4.6s linear infinite}
.captrace .ct-glow{stroke:rgba(255,176,86,.45);stroke-width:2.6;stroke-dasharray:26 74}
.captrace .ct-head{stroke:rgba(255,236,214,.95);stroke-width:2;stroke-dasharray:9 91}
[data-cap-host]:is(.cap.err,:has(.cap.err)) > .captrace .ct-glow{stroke:rgba(255,120,120,.4)}
[data-cap-host]:is(.cap.err,:has(.cap.err)) > .captrace .ct-head{stroke:rgba(255,190,190,.9)}
[data-cap-host]:is(.cap.ok,:has(.cap.ok)) > .captrace{display:none}

/* ---------- status bar ---------- */
.status{flex:none;display:flex;align-items:center;gap:18px;height:34px;padding:0 16px;
  background:var(--s1);border-top:1px solid var(--hl1);font:500 11px/1 var(--fm);color:var(--faint);letter-spacing:.08em}
.st-role{display:inline-flex;align-items:center;gap:8px;color:var(--acc2);letter-spacing:.12em}
/* the page's name takes the free space, so the right group starts where its first fill does: a fill
   before the hints (the CMS's EDIT) sits at that group's left end (Erik, 2026-09-27) */
.st-tab{margin-right:auto;color:var(--ink);min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.st-hints{order:1;display:flex;gap:12px;align-items:center;white-space:nowrap}
/* a hint is a button that lights up as Contact does, the key and its word together (0363) */
.st-hint{display:inline-flex;align-items:center;gap:6px;padding:0;background:none;border:none;cursor:pointer;
  font:inherit;letter-spacing:inherit;color:var(--faint)}
.st-hint:hover,.st-hint:focus-visible{color:var(--ink)}
.st-hint:hover kbd,.st-hint:focus-visible kbd{color:var(--ink);border-color:var(--hl3)}
/* the only footer this site has, so Terms, Privacy and the Impressum hang off it: a very dark ... at the
   right edge opens them as a small menu up from the bar (0733); shell.js sets its right and bottom */
.st-legal{order:1;display:flex;align-items:center;white-space:nowrap;margin-left:-10px}
/* bigger, narrow and close to Contact, so the three read as one mark and take less room (Erik 2026-10-05) */
.st-more{padding:0;background:none;border:none;cursor:pointer;font:inherit;font-size:1.4em;line-height:1;
  letter-spacing:-.22em;color:var(--faint)}
.st-more:hover,.st-more:focus-visible,.st-more:has(+ .st-menu:popover-open){color:var(--ink)}
.st-menu{position:fixed;inset:auto;margin:0;padding:4px;flex-direction:column;min-width:120px;
  background:var(--s3);border:1px solid var(--hl2);border-radius:7px;box-shadow:0 10px 26px rgba(0,0,0,.55);color:var(--faint)}
.st-menu:popover-open{display:flex}
.st-menu a{padding:8px 10px;border-radius:5px;color:var(--faint);text-decoration:none}
.st-menu a:hover,.st-menu a:focus-visible{color:var(--ink);background:var(--s2);outline:none}
/* the status bar and the wordmark sit in .win, OUTSIDE the container, so they keep media queries */
@media (max-width:820px){ .st-hints{display:none} }
@media (max-width:640px){ .brand,.dots{display:none} }
/* a finger needs 40px (0382): the titlebar's buttons and links, the status bar's links. Touch screens
   under 1100px only, so a desk with a mouse keeps every pixel it had */
@media (max-width:1100px) and (pointer:coarse){
  .bar-acts > :is(button,a){min-height:40px;min-width:40px;justify-content:center}
  .tabbar2{height:44px}
  .status{height:42px}
  .st-legal{align-self:stretch}
  .st-more{display:inline-flex;align-items:center;justify-content:center;height:100%;min-width:40px}
  .sb-hd{padding:15px 0}
  .sb-fold{width:40px;height:40px}
}

/* ---------- palette, help, scrim (engine.css structure + the styles.css skin) ---------- */
.fbl-scrim{position:fixed;inset:var(--inset);z-index:80;background:rgba(0,0,0,.6);border-radius:var(--r);opacity:0;pointer-events:none;transition:opacity .18s ease}
.fbl-scrim.on{opacity:1;pointer-events:auto}
.fbl-pal{position:fixed;left:50%;top:14vh;transform:translateX(-50%) translateY(8px);z-index:90;width:min(560px,92vw);
  display:none;opacity:0;transition:opacity .16s ease,transform .16s ease;
  background:var(--s1);border:1px solid var(--hl3);border-radius:var(--rc);overflow:hidden;box-shadow:0 30px 70px rgba(0,0,0,.6)}
.fbl-pal.on{display:block;opacity:1;transform:translateX(-50%) translateY(0)}
.fbl-pal input{width:100%;box-sizing:border-box;background:var(--canvas);border:none;border-bottom:1px solid var(--hl2);color:var(--ink);
  padding:15px 16px;font:400 14px/1 var(--fd);outline:none}
.fbl-pal input::placeholder{color:var(--faint)}
.fbl-pal-list{max-height:46vh;overflow:auto;margin:0;padding:0;list-style:none}
.fbl-pal-list li{display:flex;align-items:center;gap:10px;cursor:pointer;padding:11px 14px;font:400 13px/1 var(--fd);color:var(--dim)}
.fbl-pal-list .pal-row{display:contents}
.fbl-pal-list li .grow{flex:1}
.fbl-pal-list kbd{flex:none}
/* the chord column: as wide as the widest short chord, so the words line up and a row with none keeps its place */
.fbl-pal-list .pal-key{flex:none;min-width:52px}
.fbl-pal-list li.sel{background:var(--s3);color:var(--ink)}
.fbl-pal-list li:hover{background:var(--s2)}
/* the site's own confirm (0369): a native <dialog>, so the browser draws the backdrop and makes the page
   behind inert; the look is the palette's card. margin:auto puts it back in the middle, which the
   frame's reset takes from every element. Cancel is the outline, the confirm the light gray fill */
.fbl-confirm{width:min(420px,92vw);margin:auto;padding:22px 22px 18px;background:var(--s1);border:1px solid var(--hl3);
  border-radius:var(--rc);color:var(--ink);box-shadow:0 30px 70px rgb(0 0 0 / .6)}
.fbl-confirm::backdrop{background:rgb(0 0 0 / .6)}
.fbl-cf-q{margin:0 0 20px;font:500 14px/1.55 var(--fd);color:var(--ink)}
.fbl-cf-acts{display:flex;justify-content:flex-end;gap:8px}
.fbl-cf-acts button{height:32px;padding:0 14px;border:1px solid var(--hl2);border-radius:8px;background:var(--s1);
  color:var(--dim);font:600 12px/1 var(--fd);cursor:pointer}
.fbl-cf-acts button:hover{color:var(--ink);border-color:var(--hl3);background:var(--s2)}
.fbl-cf-acts .fbl-cf-ok{background:var(--dim2);border-color:var(--dim2);color:var(--canvas)}
.fbl-cf-acts .fbl-cf-ok:hover{background:var(--ink);border-color:var(--ink);color:var(--canvas)}
/* microcaps section labels (Go to / UI / Contact) */
.fbl-pal-list li.fbl-pal-sec{padding:11px 14px 5px;font:600 9px/1 var(--fm);letter-spacing:.24em;
  text-transform:uppercase;color:var(--faint);cursor:default;pointer-events:none}
/* the cards are shared: the ? sheet and Settings > Shortcuts wear the same .fbl-cards (0342) */
/* as wide as its cards: one card is the old 640, two and three sit side by side, wrapping on a narrow window (0339) */
.fbl-help{position:fixed;left:50%;top:50%;transform:translate(-50%,-50%);z-index:90;width:min(640px,92vw);max-height:80vh;overflow:auto;display:none;
  background:var(--s1);border:1px solid var(--hl3);border-radius:var(--rc);padding:26px;box-shadow:0 30px 70px rgba(0,0,0,.6)}
.fbl-help.on{display:block}
.fbl-card table{width:100%;border-collapse:collapse}
.fbl-help h3{font:600 12px/1 var(--fm);letter-spacing:.2em;text-transform:uppercase;color:var(--faint);margin-bottom:18px}
.fbl-card td{padding:7px 14px 7px 0;font-size:13px;color:var(--dim)}
.fbl-help:has(.fbl-card + .fbl-card){width:min(900px,94vw)}
.fbl-help:has(.fbl-card + .fbl-card + .fbl-card){width:min(1180px,94vw)}
.fbl-card kbd{white-space:nowrap}
/* the three cards in one row; a card drops to the next line only when the box cannot hold three of
   240px, which on the Studio's 904px column and the sheet's 1180px is a zoom or a small window (0397) */
.fbl-cards{display:grid;grid-template-columns:repeat(auto-fit,minmax(min(240px,100%),1fr));gap:18px;align-items:start}
.fbl-card{min-width:0;padding:16px 18px;background:var(--canvas);border:1px solid var(--hl2);border-radius:var(--rb)}
/* a row that opens its page (Settings > Shortcuts) reads in the text's colour, never the browser's blue
   or visited purple, and dims under the pointer (0397) */
.fbl-card a{color:var(--ink);text-decoration:none}
.fbl-card a:hover{color:var(--dim)}
.fbl-card h4{font:600 10px/1 var(--fm);letter-spacing:.2em;text-transform:uppercase;color:var(--dim2);margin:0 0 10px}

/* ---------- sidebar: a second window on the desk, signed-in users only (body.side), folds to a 46px
   rail (body.side-folded), both stamped pre-paint. Same .win chrome as the site window. ---------- */
body.side .desk{display:flex;gap:calc(var(--inset) * .55)}
/* the width is Erik's, dragged from the right edge and kept in localStorage, stamped on :root
   pre-paint so a wide sidebar never snaps back from 276 on load. A deep tree runs out of room long
   before the rows do, and the name is the whole row (Erik 2026-09-17) */
.desk > .side{flex:none;width:var(--side-w,276px);min-width:0;position:relative}
/* the twin of a desk window's .dw-rez, on the other edge because the sidebar is the left column */
.sb-rez{position:absolute;top:0;right:0;bottom:0;width:6px;cursor:col-resize;z-index:2;touch-action:none}
/* a 46px rail has nothing to widen, and below 1100px the sidebar is a drawer at a width of its own */
body.side-folded .sb-rez{display:none}
/* every window on the desk grows into what is left, except the sidebar and anything that keeps a width
   of its own (data-fixed, the notes window). Without that opt-out this rule is four classes against the
   one an app can write, so the app's own width never applied and its drag handle moved nothing (0149). */
body.side .desk > .win:not(.side):not([data-fixed]){flex:1;min-width:0;position:relative}
.sb-head{flex:none;display:flex;align-items:center;gap:6px;height:50px;padding:0 8px 0 10px;background:var(--s1);border-bottom:1px solid var(--hl1)}
/* ---------- a desk window right of the site window ({% deskwin %}, deskwin.js, 0576): its width is its
   own (--dw-w: the inline start width, then the drag), dragged from its left edge. Any number open at
   once at the width each was given, none shut or squeezed to make room: the site window takes what is
   left down to 480px, and a window that does not fit floats (deskwin.js fit, Erik 2026-09-29). A desk
   holding a window is a row, a guest's too (no sidebar). The grow rule weighs no more than the staff one above (:where), so
   a window off the base that sets its own width keeps it. ---------- */
.win[data-win]{flex:none;width:var(--dw-w);min-width:0;position:relative}
@media (min-width:1101px){
  .desk:has(> .win[data-win]){display:flex;gap:calc(var(--inset) * .55)}
  .desk:where(:has(> .win[data-win])) > .win:not(.side):not([data-fixed]){flex:1;min-width:0;position:relative}
}
/* a window that does not fit beside the site window, and every window under 1100px (deskwin.js fit),
   floats over the canvas at the right, the support window's shape since 0447: under the palette and the
   help sheet, over the sidebar drawer, nothing to widen. The last opened lies on top (--dw-z). A phone
   gives it the whole width. */
.win[data-win][data-dw-float]{position:fixed;z-index:calc(86 + var(--dw-z,0));inset:calc(var(--inset) + 50px) var(--inset) var(--inset) auto;
  width:min(var(--dw-w),calc(100vw - 2 * var(--inset)));min-width:0;height:auto;border:1px solid var(--hl3)}
[data-dw-float] > .dw-rez{display:none}
@media (max-width:640px){.win[data-win][data-dw-float]{top:var(--inset);width:calc(100vw - 2 * var(--inset))}}
.dw-rez{position:absolute;top:0;left:0;bottom:0;width:6px;cursor:col-resize;z-index:2;touch-action:none}
.dw-rez:hover,.dw-rez[data-rez-held]{background:var(--hl2)}
/* the head: the same 50px chrome as the site window's titlebar; the title gray, never white (Erik, 0633:
   Page settings read --dim2 until it moved onto this base, which took the notes window's --ink) */
.dw-head{flex:none;display:flex;align-items:center;gap:8px;height:50px;padding:0 10px 0 16px;
  background:var(--s1);border-bottom:1px solid var(--hl1)}
.dw-title{flex:none;white-space:nowrap;font:600 12px/1 var(--fm);letter-spacing:.14em;color:var(--dim2)}
/* the chord beside the title, so the window says how it is opened without the help sheet (0207) */
.dw-key{font:600 9.5px/1 var(--fm);letter-spacing:.06em;padding:3px 5px;border:1px solid var(--hl2);
  border-radius:4px;color:var(--acc)}
/* the x keeps the right end with or without tools before it (Erik, 2026-09-29: title left, x right) */
.dw-x{flex:none;margin-left:auto;width:26px;height:26px;display:grid;place-items:center;border:1px solid transparent;
  border-radius:var(--rb);background:none;color:var(--faint);cursor:pointer;font-size:16px;padding:0}
.dw-x:hover{color:var(--acc);border-color:var(--hl2)}
.sb-title{flex:1;font:600 10px/1 var(--fm);letter-spacing:.2em;text-transform:uppercase;color:var(--dim);padding-left:4px}
/* inside a training the heading IS the way back (0080): a link or button ON the heading element, so it keeps
   .sb-title's type (font: inherit here took the parent's Nunito, Erik's 'weird font', 0100) */
.sb-hd{display:block;width:100%;padding:0;background:none;border:0;text-align:left;color:var(--dim);cursor:pointer;text-decoration:none}
.sb-hd:hover{color:var(--ink)}
.sb-fold{flex:none;width:26px;height:26px;display:grid;place-items:center;background:none;border:1px solid transparent;border-radius:6px;color:var(--faint);cursor:pointer;padding:0}
.sb-fold:hover{color:var(--dim);border-color:var(--hl2)}
.sb-fold svg{width:16px;height:16px;fill:none;stroke:currentColor;stroke-width:1.6;stroke-linecap:round;stroke-linejoin:round}
.sb-body{flex:1;overflow:auto;padding:6px 8px 16px}
/* one group label of the sidebar, every app's (0432): Trainings over the cards, then Resources and Guides from
   their rail.foot fills. The sidebar is the shell's, so the label is too, and a feature app never wears another's */
.sb-grp{padding:18px 10px 6px;font:600 9px/1 var(--fm);letter-spacing:.24em;text-transform:uppercase;color:var(--faint)}
/* folded, the head shrinks to its arrow: the same 50px strip in the head's colour, so the button Erik
   clicked to fold stays where his pointer is (0344) */
.sb-rail{display:none;flex-direction:column;align-items:center;gap:4px;padding:0 0 10px}
.sb-rail .sb-fold{flex:none;width:100%;height:50px;margin-bottom:8px;border:0;border-bottom:1px solid var(--hl1);border-radius:0;background:var(--s1)}
.sb-rail .sb-fold{display:flex;justify-content:center;gap:2px}
.sb-rail .sb-fold svg{width:12px;height:12px}
.sb-rail .sb-fold:hover kbd{color:var(--ink);border-color:var(--hl3)}
body.side-folded .desk > .side{width:46px}
body.side-folded .desk > .side :is(.sb-head,.sb-body){display:none}
body.side-folded .sb-rail{display:flex}
/* the titlebar button and the backdrop of the drawer below: both inert at every other width, so a
   wide desk keeps exactly the titlebar and the desk it had (0237) */
.bar-side,.side-scrim{display:none}
.bar-side svg{width:16px;height:16px;fill:none;stroke:currentColor;stroke-width:1.6;stroke-linecap:round;stroke-linejoin:round}
@media (max-width:1100px){
  body.side .desk{display:block}
  .desk > .side{display:none}
  .bar-side{display:inline-flex;align-items:center}
  /* the sidebar as a drawer over the canvas, from under the titlebar to the foot of the desk: it
     keeps its own .win chrome, only the box moves. Under the palette's z-index, over everything else. */
  body.side-open .desk > .side{display:flex;position:fixed;z-index:85;
    left:var(--inset);top:calc(var(--inset) + 50px);width:min(276px,82vw);
    height:calc(100% - var(--inset) * 2 - 50px)}
  body.side-open .side-scrim{display:block;position:fixed;inset:var(--inset);z-index:84;
    background:rgba(0,0,0,.6);border-radius:var(--r)}
  /* a fold chosen on a wide screen is kept in localStorage and would open an empty drawer here: the
     drawer is always the full rail, whatever body.side-folded says */
  body.side-open .desk > .side .sb-head{display:flex}
  body.side-open .desk > .side .sb-body{display:block}
  body.side-open .desk > .side .sb-rail{display:none}
}
/* a phone (0382): the drawer covers the screen under the titlebar, whose button closes it again */
@media (max-width:640px){
  body.side-open .desk > .side{width:calc(100% - var(--inset) * 2)}
}
/* progress is boxes, one per lesson (0080): done green, the next one orange, the rest gray, left to
   right; the row fills the width it is given, so only the boxes get thinner. gap 1px, not 2. */
.pbox{display:flex;gap:1px}
.pbox i{flex:1 1 0;min-width:0;height:4px;border-radius:1px;background:var(--sq-gray)}
.pbox i.done{background:var(--sq-green)}
.pbox i.now{background:var(--sq-orange)}

/* ---------- container breakpoints of the shell's own rules ---------- */
@container page (max-width:1100px){
  .canvas > *{--h1:2.75rem}
}
@container page (max-width:640px){
  /* Compact is already 68px, so only the h1 needs shrinking here (Erik 2026-08-28) */
  .canvas > *{--secpad:56px;--h1:2.125rem}
  h2{font-size:1.5rem}
  .sec[data-no]::before{font-size:3.5rem}
  .cap-fields{flex-direction:column;border:none;gap:10px}
  .cap-fields input{border:1px solid var(--hl3)}
  .cap-fields .btn{border:1px solid var(--acc)}
}

/* ---------- scrollbars (Erik 2026-09-03): dark and rounded everywhere. Chromium ignores these pseudo
   elements on any element that also sets scrollbar-width / scrollbar-color. ---------- */
::-webkit-scrollbar{width:10px;height:10px}
::-webkit-scrollbar-track{background:rgb(28 28 28 / 10%);border-radius:10px}
::-webkit-scrollbar-thumb{background:rgb(255 255 255 / 10%);border-radius:10px}
::-webkit-scrollbar-thumb:hover{background:rgb(255 255 255 / 15%)}
::-webkit-scrollbar-corner{background:transparent}
@supports not selector(::-webkit-scrollbar){ *{scrollbar-color:rgb(255 255 255 / 12%) transparent} }

/* ---------- the 404 page (Erik 2026-09-10, redrawn 2026-09-16) ----------
   One SVG holds the crane, the cable, the hook and the three digits, so the rig cannot drift as the
   page resizes: the lift group drops 12 units while the cable scales by the same 12 over its 76, on
   one clock, so the hook never leaves the zero. The stroke does not scale with the drawing, or the
   lines would go hairline on a phone and heavy on a wide desk. The card is small, so main centres it
   in the canvas instead of pinning it to the top (Erik: it should not be top aligned).
   The card is an architectural SECTION since the 404 round (Erik 2026-09-16, option 03 with its
   surface rewritten): the drawing at 62% standing on a thick grade line, the earth hatched below it,
   the copy on the sheet under the earth, the four ways out as one row across the foot. No brick: the
   bond Erik saw in the round is gone and the ground line took its place. */
main:has(> .nf-page){min-height:100%;display:flex;align-items:center;justify-content:center}
.nf-page{max-width:700px}
/* --nf-pad is the card's own gutter, and the two things that bleed to the card's edges, the grade and
   the button row, pull back by exactly it. --nf-earth is how deep the hatched band is drawn.
   padding-top repeats what `main > section > .sec:first-of-type` already gives this card, so the two
   cannot disagree; the foot is 0 because the button row IS the card's bottom edge. */
.nf{--nf-pad:26px;--nf-earth:48px;
  text-align:center;padding-block:calc(var(--secpad) * .42) 0;
  overflow:hidden;border-radius:inherit}
.nf .wrap{padding-inline:var(--nf-pad)}
.nf .sub{margin-inline:auto}
/* the drawing's last 30 of its 280 viewBox units are blank under the footing. At 62% width that band
   is 30/280 * 62% * 280/600 = 3.1% of the wrap, and a vertical margin resolves against that width, so
   pulling back 3.1% lands the grade line on the footing at every size instead of under a strip of air. */
.nf-art{display:block;width:62%;height:auto;margin:0 auto -3.1%;
  fill:none;stroke:var(--faint);stroke-width:1.2;stroke-linecap:round;stroke-linejoin:round}
.nf-art :is(path,circle){vector-effect:non-scaling-stroke}
.nf-art circle{fill:none}
.nf-art .nf-acc{stroke:var(--acc)}
.nf-art .nf-bcn{fill:var(--acc);stroke:none;animation:nfBcn 1.6s steps(1) infinite}
.nf-num{fill:var(--ink);stroke:none;font:800 150px/1 var(--fd)}
.nf-cable{fill:var(--faint);stroke:none;transform-box:fill-box;transform-origin:top;
  animation:nfCable 3.4s ease-in-out infinite}
.nf-lift{animation:nfLift 3.4s ease-in-out infinite}
@keyframes nfLift{0%,100%{transform:translateY(0)}50%{transform:translateY(12px)}}
@keyframes nfCable{0%,100%{transform:scaleY(1)}50%{transform:scaleY(1.162)}}
@keyframes nfBcn{0%,60%{opacity:1}60.1%,100%{opacity:.15}}
/* GRADE and the earth under it. The grade is the heaviest line on the sheet, 3px against the drawing's
   1.2, grey and never accent, so the only orange left on this page is the frame and the hook. Below it
   the earth as a section draws it: hatched at 45 degrees, the hatch line the same 1px pen as the crane
   so one hand drew both, 6px apart, and 48px deep, which is the number the desk already draws its grid on.
   It rides on the h1 because the headline is what follows the drawing and nothing stands between them:
   .nf's two pseudos cannot be placed into the flow, and .nf-page's are the Framed-55 frame (line 123).
   The h1 keeps its own type: --h1 like every other page's headline. */
.nf h1{margin-inline:calc(var(--nf-pad) * -1);padding:calc(var(--nf-earth) + 16px) var(--nf-pad) 0;
  border-top:3px solid var(--hl3);position:relative;
  background:repeating-linear-gradient(45deg,var(--hl2) 0 1px,transparent 1px 6px) 0 0 / 100% calc(var(--nf-earth) / 3) no-repeat}
/* Erik 2026-09-18: the hatch is the top third of the earth, and under it the basket weave he drew:
   three lines across, three down, a gap between each patch. A mask, so the colour is a token and the
   SVG carries no colour of its own. */
.nf h1::before{content:"";position:absolute;inset:calc(var(--nf-earth) / 3) 0 auto;height:calc(var(--nf-earth) * 2 / 3);
  background:var(--hl2);
  --nf-weave:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='32' height='32' fill='none' stroke='black' stroke-width='1'%3E%3Cpath d='M2 4.5h12M2 8h12M2 11.5h12M20.5 2v12M24 2v12M27.5 2v12M4.5 18v12M8 18v12M11.5 18v12M18 20.5h12M18 24h12M18 27.5h12'/%3E%3C/svg%3E");
  -webkit-mask:var(--nf-weave) 0 0 / 32px 32px repeat;mask:var(--nf-weave) 0 0 / 32px 32px repeat}
/* the headline sits high, the question drops to the buttons it asks about (Erik 2026-09-18) */
.nf .sub{margin-top:56px}
/* the four ways out: one row across the card's foot, edge to edge, 1px of the card showing between
   them. auto-fit drops the row to two by two and then to one as the canvas narrows, so the longest
   word never overflows the card; a button that wraps keeps the row's height, its word centred.
   Erik 2026-09-16: every one of them reads the same grey and turns orange on hover, the WORD and never
   the box. `.nf .btn` out-specifies `.btn-acc`, so the accent class carries no weight on this page. */
.nf .btn-row{display:grid;grid-template-columns:repeat(auto-fit,minmax(130px,1fr));gap:1px;
  max-width:none;margin:14px calc(var(--nf-pad) * -1) 0}
.nf .btn{display:grid;place-content:center;border:0;border-radius:0;
  background:var(--s2);color:var(--dim);padding:22px 6px;font-size:10px;line-height:1.4;letter-spacing:.08em}
.nf .btn:hover{background:var(--s2);color:var(--acc)}

/* ---------- motion: LOCKED to full; prefers-reduced-motion kills every loop of the shell ---------- */
@media (prefers-reduced-motion:reduce){
  .desk{animation:none !important}
  .rv{opacity:1;transform:none;transition:none}
  .rv .sh-dim i,.sh-dim.rv i{transform:scaleX(1);transition:none}
  .pdot::after,.sh-cur{animation:none !important}
  .card:hover{animation:none}
  .ctrace rect,.captrace rect{animation:none}
  .fbl-pal,.fbl-scrim{transition:none}
  .nf-lift,.nf-cable{animation:none}
  .nf-art .nf-bcn{animation:none}
  [data-play][data-state]::after{animation:none}
}
