/* Component layer. Utilities do layout; these carry identity.

   Every colour, face, radius and shadow below comes from a custom property that
   tailwind.config.js emits into the generated stylesheet. Nothing here restates a
   token value, so the utilities and the components cannot drift — which they did,
   the last time the palette was written down in both places.
   engine/styles.test.mjs fails on a raw hex or a literal font stack in this file. */
html{ -webkit-text-size-adjust:100%; }
/* Paper. The ruling was loud enough to read as a grid over the text; it should
   read as the stock the sheet is printed on. So: the rules drop to a whisper,
   and the surface gets a little tooth. The grain is one inline SVG turbulence
   tile — no image file, no third-party request, and it stitches so the repeat
   has no seam. */
:root{
  --grain-soft:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='180' height='180'%3E%3Cfilter id='g'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.82' numOctaves='4' stitchTiles='stitch'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3C/filter%3E%3Crect width='180' height='180' filter='url(%23g)' opacity='0.14'/%3E%3C/svg%3E");
  --grain:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='180' height='180'%3E%3Cfilter id='g'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.82' numOctaves='4' stitchTiles='stitch'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3C/filter%3E%3Crect width='180' height='180' filter='url(%23g)' opacity='0.32'/%3E%3C/svg%3E");
}
body{
  background-color:var(--paper); color:var(--ink);
  font-family:var(--font-sans);
  background-image:var(--grain), linear-gradient(var(--rule-faint) 1px,transparent 1px);
  background-size:180px 180px, 100% 34px;
  background-position:0 0, 0 -1px;
}

@media (prefers-reduced-motion:no-preference){
  .rise{ animation:rise .32s cubic-bezier(.2,.7,.3,1) both; }
  @keyframes rise{ from{opacity:0;transform:translateY(6px)} to{opacity:1;transform:none} }
}

/* --- chit: the core surface. A ticket, not a rounded card. --- */
.chit{
  /* the same stock as the sheet, one shade brighter and a lighter tooth — set as
     longhand because the `background:` shorthand would wipe the image out */
  background-color:var(--paper-card); background-image:var(--grain-soft);
  border:1px solid var(--rule); border-radius:var(--radius-chit);
  box-shadow:var(--shadow-chit);
}
.chit-head{
  display:flex; align-items:baseline; gap:.6rem;
  padding:.65rem .9rem; border-bottom:1px solid var(--rule);
  background:linear-gradient(180deg,var(--paper-card),var(--paper-head));
}
.chit-title{
  font-family:var(--font-sans); font-weight:600;
  font-size:.78rem; letter-spacing:.12em; text-transform:uppercase; color:var(--ink-soft);
}
.chit-note{ font-family:var(--font-mono); font-size:.7rem; color:var(--ink-mute); margin-left:auto; }
.chit-body{ padding:.9rem; }

/* --- perforated edge: used once, on the plan ticket --- */
.perf{ position:relative; }
.perf::after{
  content:""; position:absolute; left:0; right:0; bottom:-9px; height:9px;
  background:radial-gradient(circle at 6px 0, transparent 5px, var(--paper-card) 5px) repeat-x;
  background-size:12px 9px;
  filter:drop-shadow(0 1px 0 var(--rule));
}

/* --- rows --- */
.row{ display:flex; gap:.75rem; align-items:baseline; padding:.5rem 0; border-bottom:1px dotted var(--rule); }
.row:last-child{ border-bottom:0; }
.row-name{ flex:1; min-width:0; }
.row-num{ font-family:var(--font-mono); font-variant-numeric:tabular-nums; white-space:nowrap; }

/* --- the signature: jobs that land on the host get tape --- */
.tape{
  background:var(--tape); box-shadow:-.35rem 0 0 var(--tape), .35rem 0 0 var(--tape);
  font-weight:600;
}

/* --- badges --- */
.badge{
  display:inline-flex; align-items:center; gap:.3rem;
  font-family:var(--font-mono); font-size:.7rem; letter-spacing:.08em; text-transform:uppercase;
  padding:.12rem .4rem; border-radius:var(--radius-chit); border:1px solid transparent; white-space:nowrap;
}
.badge-blocker{ background:var(--short-soft); color:var(--short); border-color:var(--short-line); }
.badge-risk{ background:var(--watch-soft); color:var(--watch); border-color:var(--watch-line); }
.badge-ok{ background:var(--covered-soft); color:var(--covered); border-color:var(--covered-line); }
.badge-tier{ background:var(--paper-sunk); color:var(--ink-mute); border-color:var(--rule); }
.badge-tier.T0{ background:var(--carbon-soft); color:var(--carbon); border-color:var(--carbon-line); }

