/* Public Benefit Bag. The page is a shelf of cassette spines: one spine per thing the
   mechanic holds. Every surface here is a PNG drawn by tools/draw.mjs and used through
   border-image; nothing on this page is a CSS rectangle standing in for an object.

   THE SYSTEM, in one place, so nothing on the page is a one-off:

   grid      every spine shares one grid: a fixed badge column, the label, a fixed end cap.
             No spine is offset from another and no column is sized by its own content.
   type      four faces, one role each. Tanker is the display face (the wordmark, the shelf
             number), Avara every card title, Xolonium every label and every paragraph,
             RX100 every figure. Sizes come from the scale below and from nowhere else.
   colour    four states and one subject. Active is what the fund works towards, done is
             taken and paid, idle is nothing to do yet, warn is the way down, subject is the
             fund and the coin themselves. A cap, a ladder block and a track all read the
             same four.
   surface   the label's surface is the state in print: white is a fact that holds, graph
             paper is the level being worked on, cream ruled is a level already paid, blank
             ruled is a level still locked, black is the coin's own hardware, black with the
             hazard flash is the burn. Six surfaces, one meaning each.

   Scale follows his order of 2026-10-19: base 13-14px, labels 11px, ONE display element
   on the screen (the wordmark), the column capped so the window is not filled by three lines. */

@font-face { font-family: Tanker;  src: url(../type/Tanker-Regular.woff2) format("woff2");  font-weight: 400; font-display: swap; }
@font-face { font-family: Xolonium;src: url(../type/Xolonium-Regular.woff2) format("woff2");font-weight: 400; font-display: swap; }
@font-face { font-family: Xolonium;src: url(../type/Xolonium-Bold.woff2) format("woff2");   font-weight: 700; font-display: swap; }
@font-face { font-family: RX100;   src: url(../type/RX100-Regular.woff2) format("woff2");   font-weight: 400; font-display: swap; }
@font-face { font-family: Avara;   src: url(../type/Avara-Black.woff2) format("woff2");     font-weight: 900; font-display: swap; }
@font-face { font-family: Avara;   src: url(../type/Avara-Bold.woff2) format("woff2");      font-weight: 700; font-display: swap; }

:root {
  /* ---- the type scale. Nothing on the page sets a size outside this list ---- */
  --t-label: 11px;   /* a cue, a nav key, a cap, a ladder digit */
  --t-body: 13px;    /* every paragraph */
  --t-value: 14px;   /* every figure, always in the mono */
  --t-title: 20px;   /* a card's own title */
  --t-index: 24px;   /* the shelf number in the badge */
  --t-display: 32px;   /* the wordmark, once on the screen. A fixed step, not a clamp: a clamp
                          paints sizes that are not on the scale, and the label's own side
                          padding has to be at least one em of it */

  /* ---- state colour. Four states, one subject, no sixth colour -------------- */
  --c-active: #fbcf41;
  --c-done: #7cc050;
  --c-idle: #4f4f4f;
  --c-warn: #f8612e;
  --c-subject: #e4e0d4;

  /* ---- surfaces and ink ---------------------------------------------------- */
  --page: #191919;
  --ink: #141414;
  --ink-2: #57544c;   /* a paragraph on paper, 5.4 : 1 */
  --ink-3: #666359;   /* a cue on paper, 4.7 : 1 */
  --paper: #eeece4;
  --paper-2: #a8a49b;
  --metal: #c6c2b9;   /* type on the cassette body, 7 : 1 */
  --metal-2: #e8e5dd;
  --red: #c9321f;
  --amber: #fec742;

  /* ---- one grid for the whole page ---------------------------------------- */
  --col: clamp(960px, 74vw, 1360px);   /* the single container: rail and shelf share it */
  --tape-h: 196px;                     /* every spine on a page is this tall */
  --badge-w: 128px;                    /* fixed: the label starts on the same line always */
  --cap-w: 118px;                      /* fixed: every cap is the same plate */
  --gap: 7px;                          /* between spines */
  --inset: 12px;                       /* the row inside the shell */
  --pad-y: 18px;                       /* every label's inner inset, border included */
  --pad-x: 32px;
  --row-h: 40px;                       /* a key, a button, a field: one height */
}

