/* ============================================================
   TEEWINOT BASECAMP — "Alpenglow"
   ------------------------------------------------------------
   Internal tools. Brand Guide Rev A supplies the ground colours;
   the three accents below extend it for an application surface,
   which the guide treats as a separate context from marketing.

   Accents carry state and motion only, never body text — the
   guide's contrast findings are about reading, and they hold.
   ============================================================ */

/* ------------------------------------------------------- typeface */
/* Barlow, self-hosted. Latin subset only, four weights, ~90 KB total.
   SIL Open Font License, so it ships in the repo rather than from a CDN -
   BaseCamp loads nothing from an external origin.

   These URLs carry no content hash, unlike app.css. A typeface is not going
   to change; if one ever is replaced, rename the file rather than editing it
   in place, or browsers will keep the old one for a year. */
@font-face {
  font-family: "Barlow"; font-style: normal; font-weight: 400;
  font-display: swap; src: url("/assets/barlow-400.woff2") format("woff2");
}
@font-face {
  font-family: "Barlow"; font-style: normal; font-weight: 600;
  font-display: swap; src: url("/assets/barlow-600.woff2") format("woff2");
}
@font-face {
  font-family: "Barlow"; font-style: normal; font-weight: 700;
  font-display: swap; src: url("/assets/barlow-700.woff2") format("woff2");
}
@font-face {
  font-family: "Barlow"; font-style: normal; font-weight: 800;
  font-display: swap; src: url("/assets/barlow-800.woff2") format("woff2");
}

/* ------------------------------------------------------ tokens */
:root {
  /* Brand six */
  --navy:         #0F2D52;
  --navy-dark:    #091B33;
  --granite:      #2C3038;
  --granite-dark: #1A1C20;
  --hornblende:   #2E3B2A;
  --garnet:       #6B3A3A;
  --stone:        #8A847E;
  --stone-light:  #D5D2CE;
  --snow:         #F4F2ED;

  /* Alpenglow accents */
  --alpenglow:    #E8703A;
  --glacier:      #4FB0C6;
  --lichen:       #E3B23C;

  /* Functional. Per the guide, never brand colours for status. */
  --ok:   #7FBF8F;
  --bad:  #E2807C;
  --warn: #D9A441;

  /* Surfaces */
  --ground:    var(--granite-dark);
  --panel:     #23272E;
  --panel-up:  #2B3038;
  --line:      #39404A;
  --line-up:   #4A5361;
  --ink:       var(--snow);
  --ink-dim:   #A7ADB6;
  --ink-faint: #767D87;

  --radius: 6px;

  --mono: "Cascadia Mono", Consolas, ui-monospace, "SF Mono", monospace;
  --sans: "Barlow", "Segoe UI Variable Text", "Segoe UI", system-ui,
          -apple-system, "Helvetica Neue", sans-serif;
}

* { box-sizing: border-box; }

html {
  /* Stops iOS inflating text in landscape, which otherwise reflows the whole
     page the moment someone turns their phone. */
  -webkit-text-size-adjust: 100%;
}

body {
  margin: 0;
  background: var(--ground);
  color: var(--ink);
  font: 15px/1.6 var(--sans);
  -webkit-font-smoothing: antialiased;
}

/* An email address or a long identifier should wrap rather than push the page
   sideways on a narrow screen. */
code, .fact dd, .doc { overflow-wrap: anywhere; }

a { color: inherit; }

main {
  max-width: 34rem;
  margin: 0 auto;
  padding: 1.5rem 1.1rem 4rem;
  position: relative;
}

body.home main { max-width: 62rem; padding-top: 2rem; }

/* ============================================================
   THE SKY
   Fixed behind everything. The phase is set server-side from
   America/Boise so it is correct on first paint rather than
   flickering once the client decides what time it is.
   ============================================================ */