/* --- controls --- */
/* Wrapped in :where() so these carry no specificity at all. The pages reach for
   utilities on their controls — w-24, w-auto, py-1, text-[13px] — and a plain
   .field selector ties with them, so it won on load order (ui.css is linked
   after the generated stylesheet) and silently discarded every one. The
   assumption inputs were rendering 240px wide at 16px instead of 96px at 13px,
   which is why they looked centred: a full-width box with the value at its left
   edge and the unit off to the right.

   Element-qualified rather than a bare :where(), which lands one step too low:
   tailwind's preflight resets padding on input/select/textarea at element
   specificity, so a zero-specificity rule loses its padding to the reset. This
   sits exactly between the two — above the reset, below any utility. */
input:where(.field), select:where(.field), textarea:where(.field){
  width:100%; background:var(--paper-card); border:1px solid var(--rule-strong); border-radius:var(--radius-chit);
  padding:.6rem .7rem; font:inherit; color:var(--ink);
}
.field:focus{ outline:2px solid var(--carbon); outline-offset:1px; border-color:var(--carbon); }
.btn{
  display:inline-flex; align-items:center; gap:.4rem; cursor:pointer;
  font-family:var(--font-sans); font-weight:600; letter-spacing:.01em;
  padding:.6rem 1rem; border-radius:var(--radius-chit); border:1px solid var(--ink);
  background:var(--ink); color:var(--paper-card);
}
.btn:hover{ background:var(--ink-press); }
.btn:focus-visible{ outline:2px solid var(--carbon); outline-offset:2px; }
.btn-quiet{ background:var(--paper-card); color:var(--ink); border-color:var(--rule-strong); font-weight:600; padding:.3rem .55rem; font-size:.8rem; }
.btn-quiet:hover{ background:var(--paper-sunk); }
/* A summary styled as a button. The default disclosure triangle is suppressed and
   replaced with the same ▸ the nav panel uses, so the control says which way it
   goes — the answer used to appear and stay, with no way back. */
summary.btn{ list-style:none; }
summary.btn::-webkit-details-marker{ display:none; }
summary.btn::after{ content:"\25B8"; margin-left:.1rem; color:var(--ink-mute); }
details[open] > summary.btn::after{ content:"\25BE"; }
summary.btn:focus-visible{ outline:2px solid var(--carbon); outline-offset:2px; }

/* --- tool harness rows ---
   One line per tool: the name, how many arguments it takes, and a button that fires
   it with what it already holds. The description and every input live behind the row.
   Rendering all of them at once made eleven tools read as a page of blank form, and
   the two boolean selects showed only "—", naming neither argument. */
.toolrow{ border:1px solid var(--rule); border-radius:var(--radius-chit); background:var(--paper-card); }
.toolsum{
  display:flex; align-items:center; gap:.6rem; cursor:pointer;
  padding:.5rem .6rem; list-style:none;
}
.toolsum::-webkit-details-marker{ display:none; }
/* the marker leads here rather than trails, so the names stay left-aligned to scan */
.toolsum::before{ content:"\25B8"; color:var(--ink-mute); font-size:.8rem; }
details[open] > .toolsum::before{ content:"\25BE"; }
.toolsum:hover{ background:var(--paper-sunk); }
.toolsum:focus-visible{ outline:2px solid var(--carbon); outline-offset:-2px; }
.toolsum-note{ font-family:var(--font-mono); font-size:.7rem; color:var(--ink-mute); }
.toolbody{ padding:.1rem .6rem .7rem; border-top:1px solid var(--rule); }
/* the exception is worth marking; on a tool where every field is optional,
   saying so on each one is noise that reads as a demand */
.required{ color:var(--short); letter-spacing:0; }

/* --- data readout --- */
.readout{
  background:var(--readout); color:var(--readout-ink); border-radius:var(--radius-chit); padding:.75rem;
  font-family:var(--font-mono); font-size:.72rem; line-height:1.55;
  overflow:auto; max-height:22rem; white-space:pre-wrap; word-break:break-word; margin:0;
}
.rule-strike{ text-decoration:line-through; text-decoration-color:var(--short); text-decoration-thickness:2px; }
a{ color:var(--carbon); }