* { box-sizing: border-box; }
html { background: var(--page); }
body {
  margin: 0;
  background: var(--page);
  color: var(--paper);
  font: 400 var(--t-body)/1.6 Xolonium, "Segoe UI", sans-serif;
  font-synthesis: none;
  -webkit-font-smoothing: antialiased;
}
a { color: inherit; }
::selection { background: var(--amber); color: var(--ink); }

/* ---------------------------------------------------------------- the rail */
.rail {
  width: var(--col);
  margin: 16px auto 0;
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  grid-template-areas: "edge name live" "nav nav nav";
  align-items: center;
  gap: 12px 20px;
  padding: 14px 22px;
  border-style: solid;
  border-width: 22px;
  border-image: url(../img/rail.png) 60 fill / 22px / 0 stretch;
}
/* The name sits on the page's centre line, the live figure at the end of its own row, and the
   keys take a row of their own: four equal columns, so the active key is marked by its plate
   and never by being wider than its neighbours. One row would push the name off centre by a
   hundred and sixty pixels, which is what a nav of four words costs. */
.rail nav {
  grid-area: nav;
  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: 1fr;
  width: min(100%, 680px);
  justify-self: center;
  gap: 6px;
}
.rail-name,
.rail-live {
  display: flex;
  align-items: center;
  min-height: var(--row-h);
  padding: 0;
  white-space: nowrap;
}
.rail-name {
  grid-area: name;
  justify-self: center;
  font: 700 var(--t-value)/1 Xolonium;
  letter-spacing: .2em;
  text-transform: uppercase;
  color: var(--metal-2);
}
.rail-live { grid-area: live; justify-self: end; gap: 9px; }
.rail-live .k { font: 700 var(--t-label)/1 Xolonium; letter-spacing: .14em; color: #a9a59c; text-transform: uppercase; }
.rail-live .v { font: 400 var(--t-value)/1 RX100; color: var(--amber); }

.key {
  display: grid;
  place-items: center;
  min-height: var(--row-h);
  padding: 0 12px;
  border-style: solid;
  border-width: 11px;
  border-image: url(../img/key.png) 28 fill / 11px / 0 stretch;
  font: 700 var(--t-label)/1 Xolonium;
  letter-spacing: .14em;
  text-transform: uppercase;
  white-space: nowrap;
  color: #c3bfb7;
  text-decoration: none;
  transition: color .12s ease, transform .1s ease;
}
.key:hover { color: #fff; transform: translateY(-1px); }
.key:active { transform: translateY(1px); }
.key[aria-current="page"] {
  border-image-source: url(../img/key-on.png);
  color: var(--ink);
}
.key[aria-current="page"]:hover { color: var(--ink); }
.key:focus-visible { outline: 2px solid var(--amber); outline-offset: 3px; }

/* --------------------------------------------------------------- the shelf */
.shelf {
  width: var(--col);
  margin: 44px auto 0;
  display: flex;
  flex-direction: column;
  gap: var(--gap);
  padding-bottom: 72px;
}

/* one spine. Every one of them is the same height and sits on the same two vertical lines:
   the shelf reads as one object, and a state is read off colour and surface, never off size. */
.tape { position: relative; min-height: var(--tape-h); display: block; }

.tape::before {
  content: "";
  position: absolute;
  inset: 0;
  border-style: solid;
  border-width: 34px;
  border-image: url(../img/shell.png) 96 fill / 34px / 0 stretch;
  pointer-events: none;
}
.tape[data-state="active"]::before { border-image-source: url(../img/shell-next.png); }
.tape[data-state="idle"]::before { border-image-source: url(../img/shell-locked.png); }

.tape > .row {
  position: relative;
  min-height: calc(var(--tape-h) - var(--inset) * 2);
  margin: var(--inset);
  display: grid;
  grid-template-columns: var(--badge-w) minmax(0, 1fr) var(--cap-w);
  align-items: stretch;
}

/* ------------------------------------------------- the badge, the left zone */
/* It carries the spine's number on the shelf, what the spine is, and the cassette's own
   latch. One fixed width, one type size, the latch in the same place on every spine. */
.badge {
  position: relative;
  display: grid;
  grid-template-rows: auto auto auto;
  align-content: start;
  gap: 5px;
  padding: 2px 10px 0 16px;
  min-width: 0;
}
.badge .fmt {
  font: 400 var(--t-label)/1 Xolonium;
  letter-spacing: .12em;
  text-transform: uppercase;
  color: var(--metal);
}
.badge .no {
  font: 400 var(--t-index)/1 Tanker;
  letter-spacing: .01em;
  color: var(--metal-2);
}
.badge .sub {
  font: 700 var(--t-label)/1 Xolonium;
  letter-spacing: .13em;
  text-transform: uppercase;
  color: var(--metal);
  overflow: hidden;
  text-overflow: ellipsis;
}
.badge .latch {
  position: absolute;
  left: 16px;
  bottom: 2px;
  width: 36px;
  height: 26px;
  background: url(../img/latch.png) no-repeat center / 100% 100%;
}

/* --------------------------------------------------------------- the label */
/* Every variant below keeps the same inner inset: border width plus padding is always
   --pad-y / --pad-x, so the first letter of every card starts on the same line. */
.label {
  min-width: 0;
  border-style: solid;
  border-width: 6px;
  border-image: url(../img/plate-white.png) 16 fill / 6px / 0 stretch;
  padding: calc(var(--pad-y) - 6px) calc(var(--pad-x) - 6px);
  color: var(--ink);
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 7px;
  /* a grid item with overflow:hidden gets min-height 0, so a label that is a line too tall
     silently eats its own padding instead of making the spine taller: ask for min-content back */
  min-height: min-content;
  overflow: hidden;
}
/* a level already paid: cream, ruled, written on */
.label.cream {
  border-image: url(../img/plate-cream.png) 16 / 6px / 0 stretch;
  background: url(../img/tile-ruled-cream.png) repeat left bottom / 24px 30px;
}
/* a level still locked: a blank tape label, printed with rules, waiting to be written on */
.label.blank {
  border-image: url(../img/plate-blank.png) 16 / 6px / 0 stretch;
  background: url(../img/tile-ruled.png) repeat left bottom / 24px 30px;
}
/* the level being worked on: graph paper, the only surface with a grid on it */
.label.grid {
  border-image-slice: 16;
  background: url(../img/tile-grid.png) repeat left top / 18px 18px;
}
/* the coin's own hardware, and the field that reads an address */
.label.dark {
  border-image-source: url(../img/plate-black.png);
  color: var(--paper);
}
/* the way down */
.label.burn {
  /* the right slice is 480 source px: drawn into 240 CSS px it keeps the slant his tape has */
  border-width: 12px 240px 12px 24px;
  border-image: url(../img/plate-burn.png) 32 480 32 64 fill / 12px 240px 12px 24px / 0 stretch;
  padding: calc(var(--pad-y) - 12px) 0 calc(var(--pad-y) - 12px) calc(var(--pad-x) - 24px);
  color: var(--paper);
}

/* the end cap: his tapes turn the word sideways, this one keeps it horizontal (no vertical
   text). One plate per state, one width, and it runs the full height of its spine. */
.cap {
  display: grid;
  place-items: center;
  border-style: solid;
  border-width: 12px;
  border-image: url(../img/cap-v2-idle.png) 32 fill / 12px / 0 stretch;
  padding: 0 8px;
  font: 700 var(--t-label)/1.2 Xolonium;
  letter-spacing: .12em;
  text-transform: uppercase;
  text-align: center;
  color: #f2f0e8;
}
.cap.subject { border-image-source: url(../img/cap-v2-subject.png); color: var(--ink); }
.cap.active  { border-image-source: url(../img/cap-v2-active.png);  color: var(--ink); }
.cap.done    { border-image-source: url(../img/cap-v2-done.png);    color: var(--ink); }
.cap.warn    { border-image-source: url(../img/cap-v2-warn.png);    color: var(--ink); }
.cap.idle    { border-image-source: url(../img/cap-v2-idle.png); }

/* ------------------------------------------------------------ label insides */
/* the one display element on the screen */
.word { font: 400 var(--t-display)/1.02 Tanker; letter-spacing: -.01em; margin: 0; }
/* every card's own title: one face, one size, one line height */
.title { font: 900 var(--t-title)/1.25 Avara; margin: 0; color: inherit; }
.title.red { color: var(--red); }
.title.amber { color: var(--amber); }
/* every paragraph */
.said { font: 400 var(--t-body)/1.6 Xolonium; color: var(--ink-2); margin: 0; max-width: 76ch; }
.label.dark .said, .label.burn .said { color: var(--paper-2); }
/* every label on a figure, and every figure */
.cue { font: 700 var(--t-label)/1.35 Xolonium; letter-spacing: .14em; text-transform: uppercase; color: var(--ink-3); }
.label.dark .cue, .label.burn .cue { color: #8f8c84; }
.mono { font: 400 var(--t-value)/1.35 RX100; color: inherit; }
.label.blank .title, .label.blank .mono { color: #3f3c35; }
/* the blank plate is darker than the white one, so its cue needs its own ink for 4.5 : 1 */
.label.blank .cue { color: #4e4b43; }
.label.blank .said { color: var(--ink-2); }

.lines { display: flex; flex-direction: column; gap: 7px; min-width: 0; }
/* a figure row: the cue above its value is the same shape in every card */
.facts { display: flex; flex-wrap: wrap; align-items: baseline; gap: 4px 26px; min-width: 0; }
.facts > span { display: inline-flex; align-items: baseline; gap: 8px; }
.pair { display: flex; align-items: baseline; gap: 10px; min-width: 0; }
.spread { display: flex; align-items: center; justify-content: space-between; gap: 18px; flex-wrap: wrap; }
/* a printed rule is an object of its own under the row, never a background on the box that
   holds the words: a box with a fill is a box, and its text then has to clear its edges */
.rule { display: block; height: 2px; margin: 0; background: url(../img/rule-cream.png) repeat-x left center / 24px 2px; }
.label.dark .rule, .label.burn .rule { background-image: url(../img/rule-dark.png); }
.label.blank .rule { background-image: url(../img/rule-blank.png); }

/* the fund label: type on the left, the ladder of levels running to its own edge */
.label.fund {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 156px;
  align-items: stretch;
  gap: 24px;
  padding: calc(var(--pad-y) - 6px) 0 calc(var(--pad-y) - 6px) calc(var(--pad-x) - 6px);
  border-right-width: 0;
}
.label.fund > .lines { justify-content: center; }

/* the ladder: one block per level, the colour is its state and the digit is its number.
   Six blocks of equal width, so it reads as the ladder and not as a gradient. */
.rung { display: grid; grid-template-rows: auto minmax(0, 1fr); gap: 6px; min-width: 0; }
.ladder { display: grid; grid-auto-flow: column; grid-auto-columns: 1fr; gap: 3px; margin-bottom: calc(6px - var(--pad-y)); }
.ladder span {
  display: block;
  background: url(../img/bar-v2-idle.png) no-repeat center / 100% 100%;
}
.ladder span[data-state="done"] { background-image: url(../img/bar-v2-done.png); }
.ladder span[data-state="active"] { background-image: url(../img/bar-v2-active.png); }
.ladder span[data-state="active"] { animation: beat 3.2s ease-in-out infinite; }
@keyframes beat { 0%, 100% { opacity: 1; } 50% { opacity: .62; } }

/* the number chip, his little black square with a numeral in it. One style everywhere. */
.chip {
  display: inline-grid;
  place-items: center;
  min-width: 60px;
  min-height: 34px;
  padding: 8px 10px 6px;
  border-style: solid;
  border-width: 6px;
  border-image: url(../img/chip.png) 16 fill / 6px / 0 stretch;
  font: 400 var(--t-value)/1 Tanker;
  letter-spacing: .04em;
  color: var(--paper);
}

/* the progress of the level being worked on, with its own figure beside it */
.track {
  position: relative;
  height: 16px;
  border-style: solid;
  border-width: 4px;
  border-image: url(../img/track-v2.png) 12 fill / 4px / 0 stretch;
}
.track i {
  position: absolute;
  inset: -4px auto -4px -4px;
  width: 8%;
  min-width: 26px;
  border-style: solid;
  border-width: 4px;
  border-image: url(../img/track-v2-fill.png) 12 fill / 4px / 0 stretch;
  transition: width .9s cubic-bezier(.22, 1, .36, 1);
}

/* sunken strips: the mint, an address field */
.strip {
  border-style: solid;
  border-width: 8px;
  border-image: url(../img/strip-dark.png) 20 fill / 8px / 0 stretch;
  padding: 6px 14px;
  font: 400 var(--t-value)/1.35 RX100;
  color: #d8d3c6;
  word-break: break-all;
  overflow-wrap: anywhere;
  min-width: 0;
  user-select: all;
}
.strip.light { border-image-source: url(../img/strip-light.png); color: var(--ink); }
input.strip { user-select: auto; background: none; outline: none; min-height: var(--row-h); }
input.strip::placeholder { color: #8a877f; }
input.strip:focus-visible { border-image-source: url(../img/strip-light.png); outline: 2px solid var(--amber); outline-offset: 2px; }

/* keys that act: copy, check, the three platform links */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  min-height: var(--row-h);
  padding: 0 10px;
  border-style: solid;
  border-width: 12px;
  border-image: url(../img/btn-light.png) 32 fill / 12px / 0 stretch;
  font: 700 var(--t-label)/1 Xolonium;
  letter-spacing: .12em;
  text-transform: uppercase;
  color: var(--ink);
  background: none;
  text-decoration: none;
  cursor: pointer;
  transition: transform .08s ease, opacity .12s ease;
}
.btn:hover { transform: translateY(-1px); }
.btn:active { transform: translateY(1px); border-image-source: url(../img/btn-light-on.png); }
.btn:focus-visible { outline: 2px solid var(--amber); outline-offset: 3px; }
.btn[aria-disabled="true"] { opacity: .46; cursor: default; transform: none; }
.btn img { height: 15px; width: auto; display: block; }
.btn.dark { border-image-source: url(../img/btn-dark.png); color: var(--paper); }
.btn.dark:active { border-image-source: url(../img/btn-dark-on.png); }

.links { display: flex; gap: 8px; flex-wrap: wrap; }
.coinrow { display: flex; gap: 10px; align-items: center; flex-wrap: wrap; }
.coinrow .strip { flex: 1 1 260px; display: flex; align-items: center; min-height: var(--row-h); }

/* a tape that leads somewhere lifts, the way an object on a shelf does */
a.tape { text-decoration: none; color: inherit; transition: transform .18s ease, filter .18s ease; }
a.tape:hover { transform: translateY(-3px); filter: drop-shadow(0 8px 14px rgba(0, 0, 0, .55)); }
a.tape:focus-visible { outline: 2px solid var(--amber); outline-offset: 4px; }

/* the first row of an inner page, in the same container as the rail and the shelf */
.head {
  width: var(--col);
  margin: 32px auto 0;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 7px;
}
.head h1 { font: 400 30px/1.05 Tanker; margin: 0; color: var(--metal-2); }
.head p { margin: 0; font: 400 var(--t-body)/1.6 Xolonium; color: #8f8c84; max-width: 64ch; }
.shelf.under-head { margin-top: 22px; }
/* the rules page carries a paragraph per spine, so its spines are taller than the ladder's.
   Within the page they are all the same height, which is what a shelf has to look like. */
.shelf.tall { --tape-h: 208px; }

.empty { font: 400 var(--t-body)/1.6 Xolonium; color: #8f8c84; }
.visually-hidden {
  position: absolute; width: 1px; height: 1px; margin: -1px;
  clip-path: inset(50%); overflow: hidden; white-space: nowrap;
}

/* ------------------------------------------------------------- narrow sizes */
@media (max-width: 1460px) {
  .rail { gap: 14px; padding: 12px 16px; }
  .rail nav { gap: 5px; }
  .key { padding: 0 9px; letter-spacing: .1em; }
  .rail-name { letter-spacing: .16em; }
}

@media (max-width: 1180px) {
  :root { --col: 94vw; --badge-w: 112px; --cap-w: 104px; }
  .rail { padding: 10px 14px; border-width: 18px; border-image-width: 18px; }
  .key { min-height: 36px; padding: 0 10px; letter-spacing: .08em; }
  .tape::before { border-width: 28px; border-image-width: 28px; }
  .label.fund { grid-template-columns: minmax(0, 1fr) 132px; gap: 18px; }
  .shelf.tall { --tape-h: 232px; }
  .coinrow .strip { flex: 1 1 200px; }
  .btn { padding: 0 8px; letter-spacing: .09em; }
}

@media (max-width: 1000px) {
  .shelf.tall { --tape-h: 248px; }
}

@media (max-width: 860px) {
  :root { --col: 92vw; --tape-h: 0px; --inset: 9px; --pad-x: 30px; --pad-y: 20px; --t-display: 30px; }
  .shelf.tall { --tape-h: 0px; }
  .head h1 { font-size: 24px; }
  .shelf { margin-top: 26px; gap: 10px; }
  .tape { min-height: 0; }
  .tape::before { border-width: 24px; border-image-width: 24px; }
  .tape > .row {
    min-height: 0;
    grid-template-columns: minmax(0, 1fr) auto;
    grid-template-areas: "badge cap" "label label";
    gap: 8px;
  }
  /* the badge becomes one line of the same three pieces, in the same order */
  .badge {
    grid-area: badge;
    grid-template-rows: none;
    grid-auto-flow: column;
    grid-auto-columns: max-content;
    align-items: center;
    align-content: center;
    gap: 10px;
    padding: 2px 0 2px 8px;
  }
  .badge .no { font-size: var(--t-title); }
  .badge .latch { display: none; }
  .cap { grid-area: cap; width: 112px; justify-self: end; min-height: 36px; padding: 0 10px; border-width: 10px; border-image-width: 10px; }
  .label { grid-area: label; }
  .label.burn {
    border-width: 12px;
    border-image: url(../img/plate-black.png) 16 fill / 12px / 0 stretch;
    padding: calc(var(--pad-y) - 12px) calc(var(--pad-x) - 12px);
  }
  .label.fund {
    grid-template-columns: minmax(0, 1fr);
    gap: 14px;
    padding: calc(var(--pad-y) - 6px) calc(var(--pad-x) - 6px);
  }
  /* the ladder runs to the label's own edges here too, so the margins cancel its padding */
  .rung { grid-template-rows: auto 44px; }
  .ladder { margin: 0 calc(6px - var(--pad-x)) calc(6px - var(--pad-y)); }
  .head { margin-top: 24px; }
}

@media (max-width: 430px) {
  /* the name and the live figure stop sharing a row before either of them is squeezed */
  .rail { grid-template-columns: 1fr; grid-template-areas: "name" "live" "nav"; gap: 8px; }
  .rail-name { justify-self: start; }
  /* two rows of two, still equal columns */
  .rail nav { grid-auto-flow: row; grid-template-columns: 1fr 1fr; width: 100%; }
  .rail-name { justify-self: start; }
  .rail-live { justify-self: start; }
  .facts { gap: 4px 18px; }
}

@media (prefers-reduced-motion: reduce) {
  * { animation: none !important; transition: none !important; }
}
