<!-- APEX DOOR · designation=studioxfonts.com · series=260817-934b49c2 · incept=2026-08-22T08:51:06Z · config=studioxfonts.json -->
<!-- ═══════════════════════════════════════════════════════════════════════════
     RAIL-WORKS APEX DOOR — THE TEMPLATE
     PROMOTED 2026-08-06 from the LIVE rail-fax.com, which had overtaken it.

     WHY THIS PROMOTION HAPPENED, so it does not happen again:
     The previous revision was dated 2026-08-04. Work shipped after that date
     evolved the system on rail-fax.com and nothing wrote it back here. A seat
     then built demo-rail-kds against this file, believing it was current, and
     produced a surface that did not belong to the family. KAI caught it:
     "This prototype is stale, it's not to the current template spec."

     The gap was measurable, not a matter of taste:
         rail-fax.com   101 classes
         this template   48 classes   (53 missing)
     Missing outright: the whole token layer (--accent, --boh, --ok, --off,
     --rail-min, --safe-t/-b/-l, --strip, --strip2), the footer drawer
     (drawer/dhandle/dbody/car/grip), the settings panel (sset/gear), the
     FOH/BOH chips, board/row/bohgrid/hero/lede, the keyed tab variants
     (vkey/tkey), and @media (prefers-reduced-motion:reduce).
     The breakpoints were also inverted: the shipped system is DESKTOP-FIRST,
     degrading down (max-width 820/560/380), not mobile-first building up.

     A STANDARD IS NEVER "DONE". IT IS IN FORCE.
     Reading DONE as "skip this" is what caused a whole PWA to be rebuilt on an
     invented vocabulary. This file is a snapshot of something living.

     HOW TO CHECK THIS FILE IS STILL CURRENT — do this BEFORE building from it:
         curl -s https://rail-fax.com > /tmp/rf.html
         # compare the class vocabulary and the CSS custom properties.
         # If rail-fax has names this file lacks, THE LIVE SITE WINS and this
         # file is stale again. Promote it, then build.

     REFERENCE IMPLEMENTATION: https://rail-fax.com  (public)
     PRIOR REVISION: door.template.html.260804.bak
     ═══════════════════════════════════════════════════════════════════════════

     KAI'S DECISIONS, carried forward from the 260804 revision. The CSS below is
     replaced by what shipped; this rationale is not reproducible from code and
     is the reason each choice is the way it is:

   COLLAPSED — a lighter gray spine. The folder tabs stay out, just wordless, so the strip reads as navigable rather than as page furniture. (KAI 260804)
   ── THE TRIGGER — pinned bottom-left, where the thumb already is. (KAI 260804) ──
   COLLAPSED — the tabs get SLIM, not silent. (KAI 260804: "when the slim rail is in view make sure the tabs are slim as well ... so even in a slim state they are readable.") They narrow to the width of the strip and hug its edge, so the vertical title lands inside the visible 23 px and keeps its color. The rounded corners stay on the RIGHT — the page-facing edge — because that is the side a folder tab sticks out of when the spine is pinned left.
   A photographic mark is dark to its own edge, so on a dark bar it reads as a floating face with no boundary. The accent ring gives the disc an edge and ties it to the FOH badge. Generated tiles do not need it — they are already a filled shape. (KAI's mark, 260804)
   ── GEAR + SETTINGS. The circle in the top right was decoration pretending to be a control; KAI called it. Now it opens the machine. (KAI 260804) ──
-->
<!doctype html>
<html lang="en" class="wrap has-top">
<head><meta name="door-template" content="door.template.html"><meta name="door-series" content="260817-934b49c2"><meta name="door-incept" content="2026-08-22T08:51:06Z">
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1,viewport-fit=cover">
<title>StudioX Fonts</title>
<meta name="description" content="A small type foundry. One typeface so far — abeatbyKai, drawn in 2011 and downloaded over 1.2 million times since. Hand-made and custom work under Foundry.Ink.">
<meta name="theme-color" content="#0c1218">
<!-- BEFORE FIRST PAINT. Resolving the theme after the stylesheet would flash the
     wrong field, which on a black/white door is the whole design failing for a
     frame. Reads the SAME store as the settings panel — one key, not two. -->
<script>(function(){var h=document.documentElement;if(!h.dataset.skin)return;var t='system';
try{t=(JSON.parse(localStorage.getItem('rw.settings')||'{}').theme)||'system'}catch(e){}
h.dataset.mode=t==='system'?(matchMedia('(prefers-color-scheme:dark)').matches?'dark':'light'):t})();</script>
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
<meta name="apple-mobile-web-app-title" content="studioxfonts.com">
<meta property="og:title" content="StudioX Fonts"><meta property="og:description" content="A small type foundry. One typeface so far — abeatbyKai, drawn in 2011 and downloaded over 1.2 million times since. Hand-made and custom work under Foundry.Ink.">
<link rel="manifest" href="/manifest.webmanifest">
<link rel="alternate" type="text/markdown" href="/index.md" title="StudioX Fonts — machine-readable">
<link rel="alternate" type="text/plain" href="/llms.txt" title="AI front door">
<link rel="canonical" href="https://studioxfonts.com/">
<script type="application/ld+json">{"@context":"https://schema.org","@type":"WebSite","name":"StudioX Fonts","url":"https://studioxfonts.com/","description":"A small type foundry. One typeface so far \u2014 abeatbyKai, drawn in 2011 and downloaded over 1.2 million times since. Hand-made and custom work under Foundry.Ink.","publisher":{"@type":"Organization","name":"Union Made Services","url":"https://unionmadeservices.com"},"potentialAction":[{"@type":"ReadAction","target":"https://studioxfonts.com/index.md"}]}</script>
<link rel="icon" href="/icon.svg" type="image/svg+xml">
<style>

/* ── RAIL-WORKS APEX DOOR ────────────────────────────────────────────────────
   The approved FOH/BOH pattern:
   a LEFT VERTICAL RAIL whose tabs open drawers, a top bar carrying the mark and
   the FOH id, condensed display type, chip eyebrows, panel cards.
   Accent is the only per-door variable.                                      */
:root{
  /* BASE PALETTE — parameterised 260814 for the first NON-STUDIO-X tenant (WearX).
     Every value below defaults to the house colours, so a door that declares
     nothing renders byte-identically to before. A door MAY declare its own base
     in JSON ("bg", "panel", "panel2", "line", "txt", "dim", "mut") when it
     belongs to another company with its own art direction — WearX specifies
     Asphalt Black #050505 and Bone White #F5F5EF, which the house #0c1218
     cannot express. This is the sibling of "accent", which was already per-door;
     the base was the half of the palette nobody could reach. */
  --bg:#0c1218; --panel:#131b24; --panel2:#1b2530; --line:#28323f;
  --txt:#f1f4f7; --dim:#93a0af; --mut:#66717f;
  --accent:#f6821f; --accent-d:#bd6418;
  --boh:var(--txt);          /* was #7b6cff — purple is not in the trade dress */ --ok:#3ecf8e; --off:#4a5563;
  --strip:#242e3a; --strip2:#3c4a5c;
  --rail:2.1rem;
  --safe-t:env(safe-area-inset-top,0px); --safe-b:env(safe-area-inset-bottom,0px);
  --safe-l:env(safe-area-inset-left,0px);
  --disp:"Helvetica Neue","Arial Narrow",Impact,-apple-system,system-ui,sans-serif;
  --sans:-apple-system,BlinkMacSystemFont,"SF Pro Text","Segoe UI",system-ui,sans-serif;
  --mono:ui-monospace,"SF Mono",Menlo,Consolas,monospace;
}
/* ── SKIN: MONO — white/black, black/white, nothing else (KAI 260812) ────────
   For doors whose trade dress forbids the house glass: LOVEARTROBOTS.com and
   studiox.ink. A door declares  "skin":"mono"  and gets this. Every other door
   is untouched, because an attribute selector that is not present cannot match
   — which is the point: 11 live doors do not get re-themed to add a 12th.

   WHY THIS RESTATES SURFACES INSTEAD OF RETOKENISING THEM.
   The glass backgrounds (.vrail .vtab .top .drawer .vpop) are DERIVED (260815)
   rgba, each a slightly different value tuned to the dark door. Collapsing them
   into one shared token would be tidier and would also silently change all 11
   shipped doors. On a live estate, contained beats elegant.

   ACCENT IS STUBBED, NOT REMOVED. KAI: "no pop color yet stub in for future."
   --accent resolves to the foreground, so every accent rule still works and
   reads as ink. --pop is declared and unused: set it to a real hue later and
   the door gains its color without a template change.                        */
/* ══ WRAP-RAIL ZONES — head · body · foot (KAI 260815) ══════════════════════
   The left rail always had three zones (.vham / .vbars / .vfoot); the wrap rails
   had a flat list, which is why left and right did not behave the same. These
   give top, right and base the identical structure. */
.rrail[data-railstack],.hrail[data-railstack]{display:flex;align-items:center;justify-content:center;gap:.5rem}
/* RAIL-TOP carries the state switcher AND the door's identity tabs */
.hrail.top .rsbar{flex:none;flex-wrap:nowrap}
.hrail.top .rtab,.hrail.top .rsb{white-space:nowrap}
.hrail.top .rbars{flex:0 1 auto}
.rrail[data-railstack]{flex-direction:column;justify-content:flex-start;align-items:stretch}
.rbars{flex:1;display:flex;align-items:center;justify-content:center;min-width:0;min-height:0}
.rrail .rbars,.vrail .rbars{flex:1;flex-direction:column;overflow-y:auto;scrollbar-width:none;
  width:100%;align-self:stretch;align-items:stretch;
  /* KAI 260815 — the bars FLOW FROM THE BOTTOM: dots sit at the thumb, tabs
     stack up from them. flex:1 lives here, not on .vbars alone, or the right
     rail centres its tabs mid-height while the left stacks them. */
  justify-content:flex-end}   /* stack up from the bottom */
.rrail .rbars::-webkit-scrollbar,.vrail .rbars::-webkit-scrollbar{display:none}
/* JUNCTION CLEARANCE (KAI 260815) — where a side rail meets the tray bar.
   Both side rails must reserve the SAME room: the left reserved 3.9rem and the
   right .3rem, so the right rail's dots and tabs ran into the tray. A shared
   variable, so the two junctions cannot drift apart again. */
/* THE SIDE RAILS STOP AT THE HORIZONTAL BARS — at every width. These lived
   inside the 820px band, so on a phone the rails ran to top:0 and straight
   under the site header. A layout law must not be scoped to one breakpoint. */
html.wrap .vrail,html.wrap .rrail{bottom:calc(var(--railstop-b) + var(--safe-b))}
html.has-top .vrail,html.has-top .rrail{top:calc(var(--railstop-t) + var(--safe-t))}
:root{--junction:5rem;
  /* where the side rails stop: below the header+RAIL-TOP, above the tray */
  --hdr-h:4.2rem; --ftr-h:2.0rem;
  /* ONE SOURCE FOR THE TOP STACK (KAI 260816). The header height was written in
     three places — the header rule, its tablet override, and this hard 6.1rem —
     so thinning the header at 820px left a 14px dead strip between it and
     RAIL-TOP. Derive instead of repeat. 2.4rem is RAIL-TOP's own
     height — MEASURED at 37px, not guessed. The old 1.9rem made the side rails
     start 6px ABOVE the bottom of RAIL-TOP, so they had been overlapping it. */
  --railstop-t:calc(var(--hdr-h) + 2.4rem); --railstop-b:calc(2.9rem + var(--ftr-h))}   /* KAI asked for more room at the rail/tray junction */
.rfoot{flex:none;display:flex;align-items:center;gap:.3rem}
.vrail .rfoot,.rrail .rfoot{flex-direction:column;align-items:stretch;width:100%;padding-bottom:0}
/* one bar visible at a time — the hamburger steps through them */
.rbars > .rtabs{display:none}
.rbars > .rtabs.on{display:flex}

/* the hamburger, same glyph and states as the left rail's */
/* ── BAR INDICATOR (primitive) ─────────────────────────────────────────────
   A transparent container of dots — one per bar, filled = current. Replaces the
   hamburger because THE CONTROL STEPS, IT DOES NOT OPEN A MENU: a hamburger
   glyph promises a menu and delivers a page-turn. Dots also carry information a
   hamburger cannot — how many bars there are, and which one you are on.
   Transparent by design so it can be dropped anywhere a stepper is needed. */
.rham{flex:none;align-self:center;display:inline-flex;gap:3px;align-items:center;justify-content:center;
  min-width:var(--bar);min-height:var(--bar);padding:.35rem .2rem;cursor:pointer;
  background:transparent;border:0;flex-wrap:wrap;max-width:5.2rem;
  font:800 .78rem/1 var(--mono);color:var(--mut);letter-spacing:0}
/* SELF-RESOLVING BEYOND ~6: the container wraps rather than growing a rail-long
   line of dots. 3 · 4 · 6 sit on one run; more wrap to a second. */
.vrail .rham,.rrail .rham{flex-direction:column;max-width:none;max-height:5.2rem;
  align-content:center}
/* the brackets are the mark only where they read as brackets — horizontal */
.vrail .rham::before,.vrail .rham::after,
.rrail .rham::before,.rrail .rham::after{content:none}
/* the brackets ARE the mark — always horizontal, even on a vertical rail: this
   is a glyph, not a rail element, and [··] rotated stops being [··] */
.rham::before{content:"[";margin-right:1px}
.rham::after{content:"]";margin-left:1px}
.rham:hover::before,.rham:hover::after,
.rham:focus-visible::before,.rham:focus-visible::after{color:var(--accent-ink)}
.rham span{display:block;width:4px;height:4px;border-radius:50%;
  background:var(--mut);transition:background .18s,transform .18s}
/* AN EMPTY SLOT IS A SOCKET, NOT A DESTINATION (KAI 260816). The fixture is
   always three dots so the control has one learnable shape everywhere; a slot
   with no choice behind it stays visible — otherwise the fixture is not fixed —
   but is dimmed to a hairline outline and hidden from the accessibility tree.
   It reads as "there is nothing here", never as "there is something here". */
.rham span.void{background:transparent;box-shadow:inset 0 0 0 1px var(--mut);opacity:.28}
.rham:hover span.void{background:transparent}
.rham span.on{background:var(--accent);transform:scale(1.35)}
.rham:hover span:not(.void),.rham:focus-visible span:not(.void){background:var(--dim)}
.rham:hover span.on,.rham:focus-visible span.on{background:var(--accent)}

/* ══ THE RAIL TAB — ONE COMPONENT, FOUR EDGES (KAI 260815) ═════════════════
   "the left tabs are a very nice lean into tabbed functionality — if we carried
    that tabbing to all rails ... that common visual and functionality will look
    very pro."
   THE LEFT RAIL IS THE MODEL. The others adopt its look rather than meeting in
   the middle: bordered, blurred, sitting on the material, and ROUNDED ON THE
   EDGE AWAY FROM THE SCREEN EDGE — the tab tucks into its rail and rounds
   toward the content. That single rule rotates cleanly through all four sides.

   Five separate tab components existed before this (.vtab, .rrail button,
   .hrail button, .dtab, and the header .rsb). Five things to learn and five
   places to drift.

   FLEXBOX IS AXIS-AGNOSTIC, which is why one rule serves four edges:
   justify-content works on the MAIN axis, so the same declaration centres
   horizontally on a top rail and vertically on a side rail.

   TABS MUST NOT MOVE WHEN A STATE CHANGES. Rail-States alter what is REACHABLE,
   never what is THERE — an unavailable tab dims, it does not vanish. Centred
   tabs that reshuffle on every state change would destroy muscle memory, which
   is the exact opposite of why we are unifying them. */
.rtabs{display:flex;justify-content:center;align-items:center;gap:.3rem;min-width:0;
  /* MUST have a definite size. container-type:inline-size makes the element
     SIZE-CONTAINED on the inline axis — its width stops deriving from its
     children. On an auto-width flex item that collapses the strip, and the
     first tab is pushed outside the rail's overflow where it cannot be
     scrolled to. (KAI BUSKIRK vanished from the top rail exactly this way.) */
  flex:1 1 auto;width:100%;
  container-type:inline-size}
.rtabs.col{flex-direction:column;width:100%;align-items:stretch}
/* KAI 260815 — VERTICAL BARS FLOW FROM THE BOTTOM.
   "the left and right rail-bar may need to flow from the bar's bottom position —
   rail-dots are at bottom left and right, then the rail-tabs on after."
   The bar carries flex:1 1 auto so it fills the rail, and .rtabs centres its own
   tabs — which is why setting justify-content on .rbars did nothing. The
   alignment has to land on the BAR. Thumb reach is at the bottom of a held
   tablet, so that is where the controls start. */
.vrail .rtabs.col,.rrail .rtabs.col{justify-content:flex-end}
/* SELF-DISTRIBUTE MEANS SPACED, NOT STRETCHED. `flex:1 1 0` made two tabs each
   take half a 1200px rail — inflation, not distribution. The LEFT RAIL is the
   model and its tabs sit at natural size with gaps between them, so a rail with
   room to give gets MORE BREATHING ROOM, never wider tabs. */
@container (min-width:30rem){ .rtabs{gap:.55rem} }
@container (min-width:56rem){ .rtabs{gap:.9rem} }

.rtab{flex:0 1 auto;display:inline-flex;align-items:center;justify-content:center;
  min-height:var(--bar);                 /* SLIVER AT REST — KAI 260815, quartered */
  font:800 .52rem/1 var(--mono);letter-spacing:.16em;text-transform:uppercase;
  color:var(--dim);background:var(--mat-trig);
  backdrop-filter:blur(12px);-webkit-backdrop-filter:blur(12px);
  border:1px solid var(--line);cursor:pointer;padding:.6rem .7rem;
  transition:color .18s,background .18s,border-color .18s}
.rtab:hover,.rtab:focus-visible{color:var(--txt);border-color:var(--mut)}
.rtab[aria-expanded="true"],.rtab[aria-pressed="true"]{
  color:var(--bg);background:var(--accent);border-color:var(--accent-ink)}
/* UNREACHABLE IN THIS STATE — dimmed, never removed. Position stays constant. */
.rtab[aria-disabled="true"],.rtab:disabled{opacity:.38;cursor:default}
.rtab[aria-disabled="true"]:hover{color:var(--dim);border-color:var(--line)}

/* the radius rotates with the edge: round toward the content, square to the rim */
.vrail .rtab{border-radius:12px 0 0 12px;writing-mode:vertical-rl;transform:rotate(180deg);
  width:100%;min-width:0;box-sizing:border-box}
.rrail .rtab{border-radius:0 12px 12px 0;writing-mode:vertical-rl;transform:rotate(180deg);
  width:100%;min-width:0;box-sizing:border-box}
.hrail .rtab{min-height:var(--bar);padding:.22rem .6rem;font-size:.54rem;letter-spacing:.13em}
.hrail.top .rtab{border-radius:0 0 10px 10px}
.hrail.bot .rtab,.dhandle .rtab{border-radius:12px 12px 0 0}

/* ══ RAIL-STATES · RAIL-TRAY · RAIL-GATE (KAI 260815) ══════════════════════
   FOUR STATES: FOH · LOCKER · FLOOR · BOH. One frame, one grammar; only the
   CONTENTS change. Every prior "perspective" system failed the same way —
   people lost track of which state they were in — so the indicator is LOUD and
   lives in the header, never subtle, never hidden.
   The LOCKER is a ROOM (a member's own record space: standing, notices,
   approvals, skills), not the gate. The GATE is the KEY — a transaction. */
.rsbar{display:flex;align-items:center;gap:.3rem;flex-wrap:wrap;min-width:0}
.rsnow{display:none;font:800 .56rem/1 var(--mono);letter-spacing:.19em;text-transform:uppercase;
  color:var(--bg);background:var(--accent);padding:.42rem .6rem;border-radius:999px;white-space:nowrap}
.rsb{font:800 .54rem/1 var(--mono);letter-spacing:.15em;text-transform:uppercase;
  background:transparent;color:var(--dim);border:1px solid var(--line);border-radius:999px;
  padding:.36rem .55rem;cursor:pointer}
.rsb[aria-pressed="true"]{color:var(--txt);border-color:var(--accent-ink)}
.rsb[data-gated="1"]::after{content:" ·";color:var(--mut)}
.rsb.locked{opacity:.55}
@media (max-width:620px){
  .rsnow{display:none}          /* the highlighted pill already says which state */
  .rsb{padding:.3rem .44rem;letter-spacing:.08em;font-size:.5rem}
  header.top .nm i{display:none}      /* the stage line costs a row it cannot afford */
}

/* state visibility — an element declares which states it belongs to */
[data-rs-show]{display:none}
html[data-rs="foh"]    [data-rs-show~="foh"],
html[data-rs="locker"] [data-rs-show~="locker"],
html[data-rs="floor"]  [data-rs-show~="floor"],
html[data-rs="boh"]    [data-rs-show~="boh"],
[data-rs-show~="all"]{display:block}

/* THE TRAY — tiles for what is in play. Not back matter: a build stamp is not
   in play, so provenance moved to the gear and the tray took the base. */
.tray{display:grid;gap:.5rem;margin:0 0 .9rem}
@media(min-width:620px){.tray{grid-template-columns:repeat(2,1fr)}}
.tile{background:var(--panel2);border:1px solid var(--line);border-left:3px solid var(--accent);
  border-radius:10px;padding:.65rem .8rem}
.tile .tn{display:block;font:800 .78rem/1.25 var(--sans);color:var(--txt)}
.tile .td{display:block;font:400 .74rem/1.45 var(--sans);color:var(--dim);margin-top:.15rem}
.tile .tt{display:block;font:800 .54rem/1 var(--mono);letter-spacing:.15em;text-transform:uppercase;
  color:var(--mut);margin-top:.4rem}
.tray:empty{display:none}
.tile[data-hid="1"]{display:none}

/* THE GATE */
.gbtn{display:block;width:100%;margin:0 0 .5rem;padding:.7rem 1rem;border-radius:999px;cursor:pointer;
  border:1px solid var(--accent);background:var(--accent);color:var(--bg);
  font:800 .66rem/1 var(--mono);letter-spacing:.16em;text-transform:uppercase}
.gbtn.ghost{background:transparent;color:var(--accent-ink)}
.gnote{font:800 .6rem/1.4 var(--mono);letter-spacing:.12em;text-transform:uppercase;color:var(--mut);margin:.5rem 0 0}
.provbox{border-bottom:1px solid var(--line);padding-bottom:.7rem;margin-bottom:.2rem}
.provbox .bld{margin:0;font:400 .66rem/1.5 var(--mono);color:var(--mut);word-break:break-word}

html[data-skin="press"]{
  /* colour already comes from the door's own base palette (bg/panel/txt/...),
     so the skin only sets TYPE and the art treatment. */
  --disp:"Haettenschweiler","Anton","Arial Narrow","Impact","Franklin Gothic Heavy",var(--sans);
}
html[data-skin="press"] h1{font-family:var(--disp);text-transform:uppercase;
  letter-spacing:-.004em;line-height:.88;font-weight:400}
html[data-skin="press"] h2{font-family:var(--disp);text-transform:uppercase;letter-spacing:.005em;font-weight:400}
html[data-skin="press"] .st .n{font-family:var(--disp);text-transform:uppercase;font-weight:400;font-size:1.18rem}
html[data-skin="press"] .st .n .d{font-family:var(--sans);text-transform:none;
  letter-spacing:0;font-weight:400;font-size:.82rem;line-height:1.5}
/* HERO ART — the supplied lockup, led with. This is the graphic bridge to the
   client's existing storefront; a shared palette alone was not enough. */
.heroart{display:block;width:min(100%,660px);height:auto;margin:0 0 1.5rem;
  filter:drop-shadow(0 10px 30px rgba(0,0,0,.55))}
/* PAINT BAND — the texture strip as a divider, the device their own site uses. */
.paintband{height:clamp(64px,9vw,116px);background-size:cover;background-position:center;
  border-top:1px solid var(--line);border-bottom:1px solid var(--line);margin:1.6rem 0 0}
/* ── SKIN: INSTRUMENT ──────────────────────────────────────────────────────
   FreeDaNode / FreeDaTime trade dress, MEASURED off the live hand-rolled page
   on 260816 rather than eyeballed:

     bg #0a0906 · ink #e8d9b0 · amber #ffb000 · dim #8a7d5c
     line #2a2418 · panel #151309 · panel-2 #0e0c07
     type 15px/1.6 ui-monospace — no webfont, no external request
     texture: 1px scanline over a soft bloom from above

   THE PALETTE IS NOT HERE. It rides the door's own base tokens, declared per
   door, so this skin is TYPE + TEXTURE only and another tenant can wear it in
   another hue. The bloom uses --mat-glow, which the serializer derives from
   whatever accent that door declared. A skin that hardcodes one client's brand
   is a copy of a site, not a skin.

   The whole page is monospace on purpose. On an instrument, mono is not a
   stylistic choice — digits have to hold their column or a readout lies. */
html[data-skin="instrument"]{--sans:var(--mono);--disp:var(--mono)}
html[data-skin="instrument"] body{
  background-image:
    repeating-linear-gradient(0deg, rgba(0,0,0,.28) 0 1px, transparent 1px 3px),
    radial-gradient(900px 460px at 50% -12%, var(--mat-glow), transparent 72%);
  background-attachment:fixed}
/* The base h1 uppercases. The source page is SENTENCE CASE — "Read from the
   clock." — and on a monospace face uppercase reads as shouting rather than as
   an instrument label. Match the original. */
html[data-skin="instrument"] h1,
html[data-skin="instrument"] h2{font-family:var(--mono);font-weight:700;
  letter-spacing:-.01em;text-transform:none}
html[data-skin="instrument"] .chip,
html[data-skin="instrument"] .kname,
html[data-skin="instrument"] .ktag{font-family:var(--mono)}
/* the readout — digits that glow the way the hand-rolled clock did */
html[data-skin="instrument"] .readout{
  font:700 clamp(1.6rem,7vw,3rem)/1 var(--mono);letter-spacing:.06em;
  color:var(--accent-ink);text-shadow:0 0 18px var(--mat-glow),0 0 4px var(--mat-glow);
  font-variant-numeric:tabular-nums;text-align:center;
  background:var(--panel2);border:1px solid var(--line);border-radius:10px;
  padding:.9rem .6rem;margin:.2rem 0}
/* SCANLINES ARE MOTION-ADJACENT. A 1px repeating overlay is exactly the pattern
   that triggers discomfort for some readers; honour the OS setting. */
@media (prefers-reduced-motion:reduce){
  html[data-skin="instrument"] body{background-image:
    radial-gradient(900px 460px at 50% -12%, var(--mat-glow), transparent 72%)}
}

/* WHAT THE LIGHT MODE FOUND, 260816. The scanline is `rgba(0,0,0,.28)` — a
   subtle texture over #0a0906 and a BARCODE over #f7f9fa. It was written as a
   fixed value because the ground it sat on was never going to change; the
   moment the ground could change, the value became a bug.

   A TEXTURE IS RELATIVE TO ITS GROUND. On light ground it inverts and thins. */
html[data-skin="instrument"][data-mode="light"] body{
  background-image:
    repeating-linear-gradient(0deg, rgba(0,0,0,.055) 0 1px, transparent 1px 3px),
    radial-gradient(900px 460px at 50% -12%, var(--mat-glow), transparent 72%)}

html:not([data-skin="mono"])[data-mode="light"]{--bg:#f7f9fa;--panel:#eff1f3;--panel2:#e3e7eb;--line:#cbd0d7;--txt:#1c2126;--dim:#50565e;--mut:#6e747c;--mat-tint:rgba(239,241,243,.62);--mat-deep:rgba(239,241,243,.97);--mat-head:rgba(247,249,250,.94);--mat-trig:rgba(227,231,235,.7);--mat-edge:rgba(203,208,215,.8);--mat-glow:rgba(246,130,31,.10);--accent-ink:#b75807;--ok:#1a7f4b;--off:#9aa0a6;color-scheme:light}html:not([data-skin="mono"])[data-mode="light"] .scrim{background:rgba(0,0,0,.28)}
html[data-skin="mono"]{
  --mono-ink:#000; --mono-field:#fff;
  --bg:var(--mono-field); --panel:var(--mono-field); --panel2:var(--mono-field);
  --line:rgba(0,0,0,.22);
  --txt:var(--mono-ink); --dim:rgba(0,0,0,.68);
  /* .55 measured 4.76:1 on white -- AA passes, but with no margin at all for a
     .78rem link. .60 measures 5.74:1. Cheap headroom. */
  --mut:rgba(0,0,0,.60);
  --accent:var(--mono-ink); --accent-d:var(--mono-ink);
  --boh:var(--mono-ink); --ok:var(--mono-ink); --off:rgba(0,0,0,.32);
  --strip:rgba(0,0,0,.12); --strip2:rgba(0,0,0,.3);
  --glass:var(--mono-field); --glass2:rgba(0,0,0,.06);
  --pop:; /* RESERVED — the future accent. Nothing reads it yet. */
  --disp:var(--mono);
}
html[data-skin="mono"][data-mode="dark"]{
  --mono-ink:#fff; --mono-field:#000;
  --line:rgba(255,255,255,.28);
  --dim:rgba(255,255,255,.78); --mut:rgba(255,255,255,.62);
  --off:rgba(255,255,255,.35);
  --strip:rgba(255,255,255,.16); --strip2:rgba(255,255,255,.34);
  --glass2:rgba(255,255,255,.09);
}
/* No stored choice and no JS: follow the system. The script writes data-mode on
   every load, so this only ever decides the no-JS case. */
@media (prefers-color-scheme:dark){
  html[data-skin="mono"]:not([data-mode="light"]){
    --mono-ink:#fff; --mono-field:#000;
    --line:rgba(255,255,255,.28);
    --dim:rgba(255,255,255,.78); --mut:rgba(255,255,255,.62);
    --off:rgba(255,255,255,.35);
    --strip:rgba(255,255,255,.16); --strip2:rgba(255,255,255,.34);
    --glass2:rgba(255,255,255,.09);
  }
}
/* Flatten the glass. No blur, no gradient, no atmospheric shadow — a mark on a
   field, per the trade dress. Hairlines carry the structure instead. */
html[data-skin="mono"] .vrail,
html[data-skin="mono"] .hrail,
html[data-skin="mono"] .rrail,
html[data-skin="mono"] .vtab,
html[data-skin="mono"] .top,
html[data-skin="mono"] .drawer,
html[data-skin="mono"] .sset,
html[data-skin="mono"] .blktcp button{
  background:var(--glass);backdrop-filter:none;-webkit-backdrop-filter:none;box-shadow:none}
html[data-skin="mono"] .vrail{border-right:1px solid transparent}
/* The wrap rails take the same hairline instead of the house glass. Caught by
   audit rule MONO DOOR CARRIES A HUE the moment the wrap CSS landed — the new
   rails were painted rgba(19,27,36,.5), the house blue, on black-and-white doors.
   The audit found my bug before any door shipped it. */
html[data-skin="mono"] .hrail.top{border-bottom:1px solid var(--line)}
html[data-skin="mono"] .hrail.bot{border-top:1px solid var(--line)}
html[data-skin="mono"] .rrail{border-left:1px solid transparent}
html[data-skin="mono"] .rrail:not(.open){background:transparent;border-left-color:transparent}
html[data-skin="mono"] .hrail button:hover,
html[data-skin="mono"] .hrail button[aria-expanded="true"],
html[data-skin="mono"] .rrail button:hover,
html[data-skin="mono"] .vrail:not(.open){background:transparent;border-right-color:transparent}
html[data-skin="mono"] .vtab:hover,
html[data-skin="mono"] .rtab.act{background:var(--glass2)}
html[data-skin="mono"] .vrail.open,
html[data-skin="mono"] .sheet.open,
html[data-skin="mono"] .sset.open,
html[data-skin="mono"] .apanel.open,
html[data-skin="mono"] .toolpane.open{box-shadow:none}
html[data-skin="mono"] .dot.on,
html[data-skin="mono"] .dot.work{box-shadow:none}
html[data-skin="mono"] .dot.off{background:var(--off)}
/* ── EVERY REMAINING HUE, NEUTRALISED ─────────────────────────────────────────
   MEASURED, NOT GUESSED. The mono block above flattened the glass, but a browser
   sweep of COMPUTED colors on the live door still found four hues on visible
   elements -- .tkey, .chip (#12100a), .key a and the
   pressed .seg button (#231c10). A source grep then found 31 rules carrying a
   hardcoded non-gray literal. Four were visible TODAY; the rest are one new
   sheet type away from being visible tomorrow.
   So this covers the class, not the four instances. Grep for the class of defect
   is now an audit rule (audit-doors.py: mono-hue), so a rule added later that
   carries a hue and no mono override FAILS THE BUILD instead of shipping. */
html[data-skin="mono"] .chip,
html[data-skin="mono"] .tkey,
html[data-skin="mono"] .key a,
html[data-skin="mono"] .btns button,
html[data-skin="mono"] .ap-mail button,
html[data-skin="mono"] .seg button[aria-pressed="true"],
html[data-skin="mono"] .blktcp button.pri,
html[data-skin="mono"] .blktcp button.done,
html[data-skin="mono"] .cap-h,
html[data-skin="mono"] .cap-b,
html[data-skin="mono"] .cap-c.done,
html[data-skin="mono"] .mcopy.done,
html[data-skin="mono"] .toast,
html[data-skin="mono"] .toast.warn{
  background:var(--glass2);border-color:var(--line);color:var(--txt)}
html[data-skin="mono"] .sx.go i{background:var(--txt)}
/* No red on a black-and-white door. An error still has to READ as an error, so
   it keeps full-contrast ink and leans on its own wording, not on hue. */
html[data-skin="mono"] .sendmsg.bad,
html[data-skin="mono"] .amsg.bad{color:var(--txt);font-weight:700}
/* The scrim is a dim, not a tint -- and it needs BOTH modes. Writing only the
   dark override left light mode on the house rgba(6,9,12,.55), a faintly blue
   black. Measured on the live door; invisible to the eye, still a hue. */
html[data-skin="mono"] .scrim{background:rgba(0,0,0,.55)}
html[data-skin="mono"][data-mode="dark"] .scrim{background:rgba(255,255,255,.14)}
/* 21:1 on the mark itself. Condensed display type is house furniture; a mono
   door wants the mark to read as set text, so display collapses to the mono
   stack (see --disp above) and the tracking comes off. */
html[data-skin="mono"] h1{letter-spacing:0}
html[data-skin="mono"] .mk{border:1px solid var(--line);background:var(--glass)}
*{box-sizing:border-box;margin:0;padding:0}
html{-webkit-text-size-adjust:100%}
body{background:var(--bg);color:var(--txt);font:400 15px/1.6 var(--sans);min-height:100dvh;overflow-x:hidden}
a{color:inherit}
/* KEYBOARD FOCUS — one visible ring, everywhere (260812). The door had focus
   styles that only CHANGE A COLOR to var(--accent). On the dark doors that is
   thin; on a mono door --accent IS the foreground, so keyboard focus was
   literally invisible. currentColor works in both fields and in both themes
   without a second token. :focus-visible, so a mouse click never draws it. */
:focus-visible{outline:2px solid currentColor;outline-offset:2px;border-radius:2px}

/* ── LEFT VERTICAL RAIL — tabs open the drawers ── */
:root{--rail-min:1.45rem;--bar:1.5rem;
  /* GLASS is derived from the door's own PANEL colour (260815). It was
     hardcoded to the house rgba(19,27,36,.5), which is invisible against
     the house #0c1218 — that is where the 'sliver' rail comes from — but
     showed as a pale block against a tenant's Asphalt Black. The base
     palette was parameterised and this one was missed, so the effect was
     accidental on one door and a bug on another. Now it is intentional
     everywhere: the collapsed rail always reads as a sliver of its own
     surface, whatever that surface is. */
  --glass:rgba(19,27,36,.55);
  /* ── THE MATERIAL ──────────────────────────────────────────────────────
     Not a colour: a translucent layer tinted from the surface behind it,
     blurred, and separated by a HAIRLINE — the hairline is the sliver of
     light, the panel is what disappears. Derived per door, so a tenant's
     material is theirs by construction rather than by an override that
     happens to fire. Standard technique (backdrop-filter), named properly. */
  --mat-tint:rgba(19,27,36,.5); --mat-deep:rgba(19,27,36,.97); --mat-head:rgba(12,18,24,.94);
  --mat-glow:rgba(246,130,31,.07);
  /* TEXT/BORDER accent. Light mode overrides it with a contrast-derived value;
     in dark mode it is the accent itself, so nothing that worked changes. */
  --accent-ink:var(--accent);
  --mat-trig:rgba(27,37,48,.6); --mat-edge:rgba(40,50,63,.6); --mat-blur:14px}
.vrail{position:fixed;left:0;top:0;bottom:0;width:calc(var(--rail) + var(--safe-l));
  padding-left:var(--safe-l);background:var(--mat-tint);backdrop-filter:blur(var(--mat-blur));
  -webkit-backdrop-filter:blur(var(--mat-blur));border-right:1px solid var(--mat-edge);
  display:flex;flex-direction:column;align-items:center;z-index:69;padding-block:.6rem;
  transform:translateX(calc(-1 * (var(--rail) - var(--rail-min))));
  transition:transform .3s cubic-bezier(.32,.72,0,1),background .22s}
.vrail.open{transform:translateX(0);box-shadow:4px 0 14px rgba(0,0,0,.18)}
/* COLLAPSED — a lighter gray spine. The folder tabs stay out, just wordless,
   so the strip reads as navigable rather than as page furniture. (KAI 260804) */
.vrail:not(.open){background:transparent;border-right-color:transparent}
/* ── THE TRIGGER — pinned bottom-left, where the thumb already is. (KAI 260804) ── */
.vrail.open ~ /* ── THE TABS — a vintage file folder. Rounded outer corner, stacked, and they
   stay peeking out when the rail is thin so the strip reads as navigable. ── */
/* ── RAIL-BAR STACK + HAMBURGER (KAI 260812) ─────────────────────────────────
   "RAIL-BAR on left should be able to carry a small hamburger icon at the top
    of the rail so as to be able to nav through the RAIL-BAR STACK."
   Bars are stacked in one column; only the active one is displayed. The
   hamburger steps to the next and wraps. Hidden entirely at one bar.        */
.vbars{flex:1;align-self:stretch;display:flex;flex-direction:column;justify-content:flex-end}
/* THE HAMBURGER — a NAKED ICON, not a tab (KAI 260812: "your Hamburger tab is
   styled wrong looks like a bar code ... take off the tab style container
   wrapper and leave it as a standard looking hamburger"). The folder-tab
   chrome plus upright strokes read as a barcode sitting in a column of tabs.
   No border, no panel, horizontal bars — what a hamburger has always been. */
/* Open state becomes an X — the control says what it will do next. */
.vrail:not(.open) /* HOME — appears only when you are off the top bar. (KAI: "have home button to
   bring the user back to the top rail in the stack".) */
/* ── THE RAIL FOOT — tabs pinned to the bottom ─────────────────────────────
   KAI 260815: "there is a trigger tab I don't feel we need, it's causing
   conflict." Correct, and it was documented in this very comment: the expander
   trigger forced the foot to reserve ~7.4rem so CHAT would not sit under it.
   REMOVED. Its only job was expanding the rail, and every tab already does that
   (a tab click calls railOpen(true)), as does Settings > Rail > Stay open.
   A control whose function is already covered, that costs 7.4rem of rail to
   avoid colliding with, is furniture. The foot now clears only the base rail. */
/* THE JUNCTION PADDING IS GONE (KAI 260816, measured).
   KAI asked on 260815 for "a bit more padding on the bottom where the rail-tabs
   are ... where the junction is, where the side rails meet the bottom tray."
   That was right THEN: the rails ran down to the tray and the last tab crowded
   it. It is wrong NOW — the rails stop above the tray by construction
   (--railstop-b), and the tray stops above RAIL-FOOTER. So `--junction: 5rem`
   was reserving 80px for a collision that can no longer happen, and that 80px
   is exactly why the rail contents did not reach the bottom.

   Same shape as the landscape display:none: A GUARD THAT OUTLIVED ITS HAZARD
   IS INDISTINGUISHABLE FROM A BUG. */
.vfoot{align-self:stretch;display:flex;flex-direction:column;gap:.4rem;padding-bottom:0}
/* A door with no foot tabs must not reserve the space. Measured 260812:
   printshop.fyi declares no social or chat, and the empty .vfoot still held
   118px, pushing the centered rail bars off center for no reason. */
.vfoot:empty{display:none}
.vrail:not(.open) .vfoot{align-items:flex-end}
/* THE POPOUT — a real menu, not a cycle. KAI: "if triggered need a popout or
   drop down to better navigate the Rail-Bar stack". Cycling makes you count;
   a list lets you go straight to the bar you want. */
/* THE STACK IS BUILT FROM .st — the house info tile (KAI 260812: "a small list
   of stacked rail-tiles our standard info containers for better flex box
   organizational identity"). Same container as the status board and the imprint
   rows, so the dropdown is recognisably part of the system rather than a
   bespoke menu. Only the element changes: <button> so it is clickable and
   keyboard-reachable. The class, the grid and the dot are unchanged. */
/* KAI 260812: "make the rail-bar number titles small and the description louder
   ... its just a target market — what the rail-bar does is more important."
   So the tile inverts: the bar's NAME drops to a small eyebrow and the tabs it
   carries become the loud line. No new class — the eyebrow reuses .d, moved
   above the name text, because a second mechanism for one label is a second
   thing to keep in sync. */
.vbarname{font:800 .42rem/1 var(--mono);letter-spacing:.16em;text-transform:uppercase;
  color:var(--mut);text-align:center;padding:.25rem 0 .4rem}
.vrail:not(.open) .vbarname{display:none}
/* SHEET SUB-TITLE — the core title stays one word; the description sits under it.
   KAI: "TITLE IMPRESSUM/IMPRINT DETAIL TRANSPARENCY NOTICE". */
.sheet .sub{font:400 .74rem/1.35 var(--sans);letter-spacing:.02em;text-transform:none;
  color:var(--dim);padding:0 1.15rem .9rem;margin-top:-.55rem}
/* PARITY: identical geometry, mirrored radius. KAI 260815 — 'the left and
   right Rail-Bars need to look familiar, basically function exactly alike.' */
.vrail .rtab,.rrail .rtab{min-height:4.4rem;padding:.6rem .15rem;letter-spacing:.16em;font-size:.54rem}
.rtab:hover{color:var(--accent-ink);background:var(--mat-deep)}

.rtab.vkey{color:var(--boh)}
.rtab.vkey[aria-expanded="true"]{color:var(--bg);background:var(--boh);border-color:var(--boh)}
/* COLLAPSED — the tabs get SLIM, not silent. (KAI 260804: "when the slim rail
   is in view make sure the tabs are slim as well ... so even in a slim state
   they are readable.") They narrow to the width of the strip and hug its edge,
   so the vertical title lands inside the visible 23 px and keeps its color.
   The rounded corners stay on the RIGHT — the page-facing edge — because that
   is the side a folder tab sticks out of when the spine is pinned left. */
.vrail:not(.open) .vrail:not(.open) .rtab,.rrail:not(.open) .rtab{width:var(--rail-min);min-width:0;padding:.7rem 0;font-size:.5rem;letter-spacing:.12em;box-shadow:none;overflow:hidden}
/* the drawer the tabs slide out — from the LEFT, over the content, like the extension */
.sheet{position:fixed;top:0;bottom:0;left:calc(var(--rail) + var(--safe-l));
  width:min(420px,calc(100vw - var(--rail) - 1rem));background:var(--panel);
  border-right:1px solid var(--line);
  transform:translateX(-102%);opacity:0;
  transition:transform .34s cubic-bezier(.32,.72,0,1),opacity .22s ease-out;
  z-index:68;display:flex;flex-direction:column;padding-top:var(--safe-t)}
.sheet.open{transform:translateX(0);opacity:1;box-shadow:6px 0 20px rgba(0,0,0,.2)}
.sheet h3{font:800 .58rem/1 var(--mono);letter-spacing:.18em;text-transform:uppercase;
  color:var(--accent-ink);padding:1.15rem 1.15rem .8rem;border-bottom:1px solid var(--line)}
.sheet .sc{overflow-y:auto;-webkit-overflow-scrolling:touch;overscroll-behavior:contain;padding:1.15rem;display:grid;gap:.55rem;align-content:start}
.sheet .sc a{border:1px solid var(--line);border-radius:4px;background:var(--panel2);
  padding:.75rem .85rem;min-height:44px;text-decoration:none;font-weight:620;font-size:.9rem}
.sheet .sc a span{display:block;color:var(--mut);font-size:.78rem;font-weight:400;margin-top:.1rem}
/* FIX 260812 (KAI saw it on the .com origin door). The rule above was written for
   the BOH links, which are  <a>TEXT<span>note</span></a>  — one span, and the bare
   text node carries the name. But <a class="" href="https://kaibuskirk.com"><span class="n">Kai Buskirk</span><span class="d">technology, merch, design, apps</span></a><a class="" href="https://studiox88.com"><span class="n">Studio X</span><span class="d">The studio — cloud, web &amp; AI since 1988</span></a><a class="" href="https://rail-works.com"><span class="n">Rail-Works</span><span class="d">The suite — rails for work that moves</span></a><a class="" href="https://cadre.fyi"><span class="n">Cadre</span><span class="d">The cadre gateway</span></a><a class="" href="https://freedatime.com"><span class="n">FreeDaTime</span><span class="d">A free multi-witness grounding clock. Authoritative local and UTC time, read not computed.</span></a><a class="" href="https://printshop.link"><span class="n">Printshop</span><span class="d">The print rail and shop network</span></a><a class="" href="https://rail-fax.com"><span class="n">Rail-Fax</span><span class="d">Send a fax without a fax machine</span></a><a class="" href="https://rail-mail.com"><span class="n">Rail-Mail</span><span class="d">Mail between machines</span></a><a class="" href="https://unionmadeservices.com"><span class="n">Union Made Services</span><span class="d">The shop — sites, storefronts and tools for locals</span></a><a class="" href="https://union.ink"><span class="n">Union.ink</span><span class="d">Union labor, on contract</span></a><a class="" href="https://freedanode.com"><span class="n">FreeDaNode</span><span class="d">The multi-witness grounding network behind FreeDaTime. Free forever, open source, attribution only.</span></a><a class="" href="https://unionmadesystems.com"><span class="n">Union Made Systems</span><span class="d">The rails Union Made Services runs on</span></a><a class="" href="https://unionmadetrades.com"><span class="n">Union Made Trades</span><span class="d">The register of union, allied and union-supporting businesses across every trade. Issues no label of its own.</span></a><a class="" href="https://unionprintingtrades.com"><span class="n">Union Printing Trades</span><span class="d">A public register of Union Made Trades shops. Every entry records who verified it and when.</span></a><a class="" href="https://abeatbykai.com"><span class="n">A Beat By Kai</span><span class="d">Held</span></a><a class="" href="https://doodles-noodles.com"><span class="n">Doodles &amp; Noodles</span><span class="d">Draw without a plan, play without a plan. A sketchbook from Studio X Ink.</span></a><a class="" href="https://foundry.ink"><span class="n">Foundry.Ink</span><span class="d">Custom lettering, hand-inked art and typefaces cut to brief.</span></a><a class="" href="https://loveartrobots.com"><span class="n">LOVE-ART-ROBOTS</span><span class="d">LOVE is why. ART is what. ROBOTS are how. A creative promotional project from Studio X Ink.</span></a><a class="" href="https://shipwerx.com"><span class="n">ShipWerx</span><span class="d">The shipping rail</span></a><a class="" href="https://studiox.ink"><span class="n">Studio X Ink</span><span class="d">The creative imprint of Studio X — art, design, graphics, robots. The way into the work.</span></a><a class="" href="https://union-delivery.com"><span class="n">Union Delivery</span><span class="d">Chartered local delivery</span></a><a class="" href="https://wearx.fyi"><span class="n">Wear X</span><span class="d">Boutique merchandising, branding and product development. Brand, product and production under one roof.</span></a> emits TWO spans and no text node:
   <a><span class="n">Name</span><span class="d">note</span></a>. So `a span` caught
   the NAME as well and rendered it muted, small and 400-weight — name and note
   became indistinguishable. .fam already made this distinction in the page grid;
   the sheet never did. Same markup, two different stylings, one of them wrong. */
.sheet .sc a .n{display:block;color:var(--txt);font-size:.94rem;font-weight:620;margin-top:0}
.sheet .sc a .d{display:block;color:var(--mut);font-size:.81rem;font-weight:400;margin-top:.12rem}
/* ── CONTACT ROW (KAI 260812) ────────────────────────────────────────────────
   "if i click there is no automated mail client modal or email launcher?"
   The href IS a real mailto — verified resolving in-browser. But mailto only
   opens something if the OS or browser has a handler registered, and plenty of
   desktops have none. An imprint's whole job is being contactable, so it must
   not depend on that. The address is now also copyable in one tap.           */
/* ── THE CATALOG TAB — a page deck (KAI 260812) ───────────────────────────────
   KAI's architecture, and it is the right one: the RAIL is the network layer
   (lookup, fast, sparse) and PAGE-FAITHFUL lives in exactly one tab. That gives
   one place where fidelity is the goal and everywhere else where it is not, so
   the KB never degrades into a slow PDF viewer.
   It also makes the Mini Print Engine tractable: this tab IS the print target.
   Whatever renders here emits the PDF. Nothing else on the door thinks about
   pagination, bleed or spreads.
   HONEST LIMIT: page-faithful is not sheet-identical. A two-page spread is
   unreadable on a phone. Spreads on wide screens, one page on narrow -- the
   design language matches per page; the physical sheet does not.            */
.deck{display:flex;flex-direction:column;gap:.6rem;min-height:0}
.decknav{display:flex;align-items:center;gap:.5rem;flex:none}
.decknav button{cursor:pointer;background:var(--panel2);border:1px solid var(--line);
  border-radius:4px;color:var(--dim);font:800 .6rem/1 var(--mono);letter-spacing:.1em;
  min-height:40px;padding:0 .8rem;transition:border-color .2s,color .2s}
.decknav button:hover:not(:disabled){border-color:var(--accent-ink);color:var(--accent-ink)}
.decknav button:disabled{opacity:.35;cursor:default}
.decknav .pno{margin-left:auto;font:800 .58rem/1 var(--mono);letter-spacing:.14em;color:var(--mut)}
.deckview{border:1px solid var(--line);border-radius:6px;background:var(--panel);
  overflow:auto;-webkit-overflow-scrolling:touch;padding:1.1rem}
.page{display:none}
.page.on{display:block}
.page h4{font:800 clamp(1.5rem,5vw,2.2rem)/1 var(--disp);letter-spacing:-.01em;
  text-transform:uppercase;color:var(--accent-ink);margin-bottom:.15rem}
.page .kick{font:800 .56rem/1 var(--mono);letter-spacing:.2em;text-transform:uppercase;
  color:var(--mut);margin-bottom:.9rem}
/* ONE image per spread. KAI 260812: "the visuals are there for human connection
   so its a good start the rest is just text links and data tiles we controll."
   So the picture does the human job and nothing else — no captions, no collage,
   no carousel. Everything that carries information is text or a tile. Lazy and
   width-bound so a phone on a shop floor is not paying for it. */
.page figure{margin:0 0 1rem;border-radius:6px;overflow:hidden;border:1px solid var(--line);
  background:var(--panel2)}
.page figure img{display:block;width:100%;height:auto;aspect-ratio:16/7;object-fit:cover}
.page .cols{display:grid;gap:1rem;grid-template-columns:1fr}
@media (min-width:820px){
  /* TRAY INSET IS A WIDE-SCREEN LUXURY (KAI 260815). Insetting the tray by both
     rails costs ~46px, which a 430px phone cannot spare — the bar got tight and
     its end cues crowded. On a phone the tray spans FULL WIDTH and rides OVER
     the rails; it already sits above them (z 70 vs 69). From 820px up there is
     room, and the rails read continuously top to bottom. */
  /* the tray spans with the other horizontal bars — KAI 260815 */.page .cols{grid-template-columns:1fr 1fr}}
.page .blk{border-top:2px solid var(--accent);padding-top:.6rem;min-width:0}
/* A block holding a swatch grid SPANS THE SPREAD (fix 260812). KAI's screenshot
   showed one chip per row on desktop, and the chips were already obeying
   max-width:150px -- so flex-wrap was fine and the CONTAINER was wrong. At
   820px+ .cols is two columns, which left a 32-swatch block in a ~134px track
   where exactly one chip fits. Correct behaviour for that width; wrong width for
   that content. Colour grids get the full spread. */
.page .blk.wide{grid-column:1 / -1}
.page .blk h5{font:800 .68rem/1.2 var(--mono);letter-spacing:.12em;text-transform:uppercase;
  color:var(--txt);margin-bottom:.35rem}
.page .blk p{color:var(--dim);font-size:.84rem;line-height:1.55;margin-bottom:.6rem}
/* ── SOLO MODE — the catalog as its own micro-site (KAI 260812) ───────────────
   "maybe that is a micro mini website portal where the catalog acts like a
    website in a website with proper header, nav, and potential footer ... later
    be able to click and launch it out into its own browser window."
   Same build, same JSON, one class on <html>. Set by hostname (printshop-guide)
   or ?view=catalog. Nothing is duplicated: a second artifact is a second thing
   to keep in sync, and the whole point of this estate is that there is one. */
/* ── THE BOARD (KAI 260812) — kanban on the rail, same tiles as everywhere ────
   "feels like you want a kanban style dashboard on the rail - i never thought
    of that ... well maybe we did with the cadre-hub."
   We did. A board is not a new app: it is .st tiles grouped by state. Same
   component as the status rows and the imprint specs, so a shop that can read
   one surface can read this one. Columns scroll sideways on a phone rather than
   collapsing, because a board that reflows into one list stops being a board. */
.board-cols{display:grid;grid-auto-flow:column;grid-auto-columns:minmax(230px,1fr);
  gap:.6rem;overflow-x:auto;-webkit-overflow-scrolling:touch;padding-bottom:.5rem;
  scroll-snap-type:x proximity}
.bcol{min-width:0;scroll-snap-align:start;border:1px solid var(--line);border-radius:6px;
  background:var(--panel);display:flex;flex-direction:column}
.bcol > h6{font:800 .54rem/1 var(--mono);letter-spacing:.16em;text-transform:uppercase;
  color:var(--accent-ink);padding:.7rem .8rem .55rem;border-bottom:1px solid var(--line);
  display:flex;justify-content:space-between;gap:.5rem}
.bcol > h6 b{color:var(--mut);font-weight:800}
.bcol .st{padding:.7rem .8rem;grid-template-columns:auto 1fr;gap:.6rem}
.bcol .st .tag{display:none}
.bcol .st .n{font-size:.86rem}
.bcol .st .d{font-size:.76rem}
.bcol .empty{padding:.9rem .8rem;color:var(--mut);font-size:.74rem}
/* SOLO KEEPS THE RAIL. KAI 260812: "the catalog may need to carry the same PWA
   Rail-Works structure so as to never stress out the user for another UXUI thing
   they need to learn."
   Correct, and my first pass was wrong: it hid the whole door and left a second
   interface with no rail, no tabs, no gear, no drawer. Popping out must change
   the CONTENT, not the vocabulary.
   So solo hides only the door's own front-of-house sections and promotes the
   catalog into the page body. Rail, trigger, bar stack, gear, settings and the
   footer drawer all stay exactly where the user already found them, and every
   other tab still slides out over the catalog as usual. */
/* ── FACE MODE (KAI 260812) ───────────────────────────────────────────────────
   "lets make LOVEARTROBOTS.com just be the face of LAR".
   Same build, same rail, no boards. Solo strips the door's FOH to promote ONE
   sheet; face strips it to promote ONE section -- the mark itself. A door opts
   in by declaring "face": {...} and the mode turns on by hostname or ?view=face.
   The rail stays: KAI, "never stress out the user for another UXUI thing they
   need to learn." A face is a different first view, not a different site. */
.faceonly{display:none}
html.face .faceonly{display:grid}
html.face .hero,html.face #status,html.face #boh,html.face #works,
html.face #encounter{display:none!important}
html.face main .inner{display:grid;place-items:center;min-height:78dvh}
/* An accessible name for a mark made of punctuation. A screen reader saying
   "black heart suit plus pencil" is not the brand. */
.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;
  clip:rect(0 0 0 0);clip-path:inset(50%);white-space:nowrap;border:0}
html.solo .hero,html.solo #status,html.solo #boh,html.solo #works{display:none!important}
html.solo #sheet-catalog{position:static;inset:auto;width:auto;max-width:none;transform:none;
  opacity:1;visibility:visible;border:0;border-radius:0;box-shadow:none;background:transparent;
  display:block;padding:0;margin:0 0 0 calc(var(--rail-min) + var(--safe-l))}