/* small screens: keep rows readable when badges wrap */
@media (max-width:480px){
  .row{ flex-wrap:wrap; }
  .row-num{ margin-left:auto; }
  .badge{ margin-left:0; margin-right:.35rem; }
  .chit-body{ padding:.75rem; }
}
.sr-only{ position:absolute; width:1px; height:1px; padding:0; margin:-1px; overflow:hidden; clip:rect(0,0,0,0); white-space:nowrap; border:0; }

/* A run finishes in well under a frame. Flash the line that reports it, so a click
   is visibly acknowledged rather than appearing to do nothing. */
@keyframes runflash { from { background:var(--tape-soft); } to { background:transparent; } }
.flash{ animation:runflash 1.1s ease-out 1; border-radius:var(--radius-chit); }
@media (prefers-reduced-motion: reduce){ .flash{ animation:none; } }

/* The plan on screen no longer matches the text in the box. */
.chit.stale{ opacity:.55; }
.chit.stale .chit-note{ color:var(--short); }

/* --- sheet head: a page opens the way a chit does ------------------------------
   The big bold headline over a paragraph is the shape of every landing page ever
   built, so it says nothing about what this is. A page here opens like the head
   of a work order instead: the sheet name and its facts on one ruled line — the
   same grammar as .chit-head, at page scale — then a plain sentence saying what
   the page does. The rule is closed with the carbon tick that runs across the
   masthead, which is the only colour the head needs.

   The title is set at reading size on purpose. Nothing here is being sold; it is
   being labelled, and a label that shouts is harder to read, not easier. */
.sheethead{ margin-bottom:1.6rem; }
.sheethead-band{
  position:relative; display:flex; align-items:baseline; gap:1rem;
  padding-bottom:.45rem; margin-bottom:.85rem; border-bottom:1px solid var(--rule);
}
.sheethead-band::after{
  content:""; position:absolute; left:0; bottom:-1px; width:2.75rem; height:2px;
  background:var(--carbon);
}
.kicker{
  margin:0; font-family:var(--font-mono); font-size:.7rem;
  letter-spacing:.18em; text-transform:uppercase; color:var(--ink-soft);
}
/* sheets come in a run and are numbered, the way a docket numbers its pages */
.kicker-no{ color:var(--carbon); margin-right:.6rem; }
/* the facts about this sheet, stamped where a chit stamps its count */
.sheethead-meta{
  margin-left:auto; text-align:right; font-family:var(--font-mono);
  font-size:.7rem; letter-spacing:.1em; color:var(--ink-mute);
}
.sheethead h1{
  margin:0;
  /* The serif appears exactly once on a page: here. It is the title face, not a
     second UI face — labels, buttons and the wordmark stay in the sans, or the
     page reads as two designs arguing. Nothing else may set this family, which
     is why there is no utility class for it. */
  font-family:var(--font-title);
  font-variation-settings:'opsz' 144,'wght' 500,'SOFT' 24,'WONK' 1;
  font-size:clamp(1.6rem,4.4vw,2.15rem); line-height:1.16; letter-spacing:-.005em;
  /* A measure, not a shrink-wrap: 40rem still sits inside the 48rem column, so a
     title only breaks when it genuinely runs out of room. `balance` was here and
     is the reason these wrapped early — it equalises line lengths, which means
     pulling every line short. On a phone that left the title using 77% of the
     width while the paragraph under it used 96%. `pretty` keeps the orphan
     avoidance and drops the shortening. */
  max-width:40rem; text-wrap:pretty;
}
/* the home page is the same head, one step up — not a different animal */
.sheethead-hero h1{ font-size:clamp(1.85rem,5.6vw,2.7rem); }
.lede{ margin:.7rem 0 0; max-width:34rem; font-size:1rem; line-height:1.6; color:var(--ink-soft); }
.lede-note{ margin:.6rem 0 0; max-width:34rem; font-size:.875rem; line-height:1.55; color:var(--ink-mute); }
@media (max-width:480px){
  .sheethead{ margin-bottom:1.25rem; }
  .lede-note{ font-size:.82rem; }
}

