/* CORRUPTED ETHER — palette sampled directly from the collection's own art.
   Substrate from the backgrounds; the five accents are the generator's palette families. */
:root {
  --ink:        #0a0a0b;   /* black */
  --ink-2:      #121213;
  --ink-3:      #1b1b1d;
  --vellum:     #f2f0ed;   /* white */
  --vellum-dim: #8d8b88;   /* grey */
  /* [OWNER 2026-08-24] The accent is Robinhood's green -- the chain the collection
     lives on. It replaced the original red; the tokens were renamed with the colour
     so nothing is ever called red while holding green. */
  --accent:      #00c805;
  --accent-hot:  #2be34a;
  --accent-deep: #0a3d14;
  /* Frames and ornaments never take the raw accent. The old red was dark enough to
     read as wrought iron; Robinhood green is neon, and on the reliquary frames it
     read as a rendering glitch -- corners blazing like laser tabs. Structure gets
     this bottle green; the bright accent stays on type and controls. */
  --accent-frame: #0b7d29;
  --rule:       #2a2a2d;

  /* collection palette families — used for frame tinting and the filter only */
  --corruption: #b455c8;
  --necrotic:   #4fb562;
  --infernal:   #e8380c;
  --royal:      #d8a63f;
  --bone:       #a8a5a0;
  --gold:       #cea64a;   /* the bestiary is gilt, not palette-coded */

  /* ---- the cursor ----------------------------------------------------
     A dagger, drawn for this site rather than downloaded: no licence to honour, no
     file to host, no extra request, and it matches the thorn corners already on every
     frame. Both states are inline SVG, redrawn by generator/make_cursors.py.

     Keywords here, images only where there is a real pointer — a phone has no cursor
     to style, and a stylus should keep whatever the system gives it. */
  --cur-arrow: auto;
  --cur-point: pointer;

  --display: 'Grenze Gotisch', 'Palatino Linotype', 'Book Antiqua', Palatino, serif;
  --body:    'Spectral', Georgia, serif;
  --gut:     clamp(1.25rem, 4vw, 3.5rem);
  --max:     1240px;
}
@media (hover: hover) and (pointer: fine) {
  :root {
  --cur-arrow: url("data:image/svg+xml,%3Csvg%20xmlns%3D%27http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%27%20width%3D%2726%27%20height%3D%2726%27%20viewBox%3D%270%200%2026%2026%27%3E%3Cg%20stroke%3D%27%230a0a0b%27%20stroke-width%3D%272.6%27%20stroke-linejoin%3D%27round%27%20stroke-linecap%3D%27round%27%20fill%3D%27%230a0a0b%27%3E%3Cpath%20d%3D%27M1.80%201.80%20L14.61%209.77%20L14.41%2011.65%20L12.64%2012.30%20Z%27%2F%3E%3Cpath%20d%3D%27M16.77%208.30%20L17.88%209.16%20L12.83%2015.62%20L11.73%2014.76%20Z%27%2F%3E%3Cpath%20d%3D%27M15.81%2011.16%20L19.35%2013.93%20L17.82%2015.90%20L14.27%2013.13%20Z%27%2F%3E%3Ccircle%20cx%3D%2719.29%27%20cy%3D%2715.47%27%20r%3D%272.2%27%2F%3E%3C%2Fg%3E%3Cpath%20d%3D%27M15.81%2011.16%20L19.35%2013.93%20L17.82%2015.90%20L14.27%2013.13%20Z%27%20fill%3D%27%238d8b88%27%2F%3E%3Cpath%20d%3D%27M16.77%208.30%20L17.88%209.16%20L12.83%2015.62%20L11.73%2014.76%20Z%27%20fill%3D%27%23c9c5be%27%2F%3E%3Ccircle%20cx%3D%2719.29%27%20cy%3D%2715.47%27%20r%3D%271.5%27%20fill%3D%27%23c9c5be%27%2F%3E%3Cpath%20d%3D%27M1.80%201.80%20L14.61%209.77%20L14.41%2011.65%20L12.64%2012.30%20Z%27%20fill%3D%27%23f2f0ed%27%2F%3E%3Cpath%20d%3D%27M4.32%203.77%20L12.20%209.93%27%20stroke%3D%27%23c9c5be%27%20stroke-width%3D%27.85%27%20stroke-linecap%3D%27round%27%20opacity%3D%27.7%27%2F%3E%3Cpath%20d%3D%27M16.83%2011.96%20L15.29%2013.93%27%20stroke%3D%27%230a0a0b%27%20stroke-width%3D%27.7%27%20opacity%3D%27.5%27%2F%3E%3Cpath%20d%3D%27M17.86%2012.76%20L16.32%2014.73%27%20stroke%3D%27%230a0a0b%27%20stroke-width%3D%27.7%27%20opacity%3D%27.5%27%2F%3E%3Cpath%20d%3D%27M18.88%2013.56%20L17.34%2015.53%27%20stroke%3D%27%230a0a0b%27%20stroke-width%3D%27.7%27%20opacity%3D%27.5%27%2F%3E%3C%2Fsvg%3E") 2 2, auto;
  --cur-point: url("data:image/svg+xml,%3Csvg%20xmlns%3D%27http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%27%20width%3D%2726%27%20height%3D%2726%27%20viewBox%3D%270%200%2026%2026%27%3E%3Cg%20stroke%3D%27%230a0a0b%27%20stroke-width%3D%272.6%27%20stroke-linejoin%3D%27round%27%20stroke-linecap%3D%27round%27%20fill%3D%27%230a0a0b%27%3E%3Cpath%20d%3D%27M1.80%201.80%20L14.61%209.77%20L14.41%2011.65%20L12.64%2012.30%20Z%27%2F%3E%3Cpath%20d%3D%27M16.77%208.30%20L17.88%209.16%20L12.83%2015.62%20L11.73%2014.76%20Z%27%2F%3E%3Cpath%20d%3D%27M15.81%2011.16%20L19.35%2013.93%20L17.82%2015.90%20L14.27%2013.13%20Z%27%2F%3E%3Ccircle%20cx%3D%2719.29%27%20cy%3D%2715.47%27%20r%3D%272.2%27%2F%3E%3C%2Fg%3E%3Cpath%20d%3D%27M15.81%2011.16%20L19.35%2013.93%20L17.82%2015.90%20L14.27%2013.13%20Z%27%20fill%3D%27%238d8b88%27%2F%3E%3Cpath%20d%3D%27M16.77%208.30%20L17.88%209.16%20L12.83%2015.62%20L11.73%2014.76%20Z%27%20fill%3D%27%23f2f0ed%27%2F%3E%3Ccircle%20cx%3D%2719.29%27%20cy%3D%2715.47%27%20r%3D%271.5%27%20fill%3D%27%23f2f0ed%27%2F%3E%3Cpath%20d%3D%27M1.80%201.80%20L14.61%209.77%20L14.41%2011.65%20L12.64%2012.30%20Z%27%20fill%3D%27%2300c805%27%2F%3E%3Cpath%20d%3D%27M4.32%203.77%20L12.20%209.93%27%20stroke%3D%27%238af0a3%27%20stroke-width%3D%27.85%27%20stroke-linecap%3D%27round%27%20opacity%3D%27.7%27%2F%3E%3Cpath%20d%3D%27M16.83%2011.96%20L15.29%2013.93%27%20stroke%3D%27%230a0a0b%27%20stroke-width%3D%27.7%27%20opacity%3D%27.5%27%2F%3E%3Cpath%20d%3D%27M17.86%2012.76%20L16.32%2014.73%27%20stroke%3D%27%230a0a0b%27%20stroke-width%3D%27.7%27%20opacity%3D%27.5%27%2F%3E%3Cpath%20d%3D%27M18.88%2013.56%20L17.34%2015.53%27%20stroke%3D%27%230a0a0b%27%20stroke-width%3D%27.7%27%20opacity%3D%27.5%27%2F%3E%3C%2Fsvg%3E") 2 2, pointer;
  }
}


* { box-sizing: border-box; }
html { scroll-behavior: smooth; }
body {
  margin: 0;
  background: var(--ink);
  color: var(--vellum);
  font-family: var(--body);
  font-size: 17px;
  line-height: 1.65;
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
  cursor: var(--cur-arrow);
}

/* cursor inherits, so a typed-in field would otherwise get the dagger instead of the
   I-beam that tells you it takes text. */
input[type="search"], input[type="text"], input[type="number"], textarea { cursor: text; }
input[type="checkbox"], input[type="radio"] { cursor: var(--cur-point); }

/* Everything the browser makes a pointer on its own — links above all. These never
   passed through a `cursor: pointer` of ours, so they were the one place the system
   arrow still showed through. */
a[href], button, select, summary, label, [role="button"] { cursor: var(--cur-point); }
button:disabled, select:disabled { cursor: var(--cur-arrow); }
img { display: block; max-width: 100%; }
figure { margin: 0; }   /* browser default margins were shrinking every gallery tile */
a { color: inherit; }
:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }

.wrap { max-width: var(--max); margin: 0 auto; padding: 0 var(--gut); }