.sky {
  position: fixed;
  inset: 0;
  z-index: -1;
  overflow: hidden;
  background: linear-gradient(#0d1017, #1A1C20 55%);
}

.sky[data-phase="dawn"] {
  background: linear-gradient(#1B2436 0%, #46354A 42%, #8A5340 72%, #21252C 100%);
}
.sky[data-phase="day"] {
  background: linear-gradient(#1D3355 0%, #27435F 45%, #2A3038 100%);
}
.sky[data-phase="dusk"] {
  background: linear-gradient(#171C2B 0%, #3E2F44 38%, #7A4433 68%, #1D2027 100%);
}
.sky[data-phase="night"] {
  background: linear-gradient(#0A0D14 0%, #131A28 50%, #171A20 100%);
}

/* Alpenglow itself: the light on the peaks. */
.sky::after {
  content: "";
  position: absolute;
  left: 50%;
  bottom: 16%;
  width: 74vw;
  height: 42vh;
  transform: translateX(-50%);
  background: radial-gradient(ellipse at center bottom,
              rgba(232,112,58,.30), rgba(232,112,58,0) 68%);
  opacity: 0;
  animation: bloom 2.6s .4s ease-out forwards;
}
.sky[data-phase="night"]::after { display: none; }
.sky[data-phase="day"]::after {
  background: radial-gradient(ellipse at center bottom,
              rgba(79,176,198,.20), rgba(79,176,198,0) 68%);
}

@keyframes bloom { to { opacity: 1; } }

/* ------------------------------------------------ contour lines */
.topo {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  opacity: .55;
}

.topo path {
  fill: none;
  stroke: #4FB0C6;
  stroke-width: 1;
  opacity: .20;
  stroke-dasharray: 1600;
  stroke-dashoffset: 1600;
  animation: draw 3s cubic-bezier(.2,.7,.3,1) forwards;
}

/* Staggered, so the map reads as being surveyed rather than switched on. */
.topo path:nth-child(1) { animation-delay: .05s; }
.topo path:nth-child(2) { animation-delay: .20s; }
.topo path:nth-child(3) { animation-delay: .35s; }
.topo path:nth-child(4) { animation-delay: .50s; }
.topo path:nth-child(5) { animation-delay: .65s; }
.topo path:nth-child(6) { animation-delay: .80s; }

@keyframes draw { to { stroke-dashoffset: 0; } }

/* --------------------------------------------------- ridgelines */
.ridge {
  position: absolute;
  left: 50%;
  bottom: 0;
  width: 170%;
  /* --px is written by app.js on pointer move. With scripts blocked it stays
     0 and the ridges simply sit still, which is a fine picture. */
  transform: translateX(-50%) translate3d(calc(var(--px, 0) * var(--depth) * 1px), 0, 0);
  transition: transform .6s cubic-bezier(.2,.7,.3,1);
}
.ridge--far  { --depth: .6; opacity: .55; }
.ridge--mid  { --depth: 1.4; opacity: .85; }
.ridge--near { --depth: 2.6; opacity: 1; }

.ridge--far  .rock { fill: #2A3444; }
.ridge--mid  .rock { fill: #222A36; }
.ridge--near .rock { fill: #171B22; }

.ridge .snowcap { fill: #F4F2ED; opacity: .09; }
.ridge--near .snowcap { opacity: .15; }

/* Inner pages keep the same sky, turned down - the point is continuity, not
   competing with a form for attention. */
body:not(.home) .sky { opacity: .5; }

/* ============================================================
   CHROME
   ============================================================ */
.chrome {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  flex-wrap: wrap;
  padding-bottom: .8rem;
  margin-bottom: 1.4rem;
  border-bottom: 1px solid var(--line);
}

.chrome .brand {
  display: inline-flex;
  align-items: center;
  gap: .55rem;
  font-weight: 700;
  letter-spacing: .16em;
  text-transform: uppercase;
  font-size: .8rem;
  text-decoration: none;
}
.chrome .brand svg { width: 17px; height: 17px; }
.chrome .brand .peak { fill: var(--snow); transition: fill .2s ease; }
.chrome .brand:hover .peak { fill: var(--alpenglow); }

.chrome .who { font-size: .78rem; color: var(--ink-dim); }
.chrome .who a { color: var(--ink-dim); }
.chrome .who a:hover { color: var(--alpenglow); }

/* Sign-out is a POST because it discards the Google token. It still has to
   read as the link it replaced, so the button is stripped back to one. */
.signout { display: inline; }
.signout button {
  background: none; border: 0; padding: 0; margin: 0;
  font: inherit; color: var(--ink-dim); cursor: pointer;
  text-decoration: underline;
}
.signout button:hover { color: var(--alpenglow); }

/* ============================================================
   HOMEPAGE
   ============================================================ */
.hero { margin: 1.6rem 0 2.6rem; }

.overline {
  display: block;
  font-family: var(--mono);
  font-size: .68rem;
  font-weight: 600;
  letter-spacing: .2em;
  text-transform: uppercase;
  color: var(--ink-faint);
  margin-bottom: .8rem;
}
.overline .phase { color: var(--alpenglow); }

.hero h1 {
  margin: 0;
  font-size: clamp(1.9rem, 5.5vw, 3rem);
  font-weight: 800;
  letter-spacing: -.015em;
  line-height: 1.1;
}
.hero .sub {
  margin: .65rem 0 0;
  color: var(--ink-dim);
  max-width: 34rem;
}

/* ---------------------------------------------------- the route */
/* A dashed trail across the top of the grid. Each camp hangs off it by a
   stem, and hovering a camp lights its stem: the route tracing, without
   any fragile geometry between two moving boxes. */
/* The trailhead: where the route starts, above the grid. */
.route {
  position: relative;
  border-top: 1px dashed var(--line);
}
.route::before {
  content: "";
  position: absolute;
  top: -5px;
  left: 0;
  width: 9px;
  height: 9px;
  border-radius: 50%;
  background: var(--alpenglow);
  box-shadow: 0 0 0 4px rgba(232,112,58,.18);
}

.camps {
  list-style: none;
  margin: 0;
  padding: 1.5rem 0 0;
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr));
  gap: 1.25rem;
}

.camp { position: relative; }

.camp a,
.camp .card {
  display: block;
  height: 100%;
  padding: 1.3rem 1.25rem 1.35rem;
  background: linear-gradient(var(--panel), #1F232A);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  text-decoration: none;
  position: relative;
  overflow: hidden;
  opacity: 0;
  transform: translateY(14px);
  animation: plant .7s cubic-bezier(.2,.8,.25,1) forwards;
  transition: transform .25s cubic-bezier(.2,.8,.25,1),
              border-color .25s ease,
              box-shadow .25s ease;
}

.camp:nth-child(1) a, .camp:nth-child(1) .card { animation-delay: .10s; }
.camp:nth-child(2) a, .camp:nth-child(2) .card { animation-delay: .18s; }
.camp:nth-child(3) a, .camp:nth-child(3) .card { animation-delay: .26s; }
.camp:nth-child(4) a, .camp:nth-child(4) .card { animation-delay: .34s; }
.camp:nth-child(5) a, .camp:nth-child(5) .card { animation-delay: .42s; }
/* Seven camps, not five. The last two arrived after this stagger was written
   and were landing instantly while the ones above them were still settling. */
.camp:nth-child(6) a, .camp:nth-child(6) .card { animation-delay: .50s; }
.camp:nth-child(7) a, .camp:nth-child(7) .card { animation-delay: .58s; }

@keyframes plant {
  from { opacity: 0; transform: translateY(14px); }
  to   { opacity: 1; transform: translateY(0); }
}

/* A peak catching light down the leading edge. */
.camp a::before {
  content: "";
  position: absolute;
  left: 0;
  top: 0;
  bottom: 0;
  width: 2px;
  background: linear-gradient(var(--alpenglow), var(--garnet));
  transform: scaleY(0);
  transform-origin: top;
  transition: transform .3s cubic-bezier(.2,.8,.25,1);
}
.camp a:hover::before,
.camp a:focus-visible::before { transform: scaleY(1); }

.camp a:hover,
.camp a:focus-visible {
  transform: translateY(-4px);
  border-color: var(--line-up);
  box-shadow: 0 14px 34px rgba(0,0,0,.42);
}
.camp a:focus-visible { outline: 2px solid var(--glacier); outline-offset: 2px; }

.camp .marker {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: .5rem;
  margin-bottom: .9rem;
}
.camp .no {
  display: inline-flex;
  align-items: center;
  gap: .5rem;
  font-family: var(--mono);
  font-size: .64rem;
  letter-spacing: .18em;
  text-transform: uppercase;
  color: var(--ink-faint);
  transition: color .25s ease;
}

/* The waypoint diamond. Inside the card rather than hanging above it, so it
   cannot end up pointing at whatever the grid wrapped onto the row above. */
.camp .no::before {
  content: "";
  width: 7px;
  height: 7px;
  flex: none;
  transform: rotate(45deg);
  background: var(--stone);
  transition: background .25s ease, transform .25s ease;
}
.camp:hover .no,
.camp:focus-within .no { color: var(--ink-dim); }
.camp:hover .no::before,
.camp:focus-within .no::before {
  background: var(--alpenglow);
  transform: rotate(45deg) scale(1.35);
}
.camp h2 {
  margin: 0 0 .35rem;
  font-size: 1.16rem;
  font-weight: 700;
  letter-spacing: -.01em;
}
.camp p {
  margin: 0;
  font-size: .87rem;
  color: var(--ink-dim);
  line-height: 1.55;
}

.status {
  font-family: var(--mono);
  font-size: .6rem;
  letter-spacing: .14em;
  text-transform: uppercase;
  padding: .2rem .5rem;
  border-radius: 3px;
  white-space: nowrap;
}
.status--open { color: var(--ok);     background: rgba(127,191,143,.12); }
.status--soon { color: var(--lichen); background: rgba(227,178,60,.12); }

/* A camp not yet reachable should read as planned, not broken. */
.camp--soon a { border-style: dashed; }
.camp--soon h2 { color: var(--ink-dim); }

/* ============================================================
   SECTION PAGES
   ============================================================ */
.section-head { margin-bottom: 1.5rem; }
.section-head .overline { margin-bottom: .5rem; }
.section-head h1 {
  margin: .2rem 0 0;
  font-size: 1.8rem;
  font-weight: 800;
  letter-spacing: -.015em;
}

.panel {
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 1.4rem 1.3rem;
}
.panel h2 {
  margin: 0 0 .6rem;
  font-family: var(--mono);
  font-size: .68rem;
  letter-spacing: .18em;
  text-transform: uppercase;
  color: var(--ink-faint);
}
.panel p { margin: 0; color: var(--ink-dim); }

.back {
  display: inline-block;
  margin-top: 1.6rem;
  font-size: .84rem;
  color: var(--ink-dim);
  text-decoration: none;
}
.back:hover { color: var(--alpenglow); }

/* ----------------------------------------------------------- Chief */
.ask { display: grid; gap: .5rem; margin-bottom: 1.6rem; }
.ask label {
  font-family: var(--mono);
  font-size: .64rem;
  letter-spacing: .16em;
  text-transform: uppercase;
  color: var(--ink-faint);
}
.ask textarea {
  width: 100%;
  padding: .7rem .65rem;
  font: inherit;
  resize: vertical;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: #171B21;
  color: var(--ink);
  transition: border-color .2s ease, box-shadow .2s ease;
}
.ask textarea:focus {
  outline: none;
  border-color: var(--glacier);
  box-shadow: 0 0 0 3px rgba(79,176,198,.16);
}
.ask textarea:disabled, .ask button:disabled {
  opacity: .5;
  cursor: not-allowed;
}
.ask button { width: auto; justify-self: start; margin: 0; }

.asked {
  border-left: 2px solid var(--line);
  padding: .1rem 0 .1rem 1rem;
  margin-bottom: 1.2rem;
}
.asked p { margin: .2rem 0 0; color: var(--ink-dim); }

.answer {
  background: var(--panel);
  border: 1px solid var(--line);
  border-left: 2px solid var(--alpenglow);
  border-radius: var(--radius);
  padding: 1.2rem 1.3rem;
  margin-bottom: 1.6rem;
}
.answer p:last-child, .answer ul:last-child { margin-bottom: 0; }

/* The answer as it arrives. Shaped like .answer so the streamed text and the
   stored one that replaces it a moment later sit in the same place, rather
   than the page jumping when the thread loads.

   Plain text, deliberately: what streams is words, and the markdown is
   rendered server-side on the page this navigates to. Half-parsed markdown
   flickering as it arrives would be worse than waiting a second for it. */
/*
 * "Adding it to your tasks…", while a tool runs.
 *
 * The longest wait in the product is between the two model calls a tool-using
 * question costs, and it used to be silent: the words stopped and nothing said
 * why. Quiet and italic rather than a panel of its own - it is a note about
 * what is happening, not part of the answer, and it disappears the moment
 * Chief starts talking again.
 */
.doing {
  margin: 0 0 .7rem;
  font-size: .85rem; font-style: italic;
  color: var(--ink-faint);
  animation: doing-pulse 1.6s ease-in-out infinite;
}
@keyframes doing-pulse {
  0%, 100% { opacity: .55; }
  50%      { opacity: 1; }
}
@media (prefers-reduced-motion: reduce) {
  .doing { animation: none; opacity: .85; }
}

.live {
  background: var(--panel);
  border: 1px solid var(--line);
  border-left: 2px solid var(--alpenglow);
  border-radius: var(--radius);
  padding: 1.2rem 1.3rem;
  margin-bottom: 1.6rem;
  white-space: pre-wrap;
  overflow-wrap: anywhere;
  /* A caret, so an answer that pauses mid-thought still looks alive. */
  min-height: 1.2em;
}
.live::after {
  content: '';
  display: inline-block;
  width: .5ch; height: 1em;
  margin-left: .15ch;
  vertical-align: text-bottom;
  background: var(--alpenglow);
  animation: caret 1.1s steps(2, start) infinite;
}
@keyframes caret { to { visibility: hidden; } }

@media (prefers-reduced-motion: reduce) {
  .live::after { animation: none; }
}

/* Earlier turns sit quieter than the one just answered, so the eye lands on
   the newest without the older ones disappearing. */
.transcript { margin-bottom: 1.2rem; }
.answer--past {
  border-left-color: var(--stone);
  background: #1E222A;
  color: var(--ink-dim);
}

.scope {
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: .7rem .9rem;
  margin-bottom: 1.4rem;
  font-size: .88rem;
}
.scope summary {
  cursor: pointer;
  font-family: var(--mono);
  font-size: .66rem;
  letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--ink-faint);
}
.scope summary:hover { color: var(--ink-dim); }
.scope ul { margin: .7rem 0 .5rem; padding-left: 1.2rem; color: var(--ink-dim); }
.scope li { margin: .2rem 0; }

/* --------------------------------------------------------- my info */
.facts { margin: 0 0 2rem; }
.fact {
  display: grid;
  grid-template-columns: 9rem 1fr;
  gap: .8rem;
  padding: .7rem 0;
  border-bottom: 1px solid var(--line);
}
.fact:first-child { border-top: 1px solid var(--line); }
.fact dt {
  font-family: var(--mono);
  font-size: .64rem;
  letter-spacing: .16em;
  text-transform: uppercase;
  color: var(--ink-faint);
  padding-top: .15rem;
}
.fact dd { margin: 0; }
.fact code {
  font: .86em/1.4 var(--mono);
  background: #171B21;
  border: 1px solid var(--line);
  border-radius: 3px;
  padding: .08em .32em;
}

.aside { color: var(--ink-faint); font-size: .82rem; }
.fact .aside { display: inline; }

.group-head {
  margin: 2rem 0 .7rem;
  font-family: var(--mono);
  font-size: .64rem;
  letter-spacing: .18em;
  text-transform: uppercase;
  color: var(--ink-faint);
  font-weight: 600;
}

.caps { list-style: none; margin: 0 0 .9rem; padding: 0; }
.caps li {
  position: relative;
  padding-left: 1.3rem;
  margin: .35rem 0;
  font-size: .92rem;
}
.caps li::before {
  content: "";
  position: absolute;
  left: 0;
  top: .5em;
  width: 6px;
  height: 6px;
  transform: rotate(45deg);
  background: var(--alpenglow);
}

.panel h3 { margin: 0 0 .5rem; font-size: 1rem; font-weight: 700; }
.panel p + p { margin-top: .7rem; }

@media (max-width: 34rem) {
  .fact { grid-template-columns: 1fr; gap: .2rem; }
}

/* ------------------------------------------------------- handbook */
/* Prose keeps a narrow measure. Past roughly 75 characters a line is
   measurably harder to read, and this is the one part of BaseCamp meant to be
   read rather than scanned. */
body.prose main { max-width: 44rem; }

.lede { margin: .6rem 0 0; color: var(--ink-dim); font-size: 1.02rem; }

.entries { list-style: none; margin: 0; padding: 0; display: grid; gap: .8rem; }
.entries a {
  display: block;
  padding: 1.1rem 1.2rem;
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  text-decoration: none;
  transition: border-color .2s ease, transform .2s ease;
}
.entries a:hover {
  border-color: var(--line-up);
  transform: translateY(-2px);
}
.entries h2 { margin: 0; font-size: 1.08rem; font-weight: 700; }
.entries p { margin: .3rem 0 0; font-size: .88rem; color: var(--ink-dim); }

.contents {
  border-left: 2px solid var(--line);
  padding: .1rem 0 .1rem 1rem;
  margin-bottom: 2rem;
}
.contents h2 {
  margin: 0 0 .4rem;
  font-family: var(--mono);
  font-size: .62rem;
  letter-spacing: .16em;
  text-transform: uppercase;
  color: var(--ink-faint);
}
.contents ul { list-style: none; margin: 0; padding: 0; }
.contents li { font-size: .86rem; margin: .2rem 0; }
.contents li.lvl-3 { padding-left: 1rem; }
.contents a { color: var(--ink-dim); text-decoration: none; }
.contents a:hover { color: var(--alpenglow); }

/* Rendered Markdown. */
.doc { font-size: 1rem; line-height: 1.72; }
.doc h2 {
  margin: 2.4rem 0 .7rem;
  font-size: 1.35rem;
  font-weight: 700;
  letter-spacing: -.01em;
  padding-top: .6rem;
  border-top: 1px solid var(--line);
}
.doc h3 { margin: 1.8rem 0 .5rem; font-size: 1.08rem; font-weight: 700; }
.doc h4 { margin: 1.4rem 0 .4rem; font-size: .95rem; font-weight: 700; color: var(--ink-dim); }
.doc p { margin: 0 0 1.05rem; }
.doc ul, .doc ol { margin: 0 0 1.05rem; padding-left: 1.3rem; }
.doc li { margin: .35rem 0; }
.doc a { color: var(--glacier); text-underline-offset: .2em; }
.doc a:hover { color: var(--alpenglow); }
.doc strong { color: var(--snow); font-weight: 700; }
.doc hr { border: none; border-top: 1px solid var(--line); margin: 2rem 0; }

.doc code {
  font: .88em/1.4 var(--mono);
  background: #171B21;
  border: 1px solid var(--line);
  border-radius: 3px;
  padding: .08em .32em;
}
.doc pre {
  background: #14181E;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: .9rem 1rem;
  overflow-x: auto;
  margin: 0 0 1.05rem;
}
.doc pre code { background: none; border: none; padding: 0; font-size: .84rem; }

.doc blockquote {
  margin: 0 0 1.05rem;
  padding: .1rem 0 .1rem 1rem;
  border-left: 2px solid var(--alpenglow);
  color: var(--ink-dim);
}
.doc blockquote p { margin: 0; }

.doc .table-wrap { margin: 0 0 1.05rem; }
.doc td { font-size: .9rem; }

/* ------------------------------------------------ register browser */
/* The registers page is a table, so it gets the wide column. */
body.registers main { max-width: 62rem; }

.search {
  display: flex;
  gap: .5rem;
  margin-bottom: .9rem;
}
.search input[type=search] {
  flex: 1;
  min-width: 0;
  padding: .62rem .6rem;
  font: inherit;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: #171B21;
  color: var(--ink);
  transition: border-color .2s ease, box-shadow .2s ease;
}
.search input[type=search]:focus {
  outline: none;
  border-color: var(--glacier);
  box-shadow: 0 0 0 3px rgba(79,176,198,.16);
}
.search button { width: auto; margin: 0; padding: .62rem 1.2rem; }

.count {
  font-family: var(--mono);
  font-size: .72rem;
  letter-spacing: .06em;
  color: var(--ink-faint);
  margin: 0 0 1rem;
}
.count a { color: var(--glacier); }

/* A register can outgrow a phone in one direction only, so it scrolls in that
   direction inside its own box rather than making the page scroll sideways. */
.table-wrap {
  overflow-x: auto;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--panel);
}

table {
  width: 100%;
  border-collapse: collapse;
  font-size: .86rem;
}
thead th {
  position: sticky;
  top: 0;
  text-align: left;
  font-family: var(--mono);
  font-size: .62rem;
  font-weight: 600;
  letter-spacing: .16em;
  text-transform: uppercase;
  color: var(--ink-faint);
  background: var(--panel-up);
  padding: .6rem .75rem;
  border-bottom: 1px solid var(--line);
  white-space: nowrap;
}
tbody td {
  padding: .55rem .75rem;
  border-bottom: 1px solid #2C323B;
  vertical-align: top;
}
tbody tr:last-child td { border-bottom: none; }
tbody tr:hover { background: #262B33; }

td.num { font-family: var(--mono); white-space: nowrap; color: var(--snow); }
td.status-cell { font-family: var(--mono); font-size: .74rem; color: var(--ink-dim); }

mark {
  background: rgba(227,178,60,.28);
  color: var(--snow);
  border-radius: 2px;
  padding: 0 .1em;
}

.empty {
  padding: 2rem 1rem;
  text-align: center;
  color: var(--ink-faint);
  border: 1px dashed var(--line);
  border-radius: var(--radius);
}

.problems { margin: .5rem 0 0; padding-left: 1.1rem; font-size: .84rem; }
.problems .sev {
  font-family: var(--mono);
  font-size: .62rem;
  text-transform: uppercase;
  letter-spacing: .1em;
  margin-right: .4rem;
}

/* ------------------------------------------------- numbers form */
.tabs { display: flex; gap: .5rem; margin-bottom: 1.2rem; }
.tabs a {
  flex: 1; text-align: center; padding: .6rem .5rem; border-radius: var(--radius);
  border: 1px solid var(--line); background: transparent;
  text-decoration: none; color: var(--ink-dim); font-size: .9rem;
  transition: color .2s ease, border-color .2s ease, background .2s ease;
}
.tabs a:hover { color: var(--ink); border-color: var(--line-up); }
.tabs a[aria-current="page"] {
  background: var(--panel-up); color: var(--ink); font-weight: 600;
  border-color: var(--line-up);
}

.preview {
  background: linear-gradient(var(--panel), #1F232A);
  border: 1px solid var(--line); border-radius: var(--radius);
  padding: .85rem .9rem; margin-bottom: 1.1rem;
  position: relative; overflow: hidden;
}
.preview::before {
  content: ""; position: absolute; left: 0; top: 0; bottom: 0; width: 2px;
  background: linear-gradient(var(--alpenglow), var(--garnet));
}
.preview .label {
  font-family: var(--mono);
  font-size: .62rem; letter-spacing: .16em; color: var(--ink-faint);
  text-transform: uppercase;
}
.preview .value {
  font: 600 1.6rem/1.25 var(--mono);
  margin-top: .2rem; word-break: break-all; color: var(--snow);
}

label { display: block; font-size: .72rem; color: var(--ink-dim); margin-bottom: .25rem; }

/*
 * Date and email inputs are here for the same reason the selects below are:
 * left at the browser's default they sit next to a styled text field looking
 * like a different application. `color-scheme: dark` is what makes the date
 * picker's own calendar and its spin arrows dark rather than a white panel
 * dropped onto the page.
 */
input[type=text],
input[type=email],
input[type=date] {
  width: 100%; padding: .62rem .6rem; font: inherit;
  border: 1px solid var(--line); border-radius: var(--radius);
  background: #171B21; color: var(--ink);
  transition: border-color .2s ease, box-shadow .2s ease;
}
input[type=date] { color-scheme: dark; }
input[type=text]:focus,
input[type=email]:focus,
input[type=date]:focus {
  outline: none; border-color: var(--glacier);
  box-shadow: 0 0 0 3px rgba(79,176,198,.16);
}

/* ------------------------------------------------------------- selects */

/*
 * A select left unstyled is the browser's, and next to the inputs above it
 * looks like a different application. `appearance: none` takes the native
 * control away, which means the arrow has to be drawn back on - an inline SVG
 * in a data URI, so nothing is fetched and the CSP stays as strict as it is.
 *
 * The option list itself is still the operating system's and cannot be styled.
 * `color-scheme: dark` is what stops it opening as a white rectangle over a
 * dark page, which is the part people actually notice.
 */
select {
  width: 100%; padding: .62rem 2.1rem .62rem .6rem; font: inherit;
  border: 1px solid var(--line); border-radius: var(--radius);
  background-color: #171B21; color: var(--ink);
  color-scheme: dark;
  appearance: none; -webkit-appearance: none;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 8' fill='none' stroke='%23A7ADB6' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M1 1.5 6 6.5 11 1.5'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right .7rem center;
  background-size: .7rem;
  cursor: pointer;
  transition: border-color .2s ease, box-shadow .2s ease, background-color .2s ease;
}
select:hover { border-color: var(--line-up); background-color: #1B2027; }
select:focus-visible {
  outline: none; border-color: var(--glacier);
  box-shadow: 0 0 0 3px rgba(79,176,198,.16);
}
select:disabled { opacity: .5; cursor: not-allowed; }

/* --------------------------------------------------------- file inputs */

/*
 * The native file input is a grey button and a filename, and no amount of
 * wishing changes the text on it. What can be styled is the button part, so
 * the control is made to read as one field: a dashed drop-zone that says what
 * it wants, with the browser's button inside it looking like everything else.
 *
 * Deliberately not a JavaScript replacement. A hidden input behind a fake
 * button breaks keyboard focus and screen readers unless it is done very
 * carefully, and this page still has to work with scripts blocked.
 */
input[type=file] {
  width: 100%; padding: .7rem; font: inherit; font-size: .85rem;
  color: var(--ink-dim);
  border: 1px dashed var(--line-up); border-radius: var(--radius);
  background: #171B21;
  cursor: pointer;
  transition: border-color .2s ease, background .2s ease;
}
input[type=file]:hover { border-color: var(--glacier); background: #1B2027; }
input[type=file]:focus-visible {
  outline: none; border-color: var(--glacier);
  box-shadow: 0 0 0 3px rgba(79,176,198,.16);
}
input[type=file]::file-selector-button {
  font: inherit; font-size: .7rem; font-weight: 600;
  letter-spacing: .06em; text-transform: uppercase;
  padding: .45rem .8rem; margin-right: .8rem;
  border: 1px solid var(--line-up); border-radius: var(--radius);
  background: var(--panel-up); color: var(--ink);
  cursor: pointer;
  transition: background .2s ease, border-color .2s ease;
}
input[type=file]:hover::file-selector-button {
  background: var(--garnet); border-color: var(--garnet);
}
.hint { font-size: .72rem; color: var(--ink-faint); margin: .3rem 0 .9rem; }

/*
 * The template link under the preview. The link half has to read as a link -
 * .hint's colour would bury it - while the explanation stays quiet.
 */
.template { font-size: .74rem; margin: -.5rem 0 1.1rem; }
.template a { color: var(--ink); }
.template span { color: var(--ink-faint); margin-left: .5rem; }
.field { margin-bottom: .3rem; }

button {
  width: 100%; padding: .8rem 1rem;
  font: 700 .88rem/1 var(--sans);
  letter-spacing: .06em; text-transform: uppercase;
  border: 1px solid transparent; border-radius: var(--radius);
  background: var(--garnet); color: var(--snow);
  cursor: pointer; margin-top: 1rem;
  transition: background .2s ease, transform .12s ease;
}
button:hover { background: #7E4444; }
button:active { transform: translateY(1px); }

.flash {
  border-radius: var(--radius); padding: .85rem .9rem; margin-bottom: 1.2rem;
  border: 1px solid; font-size: .9rem;
}
.flash.ok   { border-color: rgba(127,191,143,.4); background: rgba(127,191,143,.10); color: var(--ok); }
.flash.bad  { border-color: rgba(226,128,124,.4); background: rgba(226,128,124,.10); color: var(--bad); }
/*
 * Amber, for the messages where the thing you pressed the button for DID
 * happen and something alongside it did not - an ECN signed but nobody
 * emailed, an attachment removed but its Drive copy left behind. Those read
 * as failures in red and as successes in green, and are neither.
 */
.flash.warn { border-color: rgba(217,164,65,.4);  background: rgba(217,164,65,.10);  color: var(--warn); }

/*
 * A flash arrives after a redirect, so it is new information on a page that
 * otherwise looks identical to the one just left. The slide is what makes it
 * register as new rather than as something that was always there.
 */
.flash { animation: flash-in .28s ease-out both; }
@keyframes flash-in {
  from { opacity: 0; transform: translateY(-.4rem); }
  to   { opacity: 1; transform: none; }
}
.flash .num {
  font: 600 1.3rem/1.3 var(--mono); display: block;
  margin: .2rem 0 .55rem; color: var(--snow); word-break: break-all;
}
.flash button { width: auto; padding: .35rem .8rem; margin: 0; font-size: .7rem; }

/*
 * The one thing that would fix this, next to the sentence saying what went
 * wrong. It inherits the flash's colour rather than taking the link blue: it
 * is part of the message, and a blue link inside a red box reads as a footnote
 * to it rather than as the way out.
 */
.flash-do {
  display: inline-block; margin-left: .35rem;
  color: inherit; font-weight: 600;
  text-decoration: underline; text-underline-offset: .18em;
}
.flash-do:hover, .flash-do:focus-visible { text-decoration-thickness: 2px; }

.rules {
  margin-top: 2.5rem; padding-top: 1rem; border-top: 1px solid var(--line);
  font-size: .74rem; color: var(--ink-faint);
}

/* --------------------------------------------------- sign-in */
.center { text-align: center; padding-top: 4rem; }
.wordmark { font-size: 2rem; font-weight: 800; letter-spacing: -.01em; }
.mark {
  color: var(--ink-faint); font-weight: 700; margin-right: .45rem;
  padding-right: .5rem; border-right: 1px solid var(--line);
}
.center .signin {
  display: inline-block; margin-top: 1.3rem; padding: .8rem 1.5rem;
  border: 1px solid var(--line); border-radius: var(--radius);
  background: var(--panel-up); color: var(--ink);
  text-decoration: none; font-weight: 600;
  transition: border-color .2s ease, background .2s ease;
}
.center .signin:hover { border-color: var(--alpenglow); background: #333944; }

/* ============================================================
   Motion is decoration. Everything above stays legible and
   operable with it switched off.
   ============================================================ */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .001ms !important;
    animation-delay: 0ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .001ms !important;
  }
  .topo path { stroke-dashoffset: 0; }
  .camp a, .camp .card { opacity: 1; transform: none; }
  .sky::after { opacity: 1; }
  .ridge { transition: none; }
}

@media (max-width: 34rem) {
  body.home main { padding-top: 1rem; }
  .camps { padding-top: 1.2rem; gap: 1rem; }

  /* iOS zooms the viewport when a focused field is under 16px, and then does
     not zoom back out. Every input on a phone is therefore 16px. */
  input[type=text], input[type=email], input[type=date],
  input[type=search], textarea { font-size: 16px; }

  /* The search field and its button stop fighting for one line. */
  .search { flex-wrap: wrap; }
  .search input[type=search] { flex: 1 1 100%; }
  .search button { width: 100%; }

  /* The coordinates line is long; let it breathe rather than tracking wide. */
  .overline { letter-spacing: .12em; }

  /* Tables are the one thing that genuinely needs the horizontal scroll. */
  .table-wrap { -webkit-overflow-scrolling: touch; }
  thead th, tbody td { padding: .5rem .6rem; }
}

/* ------------------------------------------------- Chief's library */
/* Only present when there is a database behind Chief. Without one the page
   keeps body.prose and its narrow measure, and none of this applies. */

body.chief main { max-width: 68rem; }

.withlibrary {
  display: grid;
  grid-template-columns: 15rem minmax(0, 1fr);
  gap: 2rem;
  align-items: start;
}

/* The reading column keeps the prose measure. The sidebar buys width for the
   page; it does not buy width for the sentences. */
.reading { max-width: 44rem; min-width: 0; }

.library {
  position: sticky;
  top: 1.5rem;
  font-size: .88rem;
  border-right: 1px solid var(--line);
  padding-right: 1.2rem;
}

.newchat {
  display: block;
  padding: .5rem .7rem;
  margin-bottom: 1rem;
  background: var(--panel);
  border: 1px solid var(--line-up);
  border-radius: var(--radius);
  text-align: center;
  text-decoration: none;
  color: var(--ink);
}
.newchat:hover { border-color: var(--alpenglow); color: var(--alpenglow); }

.shelf { margin-bottom: 1.1rem; }
.shelf h3 {
  margin: 0 0 .35rem;
  font: 600 .7rem/1.4 var(--mono);
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--ink-faint);
}
.shelf ul { list-style: none; margin: 0; padding: 0; }
.shelf li a {
  display: block;
  padding: .3rem .45rem;
  border-radius: 4px;
  color: var(--ink-dim);
  text-decoration: none;
  /* One line each, so the sidebar stays a list rather than becoming a wall. */
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.shelf li a:hover { background: var(--panel); color: var(--ink); }
.shelf li a[aria-current="page"] {
  background: var(--panel-up);
  color: var(--ink);
  box-shadow: inset 2px 0 0 var(--alpenglow);
}

.library .empty { margin: .4rem 0 1rem; font-size: .8rem; }

.newproject { margin-top: 1.4rem; padding-top: 1rem; border-top: 1px solid var(--line); }
.newproject label {
  display: block;
  font: 600 .7rem/1.4 var(--mono);
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--ink-faint);
  margin-bottom: .35rem;
}
.newproject input[type=text] { width: 100%; margin-bottom: .4rem; }
.newproject button { width: 100%; }

.tools { margin-top: 1.6rem; font-size: .9rem; }
.tools summary { cursor: pointer; color: var(--ink-dim); }
.tools summary:hover { color: var(--alpenglow); }
.tools form { margin-top: .9rem; }
.tools select { width: 100%; margin-bottom: .5rem; }
.tools .danger { padding-top: .9rem; border-top: 1px solid var(--line); }
.tools .danger button { border-color: var(--garnet); color: var(--bad); }
.tools .danger button:hover { background: var(--garnet); color: var(--snow); }

/* Below this the two columns cannot both hold their measure, so the library
   goes back to being a list above the conversation rather than beside it. */
@media (max-width: 52rem) {
  .withlibrary { grid-template-columns: 1fr; gap: 1.2rem; }
  .library {
    position: static;
    border-right: 0;
    padding-right: 0;
    padding-bottom: 1.2rem;
    border-bottom: 1px solid var(--line);
  }
  .newproject { margin-top: 1rem; }
}

/* ------------------------------------------------------------ tasks */

.tasks { list-style: none; margin: 1.4rem 0 0; padding: 0; display: grid; gap: .5rem; }

.task {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  gap: .8rem;
  align-items: start;
  padding: .8rem .9rem;
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: var(--radius);
}

/* The tick is a submit button, not a checkbox: it has to survive scripts being
   blocked, and a checkbox that does not post anywhere is a lie. */
.tick { margin: 0; }
.tick button {
  width: 1.6rem;
  height: 1.6rem;
  padding: 0;
  line-height: 1;
  border: 1px solid var(--line-up);
  border-radius: 4px;
  background: transparent;
  color: var(--ok);
}
.tick button:hover { border-color: var(--ok); }

.task .what { min-width: 0; }
.task .title { margin: 0; font-weight: 500; }
.task .notes { margin: .25rem 0 0; color: var(--ink-dim); font-size: .88rem; }
.task .meta { margin: .35rem 0 0; font-size: .78rem; color: var(--ink-faint); }

.task--done { opacity: .6; }
.task--done .title { text-decoration: line-through; }

.due { font-family: var(--mono); letter-spacing: .04em; }
.due--late { color: var(--bad); }
.due--none { color: var(--ink-faint); }

.task .tools { margin-top: .6rem; font-size: .85rem; }
.task .tools summary { cursor: pointer; color: var(--ink-dim); }
.task .tools summary:hover { color: var(--alpenglow); }
.task .tools input[type=text],
.task .tools textarea { width: 100%; }
.task .tools .danger { margin-top: .6rem; }
.task .tools .danger button { border-color: var(--garnet); color: var(--bad); }
.task .tools .danger button:hover { background: var(--garnet); color: var(--snow); }

.newtask {
  margin-top: 2rem;
  padding-top: 1.4rem;
  border-top: 1px solid var(--line);
}
.newtask h2 { margin: 0 0 .8rem; font-size: 1rem; }
.newtask input[type=text], .newtask textarea { width: 100%; }

/* Due date and assignee sit side by side while there is room for both. */
.field-row { display: flex; gap: 1rem; flex-wrap: wrap; margin-top: .6rem; }
.field-row > div { flex: 1 1 12rem; }
.field-row select { width: 100%; }

@media (max-width: 34rem) {
  .task { gap: .6rem; padding: .7rem .75rem; }
  .field-row > div { flex: 1 1 100%; }
}

/* A project is a folder; removing one must not look like removing what is in
   it, so the control is small and sits beside the name rather than under the
   conversations. The threads survive - see threadstore.ts. */
.shelf-head { display: flex; align-items: baseline; justify-content: space-between; gap: .5rem; }
.shelf-head h3 { margin-bottom: .35rem; }
.shelf-head form { margin: 0; }
.shelf-head button {
  padding: 0 .35rem;
  line-height: 1.2;
  border: 0;
  background: transparent;
  color: var(--ink-faint);
  font-size: 1rem;
}
.shelf-head button:hover { color: var(--bad); background: transparent; }

/* The model picker, shown only when starting a conversation. */
.pickmodel { margin: .9rem 0 .2rem; }
.pickmodel label {
  display: block;
  font: 600 .7rem/1.4 var(--mono);
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--ink-faint);
  margin-bottom: .35rem;
}
.pickmodel select { width: 100%; }
.pickmodel .hint { margin: .35rem 0 0; }

.answeredby {
  margin: 1.4rem 0 0;
  font-family: var(--mono);
  font-size: .76rem;
  letter-spacing: .04em;
  color: var(--ink-faint);
}

/* --------------------------------------------------- budget and usage */

.meter .big { margin: .3rem 0 .6rem; font-size: 2rem; font-weight: 600; }
.meter .big .of { font-size: 1rem; font-weight: 400; color: var(--ink-dim); }

.bar {
  height: .5rem;
  border-radius: 999px;
  background: var(--ground);
  border: 1px solid var(--line);
  overflow: hidden;
}
.bar span {
  display: block;
  height: 100%;
  background: var(--glacier);
  transition: width .3s ease;
}
/* Past the line the bar itself is the warning, not a sentence under it. */
.bar span[style*="100%"] { background: var(--bad); }

.status--no { background: var(--garnet); color: var(--snow); }

.reqs { list-style: none; margin: 1.2rem 0 0; padding: 0; display: grid; gap: .8rem; }
.req {
  padding: .9rem 1rem;
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: var(--radius);
}
.req-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: .6rem;
}
.req .notes { margin: .4rem 0 0; color: var(--ink-dim); font-size: .9rem; }

/* A decision with a sentence attached reads differently from a bare verdict. */
.req .verdict {
  margin: .5rem 0 0;
  padding-left: .7rem;
  border-left: 2px solid var(--line-up);
  color: var(--ink);
  font-size: .9rem;
}

.decide { margin-top: .8rem; }
.decide textarea { width: 100%; }
.decide .field-row { margin-top: .5rem; }
.decide button.no { border-color: var(--garnet); color: var(--bad); }
.decide button.no:hover { background: var(--garnet); color: var(--snow); }

/* ------------------------------------------- Chief's confirmation card */
/* Chief fills this in; a person presses the button. It is styled to be read
   rather than skimmed, because pressing it spends a permanent number. */

.proposal {
  margin: 1.6rem 0 0;
  padding: 1.1rem 1.2rem;
  background: var(--panel-up);
  border: 1px solid var(--line-up);
  border-left: 3px solid var(--alpenglow);
  border-radius: var(--radius);
}
.proposal h2 { margin: 0 0 .3rem; font-size: 1rem; }
.proposal .field { margin-top: .9rem; }
.proposal input[type=text], .proposal textarea { width: 100%; }
.proposal .field-row { margin-top: 1.1rem; }
.proposal button.no { border-color: var(--line-up); color: var(--ink-dim); }
.proposal button.no:hover { border-color: var(--bad); color: var(--bad); background: transparent; }

/* A field the register refused, with the reason under it. */
.field--bad input[type=text], .field--bad textarea { border-color: var(--bad); }
.wrong { margin: .3rem 0 0; font-size: .8rem; color: var(--bad); }

/* The "something is happening" note app.js adds on submit. */
.waiting {
  margin: .8rem 0 0;
  font-family: var(--mono);
  font-size: .8rem;
  letter-spacing: .04em;
  color: var(--ink-dim);
}
.waiting--pulse { animation: waiting-pulse 1.4s ease-in-out infinite; }
@keyframes waiting-pulse { 0%, 100% { opacity: .45; } 50% { opacity: 1; } }
form[data-busy="1"] button { cursor: progress; }

/* The derived "Changed by" column on the parts tab, and the links out of an
   ECN's `affects`. The link already existed in the data; nothing read it back. */
td.changes { font-family: var(--mono); font-size: .8rem; white-space: nowrap; }
td.changes a { color: var(--glacier); text-decoration: none; }
td.changes a:hover { text-decoration: underline; }
td.changes .none { color: var(--ink-faint); }
tbody td a { color: var(--glacier); }

/* Whether prompt caching is paying off. Sits under the estimate caveat, which
   already says "estimate" once - saying it twice reads as hedging. */
.cachenote {
  margin: .6rem 0 0;
  padding-top: .6rem;
  border-top: 1px solid var(--line);
  font-size: .82rem;
  color: var(--glacier);
}

/* Where an ECN has got to. Derived from the review ledger, never stored. */
td.ecnstatus { white-space: nowrap; }
.badge {
  display: inline-block;
  padding: .1rem .45rem;
  border-radius: 999px;
  font: 600 .68rem/1.5 var(--mono);
  letter-spacing: .08em;
  text-transform: uppercase;
  border: 1px solid var(--line-up);
  color: var(--ink-dim);
}
.badge--approved { border-color: var(--ok); color: var(--ok); }
.badge--awaiting_check,
.badge--awaiting_approval { border-color: var(--lichen); color: var(--lichen); }
.badge--rework { border-color: var(--alpenglow); color: var(--alpenglow); }
.badge--void { border-color: var(--garnet); color: var(--bad); }
/*
 * Overdue is not void, and must not share its class.
 *
 * They are both red, which is exactly the trap: `tr:has(.badge--void)` strikes
 * a row through and fades it, which is right for a record that was withdrawn
 * and wrong for one that is merely late. A corrective action whose check has
 * been missed is the most live thing on the page.
 */
.badge--overdue {
  border-color: var(--garnet);
  color: var(--bad);
  font-weight: 700;
}

/* A voided ECN stays visible and struck through. A number that vanished from
   the register would be worse than one you can see was voided. */
tr:has(.badge--void) td { opacity: .55; }
tr:has(.badge--void) td:first-child { text-decoration: line-through; }

/* ------------------------------------------------ ECN review workflow */

/* Signatures sit above tasks: an unsigned ECN holds up somebody else's work. */
.waitingon {
  margin: 1.4rem 0 0;
  padding: 1rem 1.1rem;
  background: var(--panel-up);
  border: 1px solid var(--line-up);
  border-left: 3px solid var(--lichen);
  border-radius: var(--radius);
}
.waitingon h2 { margin: 0 0 .6rem; font-size: .95rem; }
.waitingon ul { list-style: none; margin: 0; padding: 0; display: grid; gap: .7rem; }
.waitingon li a { font-family: var(--mono); color: var(--glacier); }
.waitingon .why {
  margin-left: .5rem;
  font-size: .75rem;
  letter-spacing: .06em;
  text-transform: uppercase;
  color: var(--ink-faint);
}
.waitingon .notes { margin: .2rem 0 0; color: var(--ink-dim); font-size: .88rem; }

.turn { margin: 1rem 0 0; font-size: .95rem; color: var(--ink); }
.turn--done { color: var(--ok); }
.turn--void { color: var(--bad); }

/* Said out loud rather than left to be worked out from three matching names. */
.alone {
  margin: .6rem 0 0;
  padding: .5rem .7rem;
  border-left: 2px solid var(--lichen);
  font-size: .88rem;
  color: var(--ink-dim);
}

.history { list-style: none; margin: 1rem 0 0; padding: 0; display: grid; gap: .8rem; }
.ev {
  padding-left: .9rem;
  border-left: 2px solid var(--line-up);
}
.ev--approve { border-left-color: var(--ok); }
.ev--rework { border-left-color: var(--alpenglow); }
.ev--void { border-left-color: var(--garnet); }
/* Camp IX. A disposition and a verification are decisions; a reopen is the
   same kind of event as a rework - it sends something back. */
.ev--disposition { border-left-color: var(--lichen); }
.ev--verify { border-left-color: var(--ok); }
.ev--reopen { border-left-color: var(--alpenglow); }
.ev .what { margin: 0; font-size: .92rem; }
.ev .meta { margin: .15rem 0 0; font-size: .78rem; color: var(--ink-faint); }
.ev .note {
  margin: .35rem 0 0;
  font-size: .88rem;
  color: var(--ink-dim);
  font-style: italic;
}

.signoff { margin-top: 1.6rem; }
.signoff h2 { margin: 0 0 .3rem; font-size: 1rem; }
.signoff select, .signoff textarea { width: 100%; }
.signoff.rework { margin-top: 1rem; padding-top: 1rem; border-top: 1px solid var(--line); }
.signoff button.no { border-color: var(--garnet); color: var(--bad); }
.signoff button.no:hover { background: var(--garnet); color: var(--snow); }
.signoff.danger button { border-color: var(--garnet); color: var(--bad); }

/* Derived from the ECNs that affect the part, never stored. */
td.rev { font-family: var(--mono); font-size: .82rem; white-space: nowrap; }
td.rev .none { color: var(--ink-faint); font-family: var(--sans); }

/* ------------------------------------------------- ECN attachments */
/* Bytes live in Drive; git holds only the reference. A PDF is shown rather
   than linked, streamed from this origin so frame-src 'self' is enough. */

.atts { list-style: none; margin: 1rem 0 0; padding: 0; display: grid; gap: 1.2rem; }

/* The filename and its Remove share a line. A long name wraps and the button
   keeps its size - min-width:0 is what lets the name shrink instead of
   pushing the button off the row. */
.att-head {
  display: flex; align-items: baseline; justify-content: space-between;
  gap: 1rem;
}
.att .what { margin: 0; min-width: 0; overflow-wrap: anywhere; }
.att .what a { color: var(--glacier); }

/* Undoes the global button, which is full width because it is nearly always
   the one thing a form is for. This one is the least important control near
   it, so it stays quiet until pointed at. */
.detach { margin: 0; flex: none; }
.detach button {
  width: auto; margin: 0; padding: .25rem .6rem;
  font: 600 .66rem/1 var(--mono);
  letter-spacing: .12em;
  background: transparent; color: var(--ink-faint);
  border: 1px solid var(--line-up);
}
.detach button:hover,
.detach button:focus-visible { background: var(--garnet); color: var(--snow); }
.att .meta { margin: .15rem 0 .5rem; font-size: .78rem; color: var(--ink-faint); }
/*
 * The drawing itself, framed.
 *
 * Named `att-preview` rather than `preview`, and that is a fix rather than a
 * preference: Camp I's next-number panel is `.preview`, so the bare class also
 * matched this iframe and lent it that panel's padding and gradient. The result
 * was a pale bordered box round every drawing, which looked like a rendering
 * fault and was one.
 *
 * The white ground stays, because a PDF paints on white and anything else puts
 * a grey wash behind the drawing. It is the frame that is dark, so the sheet
 * reads as a sheet on the page rather than a hole in it.
 */
.att-preview {
  display: block;
  width: 100%;
  height: 28rem;
  border: 1px solid var(--line-up);
  border-radius: var(--radius);
  background: var(--snow);
  box-shadow: 0 6px 18px rgba(0,0,0,.35);
}
@media (max-width: 34rem) {
  .att-preview { height: 20rem; }
}
.attach { margin-top: 1.4rem; padding-top: 1.2rem; border-top: 1px solid var(--line); }
.attach input[type=file] { display: block; margin: .4rem 0 .6rem; font-size: .9rem; }


/* ============================================================
   ONE PART, AND ITS REVISIONS
   ============================================================ */

/*
 * Each revision is a panel rather than a run of headings, because the question
 * being asked is "which of these do I send" and the eye has to be able to tell
 * one revision from the next without reading.
 */
.rev {
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--panel);
  padding: .9rem 1rem;
  margin-bottom: .9rem;
  transition: border-color .2s ease, transform .2s ease;
}
.rev:hover { border-color: var(--line-up); }

/* The one in force is the answer to the question. It gets the accent edge. */
.rev--current {
  border-color: rgba(127,191,143,.35);
  background: linear-gradient(var(--panel), #1F2620);
}
.rev--void { opacity: .62; }

.rev-head {
  display: flex; align-items: center; gap: .7rem;
  flex-wrap: wrap; margin-bottom: .2rem;
}
.rev-head h3 {
  margin: 0; font-size: 1rem; letter-spacing: .01em;
  font-family: var(--mono); font-weight: 600;
}
.rev .meta { margin: 0 0 .6rem; font-size: .78rem; color: var(--ink-faint); }
.rev .atts { margin: 0; }
.rev .hint { margin: .2rem 0 0; }

/* A voided revision keeps its letter and its drawings; it is struck rather
   than hidden, because somebody may be holding a print of it. */
.rev--void .rev-head h3 { text-decoration: line-through; text-decoration-thickness: 1px; }

/* ============================================================
   POLISH
   Small things that were left at the browser's default and read
   as unfinished next to everything around them.
   ============================================================ */

/*
 * Disclosure triangles.
 *
 * The default marker is a black triangle in whatever the browser feels like,
 * and next to the mono overlines on these pages it looks like a rendering
 * artefact. Replaced with a chevron that turns - which also gives the open and
 * closed states a difference you can see at a glance rather than read.
 */
summary { list-style: none; cursor: pointer; }
summary::-webkit-details-marker { display: none; }
summary::before {
  content: ""; display: inline-block;
  width: .42rem; height: .42rem;
  margin-right: .55rem; vertical-align: baseline;
  border-right: 1.5px solid currentColor;
  border-bottom: 1.5px solid currentColor;
  transform: rotate(-45deg) translate(-1px, -1px);
  transition: transform .2s ease;
}
details[open] > summary::before {
  transform: rotate(45deg) translate(-2px, -2px);
}

/*
 * Focus, everywhere, in one place.
 *
 * Keyboard focus was visible on the inputs and invisible on links and
 * disclosure triangles. `:focus-visible` rather than `:focus` so a mouse click
 * does not leave a ring behind on something that was only pressed.
 */
a:focus-visible,
summary:focus-visible,
button:focus-visible {
  outline: 2px solid var(--glacier);
  outline-offset: 2px;
  border-radius: 3px;
}

/*
 * A table row is a link to somewhere in every register, so it should feel like
 * one. The left edge is what moves - a whole row changing colour on hover is
 * noise when you are scanning forty of them.
 */
tbody tr { transition: background .15s ease, box-shadow .15s ease; }
tbody tr:hover { box-shadow: inset 2px 0 0 var(--alpenglow); }

/* An attachment reads as a card once it has something to hover. */
.att { transition: border-color .2s ease; }

/*
 * Buttons already had a press. This gives them a lift on the way in, so the
 * primary action on a page feels like the primary action.
 */
button:not(:disabled):hover { transform: translateY(-1px); }
button:not(:disabled):active { transform: translateY(1px); }
button:disabled { opacity: .45; cursor: not-allowed; }

/*
 * Links inside prose grow their underline rather than switching it on, which
 * is the difference between a page that responds and one that flickers.
 */
.doc a, .aside a, .rev .meta a {
  text-decoration-color: rgba(79,176,198,.45);
  text-underline-offset: .16em;
  transition: text-decoration-color .2s ease, color .2s ease;
}
.doc a:hover, .aside a:hover, .rev .meta a:hover {
  color: var(--glacier); text-decoration-color: var(--glacier);
}

/* ============================================================
   MOVING BETWEEN PAGES
   ============================================================ */

/*
 * BaseCamp is server-rendered with no client router, and every navigation is
 * therefore a full document load - which browsers paint as a white flash and a
 * jump. `@view-transition` lets the browser cross-fade one document into the
 * next with no JavaScript at all, so the sky and the chrome appear to stay put
 * while the content under them changes, which is what they look like anyway.
 *
 * Progressive: browsers that do not know this rule ignore it and navigate the
 * way they always did. Nothing depends on it.
 */
@view-transition { navigation: auto; }

::view-transition-old(root),
::view-transition-new(root) {
  animation-duration: .18s;
  animation-timing-function: cubic-bezier(.2,.8,.25,1);
}

/*
 * The sky is fixed and identical between pages, so it is given its own
 * transition name and simply does not animate. Without this it cross-fades
 * against itself, which reads as a flicker on the one element that should be
 * the still point.
 */
.sky { view-transition-name: sky; }
::view-transition-old(sky),
::view-transition-new(sky) { animation: none; mix-blend-mode: normal; }

@media (prefers-reduced-motion: reduce) {
  ::view-transition-old(root),
  ::view-transition-new(root) { animation: none; }
}

/* ------------------------------------------------- ECN affected parts */
/* What the change covers, which is not always what it was raised for: a part
   added afterwards is appended to a ledger rather than written into the
   register row, so the panel shows the two folded together and says which is
   which. */

.ecn-parts { list-style: none; margin: 1rem 0 0; padding: 0; display: grid; gap: .5rem; }

.ecn-part {
  display: flex; align-items: baseline; justify-content: space-between; gap: 1rem;
  padding: .55rem .75rem;
  border: 1px solid var(--line);
  border-radius: 4px;
  background: var(--panel);
  transition: border-color .2s ease, transform .2s ease;
}
.ecn-part:hover { border-color: var(--line-up); transform: translateX(2px); }
.ecn-part .what { margin: 0; min-width: 0; overflow-wrap: anywhere; font: 600 .95rem/1.3 var(--mono); }
.ecn-part .what a { color: var(--glacier); }

/* Quiet, because it is a footnote on a part rather than a status. The part
   number is the thing being read here. */
.tag {
  display: inline-block; margin-left: .5rem;
  padding: .1rem .4rem;
  font: 600 .6rem/1.4 var(--mono);
  letter-spacing: .1em; text-transform: uppercase;
  color: var(--ink-faint);
  border: 1px solid var(--line-up);
  border-radius: 3px;
  vertical-align: middle;
}

/* ------------------------------------------------- the vendor portal */
/* A different building, not a narrower Camp II - so it gets its own furniture
   rather than borrowing the register's. The list is the whole page for
   somebody who has been granted three parts, so it is sized like content
   rather than like an index. */

.granted { list-style: none; margin: 1.5rem 0 0; padding: 0; display: grid; gap: .9rem; }

.grant {
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--panel);
  padding: 1rem 1.15rem;
  transition: border-color .2s ease, transform .2s ease, background .2s ease;
}
.grant:hover {
  border-color: var(--glacier);
  background: var(--panel-up);
  transform: translateY(-2px);
}

.grant-link {
  display: flex; flex-wrap: wrap; align-items: baseline; gap: .2rem .8rem;
  text-decoration: none;
}
.grant-number {
  font: 700 1.15rem/1.2 var(--mono);
  letter-spacing: .04em;
  color: var(--glacier);
}
.grant-name {
  font-size: .9rem; color: var(--ink-dim);
  text-transform: uppercase; letter-spacing: .08em;
}
.grant .meta { margin: .4rem 0 0; font-size: .78rem; color: var(--ink-faint); }

/* The standing terms. Quiet but never collapsed: it is on every page of the
   portal on purpose, and a notice you have to open is one nobody read. */
.notice {
  margin: 2.5rem 0 0;
  padding: 1.1rem 1.25rem;
  border: 1px solid var(--line);
  border-left: 3px solid var(--lichen);
  border-radius: var(--radius);
  background: rgba(227, 178, 60, .05);
}
.notice h2 {
  margin: 0 0 .5rem;
  font: 700 .7rem/1 var(--mono);
  letter-spacing: .14em; text-transform: uppercase;
  color: var(--lichen);
  border: 0; padding: 0;
}
.notice p { margin: 0; font-size: .85rem; color: var(--ink-dim); }

/* "optional", beside a label. Quieter than the label it qualifies, because it
   is a fact about the field rather than the name of it - and a required field
   says nothing at all, so this is the only mark to read. */
.opt {
  margin-left: .4rem;
  font: 600 .6rem/1 var(--mono);
  letter-spacing: .1em; text-transform: uppercase;
  color: var(--ink-faint);
}

/* ------------------------------------------------- export control */
/* The one classification in BaseCamp with legal weight, so it is allowed to be
   the loudest thing on the page it appears on. Garnet rather than the amber
   used for "something alongside this did not happen" - this is not a degraded
   success, it is a restriction. */

.is-controlled {
  font: 700 .78rem/1.4 var(--mono);
  letter-spacing: .12em;
  color: var(--bad);
}

/* ------------------------------------------------- quality level */
/* Amber, not garnet: an item with no level is not restricted, it is owed a
   decision - and until it gets one it is treated as QL-1, which is the strict
   side rather than a fault. */

.is-unclassified {
  font: 700 .78rem/1.4 var(--mono);
  letter-spacing: .12em;
  color: var(--warn);
}

.export {
  border: 1px solid var(--line);
  border-left: 3px solid var(--bad);
  border-radius: var(--radius);
  background: rgba(226,128,124,.06);
  padding: 1rem 1.15rem;
  margin-top: 1rem;
}
.export .what {
  margin: 0 0 .3rem;
  font: 700 .72rem/1 var(--mono);
  letter-spacing: .14em; text-transform: uppercase;
  color: var(--bad);
}
.export .meta { margin: 0 0 .8rem; font-size: .82rem; color: var(--ink-dim); }
.export .detach { margin: 0; }

/* Marking one is a legal act with consequences for everybody already holding
   the drawing, so the button is the quiet destructive style rather than the
   page's primary - it should not be the thing your eye lands on. */
.export-mark button { width: auto; }

/* A release that exists and is doing nothing. Red, because "shared" that is not
   shared is worse than not shared: somebody is waiting for a drawing. */
.ecn-part--blocked {
  border-color: rgba(226,128,124,.5);
  background: rgba(226,128,124,.06);
}
.blocked-why {
  margin: .45rem 0 0;
  font-size: .8rem; line-height: 1.5;
  color: var(--bad);
  max-width: 46em;
}

/* The two answers to the one question. The "no" is deliberately not styled as
   a lesser choice - a page that makes one answer look like the wrong one is a
   page that produces wrong answers. */
.declare-no { margin-top: 1.6rem; }
.declare-no button { width: auto; }

/* Bordered rather than filled, and not red. `.no` elsewhere means destructive,
   which this is not: it is one of two honest answers, and painting it as the
   dangerous one is how you get people picking the other. */
.declare-no button.no {
  background: transparent;
  color: var(--ink-dim);
  border: 1px solid var(--line-up);
  padding: .62rem 1.1rem;
}
.declare-no button.no:hover,
.declare-no button.no:focus-visible {
  background: var(--panel-up);
  color: var(--ink);
  border-color: var(--ink-faint);
}

/* ------------------------------------------------- the public documents */
/* /privacy and /terms, read signed out and often on a phone by somebody
   deciding whether to trust this at all. Wider measure than the app's panels
   and no chrome: it is a document, not a screen. */
.legal { max-width: 42em; }
.legal h2 {
  margin: 2rem 0 .6rem;
  font-size: 1rem; letter-spacing: .01em;
  border: 0; padding: 0;
}
.legal p, .legal li { line-height: 1.65; }
.legal ul { padding-left: 1.1rem; margin: .6rem 0; }
.legal li { margin-bottom: .5rem; }
.legal .meta { margin-top: 2.5rem; font-size: .8rem; color: var(--ink-faint); }

/* ------------------------------------------------- the export switch */
/* A switch that is a button, because a checkbox would need script to submit
   and with script blocked it would look like it had worked while recording
   nothing. It shows the state on the SERVER, which is the only state that
   decides whether a vendor sees the drawing. */

.export { margin-top: 1rem; }

.switch {
  width: auto; margin: 0;
  display: inline-flex; align-items: center; gap: .7rem;
  padding: .5rem .9rem .5rem .55rem;
  background: transparent;
  border: 1px solid var(--line);
  border-radius: 999px;
  color: var(--ink-dim);
  font: 600 .8rem/1 var(--sans, inherit);
  letter-spacing: .02em; text-transform: none;
  transition: border-color .2s ease, color .2s ease, background .2s ease;
}
.switch:hover { border-color: var(--line-up); color: var(--ink); }
.switch:focus-visible {
  outline: none; border-color: var(--glacier);
  box-shadow: 0 0 0 3px rgba(79,176,198,.16);
}

.switch-track {
  position: relative;
  width: 2.3rem; height: 1.25rem;
  border-radius: 999px;
  background: var(--line);
  transition: background .22s ease;
  flex: none;
}
.switch-thumb {
  position: absolute; top: .155rem; left: .155rem;
  width: .94rem; height: .94rem;
  border-radius: 50%;
  background: var(--ink-dim);
  transition: transform .22s ease, background .22s ease;
}

/* On is garnet rather than the usual accent: this is a restriction, and it
   should not read as a feature being switched on. */
.switch--on { border-color: rgba(226,128,124,.5); color: var(--bad); }
.switch--on .switch-track { background: rgba(226,128,124,.45); }
.switch--on .switch-thumb { background: var(--bad); transform: translateX(1.05rem); }
.switch--on:hover { border-color: var(--bad); color: var(--bad); }

@media (prefers-reduced-motion: reduce) {
  .switch, .switch-track, .switch-thumb { transition: none; }
}

.export .hint { margin-top: .7rem; max-width: 44em; }

/* A part added to an ECN after its number was issued, in Camp II's affects
   column. Marked rather than hidden: the row was issued naming the others, and
   somebody reading a quality record should be able to see which is which
   without opening the ECN. The plus is decorative - the title attribute is
   what carries it to a screen reader. */
.added-later { opacity: .85; }
.added-mark {
  margin-left: .1em;
  font-size: .75em; vertical-align: super;
  color: var(--ink-faint);
}

/* ------------------------------------------------- older revisions, folded */
/* The current revision's drawings are one click. Every older one is two: still
   there, still downloadable, but never reachable by accident. Opening a part
   and clicking the first link you see always gives you the one to build from. */

.rev-files > summary {
  font: 600 .78rem/1.4 var(--mono);
  letter-spacing: .04em;
  color: var(--ink-faint);
  padding: .35rem 0;
  transition: color .2s ease;
}
.rev-files > summary:hover { color: var(--ink-dim); }
.rev-files[open] > summary { color: var(--ink-dim); margin-bottom: .2rem; }

/* A withdrawn revision says so on the fold itself, so the warning is read
   before the drawing is reached rather than after. */
.rev--void .rev-files > summary { color: var(--bad); opacity: .85; }
.rev--void .rev-files > summary:hover { opacity: 1; }

/* ------------------------------------------------- Camp IX: quality records */

/*
 * What each disposition actually commits you to, under the picker.
 *
 * Its own class rather than `.problems`, which is Camp II's register-integrity
 * list. These are choices, not problems - and `.problems` only ever appears
 * inside a failed flash, so borrowing it would mean any styling that list
 * gained later would land on this one. See LESSONS.md, "two things must not
 * share a name".
 */
.choices {
  margin: .5rem 0 0;
  padding-left: 1.1rem;
  font-size: .84rem;
  color: var(--ink-faint);
}

.choices li { margin: .25rem 0; }

.choices .what {
  font-family: var(--mono);
  font-size: .68rem;
  text-transform: uppercase;
  letter-spacing: .08em;
  color: var(--ink);
  margin-right: .4rem;
}

/* ------------------------------------------------------------------ labels */

/*
 * A header with something on its right: the number on the left, its labels
 * and the box to add one on the right. Wraps under the number on a phone
 * rather than squeezing it.
 */
.section-head--split {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: .8rem 1.5rem;
  flex-wrap: wrap;
}
.section-head--split > div:first-child { min-width: 0; }

.labels {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: .5rem;
  max-width: 24rem;
}

/*
 * A label is glacier rather than any status colour: it is a grouping, not a
 * state, and it must not read as "approved" or "blocked" at a glance. Pills,
 * because a run of them has to read as a set of tags rather than as buttons.
 */
.chips {
  display: flex; flex-wrap: wrap; justify-content: flex-end; gap: .35rem;
  list-style: none; margin: 0; padding: 0;
}
.chip {
  display: inline-flex; align-items: center; gap: .15rem;
  padding: .12rem .2rem .12rem .6rem;
  border: 1px solid rgba(79,176,198,.38);
  border-radius: 999px;
  background: rgba(79,176,198,.09);
  font-size: .8rem; line-height: 1.4;
  transition: border-color .2s ease, background .2s ease;
}
.chip:hover { border-color: rgba(79,176,198,.7); background: rgba(79,176,198,.14); }
.chip a { color: var(--glacier); text-decoration: none; }
.chip a:hover { text-decoration: underline; text-underline-offset: .15em; }

/* The x. Undoes the global button, which is full width and shouting. */
.chip-off { margin: 0; display: inline-flex; }
.chip-off button {
  width: 1.3rem; height: 1.3rem; margin: 0; padding: 0;
  font: 400 1rem/1 var(--sans); letter-spacing: 0; text-transform: none;
  border-radius: 50%;
  background: transparent; color: var(--ink-faint);
}
.chip-off button:hover,
.chip-off button:focus-visible { background: var(--garnet); color: var(--snow); }

/* The table version: no x, smaller, and a link rather than a pill with parts. */
.chip--small {
  padding: .02rem .5rem;
  font-size: .72rem;
  color: var(--glacier);
  text-decoration: none;
  white-space: nowrap;
}
td.labels-cell .chips { justify-content: flex-start; }

/* One box for "pick one" and "make one". */
.label-add { display: flex; gap: .4rem; margin: 0; }
.label-add input[type=text] { width: 14rem; padding: .42rem .55rem; font-size: .85rem; }
.label-add button {
  width: auto; margin: 0; padding: .42rem .8rem;
  font-size: .7rem;
  background: transparent; color: var(--ink-dim);
  border: 1px solid var(--line-up);
}
.label-add button:hover { background: var(--panel-up); color: var(--ink); }
/* Read by a screen reader, not drawn: the placeholder says it for the eye. */
.label-add-label {
  position: absolute; width: 1px; height: 1px; overflow: hidden;
  clip: rect(0 0 0 0); white-space: nowrap; margin: 0;
}

@media (max-width: 34rem) {
  .labels { align-items: stretch; max-width: none; width: 100%; }
  .chips { justify-content: flex-start; }
  .label-add input[type=text] { flex: 1; width: auto; }
}

/* A filter beside the search box should not claim the whole row. */
.search select { width: auto; max-width: 14rem; }
@media (max-width: 34rem) {
  .search select { flex: 1 1 100%; max-width: none; }
}

h2 .count-inline {
  font: 600 .7rem/1 var(--mono);
  color: var(--ink-faint);
  margin-left: .35rem;
  vertical-align: middle;
}

.danger-zone button { border-color: var(--garnet); color: var(--bad); background: transparent; }
.danger-zone button:hover { background: var(--garnet); color: var(--snow); }

/*
 * What a share would do, one item to a line. Only the lines that will
 * actually go are drawn at full strength; everything left out is quiet, with
 * the export-blocked ones in the warning colour because they are the ones
 * somebody may be waiting on.
 */
.share-lines { list-style: none; margin: .3rem 0 0; padding: 0; display: grid; gap: .3rem; }
.share-line {
  display: grid; grid-template-columns: auto 1fr; gap: .1rem .7rem;
  padding: .45rem .65rem;
  border: 1px solid var(--line); border-radius: 4px;
  background: var(--panel);
  font-size: .84rem;
}
.share-line .num { grid-row: span 2; font: 600 .86rem/1.4 var(--mono); color: var(--snow); }
.share-line .share-name { color: var(--ink-dim); }
.share-line .share-what { font-size: .76rem; color: var(--ink-faint); }
.share-line--share { border-color: rgba(127,191,143,.4); }
.share-line--share .share-what { color: var(--ok); }
.share-line--export .share-what { color: var(--warn); }
.share-line--held,
.share-line--internal,
.share-line--unknown { opacity: .7; }