/* --- masthead: the top of the docket, not a website navbar --------------------
   The rest of the page is a worksheet: ruled paper, tickets, mono for anything
   measured. The header should read the same way — a carbon rule struck across
   the top of the sheet, the mark set as a filing code, and separation that only
   appears once there is something underneath it. */
/* The pages mount the header into a wrapper div. A sticky element is confined to
   its own containing block, so the wrapper would let the bar scroll away the moment
   its box left the viewport: take the wrapper out of the box tree so `sticky` is
   measured against the page. */
/* The masthead is injected by a module script, which is deferred — so the page is
   laid out once without it and again with it, and every page's content was sitting
   55px too high until the second layout. That is a jump on every load, and on a
   phone (where the script, the fonts and the vendor JSON all land later) it is a
   window in which the browser positions the viewport against a layout that is
   about to change.
   So the wrapper reserves the height before the header exists, and carries the
   sticky itself: no `display: contents` to be honoured, and the second layout is
   identical to the first. */
#siteHeader{
  position:sticky; top:0; z-index:20;
  /* border-box, so the rules have to be added back or the reserve is 3px short */
  min-height:calc(3.25rem + 3px);     /* .masthead-inner, plus the 2px and 1px rules */
  background:var(--paper-card);
  border-top:2px solid var(--carbon);
  border-bottom:1px solid var(--rule);
  transition:box-shadow .18s ease;
}
@media (prefers-reduced-motion:reduce){ #siteHeader{ transition:none; } }
#siteHeader.lifted{ box-shadow:var(--shadow-lift); }
.masthead-inner{
  display:flex; align-items:center; gap:1rem;
  max-width:48rem; margin:0 auto; padding:0 1.25rem; height:3.25rem;
}

/* The mark pairs the display face with the mono one, the way the pages do:
   the name is set, the protocol is stamped. */
.mark{ display:inline-flex; align-items:baseline; gap:.12rem; text-decoration:none; white-space:nowrap; }
.mark-name{
  font-family:var(--font-sans); font-weight:600;
  font-size:.9rem; letter-spacing:.04em; color:var(--ink);
}
.mark-dot{ color:var(--carbon); font-weight:800; }
.mark-code{
  font-family:var(--font-mono); font-size:.72rem; font-weight:500;
  letter-spacing:.14em; color:var(--ink-soft); transform:translateY(-1px);
}
.mark:hover .mark-code{ color:var(--carbon); }

/* Where you are, stamped in mono. Fills the gap the nav leaves on a phone. */
.masthead-where{
  margin-left:auto; font-family:var(--font-mono); font-size:.7rem;
  letter-spacing:.14em; text-transform:uppercase; color:var(--ink-mute);
  /* a long name wraps on a narrow phone; set it flush right and tight so the
     two lines read as one stamp rather than as a broken line */
  text-align:right; line-height:1.25;
}

.navlink{
  font-size:.82rem; color:var(--ink-mute); text-decoration:none;
  padding-bottom:.15rem; border-bottom:2px solid transparent;
}
.navlink:hover{ color:var(--ink); }
.navlink[aria-current="page"]{ color:var(--ink); font-weight:600; border-bottom-color:var(--carbon); }

/* A word, not a hamburger: the system speaks in labels everywhere else. */
.menu-btn{
  font-family:var(--font-mono); font-size:.7rem; letter-spacing:.14em;
  text-transform:uppercase; color:var(--ink-soft);
  border:1px solid var(--rule-strong); border-radius:var(--radius-chit);
  padding:.28rem .5rem; background:var(--paper-card); cursor:pointer;
}
.menu-btn:hover{ border-color:var(--carbon); color:var(--carbon); }
.menu-btn:focus-visible{ outline:2px solid var(--carbon); outline-offset:1px; }

.navpanel{ border-top:1px solid var(--rule); background:var(--paper-card); }
.navpanel a{
  display:block; padding:.6rem 1.25rem; text-decoration:none; color:var(--ink-soft);
  border-bottom:1px dotted var(--rule); font-size:.95rem;
}
.navpanel a:last-child{ border-bottom:0; }
.navpanel a[aria-current="page"]{ color:var(--ink); font-weight:600; }
.navpanel a[aria-current="page"]::before{ content:"▸ "; color:var(--carbon); }