html.solo #sheet-catalog h3{display:flex;align-items:baseline;gap:.7rem;
  padding:1rem 1.2rem .1rem;font-size:.66rem}
html.solo #sheet-catalog h3::after{content:attr(data-brand);font:400 .6rem/1 var(--sans);
  letter-spacing:.04em;color:var(--mut);text-transform:none}
html.solo #sheet-catalog .sub{padding:0 1.2rem .8rem}
html.solo #sheet-catalog .sc{overflow:visible;padding:0 1.2rem 1.2rem;display:block}
html.solo .deckview{max-width:1100px;margin:0 auto;width:100%;padding:1.6rem;
  max-height:none;overflow:visible}
html.solo .decknav{max-width:1100px;margin:0 auto;width:100%;position:sticky;top:0;z-index:5;
  background:var(--bg);padding:.6rem 0}
html.solo .page .cols{grid-template-columns:1fr}
@media (min-width:760px){html.solo .page .cols{grid-template-columns:1fr 1fr}}
@media (min-width:1180px){html.solo .page figure img{aspect-ratio:21/6}}
.solofoot{display:none}
html.solo .solofoot{display:flex;align-items:center;gap:.8rem;flex-wrap:wrap;
  padding:.9rem 0 1.4rem;border-top:1px solid var(--line);
  font:400 .68rem/1.4 var(--sans);color:var(--mut);max-width:1100px;margin:0 auto;width:100%}