/* ---------- structural type ------------------------------------------- */
.eyebrow {
  font-family: var(--body);
  font-size: .68rem;
  letter-spacing: .34em;
  text-transform: uppercase;
  color: var(--vellum-dim);
  margin: 0 0 1.1rem;
}
h1, h2, h3 { font-family: var(--display); font-weight: 400; line-height: .95; margin: 0; }
h2 { margin-bottom: 1.25rem; }
h3 { margin-bottom: .5rem; }
h1 { font-size: clamp(3.4rem, 11vw, 8.5rem); letter-spacing: -.015em; }
h2 { font-size: clamp(2.1rem, 5.5vw, 4rem); }
h3 { font-size: 1.45rem; }
p  { margin: 0 0 1.1rem; }
.lede { font-size: 1.14rem; color: #cbc8c4; max-width: 56ch; }

/* ---------- nav -------------------------------------------------------- */
.nav {
  position: sticky; top: 0; z-index: 60;
  display: flex; align-items: center; justify-content: space-between;
  gap: 1rem; padding: .85rem var(--gut);
  background: rgba(10, 10, 11, .86);
  backdrop-filter: blur(14px);
  border-bottom: 1px solid var(--rule);
}
.nav__mark {
  display: inline-flex; align-items: center; gap: .5rem;
  font-family: var(--display); font-size: 1.3rem; letter-spacing: .04em;
  text-decoration: none; white-space: nowrap;
}
.nav__mark span { color: var(--accent); }
/* Sized in em so the mark tracks the wordmark through every breakpoint below rather
   than needing its own set of overrides. */
.nav__logo {
  display: block; height: 1.5em; width: auto; flex: none;
  filter: drop-shadow(0 0 7px rgba(145, 35, 160, .38));
  transition: filter .25s ease, transform .25s ease;
}
.nav__mark:hover .nav__logo {
  filter: drop-shadow(0 0 12px rgba(145, 35, 160, .7));
  transform: translateY(-1px);
}
.nav__links { display: flex; gap: 1.6rem; font-size: .78rem; letter-spacing: .2em; text-transform: uppercase; }
.nav__links a { text-decoration: none; color: var(--vellum-dim); transition: color .2s; }
.nav__links a:hover, .nav__links a[aria-current] { color: var(--vellum); }
.nav__social { display: inline-flex; align-items: center; gap: .85rem; margin-left: .4rem; padding-left: 1.1rem; border-left: 1px solid var(--rule); }
.nav__social a { display: inline-flex; width: 1.15rem; height: 1.15rem; color: var(--vellum-dim); transition: color .2s; }
.nav__social a:hover { color: var(--accent); }
.nav__social svg { width: 100%; height: 100%; fill: currentColor; }
/* Nine links plus two icons need room. Under ~1180px the row tightens, and under
   ~900px it is allowed to wrap onto a second line rather than run off the edge. */
@media (max-width: 1180px) { .nav__links { gap: 1.05rem; font-size: .72rem; letter-spacing: .14em; } .nav__social { padding-left: .8rem; gap: .65rem; } }
@media (max-width: 900px) { .nav { flex-wrap: wrap; row-gap: .5rem; } .nav__links { flex-wrap: wrap; row-gap: .45rem; } }
@media (max-width: 560px) { .nav__social { gap: .6rem; padding-left: .7rem; margin-left: .1rem; } .nav__social a { width: 1rem; height: 1rem; } }
@media (max-width: 560px) {
  .nav { padding-left: 1.1rem; padding-right: 1.1rem; }
  .nav__mark { font-size: 1.05rem; gap: .4rem; }
  .nav__links { gap: .95rem; font-size: .66rem; letter-spacing: .12em; }
}

/* ---------- the mark, and the corruption crossing it ------------------- */
/* The page ships the finished logo as an ordinary <img>. brand.js lays a canvas over
   that exact box for three seconds, plays the stone being taken, then removes itself.
   Everything here has to survive the canvas never arriving. */
.brand-mark { position: relative; display: block; }
.brand-mark img { display: block; width: 100%; height: auto; }
.brand-mark canvas { position: absolute; inset: 0; width: 100%; height: 100%; pointer-events: none; }
/* visibility, not display: the <img> keeps holding the box open, so the canvas has
   something to measure and the page does not reflow when the two swap.
   Under JS the image starts hidden and brand.js issues the verdict: is-ready shows the
   static mark (reduced motion, failed or slow images, animation finished); is-playing
   keeps it hidden behind the canvas. The mark used to flash fully formed before the
   animation began -- the story opens on the clean stone, never on its ending. */
html.js .brand-mark:not(.is-ready) img { visibility: hidden; }
.brand-mark.is-playing img { visibility: hidden; }

/* The mark closes the wordmark, so it is sized in em off the heading and tracks it
   through every breakpoint on its own. Width rather than height, because the <img> below
   is width-driven; at .60em the diamond stands almost exactly one line tall, which keeps
   it out of the line box and stops it shunting the rest of the hero down the page. */
.hero__logo {
  display: inline-block;
  width: .60em;
  margin-left: .26em;
  vertical-align: -.12em;
  filter: drop-shadow(0 0 22px rgba(145, 35, 160, .34));
}

/* ---------- hero: the layer stack (signature) -------------------------- */
.hero { padding: clamp(3rem, 8vw, 6rem) 0 clamp(3rem, 7vw, 5rem); }
.hero__grid { display: grid; grid-template-columns: 1fr; gap: clamp(2.5rem, 5vw, 4rem); align-items: center; }
@media (min-width: 900px) { .hero__grid { grid-template-columns: 1fr .92fr; } }

.hero h1 { margin-bottom: .35rem; }
.hero h1 em { font-style: normal; color: var(--accent); }
.hero__sub { font-size: 1.2rem; color: #cbc8c4; max-width: 46ch; margin-bottom: 2rem; }

.facts { list-style: none; margin: 0 0 2.2rem; padding: 0; border-top: 1px solid var(--rule); }
.facts li {
  display: grid; grid-template-columns: 1fr auto; gap: 1rem; align-items: baseline;
  padding: .78rem 0; border-bottom: 1px solid var(--rule);
}
.facts b { font-family: var(--display); font-size: 1.5rem; font-weight: 400; letter-spacing: .01em; }
.facts span { font-size: .74rem; letter-spacing: .22em; text-transform: uppercase; color: var(--vellum-dim); }
/* The green goes on the mint offers, named as such. It used to go on whichever row
   happened to be first, so adding a row moved it. [OWNER 2026-09-04] */
.facts b { color: var(--vellum); }
.facts b.is-hot { color: var(--accent); }

/* the stack itself */
.stack { position: relative; }
.stack__frame {
  position: relative; aspect-ratio: 1; border: 1px solid var(--rule);
  background: #060607; overflow: hidden;
}
/* Fade only. These layers were sliding up and scaling down as they arrived, which on a
   stack whose whole point is that six paintings register exactly meant every one of them
   visibly drifted into place before settling. Nothing that moves a layer relative to the
   others belongs here. The arrival now reads as a brief lift in the paint instead. */
.stack__frame img {
  position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover;
  image-rendering: -webkit-optimize-contrast;   /* sharper downscale of the 1000px source */
  opacity: 0; filter: brightness(1.55) saturate(.8);
  transition: opacity .42s ease, filter .62s ease;
}
.stack__frame img.on { opacity: 1; filter: none; }
.stack__rail { list-style: none; margin: 1rem 0 0; padding: 0; display: grid; gap: .1rem; }
.stack__rail li {
  display: grid; grid-template-columns: 2.1rem 1fr auto; gap: .8rem; align-items: baseline;
  padding: .34rem 0; font-size: .74rem; letter-spacing: .2em; text-transform: uppercase;
  color: #4c4c50; transition: color .35s;
}
.stack__rail li.on { color: var(--vellum); }
.stack__rail li.on .tick { color: var(--accent); }
.stack__rail li { cursor: var(--cur-point); }
.stack__rail li.on:hover .lbl { color: var(--accent); }
.stack__rail li.struck .lbl { text-decoration: line-through; color: #5a5a5e; }
.stack__rail li.struck .tick { color: transparent; }
.stack__frame img.hid { opacity: 0; transform: none; }
.stack__bar {
  display: flex; align-items: center; justify-content: space-between; gap: 1rem;
  margin-top: .85rem; font-size: .74rem; letter-spacing: .2em; text-transform: uppercase;
  color: var(--vellum-dim); font-variant-numeric: tabular-nums;
}
.stack__bar button {
  background: none; border: 0; cursor: var(--cur-point); padding: 0;
  font-family: var(--display); font-size: 1rem; letter-spacing: .08em;
  color: var(--vellum); text-transform: none; transition: color .2s;
}
.stack__bar button:hover { color: var(--accent); }

/* palette filter */
.filters { display: flex; flex-wrap: wrap; gap: .45rem; align-items: center; margin-bottom: 2.2rem; }
.filters button {
  background: none; cursor: var(--cur-point); padding: .42rem .85rem;
  border: 1px solid var(--rule); color: var(--vellum-dim);
  font-family: var(--body); font-size: .72rem; letter-spacing: .18em; text-transform: uppercase;
  transition: color .2s, border-color .2s, background .2s;
}
.filters button:hover { color: var(--vellum); border-color: #4a4a4f; }
.filters button[aria-pressed] { color: #fff; background: var(--accent); border-color: var(--accent); }
.filters button[data-pal="Corruption"][aria-pressed] { background: var(--corruption); border-color: var(--corruption); color: #14060f; }
.filters button[data-pal="Necrotic"][aria-pressed]   { background: var(--necrotic);   border-color: var(--necrotic);   color: #06140a; }
.filters button[data-pal="Infernal"][aria-pressed]   { background: var(--infernal);   border-color: var(--infernal);   color: #fff; }
.filters button[data-pal="Royal"][aria-pressed]      { background: var(--royal);      border-color: var(--royal);      color: #140f04; }
.filters button[data-pal="Neutral"][aria-pressed]    { background: var(--bone);       border-color: var(--bone);       color: var(--ink); }
.filters button[data-pal="all"][aria-pressed] { color: var(--ink); background: var(--vellum); border-color: var(--vellum); }
.filters #filterCount { font-size: .7rem; letter-spacing: .18em; text-transform: uppercase; color: var(--vellum-dim); margin-left: .4rem; }
.tile { transition: opacity .35s, filter .35s; }
.tile.dim { opacity: .16; filter: grayscale(1); pointer-events: none; }
.tile { cursor: var(--cur-point); }
.stack__rail .num { font-family: var(--display); letter-spacing: .05em; font-size: .95rem; }
.stack__rail .tick { color: transparent; }
.stack__caption { margin-top: 1rem; font-size: .82rem; color: var(--vellum-dim); font-style: italic; }

/* ---------- coming soon (premint trials) -------------------------------- */
/* The mark, its corruption playing, and two lines. Sized so the page needs no scroll
   on a laptop: the point of a closed door is that there is nothing behind it yet. */
.soon { padding: clamp(4rem, 12vh, 8rem) 0 clamp(5rem, 14vh, 9rem); }
.soon__wrap { display: flex; flex-direction: column; align-items: center; text-align: center; }
.soon__logo {
  width: clamp(120px, 16vw, 190px);
  margin-bottom: 2.2rem;
  filter: drop-shadow(0 0 34px rgba(145, 35, 160, .35));
}
.soon .eyebrow { margin-bottom: .6rem; }
.soon h1 { font-size: clamp(2.8rem, 8vw, 6rem); }

/* ---------- the hidden guard -------------------------------------------------
   A browser hides [hidden] with a built-in `display: none`, but that is a USER AGENT
   rule and ANY author rule setting `display` beats it. So a panel written as
   `.thing { display: flex }` and toggled with `.hidden = true` goes on being drawn,
   at full size, over the page -- the attribute says gone, the screen says here.

   This has now bitten twice: the portfolio's soul card sat as an invisible sheet
   swallowing every click, and the chat's emoji picker refused to go away after
   placing a reaction. Both times the JavaScript was right and the CSS was not, and
   both times a test that read `.hidden` sided with the code instead of the screen.
   One rule ends the whole class of bug. [OWNER 2026-08-26] */
[hidden] { display: none !important; }

/* ---------- portfolio (holders) ---------------------------------------- */
.pf__gate { padding: 2.5rem 0 4rem; }
/* buttons in the portfolio are buttons, not banners: the site's base .btn stretches
   to its container and that reads fine under the mint's summon panel, but a
   full-width Sign out is a wall */
.pf .btn, .pf__gate .btn, .pf__account .btn { width: auto; display: inline-block; flex: none; padding-left: 2.2rem; padding-right: 2.2rem; }
.pf__hint { margin-top: .8rem; font-size: .9rem; color: var(--vellum-dim); min-height: 1.2em; }
.pf__hint[data-bad="1"] { color: var(--accent-hot); }
.pf__row { display: flex; align-items: center; gap: 1rem; padding: 1.2rem 0; border-bottom: 1px solid var(--rule); flex-wrap: wrap; }
.pf__addr { font-family: var(--body); font-weight: 600; font-size: 1.05rem; letter-spacing: .02em; margin-right: auto; }
.pf__row .btn { padding: .5rem 1.4rem; font-size: .92rem; }
.btn--ghost { background: transparent; border: 1px solid var(--rule); color: var(--vellum-dim); }
.btn--ghost:hover { color: var(--vellum); border-color: var(--vellum-dim); }
.pf__namebox { padding: 1.6rem 0; max-width: 30rem; }
.pf__namebox label { display: block; font-size: .72rem; letter-spacing: .18em; text-transform: uppercase; color: var(--vellum-dim); margin-bottom: .5rem; }
.pf__nameline { display: flex; gap: .6rem; }
.pf__nameline input {
  flex: 1; background: var(--ink-2); border: 1px solid var(--rule); color: var(--vellum);
  font-family: var(--body); font-size: 1rem; padding: .55rem .7rem;
}
.pf__nameline input:focus { outline: 1px solid var(--accent); border-color: var(--accent); }

.pf__headline { display: flex; align-items: baseline; gap: 1rem; padding: 1.8rem 0 .6rem; flex-wrap: wrap; }
.pf__headline-label { font-size: .72rem; letter-spacing: .2em; text-transform: uppercase; color: var(--vellum-dim); }
.pf__headline b { font-family: var(--body); font-variant-numeric: tabular-nums; font-size: clamp(1.9rem, 4.5vw, 2.8rem); font-weight: 600; }
.pf__stale { font-size: .78rem; color: var(--royal); }
/* what to show: values and amounts each hide behind a pill for a screen-share or a
   screenshot; prices and floors are market facts and stay */
.pf__show { margin-left: auto; display: flex; gap: .45rem; align-self: center; }
.pf__pill { background: none; border: 1px solid var(--rule); color: var(--vellum-dim);
  font-family: var(--body); font-size: .68rem; letter-spacing: .14em; text-transform: uppercase;
  padding: .32rem .8rem; cursor: var(--cur-point); transition: color .2s, border-color .2s; }
.pf__pill[aria-pressed="true"] { color: var(--vellum); border-color: var(--vellum-dim); }
.pf__pill:hover { color: var(--vellum); }
.pf__panel { margin-top: 2.4rem; }
/* section titles: white, readable, body face -- the blackletter belongs to
   "Portfolio" alone [OWNER 2026-08-25] */
.pf__panel h3, .pf__chartbox h3 {
  font-family: var(--body); font-weight: 600; font-size: 1.02rem; letter-spacing: .12em;
  text-transform: uppercase; color: var(--vellum);
  border-bottom: 1px solid var(--rule); padding-bottom: .5rem; margin: 0 0 .6rem;
}
/* One column template shared by the header row and every coin row, so the
   figures land under their labels by construction. `auto` columns size per
   row and can never line up -- learned the ugly way [OWNER 2026-08-25]. */
.pf { --pf-grid: minmax(0, 1fr) 8.5rem 8.5rem 8.5rem; }
.pf__cols { display: grid; grid-template-columns: var(--pf-grid); gap: 1rem; padding: .6rem 0 .45rem; font-size: .68rem; letter-spacing: .16em; text-transform: uppercase; color: var(--vellum-dim); border-bottom: 1px solid color-mix(in srgb, var(--rule) 55%, transparent); }
.pf__cols span { text-align: right; }
.pf__cols span:first-child { text-align: left; }
.pf__list { list-style: none; margin: 0; padding: 0; }
.pf__tok { display: grid; grid-template-columns: var(--pf-grid); gap: 1rem; align-items: baseline; padding: .65rem 0; border-bottom: 1px solid color-mix(in srgb, var(--rule) 55%, transparent); font-variant-numeric: tabular-nums; }
.pf__tok:hover { background: color-mix(in srgb, var(--vellum) 4%, transparent); }
@media (max-width: 620px) {
  .pf { --pf-grid: minmax(0, 1fr) 4.9rem 4.6rem 5.6rem; }
  .pf__cols, .pf__tok { gap: .55rem; }
  .pf__cols { letter-spacing: .1em; }
}
.pf__sym { font-family: var(--body); font-weight: 600; letter-spacing: .05em; }
.pf__qty, .pf__price { color: var(--vellum-dim); font-size: .92rem; text-align: right; }
.pf__usd { text-align: right; }
.pf__tok--none { display: block; color: var(--vellum-dim); font-style: italic; border-bottom: 0; }

.pf__count { color: var(--vellum-dim); letter-spacing: 0; text-transform: none; }
.pf__coll { margin: 1.1rem 0 1.5rem; }
/* each collection folds on its header -- a wallet full of airdrop spam stays scannable */
.pf__coll-head { display: flex; align-items: baseline; gap: .7rem; flex-wrap: wrap; width: 100%;
  background: none; border: 0; padding: .35rem 0; margin: 0 0 .6rem; color: inherit;
  font: inherit; text-align: left; cursor: var(--cur-point); }
.pf__coll-car { flex: none; align-self: center; width: .42rem; height: .42rem;
  border-right: 1.5px solid var(--vellum-dim); border-bottom: 1.5px solid var(--vellum-dim);
  transform: rotate(45deg); transition: transform .22s ease; }
.pf__coll--closed .pf__coll-car { transform: rotate(-45deg); }
.pf__coll-head:hover .pf__coll-car { border-color: var(--vellum); }
.pf__coll--closed .pf__coll-grid { display: none; }
.pf__coll--closed { margin-bottom: .4rem; }
.pf__coll-name { font-family: var(--body); font-weight: 600; font-size: 1rem; letter-spacing: .04em; }
.pf__coll--gold .pf__coll-name { color: var(--gold); }
.pf__coll-n { font-size: .78rem; color: var(--vellum-dim); margin-left: auto; text-align: right; font-variant-numeric: tabular-nums; }
.pf__coll-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); gap: .9rem; align-items: start; }
.pf__nft { margin: 0; background: var(--ink-2); border: 1px solid var(--rule); overflow: hidden;
           cursor: var(--cur-point); transition: transform .25s cubic-bezier(.2,.7,.3,1), border-color .25s, box-shadow .25s; }
.pf__nft:hover, .pf__nft:focus-visible { transform: translateY(-3px); border-color: var(--vellum-dim); }
.pf__nft img { width: 100%; aspect-ratio: 1; object-fit: cover; display: block; }
.pf__nft figcaption { padding: .45rem .6rem; font-size: .78rem; color: var(--vellum-dim);
                      white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.pf__nft--gold figcaption { color: var(--gold); }
.pf__nft--gold:hover { box-shadow: 0 0 22px -8px color-mix(in srgb, var(--gold) 70%, transparent); }
/* the flagship hangs in gold here too -- same gilt as the Ether and the Bestiary */
.pf__nft--gold { border-color: color-mix(in srgb, var(--gold) 65%, var(--rule));
                 box-shadow: 0 0 14px -6px color-mix(in srgb, var(--gold) 60%, transparent); }
.pf__nft-void { font-size: .7rem; color: var(--vellum-dim); }
.pf__nft-more { display: grid; place-items: center; aspect-ratio: 1; border: 1px dashed var(--rule);
                color: var(--vellum-dim); font-size: .8rem; }

.pf__charts { display: grid; grid-template-columns: 1fr; gap: 1.4rem; margin-top: 2.4rem; }
@media (min-width: 860px) { .pf__charts { grid-template-columns: 1fr 1fr; } }
.pf__chartbox { border: 1px solid var(--rule); background: var(--ink-2); padding: 1rem 1.1rem 1.2rem; }
.pf__chartbox h3 { border-bottom: 0; margin-bottom: .4rem; }
.pf__pie { width: 100%; height: 240px; display: block; }
.pf__chartbox--wide { margin-top: 2.4rem; }
.pf__equity { width: 100%; height: 220px; display: block; }
@media (min-width: 860px) { .pf__equity { height: 280px; } }
/* the window slider: whole days only, newest kept, oldest trimmed */
.pf__eqbar { display: flex; align-items: center; gap: 1rem; margin-top: .7rem; }
.pf__eqbar label { font-size: .68rem; letter-spacing: .16em; text-transform: uppercase; color: var(--vellum-dim); white-space: nowrap; }
.pf__eqbar b { color: var(--vellum); font-weight: 600; letter-spacing: .04em; }
.pf__eqbar input[type="range"] { flex: 1; appearance: none; -webkit-appearance: none; height: 2px;
  background: var(--rule); outline-offset: 4px; cursor: var(--cur-point); }
.pf__eqbar input[type="range"]::-webkit-slider-thumb { -webkit-appearance: none; width: 14px; height: 14px;
  background: var(--ink-2); border: 1.5px solid var(--accent); border-radius: 0; transform: rotate(45deg); }
.pf__eqbar input[type="range"]::-moz-range-thumb { width: 12px; height: 12px;
  background: var(--ink-2); border: 1.5px solid var(--accent); border-radius: 0; transform: rotate(45deg); }

.pf__demo { display: inline-block; margin: 1rem 0 0; padding: .3rem .7rem; border: 1px solid var(--royal); color: var(--royal); font-size: .72rem; letter-spacing: .18em; text-transform: uppercase; }
.pf__holdgate { padding: 2.5rem 0 4rem; max-width: 34rem; }
.pf__holdgate .btn { margin-top: 1.4rem; }

.pf__modal { position: fixed; inset: 0; z-index: 90; display: grid; place-items: center; padding: 1.2rem; }
.pf__modal[hidden] { display: none; }
.pf__modal-back { position: absolute; inset: 0; background: rgba(5,5,6,.82); backdrop-filter: blur(6px); }
.pf__modal-sheet { position: relative; display: grid; grid-template-columns: 1fr; gap: 0;
  width: min(920px, 100%); max-height: 92vh; overflow: auto;
  background: var(--ink-2); border: 1px solid var(--rule); }
@media (min-width: 760px) { .pf__modal-sheet { grid-template-columns: minmax(300px, 44%) 1fr; } }
.pf__modal-sheet--gold { border-color: color-mix(in srgb, var(--gold) 60%, var(--rule));
  box-shadow: 0 0 44px -12px color-mix(in srgb, var(--gold) 55%, transparent); }
.pf__modal-x { position: absolute; top: .4rem; right: .6rem; z-index: 2; background: none; border: 0;
  color: var(--vellum-dim); font-size: 1.6rem; cursor: var(--cur-point); }
.pf__modal-x:hover { color: var(--vellum); }
.pf__modal-art img { width: 100%; display: block; }
.pf__modal-meta { padding: 1.4rem 1.5rem 1.6rem; }
.pf__modal-coll { font-size: .74rem; letter-spacing: .18em; text-transform: uppercase; color: var(--vellum-dim); margin-bottom: .3rem; }
.pf__modal-sheet--gold .pf__modal-coll { color: var(--gold); }
.pf__modal-name { font-family: var(--body); font-weight: 600; font-size: 1.5rem; margin: 0 0 1rem; }
.pf__modal-stats { display: grid; grid-template-columns: repeat(auto-fit, minmax(7.5rem, 1fr)); gap: .7rem; margin: 0 0 1.2rem; }
.pf__modal-stats div { border: 1px solid var(--rule); padding: .55rem .7rem; }
.pf__modal-stats dt { font-size: .66rem; letter-spacing: .16em; text-transform: uppercase; color: var(--vellum-dim); }
.pf__modal-stats dd { margin: .2rem 0 0; font-variant-numeric: tabular-nums; font-weight: 600; }
.pf__modal-traits h4 { font-size: .74rem; letter-spacing: .18em; text-transform: uppercase; color: var(--vellum-dim); margin: 0 0 .5rem; }
.pf__traitwrap { display: flex; flex-wrap: wrap; gap: .45rem; }
.pf__trait { border: 1px solid var(--rule); padding: .35rem .6rem; font-size: .8rem; }
.pf__trait i { font-style: normal; color: var(--vellum-dim); margin-right: .45rem; font-size: .68rem; text-transform: uppercase; letter-spacing: .12em; }
.pf__modal-links { margin-top: 1.3rem; font-size: .92rem; }
.pf__modal-links a { color: var(--accent); text-decoration: none; }
.pf__modal-links a:hover { text-decoration: underline; }

/* ---------- the world band ---------------------------------------------- */
/* One wide painting of the country the souls come from, sat between the wordmark and
   the mint. Full bleed, because a landscape boxed inside the page grid stops reading as
   a landscape. It drifts as it passes: the picture is held slightly larger than its
   frame and slid across that slack, so scrolling down carries the eye off the green
   and into the blight. The scale is the slack, and script.js moves within it. */
.world {
  position: relative;
  height: clamp(300px, 52vh, 560px);
  margin: clamp(2rem, 6vw, 4.5rem) 0;
  overflow: hidden;
  background: var(--ink);
}
.world__img {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  object-fit: cover;
  /* The resting offset, matching what script.js computes for a band still below the
     fold. Left at a bare scale() the picture sits centred until the first scroll and
     then jumps. Reduced motion keeps exactly this frame. */
  transform: scale(1.16) translateX(-6%);
}
/* Fades into the page's black at both edges, so the band sits in the dark rather than
   cutting a hard-edged rectangle out of it. */
.world::after {
  content: ""; position: absolute; inset: 0; pointer-events: none;
  background: linear-gradient(to bottom,
    var(--ink) 0%, rgba(10, 10, 11, 0) 17%,
    rgba(10, 10, 11, 0) 83%, var(--ink) 100%);
}

/* ---------- mint ------------------------------------------------------- */
.mint { padding: clamp(3rem, 7vw, 5.5rem) 0; border-top: 1px solid var(--rule); }
.mint__grid { display: grid; grid-template-columns: 1fr; gap: clamp(2rem, 4vw, 3.5rem); }
@media (min-width: 860px) { .mint__grid { grid-template-columns: 1.05fr 1fr; } }

.panel { border: 1px solid var(--rule); background: linear-gradient(180deg, var(--ink-2), #0d0d0e); padding: clamp(1.5rem, 3vw, 2.4rem); }
.qty { display: flex; align-items: stretch; gap: 0; border: 1px solid var(--rule); width: fit-content; margin: 0 0 1.4rem; }
.qty button {
  width: 3.1rem; background: transparent; border: 0; color: var(--vellum);
  font-family: var(--display); font-size: 1.6rem; cursor: var(--cur-point); transition: background .2s, color .2s;
}
.qty button:hover:not(:disabled) { background: var(--ink-3); color: var(--accent); }
.qty button:disabled { opacity: .3; cursor: not-allowed; }
.qty input {
  width: 4.6rem; text-align: center; background: transparent; color: var(--vellum);
  font-family: var(--display); font-size: 1.9rem; border: 0;
  border-left: 1px solid var(--rule); border-right: 1px solid var(--rule); border-radius: 0;
  font-variant-numeric: tabular-nums; -moz-appearance: textfield; appearance: textfield;
}
.qty input::-webkit-inner-spin-button, .qty input::-webkit-outer-spin-button { -webkit-appearance: none; margin: 0; }
.qty input:focus { outline: 2px solid var(--accent); outline-offset: -2px; }
.tally { list-style: none; margin: 0 0 1.6rem; padding: 0; font-size: .95rem; }
.tally li { display: flex; justify-content: space-between; gap: 1rem; padding: .5rem 0; border-bottom: 1px dashed var(--rule); }
.tally li:last-child { border-bottom: 0; padding-top: .9rem; font-size: 1.05rem; }
.tally .free { color: var(--accent); }
.tally b { font-family: var(--display); font-weight: 400; font-size: 1.35rem; font-variant-numeric: tabular-nums; }

.btn {
  display: block; width: 100%; padding: 1rem 1.4rem; cursor: var(--cur-point);
  font-family: var(--display); font-size: 1.35rem; letter-spacing: .06em;
  color: var(--ink); background: var(--vellum); border: 1px solid var(--vellum);
  transition: background .2s, color .2s, transform .12s;
}
.btn:hover { background: var(--accent); border-color: var(--accent); color: #fff; }
.btn:active { transform: translateY(1px); }
.status { margin: .9rem 0 0; font-size: .84rem; color: var(--vellum-dim); min-height: 1.4em; }
.status:empty { margin: 0; min-height: 0; }   /* nothing to say, so no gap */
.status.warn { color: var(--accent); }


/* ---------- hand-drawn claim ------------------------------------------ */
.claim { padding: clamp(3rem, 7vw, 5rem) 0; border-top: 1px solid var(--rule); }
.claim__grid { display: grid; gap: 2rem; grid-template-columns: 1fr; }
@media (min-width: 800px) { .claim__grid { grid-template-columns: 1fr 1fr; gap: clamp(2rem,5vw,4rem); } }
.claim h2 em { font-style: normal; color: var(--accent); }

/* ---------- gallery: reliquary frames ---------------------------------- */
.gallery { padding: clamp(3rem, 7vw, 5rem) 0 clamp(4rem, 8vw, 6rem); border-top: 1px solid var(--rule); }
.grid {
  display: grid; gap: clamp(1.1rem, 2.2vw, 1.9rem);
  grid-template-columns: repeat(auto-fill, minmax(148px, 1fr));
}
@media (min-width: 700px) { .grid { grid-template-columns: repeat(auto-fill, minmax(208px, 1fr)); } }
@media (max-width: 699px) { .corner { width: 24px; height: 24px; } }

.tile, .plate { --pal: var(--vellum-dim); }
.tile { position: relative; cursor: var(--cur-point); transition: opacity .35s, filter .35s; }

/* the frame: a dark mat, a hairline in the piece's own palette, and a bloom behind */
.tile__frame {
  position: relative; padding: 7px;
  background: linear-gradient(160deg, #161617, #0d0d0e 60%);
  border: 1px solid var(--rule);
  box-shadow: 0 0 0 1px rgba(0,0,0,.6), 0 14px 30px -18px #000;
  transition: border-color .35s, box-shadow .45s, transform .45s cubic-bezier(.2,.7,.3,1);
}
.tile__frame::before {                       /* inner hairline, tinted per palette */
  content: ''; position: absolute; inset: 3px; pointer-events: none;
  border: 1px solid color-mix(in srgb, var(--pal) 38%, transparent);
  transition: border-color .35s;
}
.tile__win { position: relative; aspect-ratio: 1; overflow: hidden; background: #060607; }
.tile__win img {
  width: 100%; height: 100%; object-fit: cover;
  filter: saturate(.92) contrast(1.02);
  transition: transform .7s cubic-bezier(.2,.7,.3,1), filter .45s;
}
.tile__win::after {                          /* vignette, so every piece sits in shadow */
  content: ''; position: absolute; inset: 0; pointer-events: none;
  box-shadow: inset 0 0 46px 6px rgba(0,0,0,.66);
  transition: box-shadow .45s;
}

/* four thorn brackets, one symbol rotated into each corner */
.corner { position: absolute; width: 34px; height: 34px; color: var(--pal); opacity: .95;
          filter: drop-shadow(0 0 3px rgba(0,0,0,.9));
          transition: opacity .35s, transform .45s cubic-bezier(.2,.7,.3,1), filter .45s; pointer-events: none; }
.corner--tl { top: -5px; left: -5px; }
.corner--tr { top: -5px; right: -5px; transform: scaleX(-1); }
.corner--bl { bottom: -5px; left: -5px; transform: scaleY(-1); }
.corner--br { bottom: -5px; right: -5px; transform: scale(-1); }

/* The lift happens on the CARD, not the frame inside it. Transforming the frame while
   each corner grew a fresh drop-shadow filter pushed four overhanging SVGs through a
   compositing boundary every hover, and on some engines they came back unpainted --
   frames visibly losing their corners mid-hover. One transform on the outermost
   element moves everything as a single layer, and the corners' filter never changes:
   the frame's own bloom carries the glow. */
.framed { transition: transform .3s cubic-bezier(.2,.7,.3,1); }
.framed:hover, .framed:focus-visible { transform: translateY(-3px); }
.framed:hover .tile__frame, .framed:focus-visible .tile__frame {
  border-color: color-mix(in srgb, var(--pal) 55%, var(--rule));
  box-shadow: 0 0 0 1px rgba(0,0,0,.6),
              0 0 34px -6px color-mix(in srgb, var(--pal) 60%, transparent),
              0 18px 34px -18px #000;
}
.framed:hover .tile__frame::before { border-color: color-mix(in srgb, var(--pal) 78%, transparent); }
.framed:hover .tile__win img { transform: scale(1.045); filter: saturate(1.08) contrast(1.04); }
.framed:hover .tile__win::after { box-shadow: inset 0 0 40px 2px rgba(0,0,0,.44); }
/* The corners do NOT move, glow, or refilter on hover -- deliberately, after three
   rounds of "the borders disappear". They overhang the frame, and any per-corner
   transform or filter swap pushes four overhanging SVGs through a fresh composite on
   engines this machine cannot test; whenever that goes wrong they vanish mid-hover.
   The rest state renders correctly everywhere, so hover keeps the corners bit-for-bit
   identical to rest and only the card itself lifts. Nothing identical can break. */
.framed:hover .corner { opacity: .95; }

/* museum label, always on */
.tile__plate { padding: .7rem .1rem 0; }
.tile__no {
  font-family: var(--body); font-size: .62rem; letter-spacing: .24em; text-transform: uppercase;
  color: var(--pal); opacity: .8;
}
.tile__name {
  display: block; font-family: var(--display); font-size: 1.02rem; letter-spacing: .02em;
  line-height: 1.15; margin-top: .12rem;
}
.tile__traits {
  display: block; font-size: .68rem; letter-spacing: .1em; text-transform: uppercase;
  color: var(--vellum-dim); margin-top: .3rem; line-height: 1.5;
}
.tile.dim { opacity: .12; filter: grayscale(1) brightness(.7); pointer-events: none; }

/* ---------- bestiary: gilt frames --------------------------------------- */
.plate .tile__frame {
  padding: 9px;
  background: linear-gradient(160deg, #191510, #0c0b09 62%);
  border: 1px solid #6b5320;
}
.plate .tile__frame::before { border-color: rgba(206,166,74,.5); inset: 4px; }
.plate .gilt {
  position: absolute; width: 42px; height: 42px; color: var(--gold); opacity: .95;
  filter: drop-shadow(0 0 4px rgba(0,0,0,.85));
  transition: opacity .35s, transform .5s cubic-bezier(.2,.7,.3,1), filter .5s;
  pointer-events: none;
}
.plate .gilt--tl { top: -6px; left: -6px; }
.plate .gilt--tr { top: -6px; right: -6px; transform: scaleX(-1); }
.plate .gilt--bl { bottom: -6px; left: -6px; transform: scaleY(-1); }
.plate .gilt--br { bottom: -6px; right: -6px; transform: scale(-1); }
.plate:hover .tile__frame {
  border-color: var(--gold);
  box-shadow: 0 0 0 1px rgba(0,0,0,.6), 0 0 40px -8px rgba(206,166,74,.55), 0 18px 34px -18px #000;
}
.plate:hover .tile__frame::before { border-color: rgba(230,196,110,.9); }
.plate:hover .gilt { filter: drop-shadow(0 0 8px rgba(206,166,74,.75)); }
.plate:hover .gilt--tl { transform: translate(-4px,-4px); }
.plate:hover .gilt--tr { transform: scaleX(-1) translate(-4px,-4px); }
.plate:hover .gilt--bl { transform: scaleY(-1) translate(-4px,-4px); }
.plate:hover .gilt--br { transform: scale(-1) translate(-4px,-4px); }

/* ---------- bestiary plates -------------------------------------------- */
.plates { display: grid; gap: clamp(2.6rem, 5vw, 4.2rem); grid-template-columns: 1fr; padding-bottom: 3rem; }
@media (min-width: 760px)  { .plates { grid-template-columns: 1fr 1fr; } }
@media (min-width: 1080px) { .plates { grid-template-columns: repeat(3, 1fr); } }
.plate { border-top: 1px solid var(--rule); padding-top: 1rem; }
.plate .tile__frame { cursor: var(--cur-point); }
.plate h3 { margin: 1.3rem 0 .6rem; font-size: 1.75rem; color: var(--vellum); }
.plate p { font-size: .96rem; line-height: 1.7; color: #b2afab; margin: 0; }
.plate p::first-letter {
  font-family: var(--display); font-size: 2.5em; line-height: .82;
  float: left; margin: .06em .1em 0 0; color: var(--gold);
}

/* ---------- palette families ------------------------------------------- */
.fams { display: grid; gap: 1px; background: var(--rule); border: 1px solid var(--rule); grid-template-columns: 1fr; }
@media (min-width: 640px) { .fams { grid-template-columns: 1fr 1fr; } }
@media (min-width: 1000px) { .fams { grid-template-columns: repeat(5, 1fr); } }
.fam { background: var(--ink-2); padding: 1.4rem 1.2rem 1.6rem; }
.fam i { display: block; width: 100%; height: 3px; margin-bottom: 1.1rem; }
.fam h3 { font-size: 1.15rem; letter-spacing: .05em; }
.fam p { font-size: .88rem; color: var(--vellum-dim); margin: .4rem 0 0; }

/* ---------- anatomy ---------------------------------------------------- */
.anatomy { list-style: none; margin: 0; padding: 0; counter-reset: l; }
.anatomy li {
  display: grid; grid-template-columns: 2.4rem 1fr; gap: 1.1rem; align-items: baseline;
  padding: .85rem 0; border-bottom: 1px solid var(--rule);
}
.anatomy li::before {
  counter-increment: l; content: counter(l, upper-roman);
  font-family: var(--display); font-size: 1rem; color: var(--accent); letter-spacing: .08em;
}
/* Both of these belong in the second column, beside the numeral. Without the explicit
   placement the description auto-flowed into the next row's FIRST column — 2.4rem wide
   — and wrapped one word per line. */
.anatomy b, .anatomy span { grid-column: 2; }
.anatomy b { font-family: var(--display); font-weight: 400; font-size: 1.2rem; display: block; }
.anatomy span { font-size: .9rem; color: var(--vellum-dim); max-width: 60ch; }

/* Two readable columns of six short rows, rather than one long list down half the page. */
.anatomy__block { max-width: 62rem; }
@media (min-width: 760px) { .anatomy { columns: 2; column-gap: clamp(2rem, 5vw, 3.6rem); }
  .anatomy li { break-inside: avoid; } }

/* ---------- counts ----------------------------------------------------- */
.counts { display: grid; grid-template-columns: repeat(2, 1fr); gap: 1px; background: var(--rule); border: 1px solid var(--rule); }
@media (min-width: 720px) { .counts { grid-template-columns: repeat(5, 1fr); } }
.count { background: var(--ink-2); padding: 1.3rem 1rem; text-align: center; }
.count b { display: block; font-family: var(--display); font-size: 2.4rem; line-height: 1; }
.count span { font-size: .68rem; letter-spacing: .2em; text-transform: uppercase; color: var(--vellum-dim); }

/* ---------- lightbox --------------------------------------------------- */
.lb {
  position: fixed; inset: 0; z-index: 100; display: none;
  place-items: center; padding: var(--gut);
  background: rgba(6, 6, 7, .95); backdrop-filter: blur(6px);
}
.lb[open] { display: grid; }
.lb img { max-width: min(1000px, 92vw); max-height: 82vh; width: auto; border: 1px solid var(--rule); }
.lb figcaption { text-align: center; margin-top: 1rem; font-family: var(--display); font-size: 1.6rem; }
.lb small { display: block; font-family: var(--body); font-size: .72rem; letter-spacing: .22em; text-transform: uppercase; color: var(--vellum-dim); margin-top: .3rem; }
.lb__close { position: absolute; top: 1rem; right: 1.4rem; background: none; border: 0; color: var(--vellum); font-family: var(--display); font-size: 2rem; cursor: var(--cur-point); line-height: 1; }

/* ---------- footer ----------------------------------------------------- */
.foot { border-top: 1px solid var(--rule); padding: 2.4rem 0 3rem; font-size: .78rem; color: var(--vellum-dim); }
.foot__row { display: flex; flex-wrap: wrap; gap: 1rem 2rem; justify-content: space-between; align-items: center; }

/* ---------- reveal ----------------------------------------------------- */
.rise { opacity: 0; transform: translateY(18px); transition: opacity .7s ease, transform .7s cubic-bezier(.2,.7,.3,1); }
.rise.in { opacity: 1; transform: none; }

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { animation-duration: .001ms !important; transition-duration: .001ms !important; }
  .rise { opacity: 1; transform: none; }
  .stack__frame img { opacity: 1; filter: none; }
}

/* ---------- The Ether ---------------------------------------------------- */
.ether { padding-bottom: clamp(3rem, 7vw, 5rem); }
.ether__notice {
  margin-top: 1.2rem; padding: .7rem .9rem; font-size: .86rem; color: var(--vellum-dim);
  border-left: 2px solid var(--accent); background: var(--ink-2); max-width: 62ch;
}
.ether__bar {
  display: flex; flex-wrap: wrap; gap: .8rem 1.2rem; align-items: center;
  padding: 1rem 0; border-block: 1px solid var(--rule); margin-bottom: 1.8rem;
}
.ether__toggle, .ether__clear {
  background: none; border: 1px solid var(--rule); color: var(--vellum);
  font-family: var(--body); font-size: .72rem; letter-spacing: .18em; text-transform: uppercase;
  padding: .45rem .9rem; cursor: var(--cur-point); transition: border-color .2s, color .2s;
}
.ether__toggle:hover, .ether__clear:hover { border-color: var(--accent); color: var(--accent); }
.ether__count { font-size: .78rem; letter-spacing: .16em; text-transform: uppercase; color: var(--vellum-dim); }
.ether__sort { margin-left: auto; font-size: .72rem; letter-spacing: .16em; text-transform: uppercase; color: var(--vellum-dim); }
.ether__sort select {
  margin-left: .5rem; background: var(--ink-2); color: var(--vellum); border: 1px solid var(--rule);
  font-family: var(--body); font-size: .8rem; padding: .35rem .5rem; letter-spacing: normal; text-transform: none;
}
.ether__grid { display: grid; grid-template-columns: 1fr; gap: 2rem; }
@media (min-width: 900px) {
  .ether__grid { grid-template-columns: 258px 1fr; gap: 2.2rem; align-items: start; }
  /* A hidden aside is display:none, so it stops holding its grid cell and the items
     drop into the 232px filter track — the whole gallery collapsed to one narrow
     column whenever the Filters button was used on desktop. Give the row a single
     column for as long as the panel is shut. */
  .ether__grid:has(> .filters-panel[hidden]) { grid-template-columns: 1fr; }
}
.ether__empty { color: var(--vellum-dim); padding: 3rem 0; }

/* The filter panel holds 163 options across six groups. It scrolls ONCE: the search
   box stays put above, the chips pin below, the list of groups scrolls between them.
   Each group used to scroll inside a scrolling panel, which put two scrollbars a few
   pixels apart and made it luck which one you caught. The chips live BELOW the list
   because they come and go as boxes are ticked: above it, every appearance shoved the
   whole list down under the cursor mid-click, which is the jumpiness the owner
   reported. A footer grows downward and moves nothing. */
.filters-panel { border: 1px solid var(--rule); background: var(--ink-2); }
@media (min-width: 900px) {
  .filters-panel {
    position: sticky; top: 76px; max-height: 80vh;
    display: flex; flex-direction: column; overflow: hidden;   /* children scroll, not this */
  }
}
.fgroups { overflow-y: auto; overscroll-behavior: contain; flex: 1; }

/* one quiet scrollbar, in the site's own colours */
.fgroups { scrollbar-width: thin; scrollbar-color: #3a3a40 transparent; }
.fgroups::-webkit-scrollbar { width: 9px; }
.fgroups::-webkit-scrollbar-track { background: transparent; }
.fgroups::-webkit-scrollbar-thumb { background: #33333a; border: 3px solid var(--ink-2); border-radius: 99px; }
.fgroups::-webkit-scrollbar-thumb:hover { background: #4a4a52; }

/* ---- search ---- */
.fsearch { position: relative; padding: .7rem .7rem .6rem; border-bottom: 1px solid var(--rule); flex: none; }
.fsearch input {
  width: 100%; padding: .5rem .6rem; padding-right: 1.8rem;
  background: var(--ink); border: 1px solid var(--rule); color: var(--vellum);
  font-family: var(--body); font-size: .84rem;
}
.fsearch input::placeholder { color: #5c5c60; }
.fsearch input:focus { outline: none; border-color: color-mix(in srgb, var(--accent) 55%, var(--rule)); }
.fsearch input::-webkit-search-cancel-button { display: none; }
.fsearch button {
  position: absolute; right: 1.05rem; top: 50%; transform: translateY(-40%);
  background: none; border: 0; color: var(--vellum-dim); font-size: 1.1rem;
  line-height: 1; cursor: var(--cur-point); padding: 0 .2rem;
}
.fsearch button:hover { color: var(--accent); }

/* ---- active selections ---- */
.fchips { display: flex; flex-wrap: wrap; gap: .3rem; padding: .6rem .7rem;
          border-top: 1px solid var(--rule); flex: none; }
.fchip {
  display: inline-flex; align-items: baseline; gap: .35rem;
  background: var(--ink); border: 1px solid color-mix(in srgb, var(--accent) 40%, var(--rule));
  color: var(--vellum); font-family: var(--body); font-size: .72rem;
  padding: .25rem .45rem; cursor: var(--cur-point); max-width: 100%;
  transition: border-color .2s, color .2s;
}
.fchip i { font-style: normal; font-size: .56rem; letter-spacing: .14em;
           text-transform: uppercase; color: #6d6d74; flex: none; }
.fchip span { color: var(--accent); font-size: .85rem; }
.fchip:hover { border-color: var(--accent); color: var(--accent); }

/* ---- groups ---- */
.fgroup { border-bottom: 1px solid var(--rule); }
.fgroup:last-child { border-bottom: 0; }
.fgroup summary {
  display: flex; align-items: center; justify-content: space-between; gap: .5rem;
  cursor: var(--cur-point); padding: .7rem .8rem; font-size: .7rem; letter-spacing: .16em;
  text-transform: uppercase; color: var(--vellum); list-style: none;
  position: sticky; top: 0; z-index: 2;          /* know which group you are inside */
  background: var(--ink-3);
}
.fgroup summary::-webkit-details-marker { display: none; }
.fgroup summary::after { content: '+'; color: var(--vellum-dim); flex: none; }
.fgroup[open] summary::after { content: '–'; }
.fgroup summary:hover { color: var(--accent); }
.fgroup__name[data-hits]:not([data-hits=""])::after {
  content: ' (' attr(data-hits) ')'; color: var(--vellum-dim); letter-spacing: 0;
}
.fgroup__n:not(:empty) {
  color: #fff; background: var(--accent); border-radius: 99px;
  font-size: .6rem; padding: .05rem .38rem; margin-left: .35rem; letter-spacing: .04em;
}
.fgroup__body { padding: .3rem .8rem .7rem; }    /* no inner scroll, deliberately */
/* The sections open instantly, on purpose. An animated details height was tried and
   cut: the scroll-settle in ether.js runs at toggle time, when an animating body is
   still at zero height, so the scroll clamped against the un-expanded list and low
   groups landed adrift. The settle is what makes an opening feel deliberate; it needs
   the true height the moment it runs. */
.fgroup__none { margin: 0; padding: .1rem .8rem .8rem; font-size: .76rem;
                color: var(--vellum-dim); font-style: italic; }

/* ---- options ---- */
.fopt {
  display: grid; grid-template-columns: auto 1fr auto; gap: .5rem;
  align-items: center; padding: .3rem 0; cursor: var(--cur-point); font-size: .82rem;
  line-height: 1.35;
}
.fopt[hidden] { display: none; }
.fopt:hover .fopt__v { color: var(--accent); }
.fopt input { accent-color: var(--accent); margin: 0; flex: none; }
.fopt__v { overflow-wrap: anywhere; }            /* long names wrap, never clip */
.fopt__c {
  color: var(--vellum-dim); font-size: .7rem; font-variant-numeric: tabular-nums;
  white-space: nowrap; align-self: center;
}
.fopt:has(input:checked) .fopt__v { color: var(--accent); }

.ether__items { display: grid; gap: clamp(1.1rem, 2vw, 1.8rem); grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); }
@media (min-width: 700px) { .ether__items { grid-template-columns: repeat(auto-fill, minmax(210px, 1fr)); } }
.soul { --pal: var(--accent-frame); cursor: var(--cur-point); }
/* [OWNER 2026-08-24] The twenty one-of-ones hang in gold, in the grid and in the big
   viewer both -- the same gilt the Bestiary gives them. A touch of standing bloom so
   they read as the treasures of the wall even before the pointer finds them. */
.soul[data-pal="Legendary"], .viewer__art[data-pal="Legendary"] { --pal: var(--gold); }
.soul[data-pal="Legendary"] .tile__frame {
  border-color: color-mix(in srgb, var(--gold) 62%, var(--rule));
  box-shadow: 0 0 0 1px rgba(0,0,0,.6),
              0 0 26px -10px color-mix(in srgb, var(--gold) 55%, transparent);
}
.soul__plate { padding: .65rem .1rem 0; }
.soul__no { font-family: var(--display); font-size: 1rem; letter-spacing: .04em; }
.soul__rank { float: right; font-size: .66rem; letter-spacing: .16em; text-transform: uppercase; color: var(--accent); }
/* ranks 1-20 are the one-of-ones: their label is gilt like their frame */
.soul[data-pal="Legendary"] .soul__rank { color: var(--gold); }
.soul__traits { list-style: none; margin: .55rem 0 0; padding: 0; }
.soul__traits li {
  display: flex; justify-content: space-between; align-items: baseline;
  gap: .7rem; padding: .2rem 0;
}
.soul__traits span {
  font-size: .6rem; letter-spacing: .14em; text-transform: uppercase; color: #6d6d73;
  flex: none;
}
.soul__traits b { font-weight: 400; font-size: .88rem; color: var(--vellum-dim); text-align: right; }

/* A piece whose render has not been uploaded yet. Instead of the browser's broken
   image icon, the frame keeps an engraved plate: the catalogue number stays legible
   and the traits below it still read, so the page is presentable while the art is
   still going up. Deliberately monochrome — on this site colour means "which
   palette", and a pending piece is not making a claim about its palette. */
.tile__void { display: none; }
.tile__win.is-void img { display: none; }
.tile__win.is-void .tile__void {
  position: absolute; inset: 0;
  display: grid; place-content: center; justify-items: center; gap: .55rem;
  background:
    repeating-linear-gradient(135deg, transparent 0 7px, rgba(242,240,237,.015) 7px 14px),
    linear-gradient(160deg, #101012, #060607 70%);
}
.tile__void b {
  font-family: var(--display); font-weight: 400; font-size: clamp(1.5rem, 4.5vw, 2.2rem);
  line-height: 1; letter-spacing: .06em; color: #5e5e68;
}
.tile__void i {
  font-style: normal; font-size: .56rem; letter-spacing: .26em; text-transform: uppercase;
  color: #95443f;
}
.soul--pending { cursor: var(--cur-arrow); }

.pager { display: flex; flex-wrap: wrap; gap: .35rem; justify-content: center; margin-top: 2.6rem; }
.pager button {
  background: none; border: 1px solid var(--rule); color: var(--vellum-dim);
  font-family: var(--body); font-size: .8rem; padding: .45rem .8rem; cursor: var(--cur-point);
  font-variant-numeric: tabular-nums; transition: color .2s, border-color .2s, background .2s;
}
.pager button:hover:not(:disabled) { color: var(--vellum); border-color: #4a4a4f; }
.pager button[aria-current] { background: var(--accent); border-color: var(--accent); color: #fff; }
.pager button:disabled { opacity: .3; cursor: not-allowed; }
.pager__gap { color: var(--vellum-dim); padding: .45rem .2rem; }

.traitdb { display: grid; gap: 1px; background: var(--rule); border: 1px solid var(--rule); grid-template-columns: 1fr; }
@media (min-width: 620px) { .traitdb { grid-template-columns: 1fr 1fr; } }
@media (min-width: 1000px) { .traitdb { grid-template-columns: repeat(3, 1fr); } }
.traitdb__col { background: var(--ink-2); padding: 1.2rem 1.1rem 1.4rem; }
.traitdb__col h3 { font-size: 1.1rem; margin-bottom: .7rem; }
.traitdb__col ul { list-style: none; margin: 0; padding: 0; font-size: .82rem; }
.traitdb__col li { display: grid; grid-template-columns: 1fr auto auto; gap: .7rem; padding: .24rem 0; border-bottom: 1px dashed var(--rule); }
.traitdb__col li:last-child { border-bottom: 0; }
.traitdb__col b { font-weight: 400; font-variant-numeric: tabular-nums; }
.traitdb__col i { font-style: normal; color: var(--vellum-dim); font-size: .74rem; font-variant-numeric: tabular-nums; min-width: 4.2em; text-align: right; }

/* ---------- pay-with toggle -------------------------------------------- */
.paywith { display: flex; gap: .45rem; }
.paywith button {
  flex: 1; background: none; cursor: var(--cur-point); padding: .5rem .9rem;
  border: 1px solid var(--rule); color: var(--vellum-dim);
  font-family: var(--body); font-size: .72rem; letter-spacing: .18em; text-transform: uppercase;
  transition: color .2s, border-color .2s, background .2s;
}
.paywith button:hover { color: var(--vellum); border-color: #4a4a4f; }
.paywith button[aria-pressed] { color: #fff; background: var(--accent); border-color: var(--accent); }
.qpick { display: flex; gap: .5rem; margin: -.7rem 0 .9rem; width: fit-content; }
.qpick button {
  min-width: 3.1rem; padding: .35rem .6rem; background: transparent; border: 1px solid var(--rule);
  color: var(--vellum-dim); font-family: var(--display); font-size: 1.05rem; cursor: var(--cur-point);
  transition: color .2s, border-color .2s;
}
.qpick button:hover { color: var(--vellum); border-color: #4a4a4f; }
.qpick button[aria-pressed] { color: var(--accent); border-color: var(--accent); }
.panel__more { margin: 0 0 1.3rem; font-size: .9rem; color: var(--vellum-dim); }
.wallets { display: flex; flex-wrap: wrap; gap: .6rem; margin: .9rem 0 0; }
.wallets__lead { width: 100%; margin: 0 0 .1rem; font-size: .85rem; color: var(--vellum-dim); }
.wallets__one {
  display: inline-flex; align-items: center; gap: .5rem; padding: .5rem .9rem; background: transparent;
  border: 1px solid var(--rule); color: var(--vellum); font-family: var(--body); font-size: .9rem;
  cursor: var(--cur-point); transition: color .2s, border-color .2s;
}
.wallets__one:hover { border-color: var(--accent); color: var(--accent); }
.wallets__one img { width: 22px; height: 22px; border-radius: 4px; }

/* ---------- the summoning ----------------------------------------------
   The two seconds between paying and knowing. Same frame component as everywhere
   else — .tile__frame and the thorn corners — so a piece is revealed inside the
   object it will live in. Red only: on this site colour means "which palette", and
   catalogue.json carries no palette field, so tinting one here would be inventing it. */
.summon {
  position: fixed; inset: 0; z-index: 120;
  /* An explicit minmax(0,1fr) track rather than place-items:center. Centring alone lets
     the child size to its own content, so a three-up reveal grew wider than the phone
     and the last plate was silently clipped off the edge. */
  display: grid; grid-template-columns: minmax(0, 1fr);
  /* "safe" matters once a twenty-piece buy is on screen: plain centring pushes the top
     of a tall stage above the scroll origin, and the first row becomes unreachable. */
  align-items: safe center; justify-items: center;
  padding: var(--gut); overflow-y: auto;
  /* Near-opaque on purpose: the page behind is a wall of display type, and even a few
     percent of it showing through fights the piece being revealed. */
  background: rgba(6, 6, 7, .992); backdrop-filter: blur(14px);
}
.summon[hidden] { display: none; }
.summon__inner { width: 100%; max-width: 1100px; text-align: center; }
.summon__state { color: var(--accent); margin-bottom: 1.6rem; min-height: 1.2em; }

.summon__stage { display: grid; justify-content: center; gap: clamp(1rem, 2.5vw, 1.8rem); }
.summon__stage--many {
  /* fluid rather than capped, so three pieces on a phone sit two-up instead of stacking
     into one narrow column with the screen half empty */
  grid-template-columns: repeat(auto-fit, minmax(148px, 1fr));
  max-width: 1000px; margin-inline: auto; width: 100%;
}
.summon__plate { width: min(78vw, 420px); margin: 0 auto; }
.summon__stage--many .summon__plate { width: 100%; }
.summon__plate { --pal: var(--accent-frame); }

.summon__plate .tile__win img { position: absolute; inset: 0; opacity: 0; }
.summon__plate .summon__search { opacity: .5; filter: grayscale(.7) contrast(1.1); }
.summon__plate.is-searching .summon__search { opacity: .5; }
.summon__plate.is-resolving .summon__search,
.summon__plate.is-revealed  .summon__search { opacity: 0; }
.summon__plate.has-art .summon__art { opacity: 1; transition: opacity .55s ease; }

/* searching: the frame breathes and the corners strain outward */
@keyframes summon-pulse {
  0%, 100% { box-shadow: 0 0 0 1px rgba(0,0,0,.6), 0 0 18px -8px var(--accent); }
  50%      { box-shadow: 0 0 0 1px rgba(0,0,0,.6), 0 0 42px -4px var(--accent); }
}
.summon__plate.is-searching .tile__frame {
  border-color: color-mix(in srgb, var(--accent) 45%, var(--rule));
  animation: summon-pulse 1.05s ease-in-out infinite;
}
.summon__plate.is-searching .corner { opacity: .75; }

/* revealed: the frame settles, lit */
.summon__plate.is-revealed .tile__frame {
  border-color: color-mix(in srgb, var(--accent) 60%, var(--rule));
  box-shadow: 0 0 0 1px rgba(0,0,0,.6), 0 0 46px -10px var(--accent), 0 18px 34px -18px #000;
}
.summon__plate.is-revealed .corner { opacity: 1; }
.summon__plate.is-failed .tile__frame { border-color: var(--rule); animation: none; }
.summon__plate.is-failed .corner { opacity: .3; }

/* the pending plate, for pieces whose render is not uploaded yet */
.summon__void { display: none; }
.summon__plate.no-art .summon__void {
  position: absolute; inset: 0;
  display: grid; place-content: center; justify-items: center; gap: .55rem;
  background:
    repeating-linear-gradient(135deg, transparent 0 7px, rgba(242,240,237,.015) 7px 14px),
    linear-gradient(160deg, #101012, #060607 70%);
}
.summon__void b {
  font-family: var(--display); font-weight: 400; font-size: clamp(1.6rem, 5vw, 2.4rem);
  line-height: 1; letter-spacing: .06em; color: #5e5e68;
}
.summon__void i {
  font-style: normal; font-size: .56rem; letter-spacing: .26em; text-transform: uppercase;
  color: #95443f;
}

/* the label, written on after the art lands */
.summon__plate-label { padding: .9rem .2rem 0; text-align: left; opacity: 0; transform: translateY(8px); }
.summon__plate.is-revealed .summon__plate-label {
  opacity: 1; transform: none; transition: opacity .5s ease, transform .5s cubic-bezier(.2,.7,.3,1);
}
.summon__no { font-family: var(--display); font-size: 1.35rem; letter-spacing: .04em; }
.summon__rank {
  display: block; margin-top: .15rem;
  font-size: .66rem; letter-spacing: .2em; text-transform: uppercase; color: var(--accent);
  opacity: 0; transform: translateY(4px);
}
.summon__rank.in { opacity: 1; transform: none; transition: opacity .45s ease, transform .45s ease; }
/* These were set at 11px, which is fine for a caption nobody has to read and wrong for
   the one list a buyer actually wants after a reveal. The value is the thing being read,
   so it gets the size and the brighter ink; the slot label stays quiet beside it. */
.summon__traits { list-style: none; margin: .7rem 0 0; padding: 0; }
.summon__traits li {
  display: flex; justify-content: space-between; align-items: baseline;
  gap: .8rem; padding: .26rem 0;
}
.summon__traits span {
  font-size: .64rem; letter-spacing: .14em; text-transform: uppercase; color: #6d6d73;
  flex: none;
}
.summon__traits b {
  font-weight: 400; font-size: .95rem; color: var(--vellum); text-align: right;
}

.summon__note {
  margin: 1.8rem auto 0; max-width: 54ch; font-size: .82rem; color: var(--vellum-dim);
  font-style: italic; line-height: 1.6;
}
.summon__close {
  margin-top: 1.4rem; background: none; border: 1px solid var(--rule); color: var(--vellum);
  font-family: var(--body); font-size: .72rem; letter-spacing: .18em; text-transform: uppercase;
  padding: .55rem 1.4rem; cursor: var(--cur-point); transition: border-color .2s, color .2s;
}
.summon__close:hover { border-color: var(--accent); color: var(--accent); }
.summon__more {
  margin-top: 1.4rem; background: var(--accent); color: #000; border: 1px solid var(--accent);
  font-family: var(--body); font-size: .72rem; letter-spacing: .18em; text-transform: uppercase;
  padding: .55rem 1.4rem; cursor: var(--cur-point); transition: background .2s;
}
.summon__more:hover { background: var(--accent-hot, #2be34a); }

@media (prefers-reduced-motion: reduce) {
  .summon__plate.is-searching .tile__frame { animation: none; }
}

/* ---------- The Ether: desktop only ------------------------------------
   [OWNER] The grid does not open on a phone. This is what shows instead — the
   collection's own frame language rather than a browser error, so it reads as a
   decision rather than a fault. */
.ether__locked {
  max-width: 46ch; margin: clamp(2rem, 10vw, 5rem) auto; text-align: center;
  padding: clamp(1.5rem, 6vw, 2.5rem);
  border: 1px solid var(--rule); background: var(--ink-2);
}
.ether__locked-mark {
  width: 34px; height: 34px; color: var(--accent); opacity: .9;
  margin-bottom: 1.4rem;
}
.ether__locked h2 { font-size: clamp(1.8rem, 7vw, 2.4rem); margin-bottom: 1rem; }
.ether__locked p { font-size: .95rem; color: var(--vellum-dim); line-height: 1.7; }
.ether__locked-cta {
  margin-top: 1.3rem; color: var(--vellum) !important;
  font-size: .74rem !important; letter-spacing: .18em; text-transform: uppercase;
}
.ether__locked-cta b { font-weight: 400; color: var(--accent); }
.ether__locked-alt {
  margin-top: 1.6rem; padding-top: 1.2rem; border-top: 1px solid var(--rule);
  font-size: .84rem !important;
}
.ether__locked-alt a { color: var(--accent); text-underline-offset: .25em; }

/* Offscreen rows are skipped during layout and paint. With 25 framed tiles, each
   carrying four SVG corners and a vignette, this is the difference between a smooth
   scroll and a stuttering one. */
.ether__items .soul {
  content-visibility: auto;
  contain-intrinsic-size: auto 320px;
}

/* ---------- hero: the class border --------------------------------------
   The soul in the hero wears its palette family as a lit border. Same five colours
   the gallery frames use, because on this site colour means "which class" — so the
   hero states what it is without a caption. */
.stack__frame {
  --pal: var(--bone);
  border-color: color-mix(in srgb, var(--pal) 45%, var(--rule));
  box-shadow: 0 0 0 1px rgba(0,0,0,.55),
              0 0 34px -14px color-mix(in srgb, var(--pal) 70%, transparent);
  transition: border-color .6s ease, box-shadow .6s ease;
}
.stack__frame[data-pal="Corruption"] { --pal: var(--corruption); }
.stack__frame[data-pal="Necrotic"]   { --pal: var(--necrotic); }
.stack__frame[data-pal="Infernal"]   { --pal: var(--infernal); }
.stack__frame[data-pal="Royal"]      { --pal: var(--royal); }
.stack__frame[data-pal="Neutral"]    { --pal: var(--bone); }

/* a slow breath, so the frame reads as alive rather than lit */
@keyframes stack-breathe {
  0%, 100% { box-shadow: 0 0 0 1px rgba(0,0,0,.55),
                         0 0 30px -16px color-mix(in srgb, var(--pal) 70%, transparent); }
  50%      { box-shadow: 0 0 0 1px rgba(0,0,0,.55),
                         0 0 54px -8px color-mix(in srgb, var(--pal) 85%, transparent); }
}
.stack__frame { animation: stack-breathe 4.5s ease-in-out infinite; }

/* one bright pass around the edge each time a new soul is dealt */
.stack__frame::after {
  content: ''; position: absolute; inset: -1px; pointer-events: none; z-index: 3;
  padding: 1px; border-radius: inherit; opacity: 0;
  background: conic-gradient(from var(--sweep, 0deg),
              transparent 0deg, transparent 300deg,
              color-mix(in srgb, var(--pal) 95%, white) 340deg,
              transparent 360deg);
  -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  -webkit-mask-composite: xor; mask-composite: exclude;
}
@property --sweep { syntax: '<angle>'; inherits: false; initial-value: 0deg; }
@keyframes stack-sweep {
  0%   { opacity: 0; --sweep: 0deg; }
  12%  { opacity: 1; }
  85%  { opacity: 1; }
  100% { opacity: 0; --sweep: 360deg; }
}
.stack__frame.is-turning::after { animation: stack-sweep 1.5s ease-out 1; }

/* the class name, quietly, in the corner of the frame */
.stack__class {
  position: absolute; z-index: 4; left: .7rem; bottom: .6rem;
  font-family: var(--body); font-size: .58rem; letter-spacing: .26em;
  text-transform: uppercase; color: var(--pal);
  text-shadow: 0 0 8px rgba(0,0,0,.95), 0 0 3px rgba(0,0,0,1);
  transition: color .6s ease;
}

/* ---------- hero: the instruction -------------------------------------
   Moved above the trait rail and given weight — it was a grey italic afterthought
   under a list, and nobody read it, so nobody knew the hero was interactive. */
.stack__caption {
  display: flex; align-items: baseline; gap: .5rem;
  margin: 1rem 0 .6rem; padding: .6rem .75rem;
  border-left: 2px solid var(--accent);
  background: linear-gradient(90deg, color-mix(in srgb, var(--accent) 12%, transparent), transparent 70%);
  font-family: var(--body); font-style: normal;
  font-size: .8rem; font-weight: 600; letter-spacing: .04em; line-height: 1.45;
  color: var(--vellum);
}
.stack__caption-mark { color: var(--accent); font-size: .6rem; flex: none; line-height: 1.6; }

@media (prefers-reduced-motion: reduce) {
  .stack__frame { animation: none; }
  .stack__frame.is-turning::after { animation: none; opacity: 0; }
}

/* ---------- the reveal reacts ------------------------------------------
   Every summoning used to look identical whether the piece drawn was rank 6,600 or
   rank 3, which threw away the only moment in a mint anybody remembers. Rank now
   drives the frame: a common piece resolves and gets out of the way, a Cursed Entity
   is allowed to hold the screen and burn.                                        */

.summon__plate[data-pal="Corruption"] { --pal: var(--corruption); }
.summon__plate[data-pal="Necrotic"]   { --pal: var(--necrotic); }
.summon__plate[data-pal="Infernal"]   { --pal: var(--infernal); }
.summon__plate[data-pal="Royal"]      { --pal: var(--royal); }
.summon__plate[data-pal="Neutral"]    { --pal: var(--bone); }
.summon__plate[data-tier="mythic"],
.summon__plate[data-tier="legendary"] { --pal: var(--gold); }

/* the pill sits on the plate, not under it, so the frame reads as marked */
.summon__tier {
  position: absolute; top: 14px; left: 14px; z-index: 3;
  font-size: .58rem; letter-spacing: .26em; text-transform: uppercase;
  padding: .34rem .6rem .3rem;
  color: var(--ink); background: var(--pal);
  opacity: 0; transform: translateY(-6px);
  transition: opacity .5s ease, transform .5s ease;
  pointer-events: none;
}
.summon__tier.in { opacity: 1; transform: none; }
.summon__plate[data-tier="legendary"] .summon__tier { letter-spacing: .2em; }
.summon__tier-word { font-family: var(--display); font-size: 1.15em; font-weight: 400; color: var(--gold); }

/* the strike: one expanding ring, brighter the rarer the draw */
.summon__plate .tile__frame::after {
  content: ''; position: absolute; inset: 0; pointer-events: none;
  border: 2px solid var(--pal); opacity: 0;
}
@keyframes summon-strike {
  0%   { opacity: .9; transform: scale(1); }
  100% { opacity: 0;  transform: scale(1.13); }
}
.summon__plate.is-struck[data-tier="uncommon"] .tile__frame::after,
.summon__plate.is-struck[data-tier="rare"] .tile__frame::after,
.summon__plate.is-struck[data-tier="mythic"] .tile__frame::after,
.summon__plate.is-struck[data-tier="legendary"] .tile__frame::after {
  animation: summon-strike .85s cubic-bezier(.2,.75,.3,1) 1;
}

/* rare and up keep a lit frame after the strike passes */
.summon__plate.is-revealed[data-tier="rare"] .tile__frame,
.summon__plate.is-revealed[data-tier="mythic"] .tile__frame,
.summon__plate.is-revealed[data-tier="legendary"] .tile__frame {
  border-color: color-mix(in srgb, var(--pal) 62%, transparent);
  box-shadow: 0 0 0 1px rgba(0,0,0,.6), 0 0 44px -6px color-mix(in srgb, var(--pal) 55%, transparent);
}
.summon__plate.is-revealed[data-tier="rare"] .corner,
.summon__plate.is-revealed[data-tier="mythic"] .corner,
.summon__plate.is-revealed[data-tier="legendary"] .corner {
  filter: drop-shadow(0 0 7px color-mix(in srgb, var(--pal) 80%, transparent));
}

/* a Cursed Entity gets the only thing on the page that never stops moving */
@keyframes summon-ember {
  0%, 100% { box-shadow: 0 0 0 1px rgba(0,0,0,.6), 0 0 40px -8px color-mix(in srgb, var(--gold) 50%, transparent); }
  50%      { box-shadow: 0 0 0 1px rgba(0,0,0,.6), 0 0 74px -4px color-mix(in srgb, var(--gold) 85%, transparent); }
}
.summon__plate.is-revealed[data-tier="legendary"] .tile__frame {
  animation: summon-ember 3.4s ease-in-out infinite;
}
.summon__plate.is-revealed[data-tier="legendary"] .summon__no { color: var(--gold); }

/* ---------- card and sound ---------------------------------------------- */
.summon__share {
  margin-top: .85rem; font: inherit; font-size: .64rem; letter-spacing: .22em;
  text-transform: uppercase; color: var(--vellum-dim);
  background: none; border: 1px solid var(--rule); padding: .5rem .8rem;
  cursor: var(--cur-point); transition: color .25s, border-color .25s;
}
.summon__share:hover:not(:disabled) { color: var(--pal); border-color: var(--pal); }
.summon__share:disabled { opacity: .55; cursor: var(--cur-arrow); }

.summon__acts { display: flex; justify-content: center; }
.summon__sound {
  position: fixed; top: 1.1rem; right: 1.2rem; z-index: 4;
  font: inherit; font-size: .6rem; letter-spacing: .22em; text-transform: uppercase;
  color: var(--vellum-dim); background: none; border: 1px solid var(--rule);
  padding: .45rem .75rem; cursor: var(--cur-point); transition: color .25s, border-color .25s;
}
.summon__sound:hover { color: var(--vellum); border-color: var(--vellum-dim); }
.summon__sound[aria-pressed="true"] { color: var(--accent); border-color: var(--accent); }

@media (prefers-reduced-motion: reduce) {
  .summon__plate .tile__frame::after,
  .summon__plate.is-revealed[data-tier="legendary"] .tile__frame { animation: none; }
}

/* Both of these used to be set as fine print at the bottom of the panel, which is
   where a reader's eye goes last — and one of them is the only prompt on the page that
   tells a first-time buyer they can watch the whole thing happen before paying. */
.panel__sound {
  margin: 1.1rem 0 0; font-size: .88rem; color: var(--vellum-dim); text-align: center;
}
.panel__try {
  display: flex; justify-content: center; align-items: baseline;
  flex-wrap: wrap; gap: .1rem .55rem;
  margin-top: 1rem; padding: .85rem 1rem;
  border: 1px solid var(--rule); text-decoration: none;
  transition: border-color .25s, background .25s;
}
.panel__try:hover, .panel__try:focus-visible {
  border-color: var(--accent); background: rgba(0, 200, 5, .07);
}
.panel__try span { font-size: .88rem; color: var(--vellum-dim); }
.panel__try b {
  font-family: var(--display); font-weight: 400; font-size: 1.15rem; color: var(--accent);
}
.linkish {
  font: inherit; background: none; border: 0; padding: 0; cursor: var(--cur-point);
  color: var(--vellum-dim); border-bottom: 1px solid var(--rule);
}
.panel__sound .linkish { font-size: 1em; }
.linkish:hover { color: var(--vellum); border-color: var(--vellum-dim); }
.linkish[aria-pressed="true"] { color: var(--accent); border-color: var(--accent); }

/* ---------- the viewer --------------------------------------------------
   Replaces the shared lightbox inside the gallery. Two columns on a laptop: the plate
   at the size the work was drawn for, and beside it everything the reader actually
   wanted — the traits large enough to read, what each of them is worth, and the way
   through to the next piece.                                                     */

.viewer {
  position: fixed; inset: 0; z-index: 120;
  display: grid; place-items: center;
  padding: clamp(1rem, 3vw, 2.4rem);
  background: rgba(6, 6, 7, .96);
  backdrop-filter: blur(7px);
  --pal: var(--bone);
}
.viewer[hidden] { display: none; }
.viewer[data-pal="Corruption"] { --pal: var(--corruption); }
.viewer[data-pal="Necrotic"]   { --pal: var(--necrotic); }
.viewer[data-pal="Infernal"]   { --pal: var(--infernal); }
.viewer[data-pal="Royal"]      { --pal: var(--royal); }
.viewer[data-pal="Neutral"]    { --pal: var(--bone); }
.viewer[data-tier="mythic"],
.viewer[data-tier="legendary"] { --pal: var(--gold); }

.viewer__sheet {
  position: relative;
  display: grid; gap: clamp(1.4rem, 3vw, 3rem);
  grid-template-columns: 1fr;
  align-items: center;
  width: min(1180px, 100%);
  max-height: 100%;
}
@media (min-width: 900px) {
  .viewer__sheet {
    grid-template-columns: minmax(0, 1fr) 25rem;
    /* the arrows are fixed to the viewport edges, so the sheet keeps clear of them */
    width: min(1180px, 100% - 8rem);
  }
}

.viewer__art { width: 100%; max-width: min(70vh, 620px); margin: 0 auto; }
.viewer__art .tile__win img { opacity: 0; transition: opacity .45s ease; }
.viewer__art .tile__win img.in { opacity: 1; }

.viewer__meta { min-width: 0; }
.viewer__meta .eyebrow { color: var(--pal); margin-bottom: .7rem; }
.viewer__no { font-size: clamp(2.2rem, 4.4vw, 3.2rem); margin-bottom: .35rem; }
.viewer__rank {
  margin: 0 0 1.4rem; font-size: .72rem; letter-spacing: .2em;
  text-transform: uppercase; color: var(--vellum-dim);
}

.viewer__traits { list-style: none; margin: 0; padding: 0; border-top: 1px solid var(--rule); }
.viewer__traits li {
  display: grid; align-items: baseline; gap: .2rem .9rem;
  grid-template-columns: 5.4rem minmax(0, 1fr) auto;
  padding: .62rem 0 .5rem; border-bottom: 1px solid var(--rule);
}
.viewer__traits span {
  font-size: .6rem; letter-spacing: .18em; text-transform: uppercase; color: #5c5c60;
}
.viewer__traits b { font-weight: 400; font-size: 1rem; color: var(--vellum); }
.viewer__traits i {
  font-style: normal; text-align: right;
  font-size: .82rem; color: var(--pal); font-variant-numeric: tabular-nums;
}
.viewer__traits em {
  display: block; font-style: normal;
  font-size: .58rem; letter-spacing: .14em; text-transform: uppercase; color: #5c5c60;
}
/* the bar spans the value and figure columns, under both */
.viewer__traits u {
  grid-column: 2 / -1; height: 2px; background: var(--rule); text-decoration: none;
  position: relative;
}
.viewer__traits u::after {
  content: ''; position: absolute; inset: 0 auto 0 0;
  width: var(--w); background: color-mix(in srgb, var(--pal) 70%, transparent);
}

.viewer__acts { display: flex; gap: .6rem; margin-top: 1.5rem; flex-wrap: wrap; }
.viewer__act {
  font: inherit; font-size: .62rem; letter-spacing: .22em; text-transform: uppercase;
  color: var(--vellum-dim); background: none; border: 1px solid var(--rule);
  padding: .58rem .9rem; cursor: var(--cur-point); transition: color .25s, border-color .25s;
}
.viewer__act:hover:not(:disabled) { color: var(--pal); border-color: var(--pal); }
.viewer__act:disabled { opacity: .5; cursor: var(--cur-arrow); }
.viewer__pos {
  margin: 1.1rem 0 0; font-size: .6rem; letter-spacing: .2em;
  text-transform: uppercase; color: #5c5c60;
}

.viewer__close {
  position: fixed; top: 1rem; right: 1.4rem;
  background: none; border: 0; color: var(--vellum);
  font-family: var(--display); font-size: 2.1rem; line-height: 1; cursor: var(--cur-point);
}
.viewer__nav {
  position: fixed; top: 50%; transform: translateY(-50%);
  width: 3rem; height: 3rem; display: grid; place-items: center;
  background: rgba(10,10,11,.7); border: 1px solid var(--rule);
  color: var(--vellum); font-size: 1.1rem; cursor: var(--cur-point);
  transition: border-color .25s, color .25s, opacity .25s;
}
.viewer__nav:hover:not(:disabled) { border-color: var(--pal); color: var(--pal); }
.viewer__nav:disabled { opacity: .22; cursor: var(--cur-arrow); }
.viewer__nav--prev { left: .7rem; }
.viewer__nav--next { right: .7rem; }
@media (min-width: 1400px) { .viewer__nav--prev { left: 2rem; } .viewer__nav--next { right: 2rem; } }

/* a Cursed Entity is lit here too, so the viewer agrees with the summoning */
.viewer[data-tier="legendary"] .tile__frame,
.viewer[data-tier="mythic"] .tile__frame {
  border-color: color-mix(in srgb, var(--pal) 55%, transparent);
  box-shadow: 0 0 0 1px rgba(0,0,0,.6), 0 0 60px -10px color-mix(in srgb, var(--pal) 55%, transparent);
}

/* A layer whose art never arrived. Hidden outright rather than left to draw the
   browser's broken-image icon across the portrait. */
.stack__frame img.gone { display: none; }

/* ---------- the forge ---------------------------------------------------
   The gallery shows what exists; this shows the space around it. The frame is the
   same plate the rest of the site uses, so a soul assembled here reads as a piece
   rather than as a preview of one — and when the combination turns out to be real,
   the frame takes that piece's own palette.                                      */

.forge { padding: 0 0 clamp(3rem, 7vw, 5.5rem); }
.forge__grid { display: grid; gap: clamp(1.8rem, 4vw, 3.4rem); grid-template-columns: 1fr; }
@media (min-width: 900px) { .forge__grid { grid-template-columns: minmax(0, 1fr) 24rem; } }

.forge__stage { min-width: 0; }
.forge__plate { width: 100%; max-width: 560px; --pal: var(--accent); }
.forge__plate[data-pal="Corruption"] { --pal: var(--corruption); }
.forge__plate[data-pal="Necrotic"]   { --pal: var(--necrotic); }
.forge__plate[data-pal="Infernal"]   { --pal: var(--infernal); }
.forge__plate[data-pal="Royal"]      { --pal: var(--royal); }
.forge__plate[data-pal="Neutral"]    { --pal: var(--bone); }
.forge__plate[data-pal="Legendary"]  { --pal: var(--gold); }

.forge__plate .tile__win { background: #060607; }
.forge__layer {
  position: absolute; inset: 0; width: 100%; height: 100%;
  object-fit: cover; image-rendering: -webkit-optimize-contrast;
}
.forge__layer[hidden] { display: none; }

/* a real piece is lit; an invented one is not, and that difference is the page */
.forge__plate.is-real .tile__frame {
  border-color: color-mix(in srgb, var(--pal) 55%, transparent);
  box-shadow: 0 0 0 1px rgba(0,0,0,.6), 0 0 50px -12px color-mix(in srgb, var(--pal) 60%, transparent);
}
.forge__plate.is-real .corner { opacity: 1; }
.forge__plate:not(.is-real) .corner { opacity: .3; }

.forge__verdict {
  margin: 1.3rem 0 0; max-width: 560px;
  font-size: .95rem; line-height: 1.6; color: var(--vellum-dim);
}
.forge__verdict b { font-family: var(--display); font-weight: 400; font-size: 1.5rem; color: var(--vellum); }
.forge__plate.is-real + .forge__verdict b { color: var(--pal); }
.forge__verdict a { color: var(--accent); text-decoration: none; border-bottom: 1px solid var(--accent-deep); }
.forge__verdict a:hover { border-color: var(--accent); }

.forge__acts { display: flex; flex-wrap: wrap; gap: .6rem; margin-top: 1.4rem; }
.forge__act {
  font: inherit; font-size: .62rem; letter-spacing: .22em; text-transform: uppercase;
  color: var(--vellum-dim); background: none; border: 1px solid var(--rule);
  padding: .62rem .95rem; cursor: var(--cur-point); transition: color .25s, border-color .25s;
}
.forge__act:hover:not(:disabled) { color: var(--accent); border-color: var(--accent); }
.forge__act:disabled { opacity: .5; cursor: var(--cur-arrow); }

/* ---- the six pickers ---- */
.forge__panel { min-width: 0; }
.fpick {
  display: grid; grid-template-columns: 1fr auto; gap: .2rem .8rem;
  align-items: baseline; padding: .75rem 0; border-bottom: 1px solid var(--rule);
}
.fpick:first-of-type { border-top: 1px solid var(--rule); }
.fpick__slot {
  grid-column: 1; font-size: .6rem; letter-spacing: .2em;
  text-transform: uppercase; color: #5c5c60;
}
.fpick__rate {
  grid-column: 2; grid-row: 1; text-align: right;
  font-size: .68rem; color: var(--vellum-dim); font-variant-numeric: tabular-nums;
}
.fpick select {
  grid-column: 1 / -1;
  width: 100%; margin-top: .25rem;
  font: inherit; font-size: .95rem; color: var(--vellum);
  background: var(--ink-2); border: 1px solid var(--rule);
  padding: .5rem .6rem; cursor: var(--cur-point);
  appearance: none;
  background-image: linear-gradient(45deg, transparent 50%, var(--vellum-dim) 50%),
                    linear-gradient(135deg, var(--vellum-dim) 50%, transparent 50%);
  background-position: right 1.05rem center, right .75rem center;
  background-size: 5px 5px, 5px 5px;
  background-repeat: no-repeat;
}
.fpick select:hover { border-color: var(--vellum-dim); }
.fpick select:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.fpick option { background: var(--ink-2); color: var(--vellum); }

.forge__note {
  margin: 1.3rem 0 0; font-size: .8rem; line-height: 1.65; color: #5c5c60;
}

/* Plates in a multi-piece buy stretch to the tallest, so the card buttons line up
   instead of stepping down with each shorter trait list. */
.summon__stage--many { align-items: stretch; }
.summon__plate { display: flex; flex-direction: column; }
.summon__plate-label { flex: 1; display: flex; flex-direction: column; }
.summon__share { margin-top: auto; align-self: flex-start; }

/* Four links instead of three. "The Ether" and "The Forge" were wrapping onto two
   lines each on a handset, which turned the bar into a paragraph. */
.nav__links a { white-space: nowrap; }
@media (max-width: 560px) {
  .nav__links { gap: .8rem; font-size: .6rem; letter-spacing: .08em; }
  .nav__mark { font-size: 1rem; }
}
/* Five links will not sit beside the wordmark on a handset — squeezing them shrinks the
   type to eight pixels and still runs the last one off the edge. Stack instead: the mark
   over the links, both centred, which also has room if a sixth page ever appears. */
@media (max-width: 480px) {
  .nav { flex-direction: column; gap: .45rem; padding-top: .7rem; padding-bottom: .7rem; }
  .nav__links {
    gap: 1rem; font-size: .62rem; letter-spacing: .1em;
    justify-content: center; flex-wrap: wrap;
  }
  .nav__mark { font-size: 1.15rem; }
}

/* ---------- the trials --------------------------------------------------
   Two games, neither of which takes money. The first borrows the mint's own ceremony
   wholesale; the second builds its souls out of the Forge's layers, so it works
   without the 9.4 GB of finished renders being anywhere near this host.        */

.trial { padding: clamp(2.4rem, 6vw, 4.5rem) 0; border-top: 1px solid var(--rule); }
.trial h2 { font-size: clamp(1.9rem, 4.4vw, 3rem); }
.trial p { max-width: 54ch; color: var(--vellum-dim); }
.trial__lede { margin-bottom: clamp(1.6rem, 4vw, 2.4rem); }

.trial__grid { display: grid; gap: clamp(1.8rem, 4vw, 3.4rem); grid-template-columns: 1fr; align-items: start; }
@media (min-width: 820px) { .trial__grid { grid-template-columns: minmax(0, 1fr) 17rem; } }

.trial__acts { display: flex; flex-wrap: wrap; gap: .8rem; align-items: center; margin-top: 1.6rem; }
.btn--slim { display: inline-block; width: auto; padding: .85rem 1.6rem; }
.trial__act {
  font: inherit; font-size: .62rem; letter-spacing: .22em; text-transform: uppercase;
  color: var(--vellum-dim); background: none; border: 1px solid var(--rule);
  padding: .62rem .95rem; transition: color .25s, border-color .25s;
}
.trial__act:hover { color: var(--accent); border-color: var(--accent); }

.trial__score { list-style: none; margin: 0; padding: 0; border-top: 1px solid var(--rule); }
.trial__score li {
  display: flex; justify-content: space-between; align-items: baseline; gap: 1rem;
  padding: .7rem 0; border-bottom: 1px solid var(--rule);
}
.trial__score span { font-size: .6rem; letter-spacing: .2em; text-transform: uppercase; color: #5c5c60; }
.trial__score b {
  font-family: var(--display); font-weight: 400; font-size: 1.5rem;
  color: var(--vellum); font-variant-numeric: tabular-nums;
}
.trial__score--row { display: flex; gap: 2.2rem; border: 0; }
.trial__score--row li { display: block; border: 0; padding: 0; }
.trial__quiet { font-size: .78rem; line-height: 1.6; color: #5c5c60; margin-top: 1.2rem; }

/* ---- which is rarer ---- */
.rarer {
  display: grid; gap: clamp(1rem, 3vw, 2rem);
  grid-template-columns: repeat(2, minmax(0, 1fr));
  max-width: 760px;
}
.rarer__pick {
  display: block; width: 100%; text-align: left;
  background: none; border: 0; padding: 0; font: inherit;
  --pal: var(--rule);
}
.rarer__plate { display: block; }
.rarer__pick .tile__frame { display: block; transition: border-color .3s, box-shadow .3s, transform .3s; }
.rarer__pick .tile__win { display: block; }
.rarer__layer { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }

.rarer__pick:not([class*="is-"]):hover .tile__frame,
.rarer__pick:not([class*="is-"]):focus-visible .tile__frame {
  border-color: var(--accent); transform: translateY(-3px);
  box-shadow: 0 0 0 1px rgba(0,0,0,.6), 0 0 34px -12px var(--accent);
}

/* after the answer: the rarer one is named, the other dims out of the way */
.rarer__pick.is-rarer   { --pal: var(--royal); }
.rarer__pick.is-rarer .tile__frame {
  border-color: color-mix(in srgb, var(--royal) 60%, transparent);
  box-shadow: 0 0 0 1px rgba(0,0,0,.6), 0 0 40px -12px var(--royal);
}
.rarer__pick.is-commoner .tile__frame { opacity: .42; }
.rarer__pick.is-wrong .tile__frame { border-color: var(--accent); }
.rarer__pick.is-right .tile__frame { border-color: var(--necrotic); }

.rarer__answer { display: block; padding: .7rem .1rem 0; min-height: 2.6rem; }
.rarer__answer b { display: block; font-family: var(--display); font-weight: 400; font-size: 1.25rem; }
.rarer__answer i {
  font-style: normal; font-size: .6rem; letter-spacing: .2em;
  text-transform: uppercase; color: #5c5c60;
}
.rarer__pick.is-rarer .rarer__answer b { color: var(--royal); }

.rarer__bar {
  display: flex; flex-wrap: wrap; align-items: center; gap: 1.4rem 2.4rem;
  margin-top: clamp(1.2rem, 3vw, 1.8rem);
}
.trial__why { max-width: 62ch; margin-top: 1rem; font-size: .86rem; min-height: 3em; }
.trial__why b { font-family: var(--display); font-weight: 400; font-size: 1.15rem; color: var(--vellum); }

/* The hand card sits beside Close, because both are things you do once the ceremony
   has finished rather than during it. */
.summon__acts { display: flex; justify-content: center; gap: .7rem; flex-wrap: wrap; }
.summon__hand {
  font: inherit; font-size: .64rem; letter-spacing: .22em; text-transform: uppercase;
  color: var(--vellum-dim); background: none; border: 1px solid var(--rule);
  padding: .7rem 1.1rem; cursor: var(--cur-point);
  transition: color .25s, border-color .25s;
}
.summon__hand:hover:not(:disabled) { color: var(--accent); border-color: var(--accent); }
.summon__hand:disabled { opacity: .55; cursor: var(--cur-arrow); }

/* ---------- the assembly ------------------------------------------------
   One piece built layer by layer as you scroll past it.

   NOT PINNED, deliberately. The first version pinned the frame inside a 150vh track.
   Pinning needs a track taller than the screen to scroll through, and that track is
   what fills the viewport around the pinned band — so everything above and below the
   frame was the track's own empty space, and the sections either side only appeared at
   the very start and the very end. It read as a blank panel shoving the rest of the
   page down, because that is what it was.

   This is an ordinary block that scrolls like everything else. The layers are laid on
   according to where the band sits in the viewport, so the assembly still happens as
   you scroll, the sections above and below stay visible throughout, and the page is not
   two and a half screens longer than it needs to be.                              */

.build__track { position: relative; }
.build__stick {
  padding: clamp(2rem, 5vh, 3.4rem) 0;
  display: grid; align-items: center;
  border-top: 1px solid var(--rule);
  border-bottom: 1px solid var(--rule);
}
.build__grid {
  display: grid; gap: clamp(1.4rem, 4vw, 3.4rem);
  grid-template-columns: 1fr; align-items: center;
}
/* The painting gets the larger share; the list only needs room for six short rows. */
@media (min-width: 880px) { .build__grid { grid-template-columns: minmax(0, 1.3fr) minmax(0, 1fr); } }

/* Capped against the viewport so the whole band fits on screen at once — the assembly
   only reads if you can see all of it while it is happening. */
.build__stage { width: min(56vh, 100%); margin: 0 auto; }
.build__stage .tile__frame { --pal: var(--infernal); }
.build__layer {
  position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover;
  opacity: 0; filter: brightness(1.5) saturate(.85);
  transition: opacity .45s ease, filter .7s ease;
}
.build__layer.on { opacity: 1; filter: none; }

.build__side h2 { font-size: clamp(1.9rem, 4.2vw, 3.2rem); margin-bottom: 1.4rem; }
.build__side h2 em { font-style: normal; color: var(--accent); }

.build__list { list-style: none; margin: 0; padding: 0; border-top: 1px solid var(--rule); }
.build__list li {
  display: grid; grid-template-columns: 2.2rem 6.2rem minmax(0, 1fr); gap: .9rem;
  align-items: baseline; padding: .62rem 0; border-bottom: 1px solid var(--rule);
  color: #3f3f43; transition: color .4s ease, border-color .4s ease;
}
.build__list li.on { color: var(--vellum-dim); }
.build__list li.is-current { color: var(--vellum); border-bottom-color: var(--accent); }
.build__num { font-family: var(--display); font-size: .95rem; }
.build__slot { font-size: .58rem; letter-spacing: .2em; text-transform: uppercase; }
.build__name { font-size: .95rem; }
.build__list li.is-current .build__name { color: var(--accent); }
.build__foot { margin: 1.3rem 0 0; font-size: .82rem; line-height: 1.65; color: #5c5c60; max-width: 46ch; }

/* A short screen cannot hold the frame and the list at once, so the frame leads and
   the list rides under it at a size that still reads. */
@media (max-width: 879px) {
  .build__stage { width: min(46vh, 76vw); }
  .build__side h2, .build__side .eyebrow, .build__foot { display: none; }
  .build__list li { grid-template-columns: 1.7rem 5.2rem minmax(0, 1fr); padding: .42rem 0; }
}

/* No scroll, no pinning: show the finished piece and every layer named. */
@media (prefers-reduced-motion: reduce) {
  .build__layer { opacity: 1; filter: none; }
}

/* Held open so the rail keeps its height when a soul carries fewer than six layers.
   Hidden rather than removed: an absent row is what made the page jump. */
.stack__rail li.is-blank { visibility: hidden; pointer-events: none; }

/* ---------- command palette ---------------------------------------------
   One box that reaches any of the 6,666 pieces, any of the 163 traits, and every
   page. Opens on the key, on the nav chip, or on a forward slash.              */

.cmd {
  position: fixed; inset: 0; z-index: 200;
  display: grid; justify-items: center; align-items: start;
  padding: clamp(3rem, 12vh, 9rem) var(--gut) var(--gut);
  background: rgba(6, 6, 7, .82); backdrop-filter: blur(8px);
}
.cmd[hidden] { display: none; }
.cmd__box {
  width: min(660px, 100%);
  background: linear-gradient(180deg, var(--ink-2), #0c0c0d);
  border: 1px solid var(--rule);
  box-shadow: 0 40px 90px -30px #000, 0 0 0 1px rgba(0,0,0,.6);
}
.cmd__q {
  width: 100%; display: block; font: inherit; font-size: 1.05rem;
  color: var(--vellum); background: none; border: 0;
  border-bottom: 1px solid var(--rule); padding: 1.05rem 1.2rem;
}
.cmd__q::placeholder { color: #4f4f55; }
.cmd__q:focus { outline: none; border-bottom-color: var(--accent); }

.cmd__list {
  list-style: none; margin: 0; padding: .35rem 0;
  max-height: min(52vh, 420px); overflow-y: auto;
}
.cmd__list li {
  display: grid; grid-template-columns: 5.6rem minmax(0, 1fr) auto;
  gap: .9rem; align-items: baseline;
  padding: .58rem 1.2rem; cursor: var(--cur-point);
}
.cmd__list li.is-on { background: rgba(0, 200, 5, .12); }
.cmd__list li.is-on .cmd__label { color: var(--accent); }
.cmd__kind {
  font-size: .56rem; letter-spacing: .2em; text-transform: uppercase; color: #5c5c60;
}
.cmd__label { font-size: .98rem; color: var(--vellum); }
.cmd__hint {
  font-size: .72rem; color: #5c5c60; text-align: right;
  font-variant-numeric: tabular-nums; white-space: nowrap;
}
.cmd__none { padding: 1.1rem 1.2rem; color: var(--vellum-dim); font-size: .9rem; }

.cmd__feet {
  display: flex; gap: 1.4rem; margin: 0;
  padding: .7rem 1.2rem; border-top: 1px solid var(--rule);
  font-size: .6rem; letter-spacing: .16em; text-transform: uppercase; color: #4f4f55;
}
.cmd__feet b {
  display: inline-block; font-weight: 400; margin-right: .25rem;
  border: 1px solid var(--rule); padding: 0 .3rem; color: var(--vellum-dim);
}

/* the chip in the nav, which is how anybody finds out the key exists */
.cmd__open {
  display: inline-flex; align-items: center; gap: .4rem;
  font: inherit; background: none; border: 1px solid var(--rule);
  padding: .18rem .45rem; color: #5c5c60; cursor: var(--cur-point);
  transition: color .2s, border-color .2s;
}
.cmd__open:hover { color: var(--vellum); border-color: var(--vellum-dim); }
.cmd__mag { font-size: .82rem; line-height: 1; }
.cmd__key { font: inherit; font-size: .58rem; letter-spacing: .08em; }
@media (max-width: 620px) { .cmd__key { display: none; } }

@media (max-width: 620px) {
  .cmd { padding-top: 2rem; }
  .cmd__list li { grid-template-columns: minmax(0, 1fr) auto; gap: .2rem .8rem; }
  .cmd__kind { grid-column: 1 / -1; }
}

/* ---------- 404 ---------------------------------------------------------
   Netlify serves this for any address that does not exist. It reuses the collection's
   own word for a piece whose art has not arrived, because it is the same idea: a frame
   with nothing behind it.                                                        */

.lost { padding: clamp(2.5rem, 8vh, 6rem) 0 clamp(3rem, 8vh, 6rem); }
.lost__grid {
  display: grid; gap: clamp(2rem, 5vw, 4rem);
  grid-template-columns: 1fr; align-items: center;
}
@media (min-width: 860px) { .lost__grid { grid-template-columns: minmax(0, 1fr) minmax(0, 1.1fr); } }

.lost__plate { width: min(46vh, 100%); margin: 0 auto; --pal: var(--necrotic); }
.lost__plate .tile__win img { filter: grayscale(.7) contrast(.9) brightness(.5); }
.lost__void {
  position: absolute; inset: 0; display: grid; place-content: center; text-align: center;
  gap: .3rem;
}
.lost__void b {
  font-family: var(--display); font-weight: 400; font-size: clamp(3rem, 9vw, 5rem);
  color: var(--vellum); letter-spacing: .06em;
}
.lost__void i {
  font-style: normal; font-size: .64rem; letter-spacing: .34em;
  text-transform: uppercase; color: var(--necrotic);
}

.lost__say h1 { font-size: clamp(2.8rem, 8vw, 5.5rem); margin-bottom: 1.2rem; }
.lost__say .lede { max-width: 44ch; }

.lost__ways { list-style: none; margin: 2rem 0 0; padding: 0; border-top: 1px solid var(--rule); }
.lost__ways li {
  display: flex; justify-content: space-between; align-items: baseline; gap: 1rem;
  padding: .8rem 0; border-bottom: 1px solid var(--rule);
}
.lost__ways a {
  font-family: var(--display); font-size: 1.25rem; text-decoration: none; color: var(--vellum);
  transition: color .2s;
}
.lost__ways a:hover { color: var(--accent); }
.lost__ways span {
  font-size: .6rem; letter-spacing: .2em; text-transform: uppercase; color: #5c5c60;
}
.lost__key { margin-top: 1.6rem; font-size: .82rem; color: #5c5c60; }
.lost__key kbd {
  font: inherit; border: 1px solid var(--rule); padding: .05rem .4rem; color: var(--vellum-dim);
}

/* ---------- ambience ----------------------------------------------------
   The ember field and the familiar both live behind the page: negative z-index,
   no pointer events, and gone entirely under reduced motion (ambience.js also
   refuses to start, so this rule is the belt to that braces).              */

.ambience-canvas {
  position: fixed; inset: 0; z-index: -1; pointer-events: none;
}
.familiar {
  position: fixed; left: 0; top: 0; z-index: -1; pointer-events: none;
  opacity: 0; transition: opacity 1.6s ease;
  will-change: transform;
}
.familiar.in { opacity: .6; }
.familiar img { width: 112px; height: auto; display: block; }

@media (prefers-reduced-motion: reduce) {
  .ambience-canvas, .familiar { display: none; }
}

/* ---------- kinship and the wall label ----------------------------------
   The kinship line is the piece's sharpest true claim, sitting under the rank; the
   tomb is a museum wall label, because a $9.99 painting deserves the same four quiet
   lines a museum gives a Vermeer.                                              */

.viewer__kin {
  margin: -0.9rem 0 1.2rem; font-size: .82rem; line-height: 1.55;
  color: color-mix(in srgb, var(--pal) 72%, var(--vellum));
}
.viewer__kin:empty { display: none; }
.viewer__kin a { color: var(--vellum-dim); text-decoration: none; border-bottom: 1px solid var(--rule); }
.viewer__kin a:hover { color: var(--pal); border-color: var(--pal); }

.viewer__tomb {
  margin-top: 1.3rem; padding: .95rem 1.1rem .85rem;
  border: 1px solid var(--rule);
  background: linear-gradient(180deg, var(--ink-2), #0c0c0d);
  display: grid; gap: .16rem;
}
.viewer__tomb strong { font-weight: 400; font-size: .82rem; color: var(--vellum-dim); }
.viewer__tomb b { font-family: var(--display); font-weight: 400; font-size: 1.1rem; color: var(--vellum); }
.viewer__tomb i { font-size: .8rem; color: var(--vellum-dim); }
.viewer__tomb span { font-size: .8rem; color: var(--vellum-dim); }
.viewer__tomb em {
  font-style: normal; margin-top: .35rem;
  font-size: .6rem; letter-spacing: .16em; text-transform: uppercase; color: #5c5c60;
}

/* ---------- holders' chat ---------------------------------------------- */
.ch__gate { padding: 2rem 0 3rem; max-width: 34rem; }
.ch__gate .btn { width: auto; display: inline-block; padding: .6rem 2.2rem; margin-top: 1rem; }
.ch__name { padding: 1.2rem 0 2rem; max-width: 30rem; }
.ch__name label { display: block; font-size: .72rem; letter-spacing: .18em; text-transform: uppercase; color: var(--vellum-dim); margin-bottom: .5rem; }

/* The room is the page here, not a widget on it: full width of the wrap and as tall
   as the screen allows. It was sized like a sidebar and the emoji were unreadable.
   [OWNER 2026-08-27] */
.ch__app { display: grid; grid-template-columns: 16rem minmax(0, 1fr); border: 1px solid var(--rule);
           background: var(--ink-2); height: min(86vh, 1100px); min-height: 620px;
           margin: 1rem 0 3rem; position: relative; }
.ch__side { border-right: 1px solid var(--rule); display: flex; flex-direction: column; min-height: 0; }
.ch__me { display: flex; align-items: center; justify-content: space-between; gap: .5rem;
          padding: .75rem .85rem; border-bottom: 1px solid var(--rule); }
.ch__me-name { font-family: var(--body); font-weight: 600; font-size: .92rem; overflow: hidden;
               text-overflow: ellipsis; white-space: nowrap; }
.ch__out { padding: .28rem .7rem !important; font-size: .74rem !important; width: auto !important; flex: none; }
.ch__channels { list-style: none; margin: 0; padding: .5rem 0; overflow-y: auto; }
.ch__chan { padding: .5rem 1rem; color: var(--vellum-dim); cursor: var(--cur-point); font-size: 1rem;
            border-left: 2px solid transparent; }
.ch__chan:hover { color: var(--vellum); background: color-mix(in srgb, var(--vellum) 4%, transparent); }
.ch__chan--on { color: var(--vellum); border-left-color: var(--accent);
                background: color-mix(in srgb, var(--vellum) 6%, transparent); }
.ch__chan span { opacity: .45; margin-right: .3rem; }
.ch__chan--ann { color: var(--gold); }
.ch__chan--ann.ch__chan--on { border-left-color: var(--gold); }

.ch__main { display: flex; flex-direction: column; min-width: 0; min-height: 0; position: relative; }
.ch__bar { display: flex; align-items: center; gap: .6rem; padding: .7rem .95rem;
           border-bottom: 1px solid var(--rule); }
.ch__title { font-family: var(--body); font-weight: 600; font-size: 1.1rem; letter-spacing: .06em;
             margin: 0; color: var(--vellum); }
.ch__livedot { margin-left: auto; font-size: .68rem; letter-spacing: .14em; text-transform: uppercase;
               color: var(--vellum-dim); }
.ch__livedot[data-state="live"] { color: var(--accent); }
.ch__livedot[data-state="reconnecting"] { color: var(--gold); }

.ch__feed { flex: 1; padding: 1rem 1.3rem 1.4rem; overflow-y: auto; min-height: 0; }
.ch__more { display: block; margin: 0 auto 1rem; background: none; border: 1px solid var(--rule);
            color: var(--vellum-dim); font-family: var(--body); font-size: .78rem; padding: .3rem 1rem;
            cursor: var(--cur-point); }
.ch__more:hover { color: var(--vellum); }

.ch__msg { position: relative; padding: .28rem 5.5rem .28rem 0; margin-top: .7rem; }
.ch__msg--cont { margin-top: .05rem; }
.ch__msg:hover { background: color-mix(in srgb, var(--vellum) 3%, transparent); }
.ch__msg--flash { animation: chflash 1.2s ease-out; }
@keyframes chflash { from { background: color-mix(in srgb, var(--accent) 22%, transparent); } to { background: transparent; } }
.ch__head { display: flex; align-items: baseline; gap: .55rem; margin-bottom: .1rem; }
.ch__author { font-family: var(--body); font-weight: 600; font-size: 1rem; color: var(--vellum); }
.ch__msg--mine .ch__author { color: var(--accent); }
.ch__head time { font-size: .7rem; color: var(--vellum-dim); }
.ch__body { font-size: 1.02rem; line-height: 1.6; color: var(--vellum); overflow-wrap: anywhere; }
.ch__body a { color: var(--accent); }
.ch__link--outside { color: var(--gold); text-decoration: underline dotted; }
.ch__edited { font-size: .72rem; color: var(--vellum-dim); }
.ch__gone { color: var(--vellum-dim); font-size: .9rem; }
.ch__code { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: .88em;
            background: var(--ink); border: 1px solid var(--rule); padding: .05em .32em; }
.ch__pre { background: var(--ink); border: 1px solid var(--rule); padding: .6rem .75rem; margin: .35rem 0;
           overflow-x: auto; }
.ch__pre code { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: .84rem; }
.ch__quote { border-left: 2px solid var(--rule); margin: .2rem 0; padding: .1rem 0 .1rem .7rem;
             color: var(--vellum-dim); }
.ch__spoiler { background: var(--rule); color: transparent; border-radius: 2px; cursor: var(--cur-point);
               transition: color .15s, background .15s; }
.ch__spoiler.is-open { background: color-mix(in srgb, var(--vellum) 8%, transparent); color: var(--vellum); }
.ch__mention { color: var(--royal); background: color-mix(in srgb, var(--royal) 12%, transparent);
               padding: 0 .2em; }
.ch__mention--me { color: var(--gold); background: color-mix(in srgb, var(--gold) 18%, transparent); }
/* a custom emoji is a picture doing a word's job: it has to be legible */
.ch__emoji { height: 1.6em; width: auto; vertical-align: -.35em; }
/* on its own, with no words around it, it goes large -- the way every chat does */
.ch__body--jumbo .ch__emoji { height: 3.2em; vertical-align: -.4em; }
.ch__replied { display: block; background: none; border: 0; padding: 0 0 .15rem; text-align: left;
               font-family: var(--body); font-size: .78rem; color: var(--vellum-dim);
               cursor: var(--cur-point); overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
               max-width: 100%; }
.ch__replied::before { content: "↳ "; opacity: .6; }
.ch__replied:hover { color: var(--vellum); }
.ch__acts { position: absolute; top: .1rem; right: .3rem; display: none; gap: .3rem; }
.ch__msg:hover .ch__acts, .ch__msg:focus-within .ch__acts { display: flex; }
.ch__acts button { background: var(--ink); border: 1px solid var(--rule); color: var(--vellum-dim);
                   font-family: var(--body); font-size: .7rem; padding: .15rem .5rem;
                   cursor: var(--cur-point); }
.ch__acts button:hover { color: var(--vellum); border-color: var(--vellum-dim); }

.ch__jump { position: absolute; right: 1rem; bottom: 5.2rem; z-index: 2; background: var(--accent);
            border: 0; color: #05140a; font-family: var(--body); font-weight: 600; font-size: .78rem;
            padding: .35rem .9rem; cursor: var(--cur-point); }
.ch__reply { display: flex; align-items: center; gap: .6rem; padding: .4rem .95rem;
             border-top: 1px solid var(--rule); font-size: .8rem; color: var(--vellum-dim); }
.ch__reply b { color: var(--vellum); font-weight: 600; }
.ch__reply i { opacity: .8; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ch__reply button { margin-left: auto; background: none; border: 0; color: var(--vellum-dim);
                    font-size: 1.1rem; line-height: 1; cursor: var(--cur-point); }
.ch__composer { border-top: 1px solid var(--rule); padding: .6rem .85rem .7rem; }
.ch__composer textarea { width: 100%; background: var(--ink); border: 1px solid var(--rule);
                         color: var(--vellum); font-family: var(--body); font-size: .95rem;
                         padding: .5rem .65rem; resize: none; line-height: 1.5; }
.ch__composer textarea:focus { outline: 1px solid var(--accent); border-color: var(--accent); }
.ch__composer textarea:disabled { opacity: .45; }
.ch__composer .pf__hint { margin-top: .3rem; min-height: 1em; font-size: .8rem; }

@media (max-width: 700px) {
  .ch__app { grid-template-columns: 1fr; height: auto; }
  .ch__side { border-right: 0; border-bottom: 1px solid var(--rule); }
  .ch__channels { display: flex; overflow-x: auto; padding: .4rem .5rem; gap: .2rem; }
  .ch__chan { border-left: 0; border-bottom: 2px solid transparent; white-space: nowrap; }
  .ch__chan--on { border-left: 0; border-bottom-color: var(--accent); }
  .ch__feed { height: 55vh; }
  .ch__msg { padding-right: 0; }
  .ch__acts { position: static; display: flex; margin-top: .3rem; }
}

/* reactions, the picker, and pictures */
.ch__reacts { display: flex; flex-wrap: wrap; gap: .3rem; margin-top: .3rem; }
.ch__pill { display: inline-flex; align-items: center; gap: .3rem; background: var(--ink);
            border: 1px solid var(--rule); color: var(--vellum); font-family: var(--body);
            font-size: .95rem; line-height: 1.2; padding: .16rem .5rem; cursor: var(--cur-point); }
.ch__pill:hover { border-color: var(--vellum-dim); }
.ch__pill--mine { border-color: var(--accent);
                  background: color-mix(in srgb, var(--accent) 14%, var(--ink)); }
.ch__pill-n { font-variant-numeric: tabular-nums; font-size: .74rem; color: var(--vellum-dim); }
.ch__pill--mine .ch__pill-n { color: var(--vellum); }
/* the + only appears on hover, so a quiet message stays quiet */
.ch__pill--add { opacity: 0; color: var(--vellum-dim); padding: .12rem .5rem; }
.ch__msg:hover .ch__pill--add, .ch__msg:focus-within .ch__pill--add { opacity: 1; }
.ch__reacts:has(.ch__pill--mine) .ch__pill--add,
.ch__reacts .ch__pill--add:focus { opacity: 1; }

.ch__pick { position: absolute; z-index: 5; width: 360px; max-height: 340px; display: flex;
            flex-direction: column; background: var(--ink-2); border: 1px solid var(--rule);
            box-shadow: 0 10px 30px -12px #000; }
/* The search sits above a scrolling list, so it needs its own layer AND its focus
   ring drawn INSIDE its box: an outline is painted outside the border, which left a
   green line hanging over the list with emoji sliding under it. [OWNER 2026-08-27] */
.ch__pick input { position: relative; z-index: 2; flex: none;
                  background: var(--ink-2); border: 0; border-bottom: 1px solid var(--rule);
                  color: var(--vellum); font-family: var(--body); font-size: .92rem;
                  padding: .55rem .7rem; }
.ch__pick input:focus { outline: 1px solid var(--accent); outline-offset: -1px; }
.ch__pick-body { position: relative; z-index: 1; overflow-y: auto; overflow-x: hidden;
                 padding: .4rem; display: flex; flex-wrap: wrap; gap: .15rem; }
.ch__pick-body h4 { width: 100%; margin: .4rem 0 .2rem; font-family: var(--body); font-weight: 600;
                    font-size: .64rem; letter-spacing: .16em; text-transform: uppercase;
                    color: var(--vellum-dim); }
.ch__pick-e { background: none; border: 0; font-size: 1.5rem; line-height: 1; padding: .3rem;
              cursor: var(--cur-point); border-radius: 2px; display: grid; place-items: center;
              width: 2.5rem; height: 2.5rem; }
.ch__pick-e .ch__emoji { height: 1.7rem; }
.ch__pick-e:hover { background: color-mix(in srgb, var(--vellum) 10%, transparent); }
.ch__pick-none { width: 100%; color: var(--vellum-dim); font-size: .84rem; padding: .5rem; }

.ch__composer-row { display: flex; align-items: flex-end; gap: .5rem; }
.ch__composer-row textarea { flex: 1; }
.ch__attach { flex: none; width: 2rem; height: 2rem; background: var(--ink);
              border: 1px solid var(--rule); color: var(--vellum-dim); font-size: 1.1rem;
              line-height: 1; cursor: var(--cur-point); }
.ch__attach:hover { color: var(--vellum); border-color: var(--vellum-dim); }

.ch__feed.is-drop { outline: 2px dashed var(--accent); outline-offset: -6px; }
/* a favourited emoji keeps a gold underline wherever it is shown */
.ch__pick-e.is-fav { box-shadow: inset 0 -2px 0 var(--gold); }
.ch__pick-hint { width: 100%; margin: .1rem 0 0; padding: .35rem .3rem 0; font-size: .68rem;
                 color: var(--vellum-dim); border-top: 1px solid var(--rule); }
/* the :name: menu above the composer */
.ch__ac { position: absolute; left: .85rem; right: .85rem; bottom: 100%; z-index: 6;
          background: var(--ink-2); border: 1px solid var(--rule); max-height: 13rem;
          overflow-y: auto; box-shadow: 0 -8px 24px -14px #000; }
.ch__composer { position: relative; }
.ch__ac-row { display: flex; align-items: center; gap: .5rem; width: 100%; background: none;
              border: 0; color: var(--vellum); font-family: var(--body); font-size: .88rem;
              padding: .32rem .6rem; cursor: var(--cur-point); text-align: left; }
.ch__ac-row.is-on, .ch__ac-row:hover { background: color-mix(in srgb, var(--vellum) 9%, transparent); }
.ch__ac-row i { margin-left: auto; font-size: .68rem; letter-spacing: .12em;
                text-transform: uppercase; color: var(--vellum-dim); font-style: normal; }

/* ---------- faces ------------------------------------------------------------
   Every message carries one. The default is a plain head drawn in SVG -- no file,
   no request, and it never fails to load. A held soul or an uploaded picture takes
   its place. Grouped messages leave the column empty rather than repeating it. */
.ch__msg { display: grid; grid-template-columns: 2.6rem minmax(0, 1fr); column-gap: .7rem; }
.ch__face { grid-row: span 3; width: 2.6rem; height: 2.6rem; margin-top: .15rem;
            border: 1px solid var(--rule); background: var(--ink); overflow: hidden;
            display: block; }
.ch__face img, .ch__face svg { width: 100%; height: 100%; display: block; object-fit: cover; }
.ch__face--gold { border-color: color-mix(in srgb, var(--gold) 70%, var(--rule));
                  box-shadow: 0 0 10px -4px color-mix(in srgb, var(--gold) 70%, transparent); }
.ch__face--none { visibility: hidden; }
.ch__head, .ch__body, .ch__reacts, .ch__replied { grid-column: 2; }
.ch__msg .ch__acts { grid-column: 2; }

/* the picture the reader chose for themselves */
.ch__me { gap: .55rem; }
.ch__me-face { width: 2rem; height: 2rem; flex: none; border: 1px solid var(--rule);
               background: var(--ink); overflow: hidden; cursor: var(--cur-point); }
.ch__me-face img, .ch__me-face svg { width: 100%; height: 100%; display: block; object-fit: cover; }
.ch__me-face:hover { border-color: var(--vellum-dim); }

/* settings */
.ch__settings { position: absolute; inset: 0; z-index: 8; background: color-mix(in srgb, var(--ink) 92%, transparent);
                display: grid; place-items: center; padding: 1.5rem; }
.ch__settings-box { width: min(620px, 100%); max-height: 100%; overflow-y: auto;
                    background: var(--ink-2); border: 1px solid var(--rule); padding: 1.4rem 1.5rem 1.6rem; }
.ch__settings h3 { font-family: var(--body); font-weight: 600; font-size: 1.05rem; letter-spacing: .1em;
                   text-transform: uppercase; color: var(--vellum); margin: 0 0 .3rem;
                   border-bottom: 1px solid var(--rule); padding-bottom: .5rem; }
.ch__settings h4 { font-size: .7rem; letter-spacing: .16em; text-transform: uppercase;
                   color: var(--vellum-dim); margin: 1.2rem 0 .5rem; }
.ch__settings-x { position: absolute; top: 1rem; right: 1.2rem; background: none; border: 0;
                  color: var(--vellum-dim); font-size: 1.6rem; line-height: 1; cursor: var(--cur-point); }
.ch__settings-x:hover { color: var(--vellum); }
.ch__souls { display: grid; grid-template-columns: repeat(auto-fill, minmax(74px, 1fr)); gap: .5rem; }
.ch__soul { padding: 0; background: var(--ink); border: 1px solid var(--rule); cursor: var(--cur-point);
            aspect-ratio: 1; overflow: hidden; }
.ch__soul img { width: 100%; height: 100%; object-fit: cover; display: block; }
.ch__soul:hover { border-color: var(--gold); }
.ch__soul.is-on { border-color: var(--gold); box-shadow: 0 0 0 1px var(--gold) inset; }
.ch__avatar-row { display: flex; align-items: center; gap: 1rem; flex-wrap: wrap; }
.ch__avatar-now { width: 4.5rem; height: 4.5rem; border: 1px solid var(--rule); background: var(--ink);
                  overflow: hidden; flex: none; }
.ch__avatar-now img, .ch__avatar-now svg { width: 100%; height: 100%; object-fit: cover; display: block; }
.ch__settings .btn { width: auto; display: inline-block; padding: .45rem 1.3rem; font-size: .9rem; }

/* send button: Enter is not the only way anyone should be able to speak */
.ch__send { flex: none; background: var(--accent); border: 0; color: #05140a; font-family: var(--body);
            font-weight: 600; font-size: .9rem; padding: .5rem 1.1rem; cursor: var(--cur-point);
            align-self: stretch; }
.ch__send:hover { filter: brightness(1.1); }
.ch__send:disabled { opacity: .4; cursor: default; }
/* the two tools beside the message box */
.ch__tool { flex: none; width: 2.2rem; height: 2.2rem; display: grid; place-items: center;
            background: none; border: 1px solid var(--rule); color: var(--vellum-dim);
            cursor: var(--cur-point); padding: 0; }
.ch__tool svg { width: 1.15rem; height: 1.15rem; }
.ch__tool:hover { color: var(--vellum); border-color: var(--vellum-dim); }
.ch__tool:disabled { opacity: .35; cursor: default; }

/* pictures waiting to go, above the message box */
.ch__tray { display: flex; flex-wrap: wrap; gap: .5rem; padding: .55rem 0 .1rem; }
.ch__staged { position: relative; margin: 0; width: 84px; height: 84px; border: 1px solid var(--rule);
              background: var(--ink); overflow: hidden; }
.ch__staged img { width: 100%; height: 100%; object-fit: cover; display: block; }
.ch__staged-x { position: absolute; top: 2px; right: 2px; width: 1.25rem; height: 1.25rem;
                display: grid; place-items: center; background: rgba(5,5,6,.82);
                border: 1px solid var(--rule); color: var(--vellum); font-size: .9rem;
                line-height: 1; cursor: var(--cur-point); padding: 0; }
.ch__staged-x:hover { border-color: var(--vellum); }

/* and once sent: one keeps its shape, several become a grid */
.ch__atts { display: grid; gap: .3rem; margin-top: .45rem; max-width: 440px; }
.ch__atts--1 { grid-template-columns: 1fr; }
.ch__atts--2, .ch__atts--3, .ch__atts--4 { grid-template-columns: 1fr 1fr; }
.ch__att { margin: 0; }
.ch__att img { display: block; max-width: 100%; height: auto; border: 1px solid var(--rule);
               background: var(--ink); }
.ch__att--tile { aspect-ratio: 1; overflow: hidden; }
.ch__att--tile img { width: 100%; height: 100%; object-fit: cover; }

/* the GIF picker. Tenor's mark stays visible wherever their results are: it is a
   condition of using them, not a decoration. */
.ch__tool--text { font-family: var(--body); font-weight: 600; font-size: .68rem;
                  letter-spacing: .06em; }
.ch__gifs { position: absolute; z-index: 6; width: 380px; max-height: 400px; display: flex;
            flex-direction: column; background: var(--ink-2); border: 1px solid var(--rule);
            box-shadow: 0 10px 30px -12px #000; }
.ch__gifs[hidden] { display: none; }
.ch__gifs-top { display: flex; align-items: stretch; border-bottom: 1px solid var(--rule); }
.ch__gifs-top input { flex: 1; background: var(--ink-2); border: 0; color: var(--vellum);
                      font-family: var(--body); font-size: .92rem; padding: .55rem .7rem; }
.ch__gifs-top input:focus { outline: 1px solid var(--accent); outline-offset: -1px; }
.ch__gifs-top button { background: none; border: 0; color: var(--vellum-dim); font-size: 1.3rem;
                       line-height: 1; padding: 0 .7rem; cursor: var(--cur-point); }
.ch__gifs-top button:hover { color: var(--vellum); }
.ch__gif-grid { flex: 1; overflow-y: auto; padding: .4rem; display: grid;
                grid-template-columns: 1fr 1fr; gap: .35rem; align-content: start; }
.ch__gif { padding: 0; border: 1px solid var(--rule); background: var(--ink);
           cursor: var(--cur-point); overflow: hidden; aspect-ratio: 4 / 3; }
.ch__gif img { width: 100%; height: 100%; object-fit: cover; display: block; }
.ch__gif:hover { border-color: var(--accent); }
.ch__gifs .pf__hint { padding: 0 .7rem; min-height: 0; }
.ch__tenor { margin: 0; padding: .35rem .7rem .5rem; font-size: .64rem; letter-spacing: .14em;
             text-transform: uppercase; color: var(--vellum-dim); border-top: 1px solid var(--rule); }

/* ---------- the paintings ----------------------------------------------------
   Two wide scenes by the artist, used as ground rather than decoration: the duel
   behind the Trials, the company behind the Choir.

   Two lessons are baked into this block, both paid for by the painting silently
   turning into a black rectangle while the DOM insisted it was fine:

   1. NO NEGATIVE z-index. The first version put the image at z-index -2 inside an
      `isolation: isolate` header. That is legal CSS and it renders — until a
      compositor promotes the layer and paints it behind the page. Here the picture
      is simply the first positioned child, the scrim is ::after so it lands on top
      of it, and the words get z-index 1. Plain painting order, nothing to promote.
   2. NO TRANSFORM ANIMATION on it, for the same reason: a drifting scene was the
      first thing to vanish.

   The scrim does one job -- putting the DARKNESS WHERE THE WORDS ARE, in a band at
   the foot of the frame -- rather than veiling the whole painting to guarantee
   contrast, which is what buried it on the first attempt. [OWNER 2026-08-27] */
.soon--scene, .hero--scene { position: relative; overflow: hidden; }
.soon__scene, .hero__scene {
  position: absolute; inset: 0; width: 100%; height: 100%;
  object-fit: cover;
  opacity: .82; filter: saturate(.92) contrast(1.02);
}
.soon--scene::after, .hero--scene::after {
  content: ""; position: absolute; inset: 0; pointer-events: none;
  background:
    linear-gradient(to bottom,
      var(--ink) 0%,
      rgba(10, 10, 11, .55) 12%,
      rgba(10, 10, 11, .12) 34%,
      rgba(10, 10, 11, .55) 66%,
      rgba(10, 10, 11, .94) 88%,
      var(--ink) 100%),
    radial-gradient(140% 120% at 50% 40%,
      rgba(10, 10, 11, 0) 40%, rgba(10, 10, 11, .55) 100%);
}
/* the words sit above both */
.soon--scene .soon__wrap, .hero--scene .wrap { position: relative; z-index: 1; }

/* over a painting the type carries its own weight of shadow */
.soon--scene h1, .hero--scene h1 { text-shadow: 0 2px 26px rgba(10, 10, 11, .95), 0 1px 3px rgba(10, 10, 11, .9); }
.soon--scene .eyebrow, .hero--scene .eyebrow { text-shadow: 0 1px 12px rgba(10, 10, 11, .95); }
.hero--scene .lede, .hero--scene .hero__sub { text-shadow: 0 1px 14px rgba(10, 10, 11, .9); }

/* room for a painting, with the type pushed into the dark band at its foot */
.soon--scene { padding: clamp(7rem, 26vh, 15rem) 0 clamp(3rem, 8vh, 5rem); }
.hero--scene { padding-top: clamp(3rem, 9vh, 6rem); }

/* The mark steps back on a scene page: at its usual size it landed square on the
   barbarian and on the knight -- the very figure the painting is about. */
.soon--scene .soon__logo {
  width: clamp(64px, 8vw, 104px);
  margin-bottom: clamp(1rem, 3vh, 2rem);
  filter: drop-shadow(0 0 22px rgba(145, 35, 160, .45));
}

/* On a phone a wide painting becomes a sliver and the type has nowhere to go, so
   the picture steps back and the dark band grows. */
@media (max-width: 700px) {
  .soon__scene, .hero__scene { opacity: .6; }
  .soon--scene::after, .hero--scene::after {
    background:
      linear-gradient(to bottom,
        var(--ink) 0%, rgba(10, 10, 11, .6) 10%, rgba(10, 10, 11, .3) 28%,
        rgba(10, 10, 11, .78) 60%, var(--ink) 92%),
      radial-gradient(150% 110% at 50% 40%,
        rgba(10, 10, 11, 0) 30%, rgba(10, 10, 11, .6) 100%);
  }
  .soon--scene { padding: clamp(4rem, 16vh, 8rem) 0 clamp(2.5rem, 7vh, 4rem); }
}