.solofoot a{color:var(--accent-ink);text-decoration:none;border-bottom:1px solid currentColor}
/* POP OUT — only shown when we are NOT already solo. */
.popout{margin-left:.4rem;cursor:pointer;background:var(--panel2);border:1px solid var(--line);
  border-radius:4px;color:var(--dim);font:800 .55rem/1 var(--mono);letter-spacing:.1em;
  min-height:40px;padding:0 .7rem;transition:border-color .2s,color .2s}
.popout:hover,.popout:focus-visible{border-color:var(--accent-ink);color:var(--accent-ink)}
html.solo .popout{display:none}
/* A page picker — with seven spreads, prev/next alone is not navigation. */
.pgo{margin-left:.5rem;background:var(--panel2);border:1px solid var(--line);border-radius:4px;
  color:var(--dim);font:600 .68rem/1 var(--sans);min-height:40px;padding:0 .5rem;cursor:pointer}
.pgo:focus-visible{border-color:var(--accent-ink);outline:none}
/* ── CATALOG COMPONENTS (260812) — swatch grid + price table ─────────────────
   The two things a print catalog has that a door did not. Both stay inside the
   existing sheet, both use the existing tokens: nothing new to learn, and a
   shop's catalog looks like the rest of the estate on sight. */
/* FLEX WRAP, not grid auto-fill (fix 260812, KAI on iPad/iPhone: tiles would not
   sit side by side and "should always respond to side by side tile stacking like
   flex tiles resorting themselves").
   WHY GRID FAILED: repeat(auto-fill,minmax(84px,1fr)) sets the track MINIMUM to
   84px, but a track still cannot shrink below its item's min-content width. .sw
   carried min-width:0; the <span> inside it did NOT, so a long color name plus a
   7-character hex pushed min-content well past 84px and auto-fill fitted one
   column. It looked width-driven and was actually content-driven, which is why it
   broke on a WIDE screen and behaved on a narrow one.
   Flex wrap has no such trap: flex-basis is a starting size, items wrap whenever
   they run out of room, and min-width:0 all the way down lets them shrink. */
.swgrid{display:flex;flex-wrap:wrap;gap:.4rem;min-width:0}
.sw{flex:1 1 84px;max-width:150px;display:flex;flex-direction:column;gap:.3rem;min-width:0}
.sw i{display:block;height:38px;border-radius:4px;border:1px solid var(--line)}
.sw.est i{border-style:dashed;border-color:var(--mut)}
.sw span{font:600 .58rem/1.25 var(--sans);color:var(--dim);overflow-wrap:anywhere;
  display:flex;flex-direction:column;gap:.1rem;min-width:0}
.sw span b{font:500 .52rem/1 var(--mono);letter-spacing:.02em;color:var(--mut);
  overflow-wrap:anywhere;min-width:0}
.swnote{margin-top:.8rem;color:var(--mut);font-size:.72rem;line-height:1.5}
.ptwrap{overflow-x:auto;-webkit-overflow-scrolling:touch;border:1px solid var(--line);
  border-radius:4px;background:var(--panel)}
.ptable{border-collapse:collapse;width:100%;font:600 .74rem/1.2 var(--sans);white-space:nowrap}
.ptable th,.ptable td{padding:.55rem .7rem;border-bottom:1px solid var(--line);text-align:right}
.ptable thead th{position:sticky;top:0;background:var(--panel2);color:var(--accent-ink);
  font:800 .56rem/1 var(--mono);letter-spacing:.1em;text-transform:uppercase}
.ptable tbody th{text-align:left;color:var(--txt);background:var(--panel2)}
.ptable td{color:var(--dim)}
.ptable tbody tr:last-child th,.ptable tbody tr:last-child td{border-bottom:0}
.mailrow{display:grid;grid-template-columns:1fr auto;gap:.4rem;align-items:stretch}
.mailrow > a{min-width:0}
.mcopy{cursor:pointer;border:1px solid var(--line);border-radius:4px;background:var(--panel2);
  color:var(--dim);font:800 .5rem/1 var(--mono);letter-spacing:.12em;text-transform:uppercase;
  padding:0 .7rem;min-width:3.9rem;min-height:44px;transition:border-color .2s,color .2s,background .2s}
.mcopy:hover,.mcopy:focus-visible{border-color:var(--accent-ink);color:var(--accent-ink)}
.mcopy.done{border-color:var(--ok);color:var(--ok);background:rgba(62,207,142,.08)}
.sheet .sc a:hover{border-color:var(--accent-ink)}
.sheet.k .sc a{border-left:2px solid var(--boh)}
.sheet.k h3{color:var(--boh)}

/* ── TOP BAR — the FOH id lives here ── */
header.top{position:sticky;top:0;z-index:72;   /* above the rails, which start below it */
  margin:0;                                   /* SPANS THE TOP — full site width */
  min-height:calc(var(--hdr-h) + var(--safe-t));
  background:var(--mat-head);backdrop-filter:blur(10px);border-bottom:1px solid var(--line);
  display:flex;align-items:center;gap:.85rem;min-width:0;overflow:visible;
  /* axes split: the old `padding:0 ...` shorthand silently wiped the safe-area
     inset set on the line above it. padding-block/-inline cannot collide. */
  padding-block:calc(var(--safe-t) + .6rem) .6rem;
  padding-inline:clamp(1.9rem,5vw,3.4rem) clamp(1.3rem,4vw,3.4rem)}

/* ── SITE HEADER ZONES (KAI 260815) ────────────────────────────────────────
   "at minimum 3-5 flexbox container rows so as to promote a solid place for
    header related content."
   Five named slots. Each is a flex cell; an empty one collapses to nothing, so
   a door using three pays nothing for the other two. Content goes in a ZONE,
   never appended to the bar — that is how a header stays organised as it grows.

     mark     the icon
     ident    name + stage line
     mid      flexible middle — the door's own header content
     util     utility controls
     act      actions: KEY, settings
   ------------------------------------------------------------------------- */
header.top > .hz{display:flex;align-items:center;gap:.5rem;min-width:0}
header.top > .hz:empty{display:none}
header.top > .hz-mark{flex:none}
header.top > .hz-ident{flex:0 1 auto;min-width:0;overflow:hidden}
header.top > .hz-mid{flex:1 1 auto;justify-content:center;min-width:0}
/* the controls group takes the right end; hz-act (the ⋮) follows it. The auto
   margin lives HERE now — on hz-act it would only push the last item. */
header.top > .hz-util{margin-left:auto;gap:.35rem}
header.top > .hz-util{flex:none}
/* NO auto margin here any more. hz-util carries it (260816). TWO auto margins
   SPLIT the free space between them rather than stacking, so the controls group
   landed exactly halfway across the header — 459px on a 1024px viewport — while
   the ⋮ went to the end. Only one flex item in a row may claim the slack. */
header.top > .hz-act{flex:none}

/* ── WHERE — the jurisdiction dots (KAI 260816) ────────────────────────────
   Same family as RAIL-DOTS: a pip per destination, the one you are standing
   on lit, brackets because the group is horizontal. This one NAVIGATES rather
   than steps, and it navigates ACROSS DOMAINS — so unlike the rail stepper,
   a bare pip is not enough. A person cannot predict where an unlabelled dot
   sends them, and the penalty here is leaving the site. Each pip therefore
   carries its code.

   On a narrow phone only the ACTIVE code stays visible — you always know
   where you are, and the two you could go to stay dots. That keeps the header
   from crowding the name at 375px while never hiding your own position. */
.wdots{display:inline-flex;align-items:center;gap:.05rem;
  font:800 .5rem/1 var(--mono);letter-spacing:.14em;text-transform:uppercase;
  color:var(--mut)}
.wdots::before{content:"[";margin-right:2px}
.wdots::after{content:"]";margin-left:2px}
.wdot{display:inline-flex;align-items:center;gap:4px;text-decoration:none;
  color:var(--mut);border-radius:8px;padding:.55rem .34rem;
  /* 40px, not the 24px var(--bar) the rail dots use. A rail dot is a stepper —
     miss it and you step again. This one LEAVES THE SITE, and it is aimed at a
     thumb. Cheap insurance, and the header has 67px of height to give. */
  min-height:2.5rem;transition:color .18s,background .18s}
.wdot i{display:block;width:4px;height:4px;border-radius:50%;flex:none;
  background:var(--mut);font-style:normal;transition:background .18s,transform .18s}
.wdot.on{color:var(--txt)}
.wdot.on i{background:var(--accent);transform:scale(1.35)}
.wdot:hover,.wdot:focus-visible{color:var(--txt);background:var(--mat-trig)}
.wdot:hover i,.wdot:focus-visible i{background:var(--dim)}
.wdot.on:hover i,.wdot.on:focus-visible i{background:var(--accent)}
.wdot:focus-visible{outline:1px solid var(--accent);outline-offset:1px}
@media (max-width:479px){.wdot:not(.on) .wc{display:none}}

/* ── IMPRESSUM in the header (KAI 260816) ──────────────────────────────────
   Rides `.rtab` so the existing sheet opener drives it. `.hlink` only restyles
   it for the header — it must never redefine behaviour, or the two copies of
   the control drift apart. */
.hlink{padding:.5rem .55rem;font:800 .5rem/1 var(--mono);letter-spacing:.15em;
  border:1px solid transparent;min-height:2.5rem}
.hlink[aria-expanded="true"]{color:var(--accent-ink);border-color:var(--mat-edge)}
.hlink{white-space:nowrap}
@media (max-width:389px){.hlink{padding-inline:.35rem;letter-spacing:.08em}}

/* ── THE DOT ROW IS ONE COMPONENT (KAI 260816) ─────────────────────────────
   `.hmenu` is DELETED. It was a second dot component that merely resembled the
   rail's `.rham`, and the resemblance was the problem: they looked identical
   and behaved differently — the rails' dots lit to show your position, the
   header's told you nothing until you opened them.

   Now the header uses `.rham` itself: one dot per door, the current one lit,
   opening the same mini-tray. With three doors it renders as the familiar ⋮ by
   ARITHMETIC rather than by decoration.

   KAI: "we settle on a three dot standard and leave it at that — the pop out
   serves as the mini tray of choices, very thumbable." */
.rnav{position:relative;display:inline-flex}
header.top .rham{flex-direction:column;max-width:none;max-height:none;
  align-content:center;min-width:2.5rem;min-height:2.5rem;gap:3px;
  border-radius:8px;transition:background .18s}
header.top .rham:hover{background:var(--mat-trig)}
header.top .rham::before,header.top .rham::after{content:none}

.rnavp{position:absolute;top:calc(100% + .3rem);right:0;z-index:80;
  background:var(--mat-deep);backdrop-filter:blur(14px);
  -webkit-backdrop-filter:blur(14px);
  border:1px solid var(--mat-edge);border-radius:10px;padding:.3rem;
  min-width:11rem;box-shadow:0 8px 24px rgba(0,0,0,.34)}
.rnavp[hidden]{display:none}
/* inside the panel the dots stack as a list; the brackets are a horizontal
   device and would read as noise vertically, so they go. */
.rnavp .wdots{flex-direction:column;align-items:stretch;gap:.05rem}
.rnavp .wdots::before,.rnavp .wdots::after{content:none}
.rnavp .wdot{justify-content:flex-start;gap:.5rem;padding:.5rem .55rem;min-height:2.5rem}
.rnavp .wdot .wc{display:inline!important}

/* the same panel, opened off a RAIL rather than the header — so it must open
   INWARD, away from the edge it is pinned to. A menu that opens off-screen is
   the classic edge-anchored failure and the side rails are the likeliest place
   to hit it. */
.railnavp{top:auto;bottom:0;min-width:9.5rem}
.vrail .railnavp{right:auto;left:calc(100% + .35rem)}
.rrail .railnavp{left:auto;right:calc(100% + .35rem)}
.hrail .railnavp{bottom:auto;top:calc(100% + .3rem);left:0;right:auto}
.rnavi{display:flex;align-items:center;gap:.5rem;width:100%;
  background:none;border:0;cursor:pointer;border-radius:8px;
  padding:.55rem;min-height:2.5rem;text-align:left;
  font:800 .5rem/1 var(--mono);letter-spacing:.14em;text-transform:uppercase;
  color:var(--mut);transition:color .18s,background .18s}
.rnavi i{display:block;width:4px;height:4px;border-radius:50%;flex:none;
  background:var(--mut);transition:background .18s,transform .18s}
.rnavi:hover{color:var(--txt);background:var(--mat-trig)}
.rnavi[aria-current="true"]{color:var(--txt)}
.rnavi[aria-current="true"] i{background:var(--accent);transform:scale(1.35)}
.rnavi:focus-visible{outline:1px solid var(--accent);outline-offset:-2px}
/* the dots take the accent while their chooser is open — same law as the ⋮ */
.rham[data-open] span{background:var(--dim)}
.rham[data-open] span.on{background:var(--accent)}

/* ── RAIL-TOP ACTIONS — KEY + settings, moved off the identity bar ─────────
   KAI 260816. The header is WHO; RAIL-TOP is WHERE and state. KEY and settings
   are state controls, so they sit with the state switcher. margin-left:auto
   pins them to the right end of the rail whatever the switcher's width. */
.hrail.top .ract{display:inline-flex;align-items:center;gap:.35rem;
  margin-left:auto;flex:none}
.hrail.top .tkey{margin:0}
.hrail.top .gear{margin:0}
header.top .mk{width:30px;height:30px;border-radius:50%;flex:none}
/* A photographic mark is dark to its own edge, so on a dark bar it reads as a
   floating face with no boundary. The accent ring gives the disc an edge and
   ties it to the FOH badge. Generated tiles do not need it — they are already
   a filled shape. (KAI's mark, 260804) */
header.top .mk.mark{border:1px solid var(--accent)}
header.top .nm{display:flex;flex-direction:column;min-width:0;flex:1 1 auto;overflow:hidden}
header.top .nm b{font:800 .92rem/1.1 var(--sans);letter-spacing:.02em;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
header.top .nm i{font:800 .5rem/1.4 var(--mono);letter-spacing:.17em;text-transform:uppercase;
  color:var(--mut);font-style:normal;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
header.top .sp{flex:0 1 auto;min-width:.25rem}
.foh{flex:none;font:800 .5rem/1 var(--mono);letter-spacing:.14em;text-transform:uppercase;color:var(--accent-ink);
  border:1px solid var(--accent);border-radius:3px;padding:.34rem .5rem;white-space:nowrap}
.tkey{flex:none;font:800 .5rem/1 var(--mono);letter-spacing:.12em;text-transform:uppercase;color:var(--boh);
  border:1px solid var(--line);border-radius:3px;padding:.34rem .5rem;text-decoration:none;white-space:nowrap}
@media (max-width:560px){
  header.top{gap:.5rem;padding-inline:.75rem}
}
@media (min-width:820px){
  /* KAI: 'could be thinner on the iPad ... feels thick and heavy' */
  :root{--hdr-h:3.3rem}
  header.top{padding-block:calc(var(--safe-t) + .35rem) .35rem}
  header.top .mk{width:26px;height:26px}
  header.top .nm i{font-size:.44rem;letter-spacing:.12em}
  header.top .mk{width:26px;height:26px}
  header.top .nm b{font-size:.85rem}
  .foh,.tkey{padding:.3rem .38rem;font-size:.44rem;letter-spacing:.09em}
}
@media (max-width:380px){ .top .nm i{display:none} }

main{margin-left:calc(var(--rail-min) + var(--safe-l));padding:0 clamp(1.15rem,4vw,3rem) 0 clamp(1.75rem,5vw,3rem);overflow-x:hidden;
  padding-bottom:calc(var(--safe-b) + 5rem)}
.inner{max-width:820px;min-width:0;margin:0 auto}
section{margin-top:2.6rem}
.chip{display:inline-block;font:800 .56rem/1 var(--mono);letter-spacing:.15em;text-transform:uppercase;
  background:var(--accent);color:#12100a;padding:.42rem .7rem;border-radius:3px;margin-bottom:1.1rem}
h1{font:700 clamp(1.7rem,6.2vw,4.1rem)/1.02 var(--disp);letter-spacing:-.015em;text-transform:uppercase;
  margin-bottom:1rem;overflow-wrap:break-word;word-break:normal;hyphens:auto;max-width:100%}
h1 .g{color:var(--accent-ink);display:block}
.lede{color:var(--dim);font-size:clamp(1rem,2.3vw,1.14rem);max-width:60ch}
h2{font:800 .58rem/1 var(--mono);letter-spacing:.19em;text-transform:uppercase;color:var(--mut);margin-bottom:.95rem}
.hero{padding-top:2.6rem}
/* ── THE MASTHEAD READS TOP DOWN (KAI 260812) ────────────────────────────────
   "<USER BANNER> ... .PrintShop. (is the workbench we land on a user / client /
    vendor / partner / broker)" for "<CLIENT>.PrintShop.link".
   The masthead IS the address. Tenant on top, workbench under it, dots as the
   domain seams -- you can read where you are in the namespace off the hero.
   An untenanted door shows the SLOT rather than a blank line, so a prospect can
   see the shape of the thing they would be given. */
.tenant{display:block;font:800 clamp(.72rem,2.4vw,1rem)/1.1 var(--mono);
  letter-spacing:.22em;text-transform:uppercase;color:var(--dim);
  margin:.9rem 0 .15rem;overflow-wrap:anywhere}
.tenant.slot{color:var(--mut);opacity:.8}
.tenant em{font-style:normal;color:var(--accent-ink)}
.hero h1 .dot{color:var(--accent-ink)}


.board{border:1px solid var(--line);border-radius:4px;overflow:hidden;background:var(--panel);max-width:100%}
.st{display:grid;grid-template-columns:auto 1fr auto;gap:.9rem;align-items:center;
  padding:.85rem 1rem;border-bottom:1px solid var(--line)}
.st:last-child{border-bottom:0}
.dot{width:8px;height:8px;border-radius:50%;flex:none}
.dot.on{background:var(--ok);box-shadow:0 0 0 3px rgba(62,207,142,.13)}
.dot.work{background:var(--accent);box-shadow:0 0 0 3px rgba(216,164,60,.13)}
.dot.off{background:var(--off)}
.st .n{font-weight:600;min-width:0;overflow-wrap:anywhere}
.st .d{display:block;color:var(--mut);font-size:.85rem;font-weight:400;margin-top:.1rem}
.st .tag{font:800 .48rem/1 var(--mono);letter-spacing:.1em;text-transform:uppercase;color:var(--mut);white-space:nowrap}

.key{display:flex;gap:1rem;align-items:center;justify-content:space-between;flex-wrap:wrap;
  border:1px solid var(--line);border-left:3px solid var(--boh);border-radius:4px;
  background:var(--panel);padding:1.05rem 1.15rem}
.key .t{font-weight:650}
.key .s{display:block;color:var(--mut);font-size:.87rem;margin-top:.15rem}
.key a{display:inline-block;min-height:44px;line-height:1.6;background:var(--boh);color:var(--bg);text-decoration:none;
  font:800 .58rem/1 var(--mono);letter-spacing:.14em;text-transform:uppercase;
  padding:.75rem 1.05rem;border-radius:3px;white-space:nowrap}

.bohgrid{display:grid;gap:.55rem;grid-template-columns:repeat(auto-fill,minmax(180px,1fr));margin-top:.7rem}
.bohgrid a{display:block;border:1px solid var(--line);border-left:2px solid var(--boh);border-radius:4px;
  background:var(--panel);padding:.75rem .85rem;min-height:44px;text-decoration:none;font-weight:620;font-size:.9rem}
.bohgrid a span{display:block;color:var(--mut);font-size:.78rem;font-weight:400;margin-top:.1rem}
/* Hardened 260812 alongside .sheet .sc a — identical fragile shape: this rule
   assumes ONE span with the name in a bare text node. It is correct today, and
   was one .n/.d emitter away from the exact bug KAI found in the Works sheet.
   Naming the parts costs nothing and removes the trap. */
.bohgrid a .n{display:block;color:var(--txt);font-size:.94rem;font-weight:620;margin-top:0}
.bohgrid a .d{display:block;color:var(--mut);font-size:.81rem;font-weight:400;margin-top:.12rem}
.bohgrid a:hover{border-color:var(--boh)}
.fam{display:grid;gap:.6rem;grid-template-columns:repeat(auto-fill,minmax(225px,1fr))}
.fam a{display:block;border:1px solid var(--line);border-radius:4px;background:var(--panel);
  padding:.85rem .95rem;min-height:44px;text-decoration:none}
.fam a:hover{border-color:var(--accent-ink)}
.fam .n{font-weight:620;font-size:.94rem}
.fam .d{display:block;color:var(--mut);font-size:.81rem;margin-top:.12rem}
.fam .off{opacity:.5}

/* ── FLOATING FOOTER DRAWER — slides up over the content ─────────────────────
   Live test of the drawer pattern. Closed it is a slim handle pinned to the
   bottom; open it floats above everything without pushing the page. */
.drawer{position:fixed;left:0;right:0;bottom:calc(var(--ftr-h) + var(--safe-b));z-index:70;
  background:var(--mat-deep);backdrop-filter:blur(14px);border-top:1px solid var(--line);
  transform:translateY(calc(100% - 2.9rem));transition:transform .34s cubic-bezier(.32,.72,0,1);
  padding-bottom:var(--safe-b);max-height:min(78dvh,560px);display:flex;flex-direction:column}
.drawer.open{transform:translateY(0)}

/* ── RAIL-TOP TRAY — the location board (KAI 260816) ───────────────────────
   The base drawer, mirrored. It slides DOWN from behind RAIL-TOP, so its
   z-index sits BELOW the rails (68) and the header (72): closed, it is not
   merely hidden, it is genuinely behind them, which is why no clip-path or
   overflow trick is needed. Opened from the ▼ cue on RAIL-TOP, closed by the
   ▲ cues on its own bottom edge — the same RAIL-CUE primitive as the base
   tray, pointing the other way. */
.tdrawer{position:fixed;left:0;right:0;top:calc(var(--railstop-t) + var(--safe-t));
  /* z-70 = the SAME layer as the base drawer. At 67 it sat under the side rails
     and they painted over its edges — the first and last character of every
     line was clipped, and the right-hand column vanished under the CLOCK rail.
     A drawer is a surface that comes forward; it does not slide under furniture. */
  z-index:70;background:var(--mat-deep);backdrop-filter:blur(var(--mat-blur));
  -webkit-backdrop-filter:blur(var(--mat-blur));border-bottom:1px solid var(--line);
  /* CLOSED MUST MEAN GONE. translateY(-100%) moves the panel up by its OWN
     height, which lands its BOTTOM edge exactly on `top` — and the handle lives
     at the bottom, so a 28px strip stayed on screen COVERING RAIL-TOP once this
     surface was raised to z-70. Subtracting the offset too puts the bottom edge
     at 0, behind the header (z-72). The base drawer wants a visible closed
     handle; this one does not — RAIL-TOP's ▼ cue is its opener. */
  transform:translateY(calc(-100% - var(--railstop-t) - var(--safe-t)));
  transition:transform .34s cubic-bezier(.32,.72,0,1);
  max-height:min(72dvh,540px);display:flex;flex-direction:column}
.tdrawer.open{transform:translateY(0)}
.tdh{flex:none;display:flex;align-items:center;gap:.45rem;
  padding:.25rem clamp(1.15rem,4vw,3rem);border-top:1px solid var(--mat-edge)}
.tdh .lbl{flex:1 1 auto;text-align:center;font:800 .54rem/1 var(--mono);
  letter-spacing:.16em;text-transform:uppercase;color:var(--dim)}
.tdrawer.open .rcue{color:var(--accent-ink)}
.tdbody{overflow:auto;padding:.9rem clamp(1.15rem,4vw,3rem) .2rem;min-height:0}
.ksub{margin:0 0 .7rem;font:400 .76rem/1.4 var(--sans);color:var(--mut);max-width:62ch}

/* THE BOARD. Columns are PLACES, not stages — a stage board cannot show that
   volume moved from Reno to Phoenix. Scrolls sideways inside its own box so
   the page body never scrolls horizontally. */
.kan{display:flex;gap:.7rem;overflow-x:auto;padding-bottom:.8rem;
  scroll-snap-type:x proximity;scrollbar-width:thin}
.kcol{flex:0 0 min(80vw,254px);scroll-snap-align:start;display:flex;flex-direction:column;
  gap:.5rem;background:var(--panel);border:1px solid var(--line);border-radius:12px;padding:.6rem}
.kcol h3{display:flex;align-items:center;gap:.4rem;margin:0;min-width:0}
.kname{font:800 .56rem/1 var(--mono);letter-spacing:.15em;color:var(--txt);white-space:nowrap}
.knote{flex:1 1 auto;min-width:0;font:600 .64rem/1.2 var(--sans);color:var(--mut);
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.kct{flex:none;font:800 .54rem/1 var(--mono);color:var(--mut);background:var(--panel2);
  border-radius:999px;padding:.22rem .42rem;font-variant-numeric:tabular-nums}
.kcards{display:flex;flex-direction:column;gap:.45rem}
.kcard{display:flex;flex-direction:column;gap:.18rem;background:var(--panel2);
  border:1px solid var(--line);border-left:2px solid var(--accent);
  border-radius:8px;padding:.5rem .55rem}
.kcard[data-kstate="hold"]{border-left-color:var(--mut)}
.kcard[data-kstate="live"]{border-left-color:var(--ok)}
.ktag{font:800 .47rem/1 var(--mono);letter-spacing:.14em;text-transform:uppercase;color:var(--accent-ink)}
.kcard[data-kstate="hold"] .ktag{color:var(--mut)}
.kcard[data-kstate="live"] .ktag{color:var(--ok)}
.kn{font:700 .8rem/1.25 var(--sans);color:var(--txt)}
.kd{font:400 .73rem/1.35 var(--sans);color:var(--mut)}
.hrail.top .rcue.tcue{min-width:1.9rem;min-height:1.9rem;font-size:1rem;margin-inline:.1rem}
/* THE RAIL-CUE POINTS WHERE ITS SURFACE WILL TRAVEL NEXT — one rule, every
   tray, both edges. KAI 260816: "the rail-tray open/close primitives need to be
   consistent on both sides of the rails where the trays function · a nice
   rotating triangle for direction · having them as bookends helps thumb control."
   Closed, the top cue points DOWN because the tray comes down. Open, it rotates
   to point UP because that is where the tray goes next. The base tray has always
   done exactly this; the top one only borrowed the colour, not the motion. */
.hrail.top .rcue.tcue[aria-expanded="true"]{color:var(--accent-ink);transform:rotate(180deg)}
.dhandle{height:auto;min-height:calc(var(--bar) + .5rem);flex:none;display:flex;align-items:center;gap:.7rem;
  padding-inline:clamp(1.15rem,4vw,3rem);
  cursor:pointer;background:none;border:0;color:var(--txt);width:100%;text-align:left}
.dhandle .lbl{font:800 .54rem/1 var(--mono);letter-spacing:.16em;text-transform:uppercase;color:var(--dim)}
.dhandle{}
/* ── RAIL-CUE — the directional primitive (KAI 260815, restyled 260816) ─────
   KAI named it himself: "they could be used as directional thumb control cues
   throughout the Rail-Works system." So the name was never open — these are
   RAIL-CUES, the pair to RAIL-DOTS. Dots say WHERE YOU ARE; cues say WHERE IT
   GOES.

   THE BUG THIS RULE JUST ESCAPED. It was written as

       .dhandle /* comment *(/ .rcue{ ... }

   — a comment placed after a selector with no `{`, so the next selector glued
   on as a DESCENDANT. The whole primitive was scoped to `.dhandle`, meaning the
   RAIL-TOP cues got none of it: no pill, no transition, and no 44px target.
   KAI saw the symptom ("not looking very clean") before any check did, because
   the CSS text reads correctly and only PARSES wrong.

   KAI 260816: bigger arrows, transparent container, colour behaviour matching
   RAIL-DOTS. So the container is now nothing at all — no border, no fill — and
   the glyph carries the whole state: --mut at rest, --dim on hover, --accent
   only while its surface is open. Colour means ACTIVITY, never decoration.
   The 44px hit area survives as an invisible target, which is the point of
   separating the box from the mark. */
.rcue{display:inline-flex;align-items:center;justify-content:center;flex:none;
  min-width:44px;min-height:44px;border-radius:999px;
  border:0;background:transparent;color:var(--mut);
  cursor:pointer;font-size:1.15rem;line-height:1;padding:0;
  transition:transform .34s cubic-bezier(.32,.72,0,1),color .18s}
.rcue:hover,.rcue:focus-visible{color:var(--dim)}
.rcue:focus-visible{outline:1px solid var(--accent);outline-offset:-4px}
.rcue.r{margin-left:auto}

/* ── RAIL-FOOTER (KAI 260816) ──────────────────────────────────────────────
   "A sticky footer same as all websites as a location to test — Rail-Footer —
    so we have the viewport fenced in by a header and a footer. Very clear
    location terms."

   I had argued against a second bar at the bottom edge, because RAIL-BASE lives
   there and one-edge-one-meaning is what makes the frame learnable. KAI's
   framing dissolves that: the header and the footer are SITE CHROME and they
   FENCE the viewport; the four rails live inside the fence. So this is not a
   rival to RAIL-BASE — it is the closing bracket to RAIL-HEADER, and the pair
   reads as one idea.

   RAIL-HEADER answers WHO. RAIL-FOOTER answers WHERE.

   It is the lowest thing on the page, so RAIL-BASE and both side rails now stop
   above it (--railstop-b and the drawer both derive from --ftr-h). Nothing
   overlaps by construction rather than by remembering to check. */
.rfooter{position:fixed;left:0;right:0;bottom:0;z-index:71;
  min-height:calc(var(--ftr-h) + var(--safe-b));
  padding:0 clamp(.6rem,3vw,1.4rem) var(--safe-b);
  display:flex;align-items:center;gap:.5rem;
  background:var(--mat-head);backdrop-filter:blur(10px);
  -webkit-backdrop-filter:blur(10px);border-top:1px solid var(--mat-edge);
  font:800 .5rem/1 var(--mono);letter-spacing:.14em;text-transform:uppercase;
  color:var(--mut);overflow:hidden;white-space:nowrap}
.rfooter .fseg{flex:none}
.rfooter .fsep{flex:none;opacity:.5}
/* the live part — where you actually are. Accent, because it changes. */
.rfooter .fcrumb{display:flex;align-items:center;gap:.3rem;min-width:0;
  flex:0 1 auto;overflow:hidden}
/* A LIVE CRUMB LOOKS LIKE A CONTROL; THE LEAF DOES NOT. That difference is the
   whole reason breadcrumbs are readable — if every segment looks tappable, none
   of them tells you where you are. */
.rfooter .fbc{font:inherit;letter-spacing:inherit;text-transform:inherit;
  background:none;border:0;padding:.55rem .3rem;min-height:2.75rem;
  display:inline-flex;align-items:center;border-radius:6px;
  color:var(--dim);cursor:pointer;white-space:nowrap;
  transition:color .18s,background .18s}
.rfooter .fbc:hover,.rfooter .fbc:focus-visible{color:var(--accent-ink);background:var(--mat-trig)}
.rfooter .fbc:focus-visible{outline:1px solid var(--accent);outline-offset:-2px}
.rfooter .fbc.leaf{color:var(--accent-ink);cursor:default;padding-inline:.2rem;
  overflow:hidden;text-overflow:ellipsis;min-width:0}
/* an inert ANCESTOR — true but not where you are. Quiet, and not tappable. */
.rfooter .fbc.static{color:var(--mut);cursor:default;padding-inline:.2rem}
.rfooter .fbc.leaf:hover,.rfooter .fbc.static:hover{background:none;color:inherit}
/* THE IDENTIFIER IS THE WAY HOME (KAI 260816). It was plain text and, worse,
   it was DISPLAY:NONE below 480px — so the one part he wanted thumbable did not
   exist where thumbs are. A control hidden on the phone is not a control.

   It stays at every width now. What gives way instead is the "RAIL-WORKS /"
   prefix, which is decoration: on a narrow screen the useful facts are WHERE
   YOU ARE and HOW TO GET BACK. Never drop the control to keep the ornament. */
.rfooter .fend{margin-left:auto;flex:none;text-decoration:none;
  color:var(--mut);min-height:2.75rem;display:inline-flex;align-items:center;
  padding-inline:.6rem;margin-right:calc(-1 * clamp(.6rem,3vw,1.4rem));
  border-radius:8px;transition:color .18s,background .18s;
  overflow:hidden;text-overflow:ellipsis;min-width:0}
/* KAI 260816: "the little icons need to be orange to show a state other than
   home." So the glyph is the STATE and the accent is reserved for the one that
   is not home — you are away from the apex, and this takes you back. On the
   apex the mark stays muted because nothing is true that was not already true.
   Same law as everywhere else on the bench: colour means a live state, never
   decoration. */
.rfooter .fend::before{content:"↖";margin-right:.4rem;font-size:.8rem;
  color:var(--accent-ink);opacity:1}
.rfooter .fend:hover,.rfooter .fend:focus-visible{color:var(--accent-ink);background:var(--mat-trig)}
.rfooter .fend:focus-visible{outline:1px solid var(--accent);outline-offset:-2px}
/* on the apex itself it is still the way back to the front page, but it does not
   pretend to take you somewhere else. */
.rfooter .fend[data-self]::before{content:"⌂";color:var(--mut);opacity:.8}
@media (max-width:479px){
  .rfooter .fseg,.rfooter .fsep{display:none}
  .rfooter .fend{padding-inline:.5rem}
}
/* ACTIVE — the surface this cue controls is open */
/* A CUE POINTS WHERE ITS SURFACE WILL TRAVEL NEXT.
   Base tray: ▲ closed (it comes up) → rotates to ▼ open (it goes down).
   Top tray:  ▼ closed (it comes down) → rotates to ▲ open (it goes up).
   The top drawer's own handle cues are ▲ and only exist while it is open —
   they already point the way it will travel, so they take the colour and NOT
   the rotation. Rotating them would aim them at nothing. */
.drawer.open .rcue{color:var(--accent-ink);transform:rotate(180deg)}
.dhandle{gap:.45rem}
.dtabs{display:flex;justify-content:center;gap:.3rem;min-width:0;flex:1 1 auto;container-type:inline-size}
.dtabs::-webkit-scrollbar{display:none}
/* the label is the fallback when a door declares no tabs; with tabs it would
   fight them for the same strip, so it steps aside. */
.dtabs + .lbl{display:none}
@media (max-width:420px){ .rtab{padding:.5rem .42rem;letter-spacing:.09em} }
.dhandle .lbl{flex:0 1 auto;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.dbody{overflow-y:auto;-webkit-overflow-scrolling:touch;overscroll-behavior:contain;padding:0 clamp(1.15rem,4vw,3rem) 1.4rem;color:var(--mut);font-size:.87rem}
.dbody>*{max-width:820px;margin-inline:auto}
.dbody p{margin-bottom:.5rem}
.dbody .row{display:grid;gap:.55rem;grid-template-columns:repeat(auto-fill,minmax(190px,1fr));margin:.7rem 0 1rem}
.dbody .row a{border:1px solid var(--line);border-radius:4px;padding:.7rem .75rem;min-height:44px;text-decoration:none;
  color:var(--txt);font-size:.86rem;font-weight:600;display:flex;align-items:center}
.dbody .row a:hover{border-color:var(--accent-ink)}
.dpb{border-top:1px solid var(--line);padding-top:.8rem;margin-top:.3rem}
.dpb a{color:var(--dim);font-weight:600}
/* ── GEAR + SETTINGS. The circle in the top right was decoration pretending to
   be a control; KAI called it. Now it opens the machine. (KAI 260804) ── */
.gear{position:relative;flex:none;width:32px;height:32px;border-radius:50%;cursor:pointer;
  border:1px solid var(--line);background:var(--panel2);color:var(--dim);
  display:grid;place-items:center;padding:0;transition:color .2s,border-color .2s,transform .3s}
.gear:hover{color:var(--accent-ink);border-color:var(--accent-ink)}
.gear[aria-expanded="true"]{color:var(--accent-ink);border-color:var(--accent-ink);transform:rotate(60deg)}
.gear svg{width:16px;height:16px;display:block}
.gear::after{content:"";position:absolute;inset:-6px;border-radius:50%}
.tkey{position:relative}
.tkey::after{content:"";position:absolute;left:-4px;right:-4px;top:-12px;bottom:-12px}
.sset{position:fixed;top:0;bottom:0;right:0;width:min(330px,88vw);z-index:72;
  background:var(--panel);border-left:1px solid var(--line);
  transform:translateX(102%);opacity:0;
  transition:transform .34s cubic-bezier(.32,.72,0,1),opacity .22s ease-out;
  display:flex;flex-direction:column;padding-top:var(--safe-t)}
.sset.open{transform:translateX(0);opacity:1;box-shadow:-6px 0 20px rgba(0,0,0,.2)}
.sset h3{font:800 .58rem/1 var(--mono);letter-spacing:.18em;text-transform:uppercase;
  color:var(--accent-ink);padding:1.15rem 1.15rem .8rem;border-bottom:1px solid var(--line);
  display:flex;align-items:center;gap:.6rem}
.sset h3 button{margin-left:auto;background:none;border:0;color:var(--mut);cursor:pointer;
  font:800 1rem/1 var(--mono);padding:0 .6rem;min-width:44px;min-height:44px}
.sset .sc{overflow-y:auto;overscroll-behavior:contain;padding:1.15rem;display:grid;gap:1.3rem;align-content:start}
.sfield>span{display:block;font:800 .52rem/1 var(--mono);letter-spacing:.15em;text-transform:uppercase;
  color:var(--mut);margin-bottom:.5rem}
.sfield>em{display:block;font-style:normal;color:var(--mut);font-size:.76rem;line-height:1.5;margin-top:.45rem}
.seg{display:flex;gap:.3rem}
.seg button{flex:1;min-height:44px;cursor:pointer;border:1px solid var(--line);background:var(--panel2);
  color:var(--dim);border-radius:4px;font:800 .58rem/1 var(--mono);letter-spacing:.1em;text-transform:uppercase}
.seg button[aria-pressed="true"]{border-color:var(--accent-ink);color:var(--accent-ink);background:#231c10}
.cfield{display:flex;align-items:center;gap:.5rem;margin-top:.45rem}
.cfield[hidden]{display:none}
.cfield input[type="color"]{inline-size:2.6rem;block-size:2.2rem;padding:0;border:1px solid var(--line);
  border-radius:8px;background:none;cursor:pointer}
.cfield button{font:800 .5rem/1 var(--mono);letter-spacing:.14em;text-transform:uppercase;
  background:none;border:1px solid var(--line);border-radius:8px;color:var(--mut);
  padding:.5rem .6rem;min-height:2.2rem;cursor:pointer}
.cfield button:hover{color:var(--txt);border-color:var(--mut)}
.sset .stamp{font:.66rem/1.6 var(--mono);color:var(--mut);border-top:1px solid var(--line);padding-top:.9rem}
html.nomo *,html.nomo *::before,html.nomo *::after{transition-duration:0s!important;animation-duration:0s!important}
@media (prefers-reduced-motion:reduce){*{transition-duration:.01ms!important}}
.scrim{position:fixed;inset:0;background:rgba(6,9,12,.55);opacity:0;pointer-events:none;
  transition:opacity .3s;z-index:65}
.scrim.on{opacity:1;pointer-events:auto}



  .cwrap{display:grid;gap:1.1rem;grid-template-columns:minmax(0,1fr) minmax(0,1fr)}
  @media (max-width:820px){.cwrap{grid-template-columns:1fr}}
  fieldset{border:1px solid var(--line);border-radius:4px;padding:1rem;background:var(--panel);margin-bottom:.9rem}
  legend{font:800 .55rem/1 var(--mono);letter-spacing:.15em;text-transform:uppercase;color:var(--accent-ink);padding:0 .4rem}
  label{display:block;margin-bottom:.75rem}
  label span{display:block;font-size:.74rem;color:var(--mut);margin-bottom:.28rem}
  input,textarea,select{width:100%;min-height:44px;padding:.62rem .72rem;font:inherit;font-size:16px;color:var(--txt);background:var(--panel2);border:1px solid var(--line);border-radius:4px}
  input:focus,textarea:focus,select:focus{outline:2px solid var(--accent);outline-offset:1px;border-color:transparent}
  textarea{min-height:5.5rem;resize:vertical}
  .two{display:grid;grid-template-columns:1fr 1fr;gap:0 .7rem}
  .btns{display:flex;gap:.55rem;flex-wrap:wrap}
  .btns button{font:800 .58rem/1 var(--mono);letter-spacing:.13em;text-transform:uppercase;min-height:44px;padding:.85rem 1.1rem;border-radius:3px;cursor:pointer;border:1px solid var(--accent);background:var(--accent);color:#12100a}
  .btns button.ghost{background:transparent;color:var(--txt);border-color:var(--line)}
  .sheetbox{border:1px solid var(--line);border-radius:4px;background:var(--panel);overflow:hidden;align-self:start}
  .sheethead{display:flex;justify-content:space-between;gap:.6rem;padding:.6rem .85rem;border-bottom:1px solid var(--line);flex-wrap:wrap}
  .sheethead .t{font:800 .52rem/1 var(--mono);letter-spacing:.14em;text-transform:uppercase;color:var(--mut)}
  /* RAIL-MPE · the 72 mm block. Native on an 80 mm roll AND an iPhone screen. */
  .blkhold{display:flex;justify-content:center;padding:.8rem .4rem}
  .fieldwarn{display:none;font-style:normal;font-size:.78rem;line-height:1.45;
    color:var(--accent-ink);margin-top:.35rem;padding-left:.65rem;
    border-left:2px solid var(--accent)}
  .fieldwarn.on{display:block}
  .fieldwarn.on + *{margin-top:.4rem}
  .blkpos{position:relative}
  .blktcp{position:absolute;top:4px;right:4px;z-index:3;display:flex;gap:2px}
  .blktcp button{position:relative;border:1px solid var(--line);background:var(--mat-head);color:var(--dim);
    border-radius:3px;padding:.3rem .42rem;min-width:34px;min-height:30px;cursor:pointer;
    font:800 .52rem/1 var(--mono);letter-spacing:.08em}
  .blktcp button::after{content:"";position:absolute;left:-5px;right:-5px;top:-7px;bottom:-7px}
  .blktcp button:hover{border-color:var(--ok);color:var(--ok)}
  .blktcp button.pri{border-color:var(--ok);color:var(--ok);background:rgba(15,26,20,.94)}
  .blktcp button.done{border-color:var(--ok);color:var(--ok);background:#0f1a14}
  @media print{.blktcp{display:none!important}}
  #blk{width:72mm;background:#fff;color:#000;font-family:"Helvetica Neue",Arial,sans-serif;
    font-size:2.6mm;line-height:1.42;padding:3mm;box-shadow:0 6px 26px rgba(0,0,0,.5)}
  #blk .hd{text-align:center;border-bottom:.35mm solid #000;padding-bottom:1.2mm}
  #blk .hd b{display:block;font-size:1.5em;letter-spacing:.06em;line-height:1.12}
  #blk .hd i{font-style:normal;font:.74em/1.5 var(--mono)}
  #blk h4{font:700 .72em/1 var(--mono);letter-spacing:.14em;margin:2.4mm 0 1mm;
    border-bottom:.25mm solid #000;padding-bottom:.8mm}
  #blk dl{display:grid;grid-template-columns:auto 1fr;gap:.3mm 2mm}
  #blk dt{font:700 .76em/1.6 var(--mono);color:#333}
  #blk dd{font-weight:600}
  #blk p{margin-top:1mm}
  #blk .warn{border-left:.5mm solid #000;padding-left:1.6mm;margin-top:2mm;font-size:.9em;line-height:1.5}
  #blk .ft{margin-top:2.4mm;border-top:.35mm dashed #000;padding-top:1.2mm;font:.7em/1.5 var(--mono);text-align:center}
  /* RAIL-MPE alignment law: the data decides, never a preference.
     numbers right + tabular figures · labels and body left · masthead center · never justify */
  #blk{text-align:left;font-variant-numeric:tabular-nums;font-feature-settings:"tnum" 1,"lnum" 1}
  #blk .hd{text-align:center}
  #blk .num,#blk dd.num{text-align:right;font-variant-numeric:tabular-nums;font-feature-settings:"tnum" 1}
  #blk .row{display:grid;grid-template-columns:auto 1fr;gap:.3mm 2mm}
  #blk .row .v{text-align:right;font-variant-numeric:tabular-nums}
  #sheet{display:none}
  @media print{
   @page{size:72mm auto;margin:0}
   .vrail,header.top,.sheet,.drawer,.scrim,form,h1,.lede,.chip,.board,.key,.bohgrid,.fam,h2,footer,.sheethead,#send{display:none!important}
   main{margin:0;padding:0}.cwrap{display:block}.sheetbox{border:0}
   .blkhold{padding:0}#blk{box-shadow:none}}
  


  .sendgrid{display:grid;gap:.55rem;grid-template-columns:repeat(auto-fill,minmax(260px,1fr))}
  .sx{text-align:left;border:1px solid var(--line);border-radius:4px;background:var(--panel);
    color:var(--txt);padding:.85rem .95rem;cursor:pointer;font:620 .95rem var(--sans);
    min-height:44px;display:grid;grid-template-columns:1fr auto;gap:.2rem .6rem;align-items:start}
  .sx span{grid-column:1;color:var(--mut);font-size:.79rem;font-weight:400;line-height:1.45}
  .sx i{grid-column:2;grid-row:1/3;font:800 .46rem/1 var(--mono);letter-spacing:.09em;
    font-style:normal;padding:.3rem .4rem;border-radius:3px;white-space:nowrap;align-self:center}
  .sx.go{border-left:2px solid var(--ok)}      .sx.go i{color:var(--ok);border:1px solid #1f4b3a}
  .sx.wait{border-left:2px solid var(--accent)}.sx.wait i{color:var(--accent-ink);border:1px solid var(--line)}
  .sx.no{border-left:2px solid var(--off);opacity:.72} .sx.no i{color:var(--mut);border:1px solid var(--line)}
  .sx:hover{border-color:var(--accent-ink)}
  .sxrow{display:grid;gap:.5rem;grid-column:1/-1}
  .sxrow input{width:100%;padding:.72rem .8rem;font:inherit;font-size:16px;color:var(--txt);
    background:var(--panel2);border:1px solid var(--line);border-radius:4px}
  .sxrow input:focus{outline:2px solid var(--ok);outline-offset:1px;border-color:transparent}
  .sendmsg{margin-top:.9rem;min-height:1.3rem;font-size:.88rem;color:var(--dim)}
  .sendmsg.ok{color:var(--ok)} .sendmsg.bad{color:#ff8a8a}
  @media print{#send{display:none!important}}
  


  .apanel{position:fixed;top:0;bottom:0;left:calc(var(--rail) + var(--safe-l));
    width:min(420px,calc(100vw - var(--rail) - 1rem));background:var(--panel);
    border-right:1px solid var(--line);
    transform:translateX(-102%);opacity:0;transition:transform .34s cubic-bezier(.32,.72,0,1),opacity .22s ease-out;
    z-index:68;display:flex;flex-direction:column;padding-top:var(--safe-t)}
  .apanel.open{transform:translateX(0);opacity:1;box-shadow:6px 0 20px rgba(0,0,0,.2)}
  .apanel h3{font:800 .58rem/1 var(--mono);letter-spacing:.18em;text-transform:uppercase;
    color:var(--accent-ink);padding:1.15rem 1.15rem .8rem;border-bottom:1px solid var(--line)}
  .ap-b{overflow-y:auto;-webkit-overflow-scrolling:touch;padding:1.15rem}
  .ap-o{display:block;width:100%;text-align:left;border:1px solid var(--line);border-radius:4px;
    background:var(--panel2);color:var(--txt);padding:.85rem .95rem;margin-bottom:.55rem;
    min-height:56px;cursor:pointer}
  .ap-o b{display:block;font:800 .74rem/1 var(--mono);letter-spacing:.13em}
  .ap-o i{display:block;font-style:normal;color:var(--mut);font-size:.8rem;margin-top:.2rem}
  .ap-o:hover{border-color:var(--accent-ink)}
  .ap-o.tcp{border-left:2px solid var(--ok)}
  .ap-o.tcp b{color:var(--ok)}
  /* gated block — the shape every LLM chat uses, because it works */
  .ap-cap{display:none;margin:.2rem 0 .7rem}
  .ap-cap.on{display:block}
  .cap-h{display:flex;align-items:center;justify-content:space-between;gap:.5rem;
    background:#0a0f14;border:1px solid var(--line);border-bottom:0;
    border-radius:6px 6px 0 0;padding:.45rem .6rem .45rem .8rem}
  .cap-l{font:700 .6rem/1 var(--mono);letter-spacing:.12em;color:var(--mut)}
  .cap-c{border:1px solid var(--line);background:var(--panel2);color:var(--txt);
    border-radius:4px;padding:.45rem .8rem;min-height:36px;cursor:pointer;
    font:800 .58rem/1 var(--mono);letter-spacing:.12em;text-transform:uppercase}
  .cap-c:hover{border-color:var(--ok);color:var(--ok)}
  .cap-c.done{border-color:var(--ok);color:var(--ok);background:#0f1a14}
  .cap-b{margin:0;background:#0a0f14;border:1px solid var(--line);border-radius:0 0 6px 6px;
    padding:.75rem .8rem;max-height:44vh;overflow:auto;-webkit-overflow-scrolling:touch;
    font:.7rem/1.55 var(--mono);color:#c9d3dd;white-space:pre-wrap;word-break:break-word;
    -webkit-user-select:text;user-select:text}
  .cap-b:focus{outline:2px solid var(--ok);outline-offset:1px}
  .cap-n{font-size:.75rem;color:var(--mut);margin-top:.45rem;line-height:1.5}
  .ap-exp{display:none;margin:.2rem 0 .6rem;padding-left:.6rem;border-left:1px solid var(--line)}
  .ap-exp.on{display:block}
  .ap-exp button{display:block;width:100%;text-align:left;border:1px solid var(--line);
    border-radius:4px;background:var(--panel2);color:var(--txt);padding:.65rem .8rem;
    margin-bottom:.4rem;min-height:48px;cursor:pointer;font:620 .85rem var(--sans)}
  .ap-exp button span{display:block;color:var(--mut);font-size:.75rem;font-weight:400;margin-top:.1rem}
  .ap-exp button:hover{border-color:var(--accent-ink)}
  .ap-mail{display:none;margin-top:.3rem}
  .ap-mail.on{display:block}
  .ap-mail label span{display:block;font-size:.75rem;color:var(--mut);margin-bottom:.3rem}
  .ap-mail input{width:100%;padding:.75rem .8rem;font:inherit;font-size:16px;color:var(--txt);
    background:var(--panel2);border:1px solid var(--line);border-radius:4px}
  .ap-mail input:focus{outline:2px solid var(--accent);outline-offset:1px;border-color:transparent}
  .ap-mail button{width:100%;min-height:48px;margin-top:.6rem;border-radius:4px;cursor:pointer;
    border:1px solid var(--accent);background:var(--accent);color:#12100a;
    font:800 .6rem/1 var(--mono);letter-spacing:.14em;text-transform:uppercase}
  /* toast — small, immediate, gets out of the way */
  .toast{position:fixed;left:50%;bottom:calc(var(--safe-b) + 5.5rem);transform:translate(-50%,14px);
    z-index:90;background:#0f1a14;color:var(--ok);border:1px solid #1f4b3a;border-radius:999px;
    padding:.6rem 1.1rem;font:800 .68rem/1 var(--mono);letter-spacing:.16em;text-transform:uppercase;
    box-shadow:0 8px 26px rgba(0,0,0,.55);opacity:0;pointer-events:none;
    transition:opacity .18s ease, transform .18s ease;white-space:nowrap}
  .toast.on{opacity:1;transform:translate(-50%,0)}
  .toast.warn{background:#1a1408;color:var(--accent-ink);border-color:#4a3a12}
  .amsg{margin-top:.7rem;min-height:1.2rem;font-size:.85rem;color:var(--dim)}
  .amsg.ok{color:var(--ok)}.amsg.bad{color:#ff8a8a}
  @media print{.apanel,.ascrim{display:none!important}}
  


/* the tool panel — wider than a nav sheet, because it holds a document */
.toolpane{position:fixed;top:0;bottom:0;left:calc(var(--rail-min) + var(--safe-l));
  width:min(600px,calc(100vw - var(--rail-min) - .35rem));background:var(--bg);
  border-right:1px solid var(--line);
  transform:translateX(-104%);opacity:0;
  transition:transform .34s cubic-bezier(.32,.72,0,1),opacity .22s ease-out;
  z-index:66;display:flex;flex-direction:column;padding-top:var(--safe-t)}
.toolpane.open{transform:translateX(0);opacity:1;box-shadow:6px 0 20px rgba(0,0,0,.2)}
.toolpane>h3{font:800 .58rem/1 var(--mono);letter-spacing:.18em;text-transform:uppercase;
  color:var(--accent-ink);border-bottom:1px solid var(--line);
  padding:1.15rem 1.15rem .9rem calc(var(--rail) - var(--rail-min) + 1.15rem);
  display:flex;align-items:center;gap:.6rem;flex:none}
.toolpane>h3 button{margin-left:auto;background:none;border:0;color:var(--mut);cursor:pointer;
  font:800 1.05rem/1 var(--mono);padding:0 .6rem;min-width:44px;min-height:44px}
.tp-b{flex:1;overflow-y:auto;-webkit-overflow-scrolling:touch;overscroll-behavior:contain;
  padding:0 1rem calc(var(--safe-b) + 3rem) calc(var(--rail) - var(--rail-min) + 1rem)}
.tp-b>section{margin-top:1.6rem}
.tp-b>section:first-child{margin-top:1.2rem}
@media print{.toolpane{position:static;transform:none;box-shadow:none;border:0;width:auto;overflow:visible}
  .toolpane>h3{display:none}.tp-b{overflow:visible;padding:0}}


/* HERO_ALIGN (260809, KAI) — an OPTIONAL per-door centered masthead. Default is
   unchanged (left). kaibuskirk.com uses it: on the origin door the domain IS the
   statement, so it replaces the h1 rather than stacking a second masthead above
   it. Accent was the only per-door variable until now; this is the second, and
   it is deliberately opt-in so no other door shifts. */
.hero.mid{text-align:center}
.hero.mid .lede{margin-left:auto;margin-right:auto}
.hero.mid h1{max-width:none}

/* ── TILE FIX (KAI 260809) ───────────────────────────────────────────────────
   KAI: "the footer tiles and information are not functioning on desktop or
   tablet... boxes info seem inconsistent at best, non responsive, self
   organizing or formatted in a cohesive way."

   THREE containers use the same <span class="n">/<span class="d"> markup:
   .fam (family section), .sc (rail sheets) and .row (footer drawer).
   ONLY .fam was ever given rules. It works because its anchor is display:block
   AND .fam .d is display:block, so name and description stack.

   .sc a was inline and .row a was display:flex — with no .d rule in either, the
   description sat BESIDE the name inside a 190px tile. That is the inconsistent,
   uncohesive box KAI is looking at, and it was never a responsive-grid problem:
   .row already uses repeat(auto-fill,minmax(190px,1fr)) and self-organizes fine.
   The grid was right; the tile contents were unstyled.

   Reusing .fam's proven treatment rather than inventing a third. */
.sc a,.row a{display:block;align-items:initial}
.sc .n,.row .n{display:block;font-weight:620;font-size:.94rem;line-height:1.25}
.sc .d,.row .d{display:block;color:var(--mut);font-size:.81rem;font-weight:400;
margin-top:.14rem;line-height:1.4;overflow-wrap:anywhere}

/* Visible build stamp in the colophon (260810) — a tester must be able to see
   which build they are on without asking. Quiet, monospace, never shouty. */
.dbody .bld{margin-top:.9rem;padding-top:.7rem;border-top:1px solid var(--line);
font:600 .58rem/1.6 var(--mono);letter-spacing:.06em;color:var(--mut);text-transform:uppercase}
.dbody .bld b{color:var(--accent-ink);font-weight:700}

/* ── BOH NOT-YET ────────────────────────────────────────────────────────────
   260813. A door must not ship a link to a back of house that is not there.
   Every BOH entry renders as a NON-LINK carrying its target in data-boh, and
   the liveness probe below is the ONLY thing that can promote it to a real
   link. Default = no claim; a claim requires an answer.
   This replaces a probe that could only ever UPGRADE the label: the markup
   already said "BOH", so a failed probe changed nothing and the tab kept
   claiming a gate that had been deleted. 20 doors shipped 100 dead links. */
.bohx{display:block;opacity:.5;cursor:default}
/* the sub-label had NO rule, so 'Workboard' and 'the live estate board' ran
   together as one string on every door in the estate. Same defect class as
   the stash rows. A sub-line must be told it is a sub-line. */
.bohx span{display:block;font:400 .74rem/1.4 var(--sans);color:var(--dim);margin-top:.18rem}
.bohx i{display:block;font:800 .56rem/1 var(--mono);letter-spacing:.15em;
  text-transform:uppercase;color:var(--mut);font-style:normal;margin-top:.3rem}
.bohx i{font:800 .5rem/1 var(--mono);letter-spacing:.14em;text-transform:uppercase;
  color:var(--mut);font-style:normal;display:block;margin-top:.2rem}
.bohcta{opacity:.55;cursor:default;font:800 .62rem/1 var(--mono);
  letter-spacing:.12em;text-transform:uppercase;color:var(--mut)}

/* ══ THE RAIL-BENCH — canonical vocabulary (KAI 260815) ═══════════════════

   RAIL-BENCH is THE ENTIRE WORK SPACE. Not a zone — the whole bench you work
   at. KAI 260815: "Rail-Bench was the goal for the entire work space."
   (Corrected here: an earlier note had RAIL-BENCH as the centre. It is not.
    It is the WHOLE, and the centre is the MAIN-STAGE.)

                        RAIL-TOP
            RAIL-LEFT   MAIN-STAGE   RAIL-RIGHT
                        RAIL-BASE
            └──────── the RAIL-BENCH ─────────┘

   RAIL-LEFT   the original rail, on every door — WORKS · CONTACT · BOH · ...
   RAIL-TOP    upper edge, door-declared tabs
   RAIL-RIGHT  right edge, door-declared tabs
   RAIL-BASE   lower edge + drawer — index, provenance, attribution (back matter)
   MAIN-STAGE  the centre, where the work is performed and watched.
               KAI's own word: "The site is the stage."

   BASE, not bottom: a base is what a thing STANDS ON, and what it holds is
   load-bearing. Config key is "base"; "bottom" stays an accepted legacy alias.
   CSS classes are UNCHANGED (.vrail/.hrail/.rrail/.benchtag) — renaming shipped
   CSS across the estate risks it and buys nothing. The name people SPEAK was
   what needed fixing.
*/

.hrail{position:fixed;left:0;right:0;z-index:68;display:flex;align-items:center;
  /* FULL WIDTH (KAI 260815). It used to clear the LEFT rail only, which made the
     bar asymmetric — 46..1024 on an iPad, over the right rail and short of the
     left edge. The offset is unnecessary now that the side rails stop BELOW the
     top bar and ABOVE the base, so nothing can overlap. */
  gap:.34rem;padding:.1rem .6rem;background:var(--mat-tint);
  backdrop-filter:blur(var(--mat-blur));-webkit-backdrop-filter:blur(var(--mat-blur));
  overflow-x:auto;scrollbar-width:none}
.hrail::-webkit-scrollbar{display:none}
.hrail.top{top:0;border-bottom:1px solid var(--mat-edge);padding-top:.16rem}
.hrail.bot{bottom:0;border-top:1px solid var(--mat-edge);
  padding-bottom:calc(.34rem + var(--safe-b));justify-content:center}

/* RAIL-RIGHT — mirror of the left spine. Collapsed it is the same 23px. */
.rrail{position:fixed;right:0;top:0;bottom:0;width:calc(var(--rail) + var(--safe-r,0px));
  padding-right:var(--safe-r,0px);background:var(--mat-tint);
  backdrop-filter:blur(var(--mat-blur));-webkit-backdrop-filter:blur(var(--mat-blur));
  border-left:1px solid var(--mat-edge);display:flex;flex-direction:column;
  align-items:center;z-index:69;padding-block:.6rem;
  transform:translateX(calc(var(--rail) - var(--rail-min)));
  transition:transform .3s cubic-bezier(.32,.72,0,1),background .22s}
.rrail.open{transform:translateX(0);box-shadow:-4px 0 14px rgba(0,0,0,.18)}
.rrail:not(.open){background:transparent;border-left-color:transparent}

/* Drawers from the other three edges. Same overlay rule as .sheet. */
.sheet.fromtop{left:0;right:0;top:0;bottom:auto;height:auto;max-height:76dvh;
  transform:translateY(-102%);border-right:0;border-bottom:1px solid var(--line)}
.sheet.fromtop.open{transform:translateY(0)}
.sheet.frombot{left:0;right:0;top:auto;bottom:0;height:auto;max-height:76dvh;
  transform:translateY(102%);border-right:0;border-top:1px solid var(--line)}
.sheet.frombot.open{transform:translateY(0)}
.sheet.fromright{left:auto;right:calc(var(--rail) + var(--safe-r,0px));
  transform:translateX(102%);border-right:0;border-left:1px solid var(--line)}
.sheet.fromright.open{transform:translateX(0)}

/* ── CENTER UNIT — the fifth variant. Not an edge drawer. ────────────────────
   KAI 260817: "the Rail-Contact button ... needs to appear CENTER STAGE. Its
   slide out function is not acceptable."

   He is right, and the reason is IA, not taste. The other three rails answer
   NAVIGATIONAL questions — who / where / what else — and for those an edge
   drawer is correct: staying anchored to the edge it came from preserves the
   mapping between the cue and its surface. CONTACT is not navigation. It is
   the TERMINAL ACTION, the one thing the door exists to get to. An edge drawer
   says "peek at this"; it stays visually subordinate to the rail that spawned
   it. That is backwards for the primary CTA.

   A center unit lands ON THE BENCH — inset by the rails, not the viewport, so
   it reads as arriving at the middle of the work surface rather than sliding
   in from furniture. It rises 10px and settles instead of travelling across
   the screen: no edge to trace it back to, because it did not come from one.

   BOTH SAFETY PROPERTIES OF THE WRAP ARE PRESERVED, unchanged:
     1. position:fixed → the bench never reflows.
     2. it is a plain .sheet, so the SAME closeSheets() closes it and
        one-drawer-at-a-time still holds by construction. No JS changed.

   --railstop-b and --safe-r are not declared anywhere in this template; the
   fallbacks below are load-bearing, not decoration. */
.sheet.center{
  top:var(--railstop-t);
  bottom:calc(var(--railstop-b,3.4rem) + var(--safe-b));
  left:calc(var(--rail) + var(--safe-l));
  right:calc(var(--rail) + var(--safe-r,0px));
  margin:auto;
  width:min(30rem,calc(100vw - 2*var(--rail) - 2.5rem));
  height:max-content;
  max-height:min(70dvh,34rem);
  border:1px solid var(--line);
  border-right:1px solid var(--line);
  border-radius:.6rem;
  transform:translateY(10px) scale(.985);
  padding-top:0;
  overflow:hidden;
}
.sheet.center.open{
  transform:translateY(0) scale(1);
  opacity:1;
  box-shadow:0 18px 48px rgba(0,0,0,.38);
}
/* LANDSCAPE PHONE — under 430px tall a centered unit has no room to be
   centered in. Fall back to the bottom drawer, where the thumb already is.
   Same reasoning that drops the top rail and keeps the bottom one. */
@media (max-height:430px){
  .sheet.center{
    top:auto;bottom:0;left:0;right:0;margin:0;
    width:auto;max-height:86dvh;border-radius:0;
    transform:translateY(102%);
  }
  .sheet.center.open{transform:translateY(0)}
}

/* THE BENCH — the center never resizes. Rails float over it, so nothing the
   rails do can change what the bench is showing. */
html.has-top main{padding-top:2.6rem}
html.has-base main{padding-bottom:3.2rem}
/* MEASURED 260815 on the live apex: header.top and nav.hrail.top both occupied
   42..109px, and RAIL-TOP (z 68) covered the header (z 50) — the FRONT OF HOUSE
   toggle, the KEY and the settings gear were not merely dimmed, they were
   UNCLICKABLE. 2.6rem was the assumed rail height; the rail measures 67px.
   Pin RAIL-TOP to the top edge and seat the header directly beneath it. */
/* ORDER: the SITE HEADER is the standard bar and owns the top edge. RAIL-TOP
   is a SLIM strip beneath it — not a second header. */
html.has-top .hrail.top{top:calc(var(--hdr-h) + var(--safe-t))}

/* LANDSCAPE PHONE is the one place this hurts — under 430px tall the top and
   bottom rails eat the bench. Drop the top rail; keep bottom, where the thumb is. */
@media (max-height:430px) and (orientation:landscape){
  /* KAI 260816, on an iPhone: "when I rotate the iPhone the top rail-bar does
     not exist." It was DELETED here to reclaim vertical space — affordable when
     this bar carried a label, and not affordable now that it carries the state
     switcher, the tray cue, KEY and settings. Four controls with no other home.
     SHRINK IT, NEVER DELETE IT: a control that vanishes on rotation is worse
     than a cramped one, because the person cannot tell it from a broken app. */
  :root{--hdr-h:2.9rem}
  html.wrap .hrail.top{display:flex;min-height:0;padding-block:0}
  html.wrap .hrail.top .rtab,
  html.wrap .hrail.top .rsb{padding-block:.24rem}
  html.wrap .hrail.top .rcue.tcue{min-width:1.5rem;min-height:1.5rem;font-size:.9rem}
  html.has-top main{padding-top:.4rem}
}

/* ══ THE WRAP READS AS FOUR RAILS FROM TABLET UP ═══════════════════════════
   KAI 260814: "i need to see it on the desktop and ipad that there are 4 x
   rails and a center zone."

   On a phone the vertical rails sit COLLAPSED AND TRANSPARENT — that is the
   260804 spine, and it is right there: a 390px screen cannot spend pixels on
   frames. But from 820px up there IS room, and the wrap should be legible as a
   structure rather than inferred. So the rails become visible at rest, the
   bench gets an edge, and the four zones read as four zones.

   THIS ALSO FILLS THE TABLET GAP. Until now the template jumped from phone to
   desktop with nothing in between — an iPad landed on phone rules with a
   desktop amount of space. 820–1179px is the tablet band. */
@media (min-width:820px){
  html.wrap .vrail,
  html.wrap .rrail{transform:none;width:calc(var(--rail) + var(--safe-l))}
  html.wrap .vrail:not(.open),
  html.wrap .rrail:not(.open){background:var(--glass)}
  html.wrap .vrail{border-right:1px solid transparent}
  html.wrap .vrail:not(.open){border-right-color:transparent}
  html.wrap .rrail{border-left:1px solid transparent}
  html.wrap .rrail:not(.open){border-left-color:transparent}
  html.wrap .hrail.top{border-bottom:1px solid var(--line)}
  html.wrap .hrail.bot{border-top:1px solid var(--line)}

  /* THE BENCH — the center zone, given an actual boundary so it reads as the
     work surface the rails wrap. It never resizes; the rails float over it. */
  html.wrap main{margin-left:calc(var(--rail) + var(--safe-l));
                 margin-right:calc(var(--rail) + var(--safe-r,0px))}
  html.has-top main{padding-top:3rem}
  html.has-base main{padding-bottom:3.4rem}
  html.wrap .top{margin-left:0}
  html.wrap main .inner{position:relative}
  html.wrap main .inner::before{content:"";position:absolute;inset:-1.1rem -.9rem;
    border:1px dashed var(--line);border-radius:.5rem;pointer-events:none;opacity:.55}
  /* THE STRUCTURE TAG SHIPS OFF (KAI 260816). It was rendering on every door for
   every visitor — a development label on a production site. It is genuinely
   useful at the desk, so it is a SETTING, not a deletion and not a "mode":
   Settings already holds the build stamp for exactly this reason, and by KAI's
   own three-question test a dev mode fails Q3 — an existing room can hold it by
   changing contents. A fifth named system for one checkbox is not earned. */
.benchtag{display:none}
html.showtags.wrap .benchtag{display:block;position:fixed;left:50%;transform:translateX(-50%);
    bottom:calc(3.9rem + var(--safe-b));font:800 .46rem/1 var(--mono);
    letter-spacing:.26em;text-transform:uppercase;color:var(--mut);
    z-index:67;pointer-events:none;
    /* its own ground: bare type at 70% opacity collided with the BOH copy and
       both became unreadable. A floating label must never share pixels. */
    background:var(--bg);border:1px solid var(--line);border-radius:999px;
    padding:.3rem .7rem;opacity:.92}
}
@media (max-width:819px){ .benchtag{display:none} }

/* iPad portrait is 820 exactly on the short edge in some modes — belt and
   braces so the band is never skipped. */
@media (min-width:768px) and (max-width:819px){
  html.wrap .vrail:not(.open),
  html.wrap .rrail:not(.open){background:var(--glass)}
}
</style>
</head>
<body>

<nav class="vrail" id="vrail" aria-label="Sections" data-railstack>
  <!-- RAIL-BAR STACK (KAI 260812). The hamburger sits at the TOP of the rail and
       steps through the bars: RAIL-BAR-1, RAIL-BAR-2, and so on. A door with one
       bar never shows it — a control that does nothing is furniture. -->

  <div class="vbars rbars" id="vbars" data-bars>
    <span class="rtabs col on" data-bar="1"><button class="rtab " data-sheet="works" aria-expanded="false">WORKS</button><button class="rtab vkey" data-sheet="boh" aria-expanded="false">BOH</button></span></div>
  <div class="vfoot rfoot"></div>
</nav>

<aside class="sheet" id="sheet-works" aria-hidden="true">
  <h3>The rest of the works</h3><div class="sc"><a class="" href="https://kaibuskirk.com"><span class="n">Kai Buskirk</span><span class="d">technology, merch, design, apps</span></a><a class="" href="https://studiox88.com"><span class="n">Studio X</span><span class="d">The studio — cloud, web &amp; AI since 1988</span></a><a class="" href="https://rail-works.com"><span class="n">Rail-Works</span><span class="d">The suite — rails for work that moves</span></a><a class="" href="https://cadre.fyi"><span class="n">Cadre</span><span class="d">The cadre gateway</span></a><a class="" href="https://freedatime.com"><span class="n">FreeDaTime</span><span class="d">A free multi-witness grounding clock. Authoritative local and UTC time, read not computed.</span></a><a class="" href="https://printshop.link"><span class="n">Printshop</span><span class="d">The print rail and shop network</span></a><a class="" href="https://rail-fax.com"><span class="n">Rail-Fax</span><span class="d">Send a fax without a fax machine</span></a><a class="" href="https://rail-mail.com"><span class="n">Rail-Mail</span><span class="d">Mail between machines</span></a><a class="" href="https://unionmadeservices.com"><span class="n">Union Made Services</span><span class="d">The shop — sites, storefronts and tools for locals</span></a><a class="" href="https://union.ink"><span class="n">Union.ink</span><span class="d">Union labor, on contract</span></a><a class="" href="https://freedanode.com"><span class="n">FreeDaNode</span><span class="d">The multi-witness grounding network behind FreeDaTime. Free forever, open source, attribution only.</span></a><a class="" href="https://unionmadesystems.com"><span class="n">Union Made Systems</span><span class="d">The rails Union Made Services runs on</span></a><a class="" href="https://unionmadetrades.com"><span class="n">Union Made Trades</span><span class="d">The register of union, allied and union-supporting businesses across every trade. Issues no label of its own.</span></a><a class="" href="https://unionprintingtrades.com"><span class="n">Union Printing Trades</span><span class="d">A public register of Union Made Trades shops. Every entry records who verified it and when.</span></a><a class="" href="https://abeatbykai.com"><span class="n">A Beat By Kai</span><span class="d">Held</span></a><a class="" href="https://doodles-noodles.com"><span class="n">Doodles &amp; Noodles</span><span class="d">Draw without a plan, play without a plan. A sketchbook from Studio X Ink.</span></a><a class="" href="https://foundry.ink"><span class="n">Foundry.Ink</span><span class="d">Custom lettering, hand-inked art and typefaces cut to brief.</span></a><a class="" href="https://loveartrobots.com"><span class="n">LOVE-ART-ROBOTS</span><span class="d">LOVE is why. ART is what. ROBOTS are how. A creative promotional project from Studio X Ink.</span></a><a class="" href="https://shipwerx.com"><span class="n">ShipWerx</span><span class="d">The shipping rail</span></a><a class="" href="https://studiox.ink"><span class="n">Studio X Ink</span><span class="d">The creative imprint of Studio X — art, design, graphics, robots. The way into the work.</span></a><a class="" href="https://union-delivery.com"><span class="n">Union Delivery</span><span class="d">Chartered local delivery</span></a><a class="" href="https://wearx.fyi"><span class="n">Wear X</span><span class="d">Boutique merchandising, branding and product development. Brand, product and production under one roof.</span></a></div>
</aside>
<aside class="sheet" id="sheet-legal" aria-hidden="true">
  <h3>IMPRESSUM</h3>
  <p class="sub">Kontakt und Provenienz. · Contact and provenance.</p>
  <div class="sc"><p class="lede" style="margin:0 0 .9rem;font-size:.9rem">Angaben gemäß &sect;5 DDG. · Provider identification under German law.</p><div class="board"><div class="st"><span class="dot on"></span><span class="n">Kontakt · Contact<span class="d">legal@studiox88.com</span></span><span class="tag">Mail</span></div><div class="st"><span class="dot on"></span><span class="n">Vertreten durch · Represented by<span class="d">Kai L. Buskirk</span></span><span class="tag">Who</span></div><div class="st"><span class="dot on"></span><span class="n">Firma · Entity<span class="d">StudioX88 LLC, trading as Studio X</span></span><span class="tag">Legal</span></div><div class="st"><span class="dot on"></span><span class="n">Europa · Europe<span class="d">Am Erlachsbrunnen 2 · 97509 Kolitzheim · Deutschland</span></span><span class="tag">Where</span></div><div class="st"><span class="dot on"></span><span class="n">USA · United States<span class="d">Riverside · San Diego · Los Altos · Las Vegas</span></span><span class="tag">Where</span></div><div class="st"><span class="dot off"></span><span class="n">USt-IdNr · VAT<span class="d">Noch nicht erteilt · Not yet issued</span></span><span class="tag">Tax</span></div><div class="st"><span class="dot on"></span><span class="n">Inhalt · Content<span class="d">Kai L. Buskirk, Anschrift wie oben</span></span><span class="tag">Who</span></div></div><div class="mailrow"><a href="mailto:legal@studiox88.com"><span class="n">legal@studiox88.com</span><span class="d">Impressum, corrections, privacy</span></a><button type="button" class="mcopy" data-copy="legal@studiox88.com" aria-label="Copy legal@studiox88.com">copy</button></div></div>
</aside>
<aside class="sheet k" id="sheet-boh" aria-hidden="true">
  <h3>Back of house — members only</h3>
  <div class="sc">
    <span class="bohx" data-boh="https://work.kaibuskirk.com">Workboard<span>the live estate board</span><i>Not yet</i></span>
    <span class="bohx" data-boh="https://arc.kaibuskirk.com">Arc<span>session record + handoff</span><i>Not yet</i></span>
    <span class="bohx" data-boh="https://bus.kaibuskirk.com">The Bus<span>cadre comms board</span><i>Not yet</i></span>
    <span class="bohx" data-boh="https://members.kaibuskirk.com">Membership<span>roster + access</span><i>Not yet</i></span>
    <span class="bohx" data-boh="">Use your key<span>sign in at the gate</span><i>Not yet</i></span>
  </div>
</aside>


<nav class="hrail top" aria-label="Identity" data-railstack><button class="rcue tcue" type="button" data-ttray aria-expanded="false" aria-controls="tdbody" aria-label="Open the location board">▼</button><span class="foh">Front of House</span><div class="rbars" data-bars></div><span class="ract"><span class="tkey" id="tkey" data-boh=""><span id="tkeyl">KEY</span></span><button class="gear" id="gear" aria-expanded="false" aria-controls="sset" aria-label="Settings"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true"><circle cx="12" cy="12" r="3.2"/><path d="M19.4 15a1.6 1.6 0 0 0 .32 1.77l.06.06a2 2 0 1 1-2.83 2.83l-.06-.06a1.6 1.6 0 0 0-1.77-.32 1.6 1.6 0 0 0-1 1.47V21a2 2 0 0 1-4 0v-.11a1.6 1.6 0 0 0-1.05-1.46 1.6 1.6 0 0 0-1.77.32l-.06.06a2 2 0 1 1-2.83-2.83l.06-.06a1.6 1.6 0 0 0 .32-1.77 1.6 1.6 0 0 0-1.47-1H3a2 2 0 0 1 0-4h.11a1.6 1.6 0 0 0 1.46-1.05 1.6 1.6 0 0 0-.32-1.77l-.06-.06a2 2 0 1 1 2.83-2.83l.06.06a1.6 1.6 0 0 0 1.77.32H9a1.6 1.6 0 0 0 1-1.47V3a2 2 0 0 1 4 0v.11a1.6 1.6 0 0 0 1 1.47 1.6 1.6 0 0 0 1.77-.32l.06-.06a2 2 0 1 1 2.83 2.83l-.06.06a1.6 1.6 0 0 0-.32 1.77V9a1.6 1.6 0 0 0 1.47 1H21a2 2 0 0 1 0 4h-.11a1.6 1.6 0 0 0-1.47 1z"/></svg></button></span><button class="rcue tcue" type="button" data-ttray aria-expanded="false" aria-controls="tdbody" aria-label="Open the location board">▼</button></nav>
<nav class="rrail" id="rrail" aria-label="Context" data-railstack><div class="rbars" data-bars><span class="rtabs col on" data-bar="0"><button class="rtab" data-wrap="stdspec" aria-expanded="false">SPEC</button><button class="rtab" data-wrap="stdask" aria-expanded="false">ASK</button></span></div></nav>
<aside class="sheet fromright" id="sheet-stdspec" aria-hidden="true"><h3>Design system</h3><div class="sc"><p class="sub" style="margin:0 0 .8rem">This door’s design system, read off its own tokens when it was built. Take it and build something that matches.</p><div style="display:grid;gap:.5rem;margin:0 0 1rem"><div style="display:flex;align-items:center;gap:.5rem"><span style="width:1.4rem;height:1.4rem;border-radius:4px;flex:none;background:#f6821f;border:1px solid var(--line)"></span><span style="font:700 .72rem/1 var(--mono);color:var(--mut);letter-spacing:.08em">ACCENT</span><span style="font:600 .74rem/1 var(--mono);color:var(--dim);margin-left:auto">#f6821f</span></div><div style="display:flex;align-items:center;gap:.5rem"><span style="width:1.4rem;height:1.4rem;border-radius:4px;flex:none;background:#0c1218;border:1px solid var(--line)"></span><span style="font:700 .72rem/1 var(--mono);color:var(--mut);letter-spacing:.08em">INK</span><span style="font:600 .74rem/1 var(--mono);color:var(--dim);margin-left:auto">#0c1218</span></div><div style="display:flex;align-items:center;gap:.5rem"><span style="width:1.4rem;height:1.4rem;border-radius:4px;flex:none;background:#131820;border:1px solid var(--line)"></span><span style="font:700 .72rem/1 var(--mono);color:var(--mut);letter-spacing:.08em">PANEL</span><span style="font:600 .74rem/1 var(--mono);color:var(--dim);margin-left:auto">#131820</span></div><div style="display:flex;align-items:center;gap:.5rem"><span style="width:1.4rem;height:1.4rem;border-radius:4px;flex:none;background:#28323f;border:1px solid var(--line)"></span><span style="font:700 .72rem/1 var(--mono);color:var(--mut);letter-spacing:.08em">LINE</span><span style="font:600 .74rem/1 var(--mono);color:var(--dim);margin-left:auto">#28323f</span></div></div><div style="display:flex;align-items:center;gap:.7rem;margin:0 0 .3rem"><svg viewBox="0 0 64 64" width="52" height="52" aria-hidden="true"><rect width="64" height="64" rx="14" fill="#f6821f"/><g fill="#fff"><path d="M20 46 L30 18 L34 18 L44 46 L38 46 L35.5 39 L28.5 39 L26 46 Z M30 34 L34 34 L32 27 Z"/><rect x="14" y="50" width="36" height="3" rx="1.5"/></g></svg><span style="color:var(--dim);font-size:.78rem;line-height:1.4">The mark, on the 64-unit grid the whole estate draws on.</span></div><p style="font:700 .7rem/1 var(--mono);letter-spacing:.14em;color:var(--mut);margin:1rem 0 .5rem">MACHINE READABLE</p><div style="display:grid;gap:.4rem;font:600 .78rem/1.4 var(--mono)"><a href="/door-version.json">/door-version.json</a><a href="/manifest.webmanifest">/manifest.webmanifest</a><a href="/llms.txt">/llms.txt</a><a href="/tray.json">/tray.json</a><a href="/index.md">/index.md</a></div><p style="color:var(--mut);font-size:.76rem;line-height:1.5;margin:.8rem 0 0">Every door in the estate ships these five at the same paths. A machine can read any door without being told how.</p></div></aside>
<aside class="sheet fromright" id="sheet-stdask" aria-hidden="true"><h3>Ask</h3><div class="sc"><p class="sub" style="margin:0 0 .8rem">Bring a question to this door.</p><div class="st"><span class="dot off"></span><span class="n">No address on this door<span class="d">Nothing is recorded here yet, so nothing is printed. An address that bounces is worse than a blank — this estate has already shipped one that answered 550.</span></span><span class="tag">Not yet</span></div></div></aside><div class="benchtag" aria-hidden="true">MAIN-STAGE</div>
<header class="top">
  <span class="hz hz-mark"><img class="mk" src="/icon.svg" alt=""></span>
  <span class="hz hz-ident"><span class="nm"><b>studioxfonts.com</b><i>STUDIO X · SINCE 1988</i></span></span>
  <span class="hz hz-mid"></span>
  <!-- KAI 260816: "the .com/.us/.de will need to move to the Rail-Nav dots in
       the top right of the Rail-Header, then the impressum moves to the right
       aligned … that should further clean up." Centre-weighting the header put
       three unrelated things in the middle and left the mark stranded; grouped
       right, the header reads left-to-right as IDENTITY then CONTROLS. -->
  <span class="hz hz-util"><button class="rtab hlink" data-sheet="legal" aria-expanded="false">IMPRESSUM</button></span>
  <span class="hz hz-act">
    <!-- RAIL-NAV (KAI 260816): "move the COM, US, DE into the three dots
         Rail-Nav at the top far right — I want to see it function as a
         primitive." So the placeholder stops being a placeholder: the three
         dots ARE the door switch. Same dot vocabulary as RAIL-DOTS, same colour
         law — muted at rest, accent only while its surface is open. -->
    
  </span>
</header>

<main><div class="inner">

  <section class="hero">
    
    <span class="chip">THE FOUNDRY</span>
    <h1>One typeface.<span class="g">1.2 million downloads.</span></h1>
    <p class="lede">StudioX Fonts is a small foundry. There is exactly one typeface in it so far — abeatbyKai, drawn in Fontographer in April 2011 and quietly downloaded more than 1.2 million times since, without ever being promoted. It is free for personal use and always will be. This is where the commercial license will finally be buyable.</p>
    
  </section>

  <section id="status">
    <h2>What works today</h2>
    <div class="board"><div class="st"><span class="dot on"></span><span class="n">abeatbyKai<span class="d">97 glyphs · 2011 · free for personal use · try it above</span></span><span class="tag">Live</span></div><div class="st"><span class="dot on"></span><span class="n">Specimen tester<span class="d">The real 2011 file, embedded in this page — not a picture of it</span></span><span class="tag">Live</span></div><div class="st"><span class="dot off"></span><span class="n">Foundry.Ink<span class="d">The hand-made side — custom lettering, hand-inked art, commissioned typefaces. The 2011 byline, given a room</span></span><span class="tag">Not yet</span></div><div class="st"><span class="dot off"></span><span class="n">Commercial license<span class="d">Terms settled, checkout not built. This is the whole point of the site and it is the part that is missing</span></span><span class="tag">Not yet</span></div><div class="st"><span class="dot off"></span><span class="n">Machine-readable EULA<span class="d">Plain text an AI agent can parse, so a design tool knows what it may do</span></span><span class="tag">Not yet</span></div><div class="st"><span class="dot off"></span><span class="n">Font files on R2<span class="d">TTF, OTF and WOFF2 served from storage instead of embedded in the page</span></span><span class="tag">Not yet</span></div><div class="st"><span class="dot off"></span><span class="n">More typefaces<span class="d">There is a 2024 folder of unfinished work. One at a time</span></span><span class="tag">Not yet</span></div></div>
  </section>

  
  <style>
  @font-face{font-family:'abeatbyKai';src:url(data:font/otf;base64,T1RUTwAJAIAAAwAQQ0ZGIPdYhWUAAAWoAAAm5k9TLzKEAANQAAABAAAAAGBjbWFw2T4mDAAAA9AAAAG2aGVhZATIPl4AAACcAAAANmhoZWEVIxFnAAAA1AAAACRobXR41fQAlgAALJAAAAGEbWF4cABhUAAAAAD4AAAABm5hbWXVxwbNAAABYAAAAnBwb3N0/34AFAAABYgAAAAgAAEAAAABAACyotXRXw889QADA+gAAAAAydj9kwAAAADJ2P2T/+//OBIQAyAAAAADAAIAAAAAAAAAAQAAAyH/NAAAEf7/7//TEhAAAQAAAAAAAAAAAAAAAAAAAGEAAFAAAGEAAAACAjQBkAAFAAACvAKKAAAAjwK8AooAAAHFADIBAwAAAAAEAAAAAAAAAIAAAAMAAABIAAAAAAAAAABQeXJzAEAAICISAyH/NAAAAyEAzAAAAAEAAAAAAk0DFQAAACAAAAAAABcBGgAAAAAAAAAAACgAAAAAAAAAAAABABQAKAAAAAAAAAACAA4APAAAAAAAAAADADYASgAAAAAAAAAEABQAKAAAAAAAAAAFAFAAgAAAAAAAAAAGABQA0AABAAAAAAAAABQA5AABAAAAAAABAAoA+AABAAAAAAACAAcBAgABAAAAAAADABsBCQABAAAAAAAEAAoA+AABAAAAAAAFACgBJAABAAAAAAAGAAoBTAABAAAAAAAQAAoA+AABAAAAAAARAAcBAgADAAEECQAAACgAAAADAAEECQABABQAKAADAAEECQACAA4APAADAAEECQADADYASgADAAEECQAEABQA0AADAAEECQAFAFAAgAADAAEECQAGABQA0ACpADEAOQA4ADgAIABLAGEAaQBCAHUAcwBrAGkAcgBrAC4AYwBvAG0AYQBiAGUAYQB0AGIAeQBLAGEAaQBSAGUAZwB1AGwAYQByAEYAbwBuAHQAbwBnAHIAYQBwAGgAZQByACAANAAuADcAIABhAGIAZQBhAHQAYgB5AEsAYQBpAEYAbwBuAHQAbwBnAHIAYQBwAGgAZQByACAANAAuADcAIAA0AC8AMgAzAC8AMQAxACAARgBHADQATQCtADAAMAAwADAAMAAwADIAMAA0ADUAQQBiAGUAYQB0AGIAeQBLAGEAaakxOTg4IEthaUJ1c2tpcmsuY29tYWJlYXRieUthaVJlZ3VsYXJGb250b2dyYXBoZXIgNC43IGFiZWF0YnlLYWlGb250b2dyYXBoZXIgNC43IDQvMjMvMTEgRkc0Ta0wMDAwMDAyMDQ1QWJlYXRieUthaQAAAAMAAAADAAABIgABAAAAAAAcAAMAAQAAASIAAAEGAAAAAAAAAAAAAAADAAAAAwAAAAAAAAAAAAAAAAAAAAAAAAMEBQYHCAkKCwwNDg8AERITFBUWFxgZGhscHR4AHwAgISIjJCUmJygpKissLS4vMDEyMzQ1Njc4OTo7ADwAAD0+P0BBQkNERUZHSElKS0xNTk9QUVJTVFVWV1gAWQBaAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABbXAAAAAAAAABdXl8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAlAAAACAAIAAEAAAALAA7AD0AWgBcAHoAfAB+AKkArgCyAPwgICEiIhL//wAAACAALgA9AD8AXABfAHwAfgCpAK0AsgD8ICAhIiIS////4//j/+L/4f/g/97/3f/c/7UAAP+u/1/gPN893f4AAQAAAAAAAAAAAAAAAAAAAAAAAAAOAAAAAAAAAAAAAAAAABAAXQAAAAMAAAAAAAD/ewAUAAAAAAAAAAAAAAAAAAAAAAAAAAABAAQCAAEBAQtBYmVhdGJ5S2FpAAEBASv4EAD4HQH4HgL4HgP4HwT7IwwDnwwEevtgHBIQ+bUF9wsP90IRoBwjfhIABQEBBQcbJStOVUxMQ1KpMTk4OCBLYWlCdXNraXJrLmNvbWFiZWF0YnlLYWlOb3JtYWwAAAEBhwEAAQYAaAAACRMAHgAAIBsAPQAAQAAAfAAAQhkAXQAAXwAA4AAAcAAApQAAqgAAmQAApAAAYQIAAQAmACkAKwAtAEYAZgDbAYwCfQNPA2QDvQQXBEoEcQSEBv4HDwchB3MHpAgOCLoI7glvCe0KHwr9C3kLjAulC8IMBwxXDI4NEw2HDdoOEw4yDrcO3w7uDzcPYQ+OD8IP6BA6EJIRKRGEEhwSOBKEEqES1BMFEy0TURNiE3ETgxPoFGYUzhURFYcVzBZgFq8WyhcNFzsXSxfEGBEYUxjMGSkZaBn9GjcagxqhGtYbCBstG1EbZhu+G/chUSG2IeciByIKQ4vK+TfJAcrJ947KA/hK+bQV/Av9tPgLBvvNyhX5N/eO/TcHDvzQDkMOQw78Lp7q+Th3AYzYA9lACj7879gGQgQ+LFQK+9D4vveAAYvCxMQD9z1AClL7gMQG+wb3gBVU+4DCBg7VqXb3hM33F873sXcB9/X3jRX3NwaezQX7OAaz9xcF9zcGns4F+zcG2/exBV4GN/uxBSsG3fexBV0GOPuxBfs4BnlIBfc2BmX7GAX7NQZ4SgX3NgZE+4QFuAbT94QF7QZE+4QFuAZT98YVtPcXBe4GY/sXBQ42lOVPx/jk9hKh1trP8tYTfPeIsRWZjLiOsJ2qrhmqrpy4jsKMs4Cyc7FxtGmmYZgqqRhMnmyyjcQIy4y7wcsbysFoUpMfkGoF0gaKrQWYhpyAoB5/o32eepptp2ebYpAIs0djB16DZnZuaW9qfWSKXgg3vEbgbx7Xcdhwsl6KShlmfWpwcB5wcGt/ZoxpjGyYbqMIbqR9qK4amEJ7B1CtS8ZoHqR8p4KqiAgTvG7OBw73ipLW92zVytb3ateJdxKL1vdp1rXW92nVE/eA98v4fhWur5y2vRqKvHq2aK5KCnlmaGZoeWGKWopYnV6wZrBmtnm+jLyMtZ6urggT74D3rPfIFUEG++39qwXXBvdmwhWwZrZ6voy8jLWdrq+ur522irwIvHm1aK4eSgp6Z2hnaHhhilmKWJ1fr2YI+/L5DRUT94Cgn6SVqIoIxLheU44fjG2CcXZ0CHR2coBtG22KcZZ2oXaigKWMqoyolqOgnwj4dPz5FW2KcZZ1onaigaWMqYyolqSgn6CgpJWoisSKuF6OUoxtgnF2dHZ1coBtiggO90qU1fkX1AGL2pjV92rZA/lPyhX7UvdP9x/3ogU2BvsD+2z7JPchr5zUr7HAjtAZjbx5tGWuZq5fnVqMW4xhe2draGt4Y4lciV2eXrJgonEYZ3VueHBzcW0ZdHB8dIZ5CIR1iHV1GkujVLpcHl27w3TKG+bSt+O9H6Gy90T7PQX8Hvh/FWWuBWqreqqoGoyklqGgnwifoaSVpxuoo4J4oB+geJZ0cBqMYnhsZHcIwvwDFVFrW25LG19mmqltH26qfK+1Gsyru8qsHsSo90z7RAUO/Df41Hb3f3cBis0DzEAKSft/zQYO+8RCdvoSQwrVA/dgfRVnnG+Zdp9+pRl2tICvqRr38Ae8lrShrR6gqqWjqpqelBjaB2d8YXpmbmtiGWVaeFJLGvv5B1ObWKpdHpp1oXWndaN5nYCXh7F+GA77tEJ2+hJ3AfcW1QOxOhWYj52Wop2noaGhmqEIqrmbvsMa9/kHynjEZb0ebLRmqGGcZpoYPAedgqt6pXSfbRmiaZZiWhr78AdsgWh2Yh5+cXZ3bn1nehg7Bw77U/iVdvcLw/cPdwH3YPj5FfPBIwbC6FupUyxV6lptwi4FIFP3AAZTMb1uwOjDLruoBQ6A9zF293fa93N3Afdv2AP3vPf/Ffdu2vtu93M++3P7bzz3b/t3VAr71mp29153AfdB9ygVPgY0UQoOHA/C+0d2+at3qnemd/cmoXXClXcS+OSl98/a9x3d9w/b+LbaeNV1oXXleNp49xdXoZOhm5+VoKygdvd8+12fd95403CmcPcyNtNwphccCpj3Mvlj2Pdq2PeBrLzB95qrE6X1fRHwHBHmIAotCioKn5ScmjMKrZ2uqB+or5lLCi8Kt18eX7fAdchQCv0t9/s1CvsX2Ab7t5UVPvsZ2Ab+Jvk/FcJctlMeE8X1fUnwOgrDHxOl9X0R8MK5uMMf+PH8tBU/BjNRCv2S+eAVawZ3JwqsnjR4qzGhBhOl9X1D8PiY/MUVwly2VB4TpfV9SfBTXl1UVLpgwqajlZ+fHxPF6X1D8J+flaOmGv0I+NAVwly2VFNfXVRUuWDCw7i4wx73ffzIFWoGd0B31gVr+wGf4AagNgWhBqDgBTagB/sr5RWrnjR4rDGhBvtAqRUTm/KcqfCfn5WjphrCXbZTOgrCp6OVn54e/af39BW5uaK/xhqMz3PEWrpaMgraTgqtRAqtbJxmil8rCt8GjsKlv7u8CD37eRU5LN0G/Pn5oRVx/nylBvvm+RMV+6xC96wGHA/8SyMKHPmA998VIQqMHj4KHvcKSQoTlfSaBfBeZq+3uLCvuLexaF6MHz4KHv17+NAVe5uDnqEsCl5lZl4edYx4k3uaCMJJCl9mr7e4sK+3HxOr9X0l8LixaF4+Ch/4s/j4FTAKpZEFpIR5mHAbYkEK6PzIFXmGgoJ8G3aAmqk2CqWSBaSEeZhwG2J3dF1moW6wqJ2Zp5If/R34yBWDIgqNH5SDkYSPhZp0GKYGQvy7FYIiCo4fk4SRhI+Fm3MYpQZ7+Q84Cj/80DgKDpT3GQGL1wPX9yIVP/sZ1wYO+w8mCvghQApCBvvY/ZcF0wYO98qP1fka1QGL2fkj1gP5RfcPFdras+v3Axr3AGLnOtge1jsssPsDG/sCTwotRwo/3Opl9wFMCr1NCj9FCuOr18rKHstCCqw/jDKMMWs+SksIDvujsnb5T9ASi/dvF/dv+OgV9w84zvsIHndHBp+Ksomngpx6GRPgpXGYcXAa/OTXBw54ndL5ENUBkNf34dYD+HHkFfwIBvfE97cFwL+mxcsalYuVipYehMhyvl6zX7RWn0yMSYxRdFpdCFpdc1NJGnHXpQe4nLGsqh6rrbObuBu3irF8qm2qbZxmjl+OWXdcXmD79PvmGEP4cQcOfpDX9+nN9nf3K9USi9j3r9hv2RP62PeYFT6ABkmiUrpbHrpcw3LNitCKxqS8vLy9o8aJz4fqX884tQgT/MCvpryMyY2/ebhlsWaxXp5WjFaMXXdkZAhkZHhdVBqE2JcHrJeopKMepKOolqyJx4e3W45OjGN+a290cHRqgWWObo0YR6oHvrZ9bq0fE/qvbJ5ijVaNXHtjamgIaWpjelwbXmWbq2ofaqt7srgaDq6ydvch0fjBdwH4FtoD+Gb3MxXP0Ub4wSwG/Ab8wQVF+Bb7IdsH/Av3ZxX3u/hVBfxVBw6Xj9b4GNb3RNABi9b4BdkD95kEjEKnTMJYwljMc9SQ0Y/GqLrAusCiyorTitJyyVq/CMBaT6VEG1pef3JiH7L3aQX3sdD77wZH/A7HdZmen52mmhmonKqUqo2/jrh4sGQIsWSeXVYaVnleZ2QeZGhgeFgbWYpfnmawCGaweba6Gg7kk9j4S9P3UkMK1fhK1gP4APmmFfum+6YFT09tQzgaN6lFxlIeV8LNcdgb3dGoxsYfxsap0t8a323RTsMeT8NDpDaG91L3Uhj7PPuaFcW8d2O0H7VhoFhOGk52V2FgHl1eVXRKG1NbnbBjH160db7KGsyiw7q4HrW4v6DHGw5CsXb3+dP3n9IB97P4ChXD03UG9w33nwXS/E5E9/wH+w37nwX7LUP3Cwb7Nvv5Bd0GDoCR1Pfc0/eL1RKL2GnW95LWZtkT7Pg7+IsVmaGTrI22jr95uWKzYrNbn1OMVYxceGNlZGZ2XYlViUioVsZkCBPyN2JgRokrikygVbheuF7BcsuH0IbJocC7wLylxYjOh+hgzzi0q5+hnpacCDP3ThUT7KN0l3CMaoxofm1ycghycm1/aBtobZekch9ypH6ojK6MrpiopaKloqmXroqtiqh+onQI+2f74BWrq7KcuIy4jLN7rGsIE/KtbJxkjF6MXnpjaGppamN7XIxejGWcbKpsqnyxircIuJuyqqseDtqxdvdT0/hL1wGO1fhK1gP4iPe4FcfGqdPeGuBt0U/DHr9USaU+GzlFblBQH1BRbkQ3GjepRcZUHshS1HHgkftT+1MY9Ab3cPfaFWFfV3ZOG1Fan7NjH2C1dr7IGsegv7W2Hrq4waLMG8O7eWazH7hioVdMGkp0VFxeHg73GPcX96L3F1MK2PksNQr7F1QK++xsdvhs9xcB4tgD9zj4uzUKBjT7XgVUCoz3m9r3D9oB+KH4tBX8oTz4oQb7DwT8oTz4oQYOgJ7q+PPaAYvZ9x7c9xDbA/hE+CUVubmiv8Yaz3PEWroeWzIK2U4KrkQKCKxsnGZfGisK3gaPwqW/u7wIPPt5FTos3AYOTPdPqmz3BH33e3v3GmurEousvMH3D/c/a6sTroD4GCAKE1cALQoTrwAqCp6UnJszCqydrqgfqK+aSwoTlwAvCrZfHl+3wHXJUAr7jogjCg73mbN295jQ+E13Afha+akVNQb77P2WBe0G9wD3mAX3+Qb3AfuYBeAG/Jr33RX3Kvf09yf79AUOfJ7U977R95XTEovb97fYYNoT9PeJnhXMw6C1ux+8tqK/iciI5mHMPLAIE/jGsajA0BrFc7tcsR6vXladThv7bv2WBtv4TRX3lfccB7augHWoH6t0m2piGmZ8bGxzHnRtaH9jG/sg/AQV9773HAfCtoB0rB8T9K9ynmaOWggvkERO+wsbDvdNj9T5G9YBi9UD+Sf3bRVwY2ZqXHEIcFpYfVcbMj6szEsfTM1s2Y3ljeKr1cjICMnJ1argG/cHjOReyy4I4gZqy1y+T7FMskaePoz7AowuZj9ACD1AZCz7BRr7ArIs2Dwe2TzoZPcBjPctjvcR2NL3GQgO9yKe0/kF0wGL1fiB2QP3mZ4V9OWx1tYf19ax5vQa9wJj5zzUHtQ8JrD7Dxv7c/2VBtXTFfkF9zUH5NhuUcwfzFGtQ402jjRtP05LCEtOQWs0Gw77BJ7T967U96LTUwr4OOYV+3EGY2+Tm3sfcqZ+paYa90b3ldT7lfc6BykK/BsH+w3eR/cIHvdxBg77DrN29/bU96LTUwrY+AkV95XU+5X3OgYpCvzY2AcO96qQ1fd10Pf30xKL1Yv3cRPo97v4ChVGB/hDinVKYFRMXhlMXkx3SpAskTutTMhKymvXjeSO5K3WzcrLyNaq4oz3DIzpW88oCOEGYdpYw06uCKtSRZs4G/sGK2U/PB8T8D0/ZC2K+wSK+wG0Ld093T3sZPcDiuaK3qjYxOfQv+mX9wwIDsKzdvf21PfqQwrW+BzVA/ix+agVQfvq/Bz36kD9ldb39vgc+/bVBg78LTkK2APY+akVPv2WVAo8j9f5WXcB99/ZA/gt+akVPfznBmp+cHF0HnJ0boFqjWiNbZdyoXKifqeKrAg/Bo1VoF2yZAhls7p4wRvDu52vtB+0sJ+4wBoOmTkK3AP3d/gyFffE+AsFKQb79PxKBfhKOv2W2PdlB+73Efea++IF6AYO+xGe1PlNd1MK+DnnFftxBmJvk5t7H3KlfqWmGvjmPvzYB/sP3kj3CR73cQYO99A5Ctn5G9kD+bf5qRX7Cgb7s/0s+6r5LAX7DP2W2gaJ+Tv3tv07BcwG97n5PAX9PNkHDvcNOQrb+FXaA/j0+akVPP0aBvw9+RoFI/2W2/kyBvhO/TIF4QYO98uP1vka1gGL2fkl1QP5RvcPFdrbs+v3Axr3AWPnOtYe1zossfsDG/sDTwosRwo+3Opl9wJMCr5NCj5FCuSr18vKHspCCq0/jDKMMWo+SksIDnqzdvfG0ffV0wGL2/f02gPb99kV91MGzMOgtrofurajv4zKjNBzxVi4CLlYTKI/G/uL/ZXbBvgMBPfV9z8HurV9bq4fr2+eZ41fjlh6YmZrCGxpYXxaGw73vYzYUnb5bdESi9P5INQTuPmxzxV3jFSOeZNwrhnb2bPq9wQa9wJk6T3ZHtk+LbL7Ahv7Ai1kPT0fPT1kLfsCGvsCsi3ZPR492elk9wIb2tOhts4fE3hfssB1zxufBvs990EVE7hMTD5rMRsxPqvKSx9MymvY5Rrlq9jKyh7Ly9ir5Rvl2GtLyh/LTKs+MRoxaz5LTB4Oo7N2+U3TAYva+ADcA/e798YVoY3FkLyksrYZsregv4zHjNRzx1i6CLpYR6M2G/uK/ZXa+U33QAbFuntqrh+sbZxkjVuOVnhgZGgIaGRVekgbQAb3mPvwBeoGDlyQ1viSd/cv1gGj1vez1gP3gASMSaFUuF8IX7jBdcsby8Ght7gfuLiiwIrKivZP0/sLsC6oGFGdbbKKx4qwmKqnpaimrJewia6JqX2jcaRxl2yKZwjcBsR3vGO0HmO0WqBSjFGMWXdgY2BkdluMUowvvUbicN1yGOhuulmNRIxgfGZsbWxtZnxgjGKMZ5puqm6qfK+MtQgOLrN2+U3TAfdC2AP3j/lgFfdD0/w+Q/dC/U1UCqqQ1vlaQwrX+ATYA/idQAo+/JsGVXleZ2YeZmhfeVgbVl+fsmgfaLN6vMUa+Iw//JoHQKRLvFgevFjIctOK1IrKo76+CL++pcvWGg73USYK+VhACjoG+6X9OPuk+TgFOQb30P2XBdcGDvhoJgr6ZkAKNQb7S/0t+1D5LQVEBvtK/TD7UfkwBTwG92z9lwX1Bvc3+Qz3Pf0MBfUGDvYmCvf1+DMV94j4CwUzBvtd+8j7YvfIBS4G94/8DPuY/B8F5Qb3b/fg93P74AXlBg7ZJgr3mNkD9+b3qhX3m/iUBTYG+278Rvtu+EYFOwb3mPyTBfuY2QcOUZ7U+QXUAfhj5xX8FQb4B/kIBdH8VUL4AAf8DP0MBUn4bwcO+w4mCtRACkIG99n9lwXTBg7GM8sB+ThzFf04S/k4Bg770fj1dvdedwHXQAo/BuFRCg7okdb4VNUBitj4R9UD+N33wRWPB47acNBTxVPGR6k7jjiORG83CqdGwlEewlHObdqI5YjXrcnSCDLVB/xQ+FIVtra+oMgbxb09CoxPjEx2VF5cX11WdUyOUY5ZomK4Yrh4PAoIDtqR1vhU1fdPdwGJ1vhH2AOJ98UViTmoRMhPyE/Sbt2O2o7PqcLFCMLFp9DbGt5t0k/HHlDHRKc5iDeIRWpTSgj3tED8dQf3wft4FU2IVqFeuV66dsKNyozHob60tgi2tL2gxRvIvnVgtR+2YKFYjE+MTndXYl5iXlp0UIgIDsaR1fhW1lMK+OP3TxU1Bl9FSWcyik2KVaFetwheuHTAyRrJo8G8uB66t7+gxorjicxltkII4QaApHa6Z7FaqhlXq1ObT4o3ikNsTlBPUG5FjDiMOalFx1LHUtJu3or3EYrowcf3AwgO9Y7W+FPW9053AZbX+EfWA/jp+aYVQPuzBlPLRa03JArIxsjHqNKJ3Qj8VvstFWK4dzwKtra/oceKCMa8PQqMT40xCuGQ1/dZ0fdI2AH4g/diFWQ5S2ExilGKWZ5gsGCwc7uFxAj4lLQG0nDLVcQeU8VJqj+QNpBCbkxNTU5tQY02jDqqRsdQx1HQb9mMyYzDnb6vvq+vvKDICPyG9yIVlsGkt7SstKy7nMKKwoq6erJqsmqkYJZWCA77tbN2+IjR9xjQEovYPvdwE/DY+JsVE+j3I9EGE/D7I6MGppilpKYenJynlLONCBPon4wFz3cH+wk4RvsNH/zZBxPwVArp+1zI9ynW+FTWAYvX+EfWA/je98kVjN1u0k7GT8dEqDmJPIguCt2O347RrcPLCFUHVnZdYmUeYmRbeFOKCFxgmahkH2Snca2AsghBBptRqlu6ZrxkxnfPiNeIzqLGugjGuqnH0xr7trIVT1igtmAfYLZ1vorIisefv7S4tLi8o8aOyo7AdbdcuFygVYlIClp2UBsOjrN2+I/T91R3AYra9/DXA/iK9+sV1HHJWL0eWL1PokSIR4hTbWBSCPeuPv2X2vfmB7ybs6yqHqyrtJu7jLqMtXquaAiuaJ1iWxr74NcHDvwxOwrj9xkBi9YD1kAKQPsZ1gZHBED8ztYGDvvt+x92+YLMi/cbEszYE7D3JEAKPPsb2gb7Dv2/FbKSqpyhpQieoJSwwBoT0PjdPvzPB2eHc4R/HoB4eYFziQg/Bw5POwr3cUMK2gP3cffCFfdt97MFLAb7nPvqBfizPP2X3fcBB+b3B/dN+3QF7gYO/C4mCovXA9dACj/9l9cGDvfes3YBi9r3ttv3uNoD+cj3/BXSdsVith5ktFmiTZA4kklnWj5a2EmvOIRNhll0ZWIIYmB3UUQa++na9/IHuJewpKoeqqSsm7Qbta99cKkfqnCaaWIa/APb+AMHtJqtqaYepqqvmbUbtK17bKQfpGyYZV8a+/LaBw6Ws3b4kdUBidj389gD+Iv37BXVc8hbvB68W0+kQxtCT3JaWx9bWnNOQRr72dj34Qe7nLSurh6urrWcuhu6s3porh+uaJ1iWxr74dgHDuaR1vhT1gGL2PhF1wP43vfEFdtv0FTFHlTGR6k8JArGxgjHx6nS3Rr8VvstFWK4eDwKtra+ociKCMW8PQqNT4wxCuRGCozV+EjXA9b1FcRL0WnfiN2I0qjGxgjGx6nT3hrbb9BUxR5UxUipPI05jkRuT08IUFBtRDkaiouIix78ddUH+Az5ERW0Xp9Xik+KTnVYYGAIYGBYdk8bUFmgtmIfYrZ1vorHisqgwbe6uLrAoMqIxom8c7ReCA7oRgqK2PhH1gP43vfAFY8Hjdxu0k7HTsdEqDiIPIkuCt6O3o7RrcTLCPuz1gf8V/kRFbS4vaPFjcqOwHa3XLhcoFWKSApZdlEbTligtmAfYLZ2vorIiseev7S4CA77tTsKEoXbO/d6E+DV9/wVsJGomKEek5iam6GdCBPQoZ2ola6MCNcHbYxuhG58bHpzdHtsCNxE/M4HE+DbBg77HY/W+FXWEqDa9zjaTdsT6Nr3UBU8BlqbYatoHqxos3e7iL+IuZyzsLOxnbiHvobWYr4/pUmhGFiccqiOtIy3r6y2jAgT8LezcGMfftsHipyIs3msaaYZaqZnmGSJXollemtsbGx8ZY1fjkKwW9F1znYYE+jDeadqilqKb4N0enp4eHKCa4pzinSVdqAIdqCAn54aDvu9ksr4VdL3W3cSi9g+92QT6Pdk0RUT8DRfteAf99YHE+j3F9IGE/D7F/dbPvzxBhPo+wrQUPcfHg6ckNb4kUMK2Pfy2AP4jPjhFT774gZbeWJoaB5paGN6XBtcYpytaB9ornq0uxr34j772QdBo026Wh5au8dy1BvUx6S8uh+7vKPJ1RoOtjsKAfi7+OEVNwb7Vfxq+1b4agU7Bvd+/M4F2gYO9+o7CgH54/jhFTkG+0j8bPsW+CcFSgb7GPwq+0X4bwU6Bvds/M4F4gb3CfgN9xD8DQXkBg6BOwoB97H3yBX3WvetBS0G+yn7bfsu920FNAb3Vvur+1T7twXlBvcp93j3Kvt4BeoGDqf7M3b5lXcB+K344RU7BvtU/Fj7SvhYBTgG9278vDX7bQXdBg4gntL4QNIB+CjlFfvZBvfC+D8F0/wXRPe6B/vF/EAFRPg5Bw78RftHdvp8QwrAA8D5tBVW/nzBBg5C9/h29yp3Afg7+E8VZ7V3aX92d4BwiBl/in2OepI1rhhulnOPdohqhmxycF59cxiuZZ6rm6SfmqOOGZaNnoelgMlxGK58p4WfjpqOm5GclZmUlZOQkwgOSPkM9wYB3PcG9wb3BgP4O/lFFatypGtscXJra6VyqqukpKse+3gWq3GkbGtycmtrpHKrqqWkqx4O91TGdqF2pPcxS8vX92jVzF13ErX3A1TR1/dn1s8TXWD4vfkhFbeTgZNlgwWIiIqKhx+KBqSpg5JYTJOEnqOXjYdclIMF9wZ1FURlkIHSsQVsvhWDlTpvkoOklJ10fHWSggU99wsVgZChtoGQVm6Hhax7gXiUhpWelIcFWLMVbpSJhKeCBS+FFZCHkoiTip6JlZKNmoyWhpKAjwiMkJuRkBqNmIWSfI2DjISJhYiMhBiQjpGLkIqUio+HioOKgoWIf4yGjBiKgwWQBpiJkYWKgIqBhIeAjYWMhY2GjwhUexWOjoyMjx+ikJabiaeKoYGVeop6ioOBjHiMfJODmoyTjJGOj5GKdoCAdowIfIoVaoKPkpigj5uIlhmWiISQgBuAgoaChB+RhI6RkI+RjBmVjpGHjoKNg4V7fnSFgRiNhLuZBfgL/FMVZJp/kIeSj5YZj5aSj5aHtHsYj5Rim4aMiI6JkBmGl4+Tlo6QjI+LkImyfBiPlGWag46Ei4SIGYSIhoeIhIeCjIOSgoCIhIWIgoZ7kYCchLF9GPxM+GsVcH+PhKWXBT2ZFYOGg3qRhZKZslOTkQX4R/xkFZGTjZSKlIiifpZ0iXeKfXmOeI50mICijZWMk4+Rkgj7Rfd8FaiBpHagHqB2c5VuG1BcXFBQulzGqKOVoKAfoKCVpKgaE1zg+7P3PBVcWnRSSRpIpFK8XB5cvcZzzxvQxaO8vB+8vKPG0BrOcsRZuh66WVCiRxtoaYR8ah8+B6KqrpayG722eWiuH65onWBZGll5YGhoHmhoYHlZG1lgna5oH2iueba9GmL3QxUTa2CckJSVnBqafZl7hh6EiYSGhoR/mxifoIWRcXGjbY2OjY6OjRmVlpSNk4OUg4uDgoKGhoaIhooI+GD8SBWDhYx6fHt6jhl5jYOVjp6OnJ2Wm4STkRiJjIaOhI2Eihl4iYCAiHiId5l4n4qjiKGhhqMI/HX4FBWTlo6XiJaIl4OVfpMIkISEjYQbe4ODeoGPg5SGH5iClo6TmJCRjJKIkpKFj4SMhI2CiYOFgwj4QvxTFYiPiIyHiYaJioiOho6GjoqPjo+NjI6JkAhIjRWXtIGHe1p1yIKIqzSUjoaYkZupepaPBfsLyhWCio17lIwFo2sViJeQk5eOiZMYgoqGhomDiZQYgoqZSJSNBWzLFYKKkkeUjAVZqxWmrQV/BmtjjMwFggaJLwWVmAaWmAUTq2ChcAWXBkvEFY2WhpJ+jX6Ng4aJfoqDjoWThpKHGJGIjYaKhYqEhoiDjIOMiJCMkoyNGIKNiH6QgpiIGZeHmZSMl4yUiJKCkISPGIaOiY+MkIyRj42RipKKjYeKhgiKB5SJBfst9xIVf4CShJaWBUDPFVmZx6SHkzZoj4KXkJyGem2QgJ+ytYAF90L7KhWDjntmhn6CiH6QGX6Qh5SRmJqwGIOPe2eIg4uDjoMZfpCUhZkbmJWSmJAfO/cKFYSQaV2EgomCjYIZjnecfp+Poo+VmYeiiJ95mHeGCF2YFZSRkJKMlI+ff513jnSPfYKGdId2mXqgiIKFGJGDBcmuFVpbkYW8ugX4VvgrFaKTmZGCfgUTbWB+dwUTa2D7GPEVqJqQjo6OjowZhYB/dAX7OrQVgYqGkYqWipiQkZWMloyRg4x8jIKFh4CKCPet/LYVeoqBk4mciZySlZyMnI2Vg455jnqDgXmKCPy3+B8VgJKJkpCUkJSVi5iDjogYjoaLhYeFhoKDioKRCJb8CRV6joSVj5yOnJWTnIiciJKBiHqIeoGDeY4I1XMVnI6Vg456jXqEgXqIeYiBkoiciZ2TlZyOCA77uPjj9wD7APcu+y73W/tJdxKL1XWh9wGfEyooChMaJQp1oxWCmIOUhJAIno6UlZwakoqQiJAeE0aUhX6QeBtc+wGhuJAGkI6Lio4flIORhI6Fm3QYpQYTij/KFaacB5sGkzQKDvu4+OPBZ3etoeWhdcISi9NwpvcDnxONKAoTdSUKcLMVE3MwCqSRBaSEephvG2NBCg77qPk99wEBq6G5oMufA/dhQApqBngnCqueNXirMaEGDvusDouL+OGi90WWBvtgogf3MhT40BWgEwA1AgABADwARwBrAI0ApACsALMAzQDiAPwBFgEvATkBUAFmAXwBiQGcAbABwwHOAdYB4QHsAfYB/QIFAgsCFQIeAiQCKgIuAjwCSgJOAloCZQJwAnsChgKOApgCogKsArYCvwLIAtEC1wLdAuIC5ffFFX5nbHJafHaFdIdxighWXZ6yZh9msni5wBq/nbewsB6wsLedvxu7tH5yrB+wb51mXBpngGh2ah4LPwobXoxmr7csCguYg5SDkAiejpWVnBqSiZCIkB6Uhn6Qdxtd+wGhuI8GkI+LigsVb32craqUrJ6sH6yen5yfG6OXeWhwg217ah9meXh5dxsLjjmORG43CqZGwlEewlHPbdqJ3YjSqAshCh6MPgoIC7N2+Zd3AQtBd9UFavsBoOEGoDUFoAah4QU1nwf7K+UVC/df+UgVwly2UzoKw8K5uMMeX1AVC6aYpaSmHpubp5OzG/dx0/txBvsIOEf7DR8LnoR8lHIbbW5+cm4fbG97amYaWK1eu4weoguKYH1nb29nZxhaWmtmfXJ8cIFuiGyHahgLGrewsLe4sWdfC2p2dHtyG36Hl6SSH7v3QlaKf2WFnAULR21UUQhUUXBGOxo4qUPGTx7GUNJuC6xiW5tUG05WdWBgH2BgdlZOGk2hVgt4h4KCfBt2gJqqNgoLTHZVXlxfXFZ2TI5QjVqiYrgIDrpRo0eKRopPc1laCFlaclFGGnkLH52gjXaTfZmCGZGIkomTigiyC4qPhoQagoKGeB4LFVIK+6IEPguolpqgmpWEfI4fC1BPCFBQbUM4GjsLFaacB5sGkjQKC7N2+ZZDCgtUXl1UVLlgC7N2+M53C8CMx4zIoL62tgt2YLQftGChWAt0g3h6ewt7e3iDdAv5qhULd3ReZKFwsKedmaeTHwvL16vkG+XYbE3MH8xNC3cBiwuutJy7iriKs3usbAurS8oIS8pr1+MaC/s0dvdO1vhT1gEL+wMa+wCzL9s+HgtMik91WGJgCGBiC/zQFT8KjAgLaK5inVqMWoxgC7S4Gr51tmCtHgsb9wPps9raH1cLFUpLPWswjDKMC5oHu5y0ra0eCy1jPDwfPTxkCxv2kc+yqNQIC/teBdkGCz77F9gGCwGL2AML2AYOAAAB9AA/AAAAAAH0AAAB9AAAAKIAAQEAAAAChgAAAecAAAMyAAAC8gAAAJn//wEMAAABHAAAAX0AAAIxAAAA+gAJEf4AAACeAAABwQAAA3IAAAEtAAACKQAAAi8AAAJfAAACSAAAApUAAAHzAAACMQAAAosAAwCeAAAA5AAAAj0AAAIxAAAB/QAAA0EAGAItAAAC9QAAAsoAAAHMAAABwgAAA1IAAAJzAAAAowAAAe0AAAJKAAABvwAAA3gAAAK1AAADcwAAAisAAANlAAACVAAAAg0AAAHfAAACWwAAAvkAAAQQAAACpwAAAooAAAIC//QBwgAAAncAAAD/AAACmf//Aov//gJ3AAACpgALApIAAAEbAAACmgAAAj///wCfAAAA4wABAgAAAACiAAADhgAAAkf//gKXAAAClQABApn//wEb//oBswAAARMAAAJNAAACZwAAA5IAAAIy//8CWAAAAdH/7wCLAAAB8wAAAfkAUQL8AAABGAAAARgAAAEoAAABJAAA) format('opentype');font-display:swap}
  .abk{font-family:'abeatbyKai',system-ui,sans-serif}
  .spec{border:1px solid var(--line,#2a2a2a);border-radius:4px;padding:1rem;margin:.75rem 0;background:var(--card,#111)}
  .spec .ctl{display:flex;flex-wrap:wrap;gap:.75rem;align-items:center;margin-bottom:.9rem}
  .spec input[type=text]{flex:1 1 16rem;min-width:0;padding:.5rem .65rem;border-radius:3px;
    border:1px solid var(--line,#333);background:var(--bg,#0b0b0b);color:inherit;font:inherit}
  .spec label{font-size:.7rem;letter-spacing:.08em;text-transform:uppercase;opacity:.65;
    display:flex;gap:.4rem;align-items:center;white-space:nowrap}
  .spec input[type=range]{width:7rem;accent-color:var(--accent,#d8a43c)}
  .spec .out{min-height:5rem;word-break:break-word;line-height:1.15;padding:.4rem 0}
  .glyphs{display:grid;grid-template-columns:repeat(auto-fill,minmax(2.6rem,1fr));gap:.3rem;margin:.75rem 0}
  .glyphs span{aspect-ratio:1;display:grid;place-items:center;font-size:1.35rem;
    border:1px solid var(--line,#262626);border-radius:3px;background:var(--card,#111)}
  .dumb{font-family:ui-monospace,Menlo,monospace;font-size:.72rem;opacity:.7;
    border:1px dashed var(--line,#3a3a3a);border-radius:3px;padding:.15rem .4rem;
    display:inline-block;margin-top:.3rem}
  </style>

  <section id="specimen">
    <h2>Try it</h2>
    <div class="spec">
      <div class="ctl">
        <input type="text" id="sx" value="her luscious curvy bass — clean, simple, deep." aria-label="Specimen text">
        <label>Size <input type="range" id="ss" min="18" max="140" value="64"></label>
        <label>Track <input type="range" id="st" min="-4" max="24" value="0"></label>
      </div>
      <div class="out abk" id="so">her luscious curvy bass — clean, simple, deep.</div>
    </div>
    <p class="lede">Ninety-seven glyphs, drawn in Fontographer and released 23 April 2011. What you are typing in is the actual 2011 file, embedded in this page.</p>
  </section>

  <section id="glyphs">
    <h2>The character set</h2>
    <div class="glyphs abk">
      <span>A</span>
      <span>B</span>
      <span>C</span>
      <span>D</span>
      <span>E</span>
      <span>F</span>
      <span>G</span>
      <span>H</span>
      <span>I</span>
      <span>J</span>
      <span>K</span>
      <span>L</span>
      <span>M</span>
      <span>N</span>
      <span>O</span>
      <span>P</span>
      <span>Q</span>
      <span>R</span>
      <span>S</span>
      <span>T</span>
      <span>U</span>
      <span>V</span>
      <span>W</span>
      <span>X</span>
      <span>Y</span>
      <span>Z</span>
      <span>a</span>
      <span>b</span>
      <span>c</span>
      <span>d</span>
      <span>e</span>
      <span>f</span>
      <span>g</span>
      <span>h</span>
      <span>i</span>
      <span>j</span>
      <span>k</span>
      <span>l</span>
      <span>m</span>
      <span>n</span>
      <span>o</span>
      <span>p</span>
      <span>q</span>
      <span>r</span>
      <span>s</span>
      <span>t</span>
      <span>u</span>
      <span>v</span>
      <span>w</span>
      <span>x</span>
      <span>y</span>
      <span>z</span>
      <span>0</span>
      <span>1</span>
      <span>2</span>
      <span>3</span>
      <span>4</span>
      <span>5</span>
      <span>6</span>
      <span>7</span>
      <span>8</span>
      <span>9</span>
      <span>&amp;</span>
      <span>@</span>
      <span>#</span>
      <span>$</span>
      <span>%</span>
      <span>!</span>
      <span>?</span>
      <span>.</span>
      <span>,</span>
      <span>;</span>
      <span>:</span>
      <span>&#39;</span>
      <span>&quot;</span>
      <span>(</span>
      <span>)</span>
      <span>-</span>
      <span>–</span>
      <span>—</span>
      <span>/</span>
    </div>
    <p class="lede">97 glyphs total. Latin upper and lower case, figures, punctuation.</p>
  </section>

  <section id="foundry">
    <h2>Foundry.Ink &mdash; by Studio X</h2>
    <p class="lede" style="margin-bottom:1rem">The hand-made side. Custom lettering, hand-inked drawing, and typefaces cut to order rather than pulled off a shelf. <b>abeatbyKai was published under this name in 2011</b> &mdash; which is why DaFont still credits Foundry.ink. It is not a rename; it is the same hand, still working.</p>
    <div class="board">
      <div class="st"><span class="dot on"></span><span class="n">The name is fifteen years old<span class="d">Foundry.ink is the byline on 1.2 million downloads. It was never retired &mdash; it just never had a room of its own</span></span><span class="tag">Standing</span></div>
      <div class="st"><span class="dot work"></span><span class="n">Hand-inked lettering &amp; art<span class="d">Drawn, not generated. One-off wordmarks, custom character sets, brush and pen work</span></span><span class="tag">Building</span></div>
      <div class="st"><span class="dot work"></span><span class="n">Custom typeface commissions<span class="d">A face cut for one client, to their brief. Priced per job, not per license</span></span><span class="tag">Building</span></div>
      <div class="st"><span class="dot work"></span><span class="n">Extending abeatbyKai<span class="d">New weights and an italic &mdash; gated on whether the 2011 Fontographer source still exists anywhere</span></span><span class="tag">Held</span></div>
      <div class="st"><span class="dot off"></span><span class="n">foundry.ink points elsewhere today<span class="d">The domain is held and live since 2020, but it currently serves the general Studio X page. It should land here.<br><span class="dumb">LINK &rarr; https://foundry.ink &mdash; LIVE, but not yet pointed at this section</span> <span class="dumb">LINK &rarr; /commission &mdash; NOT BUILT</span></span></span><span class="tag">To wire</span></div>
    </div>
    <p class="lede"><b>Why <code>.ink</code>.</b> The TLD carries half the meaning here, the same way <code>.fyi</code> means the plain-language explainer. <code>union.ink</code> is union ink on press; <code>foundry.ink</code> is ink by hand. Two kinds of ink, one convention.</p>
  </section>

  <section id="numbers">
    <h2>Fifteen years in the wild</h2>
    <div class="board">
      <div class="st"><span class="dot on"></span><span class="n">1,225,071 downloads on DaFont<span class="d">Read live from dafont.com on 2026-08-10. 19 the day before. First seen 26 May 2011</span></span><span class="tag">Verified</span></div>
      <div class="st"><span class="dot on"></span><span class="n">Still moving, 15 years on<span class="d">About 25 a day, steady. It has never been promoted</span></span><span class="tag">Verified</span></div>
      <div class="st"><span class="dot work"></span><span class="n">Mirrored well beyond DaFont<span class="d">FontSpace, FontGet, Online Fonts, and a Vietnamese cut (UTM AbeatbyKai). True total is higher and, before now, uncountable</span></span><span class="tag">Reported</span></div>
      <div class="st"><span class="dot work"></span><span class="n">Free for personal use<span class="d">That is the DaFont listing, and the author note has said &ldquo;NOT FREE&rdquo; since 2011. Commercial use has always needed a license &mdash; there has just never been a way to buy one</span></span><span class="tag">The gap</span></div>
    </div>
  </section>

  <section id="license">
    <h2>Licensing</h2>
    <div class="board">
      <div class="st"><span class="dot on"></span><span class="n">Personal use &mdash; free, and staying free<span class="d">Students, personal projects, anything not making money. Download it from anywhere it already lives. No account, no email</span></span><span class="tag">Free</span></div>
      <div class="st"><span class="dot work"></span><span class="n">Commercial &mdash; desktop<span class="d">Logos, packaging, print, anything a business ships</span></span><span class="tag">Not wired</span></div>
      <div class="st"><span class="dot work"></span><span class="n">Commercial &mdash; web<span class="d">@font-face on a site you own or operate</span></span><span class="tag">Not wired</span></div>
      <div class="st"><span class="dot work"></span><span class="n">Commercial &mdash; app &amp; embedding<span class="d">Bundled inside software, games or a product</span></span><span class="tag">Not wired</span></div>
      <div class="st"><span class="dot off"></span><span class="n">There is no checkout yet<span class="d">This is honest rather than coy: the license terms are settled, the payment path is not. Until it is, commercial licensing goes through a person.<br><span class="dumb">LINK &rarr; mailto:office@studiox88.com (live)</span> <span class="dumb">LINK &rarr; /license &mdash; NOT BUILT</span> <span class="dumb">LINK &rarr; /buy &mdash; NOT BUILT, needs a payment processor</span></span></span><span class="tag">Open</span></div>
    </div>
  </section>

  <section id="provenance">
    <h2>It carries its own record</h2>
    <div class="board">
      <div class="st"><span class="dot on"></span><span class="n">Designed 23 April 2011<span class="d">Fontographer 4.7 &middot; build FG4M0000002045 &middot; 97 glyphs. The file still says so</span></span><span class="tag">In the file</span></div>
      <div class="st"><span class="dot on"></span><span class="n">&copy;1988 KaiBuskirk.com<span class="d">The copyright string inside the 2011 file. The studio date, carried by the artefact</span></span><span class="tag">In the file</span></div>
      <div class="st"><span class="dot work"></span><span class="n">FM-BM provenance block<span class="d">The font that got away, re-released as the one that keeps track of itself. Lineage 2011 &rarr; 2026, readable and auditable<br><span class="dumb">LINK &rarr; /abeatbykai.md &mdash; NOT BUILT</span></span></span><span class="tag">Drafted</span></div>
      <div class="st"><span class="dot off"></span><span class="n">No license string inside the font<span class="d">The 2011 name table carries no license URL. Any copy in the wild is mute about its own terms. Fixing that is the v2 release</span></span><span class="tag">To fix</span></div>
    </div>
  </section>

  <script>
  (function(){
    var x=document.getElementById('sx'), s=document.getElementById('ss'),
        t=document.getElementById('st'), o=document.getElementById('so');
    if(!x||!o) return;
    function draw(){
      o.textContent = x.value || 'Type something';
      o.style.fontSize = s.value + 'px';
      o.style.letterSpacing = t.value + 'px';
    }
    x.addEventListener('input',draw); s.addEventListener('input',draw); t.addEventListener('input',draw);
    draw();
  })();
  </script>


  <section id="boh">
    <h2>Back of house</h2>
    <div class="key" id="keypanel">
      <div><span class="t">Back of house</span><span class="s" id="keysub">License records and the foundry board.</span></div>
      <span class="bohcta" id="keycta" data-boh="">Not yet</span>
    </div>
    <div class="bohgrid" id="bohgrid">
      <span class="bohx" data-boh="https://work.kaibuskirk.com">Workboard<span>the live estate board</span><i>Not yet</i></span>
      <span class="bohx" data-boh="https://arc.kaibuskirk.com">Arc<span>session record + handoff</span><i>Not yet</i></span>
      <span class="bohx" data-boh="https://bus.kaibuskirk.com">The Bus<span>cadre comms board</span><i>Not yet</i></span>
      <span class="bohx" data-boh="https://members.kaibuskirk.com">Membership<span>roster + access</span><i>Not yet</i></span>
    </div>
  </section>

  <section id="works">
    <h2>The rest of the works</h2>
    <div class="fam"><a class="" href="https://kaibuskirk.com"><span class="n">Kai Buskirk</span><span class="d">technology, merch, design, apps</span></a><a class="" href="https://studiox88.com"><span class="n">Studio X</span><span class="d">The studio — cloud, web &amp; AI since 1988</span></a><a class="" href="https://rail-works.com"><span class="n">Rail-Works</span><span class="d">The suite — rails for work that moves</span></a><a class="" href="https://cadre.fyi"><span class="n">Cadre</span><span class="d">The cadre gateway</span></a><a class="" href="https://freedatime.com"><span class="n">FreeDaTime</span><span class="d">A free multi-witness grounding clock. Authoritative local and UTC time, read not computed.</span></a><a class="" href="https://printshop.link"><span class="n">Printshop</span><span class="d">The print rail and shop network</span></a><a class="" href="https://rail-fax.com"><span class="n">Rail-Fax</span><span class="d">Send a fax without a fax machine</span></a><a class="" href="https://rail-mail.com"><span class="n">Rail-Mail</span><span class="d">Mail between machines</span></a><a class="" href="https://unionmadeservices.com"><span class="n">Union Made Services</span><span class="d">The shop — sites, storefronts and tools for locals</span></a><a class="" href="https://union.ink"><span class="n">Union.ink</span><span class="d">Union labor, on contract</span></a><a class="" href="https://freedanode.com"><span class="n">FreeDaNode</span><span class="d">The multi-witness grounding network behind FreeDaTime. Free forever, open source, attribution only.</span></a><a class="" href="https://unionmadesystems.com"><span class="n">Union Made Systems</span><span class="d">The rails Union Made Services runs on</span></a><a class="" href="https://unionmadetrades.com"><span class="n">Union Made Trades</span><span class="d">The register of union, allied and union-supporting businesses across every trade. Issues no label of its own.</span></a><a class="" href="https://unionprintingtrades.com"><span class="n">Union Printing Trades</span><span class="d">A public register of Union Made Trades shops. Every entry records who verified it and when.</span></a><a class="" href="https://abeatbykai.com"><span class="n">A Beat By Kai</span><span class="d">Held</span></a><a class="" href="https://doodles-noodles.com"><span class="n">Doodles &amp; Noodles</span><span class="d">Draw without a plan, play without a plan. A sketchbook from Studio X Ink.</span></a><a class="" href="https://foundry.ink"><span class="n">Foundry.Ink</span><span class="d">Custom lettering, hand-inked art and typefaces cut to brief.</span></a><a class="" href="https://loveartrobots.com"><span class="n">LOVE-ART-ROBOTS</span><span class="d">LOVE is why. ART is what. ROBOTS are how. A creative promotional project from Studio X Ink.</span></a><a class="" href="https://shipwerx.com"><span class="n">ShipWerx</span><span class="d">The shipping rail</span></a><a class="" href="https://studiox.ink"><span class="n">Studio X Ink</span><span class="d">The creative imprint of Studio X — art, design, graphics, robots. The way into the work.</span></a><a class="" href="https://union-delivery.com"><span class="n">Union Delivery</span><span class="d">Chartered local delivery</span></a><a class="" href="https://wearx.fyi"><span class="n">Wear X</span><span class="d">Boutique merchandising, branding and product development. Brand, product and production under one roof.</span></a></div>
  </section>

</div></main>

<aside class="sset" id="sset" aria-hidden="true" aria-label="Settings">
  <h3>Settings <button type="button" id="ssetx" aria-label="Close settings">&times;</button></h3>
  <div class="sc">
    <div class="provbox"><p class="bld">build <b>260817-934b49c2</b> · template door.template.html · serialized 2026-08-22T08:51:06Z</p></div>
    <label class="sfield"><span>Text size</span>
      <div class="seg" id="sgText" role="group">
        <button type="button" data-v="15">S</button>
        <button type="button" data-v="17">M</button>
        <button type="button" data-v="19">L</button>
      </div>
      <em>Scales every page on this site. Stored on this device only.</em>
    </label>
    <label class="sfield"><span>Rail</span>
      <div class="seg" id="sgRail" role="group">
        <button type="button" data-v="auto">Auto-hide</button>
        <button type="button" data-v="stay">Stay open</button>
      </div>
      <em>Auto-hide minimises the rail five seconds after you stop using it.</em>
    </label>
    <label class="sfield"><span>Structure tags</span>
      <div class="seg" id="sgTags" role="group">
        <button type="button" data-v="off">Off</button>
        <button type="button" data-v="on">On</button>
      </div>
      <em>Shows the Rail-Works part names on the page &mdash; MAIN-STAGE and the rest.
        For building and reviewing, not for visitors. This device only.</em>
    </label>
    <label class="sfield"><span>Colour mode</span>
      <div class="seg" id="sgTheme" role="group">
        <button type="button" data-v="light">Light</button>
        <button type="button" data-v="dark">Dark</button>
        <button type="button" data-v="system">System</button>
        <button type="button" data-v="custom">Custom</button>
      </div>
      <div class="cfield" id="cAccent" hidden>
        <input type="color" id="sgAccent" aria-label="Accent colour">
        <button type="button" id="sgAccentReset">Reset</button>
      </div>
      <em>Dark is this door&rsquo;s own trade dress. <b>Light is DERIVED</b> from it &mdash; same hue, inverted lightness, accent unchanged &mdash; not art-directed, so treat it as a test surface. Custom keeps the base and swaps the accent. This device only.</em>
    </label><label class="sfield"><span>Motion</span>
      <div class="seg" id="sgMotion" role="group">
        <button type="button" data-v="full">Full</button>
        <button type="button" data-v="less">Reduced</button>
      </div>
      <em>Reduced turns off the slide animations. Your system setting is honoured either way.</em>
    </label>
    <p class="stamp" id="sstamp"></p>
  </div>
</aside>
<div class="scrim" id="scrim"></div>
<aside class="tdrawer" id="tdrawer" aria-label="RAILS"><div class="tdbody" id="tdbody"><p class="ksub">The five points of a Rail-Works door and what this one carries on each. Counted off this page when it was built — from the markup, not from the config that claims it. A point with nothing on it says so.</p><div class="kan"><section class="kcol"><h3><span class="kname">HEADER</span> <span class="knote">who</span> <span class="kct">3</span></h3><div class="kcards"><article class="kcard"><span class="ktag">Live</span><span class="kn">StudioX Fonts</span><span class="kd">The name this door answers to.</span></article><article class="kcard"><span class="ktag">Live</span><span class="kn">The mark</span><span class="kd">Generated from this door’s accent glyph, not a borrowed mark</span></article><article class="kcard"><span class="ktag">Not yet</span><span class="kn">Back of house</span><span class="kd">No gate recorded, so the key never probes and stays dark.</span></article></div></section><section class="kcol"><h3><span class="kname">TOP</span> <span class="knote">where and state</span> <span class="kct">3</span></h3><div class="kcards"><article class="kcard" data-kstate="hold"><span class="ktag">Deploy</span><span class="kn">Stage 3 of 4 — Design · Develop · Deploy · Display</span><span class="kd">Live, current, and showing no work on the bench. The door is right and it says nothing. Run series-check --live to separate Deploy from Develop — a door that has fallen behind the series looks the same from here.</span></article><article class="kcard"><span class="ktag">Live</span><span class="kn">Switch, key and cue</span><span class="kd">No door tabs declared here. The bar still carries the state switch, the key and the handle for this tray.</span></article><article class="kcard"><span class="ktag">Live</span><span class="kn">This tray</span><span class="kd">The board you are reading. It stands in until the door has something of its own to say here.</span></article></div></section><section class="kcol"><h3><span class="kname">LEFT</span> <span class="knote">go</span> <span class="kct">2</span></h3><div class="kcards"><article class="kcard"><span class="ktag">Live</span><span class="kn">2 tabs</span><span class="kd">The way into the work, and the key.</span></article><article class="kcard"><span class="ktag">Live</span><span class="kn">1 rail bar</span><span class="kd">More than one bar puts a hamburger in the rail foot.</span></article></div></section><section class="kcol"><h3><span class="kname">RIGHT</span> <span class="knote">in</span> <span class="kct">2</span></h3><div class="kcards"><article class="kcard"><span class="ktag">Live</span><span class="kn">2 tabs</span><span class="kd">What you take in, and what you bring in.</span></article><article class="kcard"><span class="ktag">Live</span><span class="kn">Machine readable</span><span class="kd">door-version.json · manifest.webmanifest · llms.txt · tray.json · index.md</span></article></div></section><section class="kcol"><h3><span class="kname">BASE</span> <span class="knote">the bar and the tray</span> <span class="kct">3</span></h3><div class="kcards"><article class="kcard"><span class="ktag">Not yet</span><span class="kn">0 items</span><span class="kd">The tray renders, and it is empty. 26 of 33 doors are in this state — the structure is there, the work is not listed yet.</span></article><article class="kcard"><span class="ktag">Not yet</span><span class="kn">State filter</span><span class="kd">Not declared, so the bench shows everything at once.</span></article><article class="kcard"><span class="ktag">Live</span><span class="kn">Series 260817-934b49c2</span><span class="kd">Built from door.template.html. Every door in the estate builds from the same one.</span></article></div></section></div></div><div class="tdh" id="tdh"><button class="rcue" type="button" data-ttray aria-expanded="false" aria-controls="tdbody" aria-label="Close the location board">▲</button><span class="lbl">RAILS</span><button class="rcue r" type="button" data-ttray aria-expanded="false" aria-controls="tdbody" aria-label="Close the location board">▲</button></div></aside>
<aside class="drawer" id="drawer">
  <div class="dhandle" id="dh">
    <button class="rcue" type="button" data-tray aria-expanded="false" aria-controls="dbody" aria-label="Open the tray">▲</button>
    <span class="lbl">The works</span>
    <button class="rcue r" type="button" data-tray aria-expanded="false" aria-controls="dbody" aria-label="Open the tray">▲</button>
  </div>
  <div class="dbody" id="dbody">
    <div class="tray"></div>
    <div class="row"><a class="" href="https://kaibuskirk.com"><span class="n">Kai Buskirk</span><span class="d">technology, merch, design, apps</span></a><a class="" href="https://studiox88.com"><span class="n">Studio X</span><span class="d">The studio — cloud, web &amp; AI since 1988</span></a><a class="" href="https://rail-works.com"><span class="n">Rail-Works</span><span class="d">The suite — rails for work that moves</span></a><a class="" href="https://cadre.fyi"><span class="n">Cadre</span><span class="d">The cadre gateway</span></a><a class="" href="https://freedatime.com"><span class="n">FreeDaTime</span><span class="d">A free multi-witness grounding clock. Authoritative local and UTC time, read not computed.</span></a><a class="" href="https://printshop.link"><span class="n">Printshop</span><span class="d">The print rail and shop network</span></a><a class="" href="https://rail-fax.com"><span class="n">Rail-Fax</span><span class="d">Send a fax without a fax machine</span></a><a class="" href="https://rail-mail.com"><span class="n">Rail-Mail</span><span class="d">Mail between machines</span></a><a class="" href="https://unionmadeservices.com"><span class="n">Union Made Services</span><span class="d">The shop — sites, storefronts and tools for locals</span></a><a class="" href="https://union.ink"><span class="n">Union.ink</span><span class="d">Union labor, on contract</span></a><a class="" href="https://freedanode.com"><span class="n">FreeDaNode</span><span class="d">The multi-witness grounding network behind FreeDaTime. Free forever, open source, attribution only.</span></a><a class="" href="https://unionmadesystems.com"><span class="n">Union Made Systems</span><span class="d">The rails Union Made Services runs on</span></a><a class="" href="https://unionmadetrades.com"><span class="n">Union Made Trades</span><span class="d">The register of union, allied and union-supporting businesses across every trade. Issues no label of its own.</span></a><a class="" href="https://unionprintingtrades.com"><span class="n">Union Printing Trades</span><span class="d">A public register of Union Made Trades shops. Every entry records who verified it and when.</span></a><a class="" href="https://abeatbykai.com"><span class="n">A Beat By Kai</span><span class="d">Held</span></a><a class="" href="https://doodles-noodles.com"><span class="n">Doodles &amp; Noodles</span><span class="d">Draw without a plan, play without a plan. A sketchbook from Studio X Ink.</span></a><a class="" href="https://foundry.ink"><span class="n">Foundry.Ink</span><span class="d">Custom lettering, hand-inked art and typefaces cut to brief.</span></a><a class="" href="https://loveartrobots.com"><span class="n">LOVE-ART-ROBOTS</span><span class="d">LOVE is why. ART is what. ROBOTS are how. A creative promotional project from Studio X Ink.</span></a><a class="" href="https://shipwerx.com"><span class="n">ShipWerx</span><span class="d">The shipping rail</span></a><a class="" href="https://studiox.ink"><span class="n">Studio X Ink</span><span class="d">The creative imprint of Studio X — art, design, graphics, robots. The way into the work.</span></a><a class="" href="https://union-delivery.com"><span class="n">Union Delivery</span><span class="d">Chartered local delivery</span></a><a class="" href="https://wearx.fyi"><span class="n">Wear X</span><span class="d">Boutique merchandising, branding and product development. Brand, product and production under one roof.</span></a></div>
    <p>Union-Made Member Work By Working Members.</p><p>abeatbyKai is free for personal use and always will be. Commercial use requires a license. Studio X · Since 1988.</p><p class="bld">build <b>260817-934b49c2</b> · template door.template.html · serialized 2026-08-22T08:51:06Z</p>
    <p class="dpb">Powered by <a href="https://unionmadeservices.com">Union Made Services</a></p>
  </div>
</aside>

<footer class="rfooter" id="rfooter">
  <span class="fseg">RAIL-WORKS</span>
  <nav class="fcrumb" id="fcrumb" aria-label="Location"></nav>
  <a class="fend" id="fend" href="https://studioxfonts.com/" data-self="1">studioxfonts.com</a>
</footer>

<script>
const dr=document.getElementById('drawer'),dh=document.getElementById('dh'),sc=document.getElementById('scrim');
function setDrawer(o){dr.classList.toggle('open',o);sc.classList.toggle('on',o);
  dh.querySelectorAll('[data-tray]').forEach(b=>b.setAttribute('aria-expanded',o));}
// THE BAR IS NO LONGER ONE BUTTON. Two chevrons are the tray triggers — one per
// thumb, because the device is held in both hands. The tabs between them act ON
// the tray: a tab opens it FILTERED to that category. A control on the tray bar
// that did something unrelated to the tray would break the rail grammar.
dh.querySelectorAll('[data-tray]').forEach(b=>
  b.addEventListener('click',()=>{ setCat(null); setDrawer(!dr.classList.contains('open')) }));
function setCat(cat){
  dh.querySelectorAll('[data-tcat]').forEach(t=>t.setAttribute('aria-pressed', t.dataset.tcat===cat?'true':'false'));
  dr.querySelectorAll('.tile').forEach(function(t){
    t.dataset.hid = (cat && t.dataset.cat!==cat) ? '1' : '';
  });
}
dh.querySelectorAll('[data-tcat]').forEach(t=>t.addEventListener('click',function(){
  var on = t.getAttribute('aria-pressed')==='true';
  // tapping the ACTIVE tab clears the filter and closes — one control, two ends
  if(on){ setCat(null); setDrawer(false); return }
  setCat(t.dataset.tcat); setDrawer(true);
}));
sc.addEventListener('click',()=>setDrawer(false));
addEventListener('keydown',e=>{if(e.key==='Escape')setDrawer(false)});
// either side rail wakes the pair
['vrail','rrail'].forEach(function(id){var r=document.getElementById(id); if(!r) return;
  r.addEventListener('pointerenter',function(){railOpen(true)});
  r.addEventListener('touchstart',function(){railOpen(true)},{passive:true});});
// drag the handle to open/close, like a real sheet
let y0=null;
dh.addEventListener('touchstart',e=>{y0=e.touches[0].clientY},{passive:true});
dh.addEventListener('touchmove',e=>{if(y0===null)return;const d=y0-e.touches[0].clientY;
 if(d>28){setDrawer(true);y0=null}else if(d<-28){setDrawer(false);y0=null}},{passive:true});
// ── RAIL AUTO-HIDE. Collapsed to a strip; one trigger opens it. ──
// KAI 260804: "auto hiding when not in use, exposes on a single exposed small
// trigger tab. tap (x) close (o) open. Close on auto minimize, left slides open to right."
// RAIL-BAR STACK — the hamburger steps to the next bar and wraps. One bar means
// no control at all: furniture that does nothing teaches people to ignore rails.
/* The original left-rail stepper (popover chooser) was deleted 260815.
   ONE stepper now drives every rail — see [data-railstack] below.
   Two implementations of the same control is the friction, not the feature. */
const vrail=document.getElementById('vrail');
let _idle;
function railOpen(o){
  // BOTH SIDE RAILS, ONE BEHAVIOUR (KAI 260815): "the left and right Rail-Bars
  // need to look familiar, basically function exactly alike." railOpen only ever
  // touched the left rail, so the right could never expand — the two ended up in
  // permanently different states and no amount of CSS parity could hide it.
  // The remaining visual differences were STATE, not style.
  vrail.classList.toggle('open',o);
  var _rr=document.getElementById('rrail'); if(_rr) _rr.classList.toggle('open',o);
  clearTimeout(_idle);
  if(o && (typeof S==='undefined' || S.rail!=='stay'))
    _idle=setTimeout(function(){ if(!document.querySelector('.sheet.open, .apanel.open, .toolpane.open')) railOpen(false) },5000);
}
// any use of the rail resets the idle timer; leaving it alone minimises it
vrail.addEventListener('pointerdown',function(){ clearTimeout(_idle) });
addEventListener('scroll',function(){ if(typeof S!=='undefined'&&S.rail==='stay')return;
  if(vrail.classList.contains('open') &&
  !document.querySelector('.sheet.open, .apanel.open, .toolpane.open')) railOpen(false) },{passive:true});

// ── SETTINGS. Everything here is stored on this device and nothing else. ──
const gear=document.getElementById('gear'), sset=document.getElementById('sset');
const S_KEY='rw.settings';
let S={text:'15',rail:'auto',motion:'full',theme:'system',tags:'off',accent:''};
try{ S=Object.assign(S, JSON.parse(localStorage.getItem(S_KEY)||'{}')) }catch(e){}
// THEME follows the system until the visitor says otherwise, and then it stops
// following it — including if the system flips while they are on the page.
const _sysDark=matchMedia('(prefers-color-scheme:dark)');
function applyTheme(){
  // THE SECOND GATE. This used to `return` unless the door declared a skin, so
  // even if the control had been rendered on a house door it would have done
  // nothing. Every door now carries a derived light palette, so every door can
  // answer the switch. KAI 260816.
  const h=document.documentElement;
  const custom = S.theme==='custom';
  h.dataset.mode = (S.theme==='system'||custom) ? (_sysDark.matches?'dark':'light') : S.theme;
  // Custom keeps the base and swaps ONLY the accent — the one variable the door
  // standard already treats as per-door, so it is the honest knob to expose.
  if(custom && S.accent) h.style.setProperty('--accent', S.accent);
  else h.style.removeProperty('--accent');
  const cf=document.getElementById('cAccent'); if(cf) cf.hidden = !custom;
  const m=document.querySelector('meta[name="theme-color"]');
  if(m) m.setAttribute('content', h.dataset.mode==='dark'?'#000000':'#ffffff');
}
_sysDark.addEventListener('change',function(){ if(S.theme==='system') applyTheme() });
function applySettings(){
  document.body.style.fontSize = S.text+'px';
  document.documentElement.classList.toggle('nomo', S.motion==='less');
  document.documentElement.classList.toggle('showtags', S.tags==='on');
  applyTheme();
  [['sgText','text'],['sgRail','rail'],['sgMotion','motion'],['sgTheme','theme'],['sgTags','tags']].forEach(function(pair){
    const g=document.getElementById(pair[0]); if(!g)return;
    g.querySelectorAll('button').forEach(function(b){
      b.setAttribute('aria-pressed', String(b.dataset.v===S[pair[1]])) });
  });
  try{ localStorage.setItem(S_KEY, JSON.stringify(S)) }catch(e){}
}
[['sgText','text'],['sgRail','rail'],['sgMotion','motion'],['sgTheme','theme'],['sgTags','tags']].forEach(function(pair){
  const g=document.getElementById(pair[0]); if(!g)return;
  g.addEventListener('click', function(e){ const b=e.target.closest('button'); if(!b)return;
    S[pair[1]]=b.dataset.v; applySettings();
    if(pair[1]==='rail'){ if(S.rail==='stay') railOpen(true); else clearTimeout(_idle) } });
});
function setSettings(o){
  sset.classList.toggle('open',o); sset.setAttribute('aria-hidden',String(!o));
  gear.setAttribute('aria-expanded',String(o)); sc.classList.toggle('on',o);
}
gear.addEventListener('click',function(e){e.stopPropagation();setSettings(!sset.classList.contains('open'))});
document.getElementById('ssetx').addEventListener('click',function(){setSettings(false)});
applySettings();
if(S.rail==='stay') railOpen(true);
document.getElementById('sstamp').textContent='Rail-Works door · build c5dac30a · settings live on this device only, never sent.';

// RAIL TABS slide a sheet out to the RIGHT, over the content — the extension pattern.
// Derived from the DOM, not hand-listed. A door that emits a new vtab + its
// matching <aside class="sheet" id="sheet-KEY"> now works with no JS edit --
// that is what makes this an extension pattern rather than a fixed pair.
// SOLO MODE. One class, set before paint so nothing flashes the wrong layout.
(function(){
  var q=new URLSearchParams(location.search);
  var solo = q.get('view')==='catalog' || /(^|\.)printshop-guide\.com$/i.test(location.hostname);
  if(solo) document.documentElement.classList.add('solo');
  // FACE MODE. Same pre-paint window as solo -- resolved before the stylesheet
  // lands so the boards never flash before being hidden.
  // loveartrobots.pages.dev is the LAR project's OWN host, so it shows the face
  // with no query string -- while the apex is still NXDOMAIN this is the only
  // link that IS the face. Safe for studiox.ink: the router rewrites the Host
  // header upstream, but location.hostname in the browser is still studiox.ink,
  // so the full door is unaffected. Verified, not assumed.
  // FACE MODE IS A DECLARATION (260815). It was a hardcoded hostname list in the
  // SHARED template, so every new splash door meant editing the file all 28 doors
  // build from — and a door could not be tested on a preview URL, because the
  // preview hostname was not on the list. The serializer knows which door it is
  // building; it does not need to sniff. Declare "face": true.
  var face = q.get('view')==='face' || false;
  if(q.get('view')==='full') face=false;   // an explicit way past the splash
  if(face) document.documentElement.classList.add('face');
  // A door can OPEN on a tab. printshop-hub.com lands on the bench; the guide
  // lands on the catalog. Same build, same rail -- only the first view differs.
  var open = q.get('open') || (/(^|\.)printshop-hub\.com$/i.test(location.hostname) ? 'bench'
            : (solo ? 'catalog' : ''));
  if(open) addEventListener('DOMContentLoaded',function(){
    // 260815: .vtab/.vtabs no longer exist — the left rail runs the shared
    // component. All three selectors here were stale, so ?open=<sheet> silently
    // did nothing. A rename is only finished when every reader is repointed.
    var t=document.querySelector('.rtab[data-sheet="'+open+'"]'); if(!t) return;
    var bar=t.closest('.rtabs');
    if(bar){ document.querySelectorAll('#vbars > .rtabs').forEach(function(b){b.classList.toggle('on',b===bar)}); }
    t.click();
  });
})();
// PAGE DECK — the CATALOG tab. Prev/next across spreads, page count, and the
// deck remembers nothing on purpose: a catalog opens at page one.
document.addEventListener('click',function(e){
  const b=e.target.closest('.decknav button'); if(!b) return;
  const deck=b.closest('.deck'), pages=[...deck.querySelectorAll('.page')];
  if(b.classList.contains('popout')){
    window.open(b.dataset.href, 'catalog', 'noopener,width=1280,height=900'); return; }
  let i=pages.findIndex(p=>p.classList.contains('on')); if(i<0)i=0;
  i=Math.min(pages.length-1,Math.max(0,i+(b.dataset.step==='next'?1:-1)));
  pages.forEach((p,k)=>p.classList.toggle('on',k===i));
  deck.querySelector('.pno').textContent=(i+1)+' / '+pages.length;
  deck.querySelectorAll('.decknav button')[0].disabled=(i===0);
  deck.querySelectorAll('.decknav button')[1].disabled=(i===pages.length-1);
  deck.querySelector('.deckview').scrollTop=0;
  const sel=deck.querySelector('.pgo'); if(sel) sel.value=String(i);
});
document.addEventListener('change',function(e){
  const sel=e.target.closest('.pgo'); if(!sel) return;
  const deck=sel.closest('.deck'), pages=[...deck.querySelectorAll('.page')], i=+sel.value;
  pages.forEach((p,k)=>p.classList.toggle('on',k===i));
  deck.querySelector('.pno').textContent=(i+1)+' / '+pages.length;
  const btns=deck.querySelectorAll('.decknav button[data-step]');
  btns[0].disabled=(i===0); btns[1].disabled=(i===pages.length-1);
  deck.querySelector('.deckview').scrollTop=0;
});
// COPY-TO-CLIPBOARD for contact rows. A mailto that opens nothing is a dead
// end on any machine without a mail handler; the address itself always works.
document.addEventListener('click',function(e){
  const b=e.target.closest('.mcopy'); if(!b) return;
  e.preventDefault(); e.stopPropagation();
  const v=b.dataset.copy||'', done=function(){ const t=b.textContent;
    b.textContent='COPIED'; b.classList.add('done');
    setTimeout(function(){ b.textContent=t; b.classList.remove('done') },1400); };
  if(navigator.clipboard && navigator.clipboard.writeText){
    navigator.clipboard.writeText(v).then(done,function(){ fallback() });
  } else fallback();
  function fallback(){ const ta=document.createElement('textarea'); ta.value=v;
    ta.setAttribute('readonly',''); ta.style.cssText='position:fixed;top:-9999px';
    document.body.appendChild(ta); ta.select();
    try{ document.execCommand('copy'); done() }catch(_){}
    document.body.removeChild(ta); }
});
const sheets={};document.querySelectorAll('aside.sheet[id^="sheet-"]').forEach(function(el){sheets[el.id.slice(6)]=el});
function closeSheets(){Object.values(sheets).forEach(x=>{x.classList.remove('open');x.setAttribute('aria-hidden','true')});
  document.querySelectorAll('.rtab[data-sheet]').forEach(t=>t.setAttribute('aria-expanded','false'));sc.classList.remove('on');
  if(typeof S==='undefined'||S.rail!=='stay')railOpen(false)}
document.querySelectorAll('.rtab[data-sheet]').forEach(b=>b.addEventListener('click',()=>{
  const k=b.dataset.sheet,el=sheets[k],was=el.classList.contains('open');
  closeSheets(); if(!was){el.classList.add('open');el.setAttribute('aria-hidden','false');
    b.setAttribute('aria-expanded','true');sc.classList.add('on');
    // Only a control INSIDE the left rail expands the left rail. The Impressum
    // moved to the site header (KAI 260816) and reuses .rtab so the opener
    // drives it — without this guard, tapping it slid open a rail on the far
    // side of the screen for no reason the person could connect to their tap.
    if(b.closest('#vrail')) railOpen(true);}
}));

// ── RAIL-TOP TRAY ─────────────────────────────────────────────────────────
// Defined AFTER closeSheets/setSettings because it calls them: one surface open
// at a time, enforced here rather than remembered at each call site.
(function(){
  var td=document.getElementById('tdrawer'); if(!td) return;
  var cues=document.querySelectorAll('[data-ttray]');
  function setTop(o){
    td.classList.toggle('open',o);
    cues.forEach(function(b){b.setAttribute('aria-expanded',o?'true':'false')});
    if(o){ setDrawer(false); closeSheets(); setSettings(false); sc.classList.add('on'); }
    else if(!dr.classList.contains('open')) sc.classList.remove('on');
  }
  cues.forEach(function(b){b.addEventListener('click',function(){
    setTop(!td.classList.contains('open')) })});
  // the base tray and the top tray are opposite edges of one bench — opening
  // either closes the other.
  dh.querySelectorAll('[data-tray]').forEach(function(b){
    b.addEventListener('click',function(){ if(td.classList.contains('open')) setTop(false) })});
  sc.addEventListener('click',function(){ setTop(false) });
  addEventListener('keydown',function(ev){ if(ev.key==='Escape') setTop(false) });
})();
(function(){
  var inp=document.getElementById('sgAccent'), rst=document.getElementById('sgAccentReset');
  if(!inp) return;
  var base=getComputedStyle(document.documentElement).getPropertyValue('--accent').trim();
  inp.value = S.accent || (/^#[0-9a-f]{6}$/i.test(base) ? base : '#f6821f');
  inp.addEventListener('input', function(){ S.accent=inp.value; applyTheme();
    try{ localStorage.setItem(S_KEY, JSON.stringify(S)) }catch(e){} });
  if(rst) rst.addEventListener('click', function(){ S.accent=''; inp.value=base||'#f6821f';
    applyTheme(); try{ localStorage.setItem(S_KEY, JSON.stringify(S)) }catch(e){} });
})();
sc.addEventListener('click',function(){closeSheets();setSettings(false)});

// ── RAIL-NAV — the three dots open the other doors ────────────────────────
(function(){
  var b=document.getElementById('hmenu'), p=document.getElementById('rnavp');
  if(!b||!p) return;
  function open(v){ b.setAttribute('aria-expanded',v?'true':'false'); p.hidden=!v; }
  b.addEventListener('click',function(e){ e.stopPropagation();
    open(b.getAttribute('aria-expanded')!=='true'); });
  // A corner menu that will not close is worse than no menu: outside click,
  // Escape, and focus leaving all dismiss it.
  document.addEventListener('click',function(e){ if(!p.contains(e.target)&&e.target!==b) open(false) });
  addEventListener('keydown',function(e){ if(e.key==='Escape') open(false) });
  p.addEventListener('focusout',function(){ setTimeout(function(){
    if(!p.contains(document.activeElement)&&document.activeElement!==b) open(false) },0) });
})();

// ── RAIL-FOOTER — the location trail, and its crumbs are live ─────────────
// KAI 260816: "is it good form if the breadcrumbs are actionable as quick links
// in the breadcrumb view?" Yes — with the convention kept: A BREADCRUMB LINKS TO
// ANCESTORS, AND THE LAST SEGMENT IS NEVER A LINK, because it is where you
// already are. So the trail GROWS an ancestor when a surface opens rather than
// making the leaf clickable:
//
//     RAIL-WORKS / FOH / MAIN-STAGE                 ← leaf, nothing to escape
//     RAIL-WORKS / BOH / MAIN-STAGE / SETTINGS      ← two live escapes
//
// Built from the DOM each time, never from a remembered variable: the trail can
// only ever describe what is actually open.
(function(){
  var bar=document.getElementById('fcrumb'); if(!bar) return;

  function go(kind){
    if(kind==='stage'){
      closeSheets(); setSettings(false); setDrawer(false);
      var t=document.getElementById('tdrawer');
      if(t && t.classList.contains('open')){
        var cue=document.querySelector('.hrail.top [data-ttray]'); if(cue) cue.click();
      }
    } else if(kind==='foh'){
      var b=document.querySelector('button.rsb[data-rs="foh"]'); if(b) b.click();
    }
  }

  function crumbs(){
    var out=[];
    var st=(document.documentElement.getAttribute('data-rs')||'').toUpperCase();
    // the STATE crumb is an escape only when there is somewhere to escape TO.
    if(st) out.push({t:st, go:(st!=='FOH' ? 'foh' : null)});

    var here=null;
    var td=document.getElementById('tdrawer');
    if(td && td.classList.contains('open')) here='TOP.RAIL-TRAY';
    else if(dr && dr.classList.contains('open')) here='FOOTER.RAIL-TRAY';
    else {
      var sh=document.querySelector('aside.sheet.open');
      if(sh){ var h=sh.querySelector('h3'); here=((h?h.textContent:'')||'SHEET').trim().toUpperCase(); }
      else if(sset && sset.classList.contains('open')) here='SETTINGS';
    }
    // MAIN-STAGE is an ANCESTOR when something is open, and the leaf when not.
    out.push({t:'MAIN-STAGE', go:(here ? 'stage' : null)});
    if(here) out.push({t:here, go:null});
    return out;
  }

  function render(){
    var c=crumbs(), frag=document.createDocumentFragment();
    c.forEach(function(x,i){
      if(i){ var s=document.createElement('span'); s.className='fsep'; s.textContent='/'; frag.appendChild(s); }
      var el;
      if(x.go){
        el=document.createElement('button'); el.type='button'; el.className='fbc';
        el.addEventListener('click', function(){ go(x.go) });
      } else {
        // NOT-A-LINK AND YOU-ARE-HERE ARE DIFFERENT THINGS. The state crumb is
        // inert while you are already in that state, but it is not the leaf —
        // and styling both in accent put two "you are here" marks in one trail,
        // which is one more than a trail can have.
        el=document.createElement('span');
        var last = (i === c.length - 1);
        el.className = 'fbc ' + (last ? 'leaf' : 'static');
        if(last) el.setAttribute('aria-current','true');
      }
      el.textContent=x.t; frag.appendChild(el);
    });
    bar.textContent=''; bar.appendChild(frag);
  }
  render();
  new MutationObserver(render).observe(document.documentElement,
    {subtree:true,attributes:true,attributeFilter:['class','data-rs','aria-hidden']});
})();
// ── RAIL WRAP wiring. Every wrap tab goes through the SAME closeSheets() the
// left rail uses, so one-drawer-at-a-time holds across all four edges by
// construction rather than by remembering to check. ──
document.querySelectorAll('[data-wrap]').forEach(function(b){
  b.addEventListener('click',function(){
    var el=document.getElementById('sheet-'+b.dataset.wrap);
    if(!el) return;
    var was=el.classList.contains('open');
    closeSheets(); setSettings(false);
    if(!was){ el.classList.add('open'); el.setAttribute('aria-hidden','false');
      b.setAttribute('aria-expanded','true'); sc.classList.add('on'); }
  });
});
// The right rail expands like the left one, on hover or focus within.
(function(){var rr=document.getElementById('rrail'); if(!rr) return;
  var open=function(v){rr.classList.toggle('open',v)};
  rr.addEventListener('mouseenter',function(){open(true)});
  rr.addEventListener('mouseleave',function(){open(false)});
  rr.addEventListener('focusin',function(){open(true)});
  rr.addEventListener('focusout',function(){open(false)});
})();
addEventListener('keydown',e=>{if(e.key==='Escape'){closeSheets();setSettings(false)}});
// BOH state: ask the fenced origin whether a back of house is there at all.
// PROMOTE-ONLY. Every BOH entry ships as a non-link saying "Not yet"; only if
// the origin answers do they become links and the tab say BOH.
//
// THREE GUARDS, each one paid for on 260813:
//  1. NO KEY -> NO PROBE. When no gate is configured the fetch never fires.
//     Every BOH host was deleted that morning, so all 20 doors were firing at a
//     hostname with no DNS.
//  2. HARD 2.5s ABORT. A fetch at a non-resolving host does not fail fast --
//     it hangs on DNS, and the page sits busy. A probe must never outlive the
//     thing it is decorating.
//  3. RUNS AFTER LOAD, IDLE. Nothing about the back of house is worth one
//     millisecond of first paint.
(function(){
  var key='';
  if(!key||key==='#'||!/^https?:/i.test(key)) return;   // guard 1
  function probe(){
    var ac=new AbortController(), t=setTimeout(function(){ac.abort()},2500);  // guard 2
    fetch(key,{mode:'no-cors',cache:'no-store',signal:ac.signal}).then(function(){
      clearTimeout(t);
      document.querySelectorAll('[data-boh]').forEach(function(el){
        var href=el.getAttribute('data-boh'); if(!href||href==='#') return;
        var a=document.createElement('a');
        a.href=href; a.className=el.className.replace(/\bbohx\b|\bbohcta\b/g,'').trim();
        if(el.id) a.id=el.id;
        el.querySelectorAll('i').forEach(function(i){i.remove()});
        a.innerHTML=el.innerHTML;
        el.replaceWith(a);
      });
      var tl=document.getElementById('tkeyl'); if(tl) tl.textContent='BOH';
      var c=document.getElementById('keycta'); if(c) c.textContent='Use your key';
    }).catch(function(){ clearTimeout(t); });   // unreachable: claim nothing
  }
  var go=function(){ (window.requestIdleCallback||setTimeout)(probe,1) };  // guard 3
  if(document.readyState==='complete') go(); else addEventListener('load',go);
})();
if('serviceWorker'in navigator)navigator.serviceWorker.register('/sw.js').catch(()=>{});
</script>

<script>
/* ── RAIL-STATES (KAI 260815) ───────────────────────────────────────────────
   FOH is open. LOCKER / FLOOR / BOH are gated by the KEY. Fake gating on
   purpose: the question under test is whether moving between states helps a
   person focus. Auth is a separate and far more expensive problem, and building
   it before the model is proven would be answering the wrong question. */
(function(){
  var bar=document.querySelector('.rsbar'); if(!bar||!bar.querySelector('.rsb')) return;
  var NOW=document.getElementById('rsnow'), R=document.documentElement,
      KEY='railbench.key', STATE='railbench.state',
      LBL={foh:'FRONT OF HOUSE',locker:'YOUR LOCKER',floor:'THE FLOOR',boh:'BACK OF HOUSE'};
  function keyed(){ try{ return localStorage.getItem(KEY)==='1' }catch(e){ return false } }
  function paint(st){
    R.setAttribute('data-rs',st);
    if(NOW) NOW.textContent=LBL[st]||st.toUpperCase();
    bar.querySelectorAll('.rsb').forEach(function(b){
      var on=b.dataset.rs===st;
      b.setAttribute('aria-pressed',on?'true':'false');
      b.classList.toggle('locked', b.dataset.gated==='1' && !keyed());
    });
    try{ localStorage.setItem(STATE,st) }catch(e){}
  }
  function gate(){
    var el=document.getElementById('sheet-railgate'); if(!el) return;
    if(typeof closeSheets==='function') closeSheets();
    el.classList.add('open'); el.setAttribute('aria-hidden','false');
    var s=document.getElementById('scrim'); if(s) s.classList.add('on');
  }
  bar.addEventListener('click',function(ev){
    var b=ev.target.closest('.rsb'); if(!b) return;
    if(b.dataset.gated==='1' && !keyed()){ gate(); return }
    paint(b.dataset.rs);
  });
  var gi=document.getElementById('gateIn'), go=document.getElementById('gateOut'),
      gn=document.getElementById('gateNote');
  function note(){ if(gn) gn.textContent = keyed() ? 'Key turned. Locker, floor and back of house are open.'
                                                   : 'Not turned. FOH only.' }
  if(gi) gi.addEventListener('click',function(){ try{localStorage.setItem(KEY,'1')}catch(e){} note(); paint('locker') });
  if(go) go.addEventListener('click',function(){ try{localStorage.removeItem(KEY)}catch(e){} note(); paint('foh') });
  var start='foh';
  try{ var st=localStorage.getItem(STATE); if(st && (st==='foh'||keyed())) start=st }catch(e){}
  note(); paint(start);
})();
</script>

<script>
/* ── RAIL-BAR STEPPER, GENERALISED (KAI 260815) ─────────────────────────────
   "Please make sure the left Rail-Bar functions as the right Rail-Bar."
   One stepper for every [data-railstack]: the hamburger STEPS to the next bar
   and wraps, which is the behaviour the left rail's own comment describes.
   A rail with one bar renders no hamburger at all — a control that does nothing
   teaches people to ignore rails. */
(function(){
  document.querySelectorAll('[data-railstack]').forEach(function(rail){
    var wrap = rail.querySelector('[data-bars]'); if(!wrap) return;
    var bars = [].slice.call(wrap.children);
    var ham  = rail.querySelector('[data-ham]');
    if(bars.length < 2 || !ham) return;
    var i = Math.max(0, bars.findIndex(function(b){ return b.classList.contains('on') }));
    function show(n){
      i = (n + bars.length) % bars.length;
      bars.forEach(function(b,k){ b.classList.toggle('on', k===i) });
      ham.setAttribute('aria-label','Rail bar '+(i+1)+' of '+bars.length);
      ham.setAttribute('aria-expanded', String(i!==0));
      // the fixture is three slots; only light one that has a bar behind it.
      [].forEach.call(ham.children, function(d,k){
        d.classList.toggle('on', bars.length <= 3 && k === i) });
    }
    show(i);

    // ── RAIL-NAV ON THE SIDE RAILS (KAI 260816) ──────────────────────────
    // "I love the Rail-Nav primitive. We may be able to apply it on the left
    //  and right Rail-Bars for consistency."
    // So the dots stop STEPPING BLIND and start OPENING A CHOOSER — you see
    // the bar you are picking instead of cycling until the right one appears.
    // Same primitive as the header ⋮: dots as trigger, labelled panel below.
    //
    // The panel is BUILT FROM THE BARS THEMSELVES, at runtime. A list emitted
    // by the serializer would be a SECOND COPY that can drift from the rail it
    // describes; derived from the bars it cannot. Every name here is already in
    // the markup (.vbarname), so nothing is hidden from an agent reading the
    // page — this is a view of existing content, not new content.
    // WRAP the dots in their own positioning context. The first version tagged
    // ham.parentNode — the rail's FOOT — with `.rnav`, and `.rnav{display:
    // inline-flex}` then overrode `.vfoot{display:flex}`. Borrowing an existing
    // element as an anchor means borrowing its layout too.
    var host = document.createElement('span');
    host.className = 'rnav';
    ham.parentNode.insertBefore(host, ham);
    host.appendChild(ham);
    var panel = document.createElement('div');
    panel.className = 'rnavp railnavp';
    panel.hidden = true;
    bars.forEach(function(bar, k){
      var nm = bar.querySelector('.vbarname');
      var label = (nm ? nm.textContent : '').trim() || ('RAIL-BAR-' + (k+1));
      var btn = document.createElement('button');
      btn.type = 'button'; btn.className = 'rnavi';
      btn.innerHTML = '<i aria-hidden="true"></i><span>' + label + '</span>';
      btn.addEventListener('click', function(e){ e.stopPropagation(); show(k); open(false) });
      panel.appendChild(btn);
    });
    host.appendChild(panel);
    ham.setAttribute('aria-haspopup','true');

    function open(v){
      panel.hidden = !v;
      ham.dataset.open = v ? '1' : '';
      [].forEach.call(panel.children, function(b,k){
        b.setAttribute('aria-current', k===i ? 'true' : 'false') });
    }
    ham.addEventListener('click', function(e){ e.stopPropagation(); open(panel.hidden) });
    document.addEventListener('click', function(e){
      if(!panel.contains(e.target) && e.target!==ham) open(false) });
    addEventListener('keydown', function(e){ if(e.key==='Escape') open(false) });
  });
})();
</script>
</body>
</html>
