/* LinguineTCG Express -- warm trattoria/menu theme. */
:root {
  --cream: #fbf6ec;
  --parchment: #f4ead5;
  --ink: #2b2118;
  --tomato: #c1440e;
  --tomato-dark: #9c360b;
  --olive: #556b2f;
  --gold: #c79a3a;
  --basil: #3f7d3f;
  --line: #d9c9a8;
  --shadow: 0 6px 20px rgba(43, 33, 24, 0.12);
  --serif: "Iowan Old Style", "Palatino Linotype", Palatino, "Book Antiqua", Georgia, serif;
  --sans: "Helvetica Neue", Helvetica, Arial, sans-serif;
}

* { box-sizing: border-box; }
html, body { margin: 0; padding: 0; }
body {
  font-family: var(--sans);
  color: var(--ink);
  background: var(--cream);
  background-image: radial-gradient(rgba(199,154,58,0.05) 1px, transparent 1px);
  background-size: 22px 22px;
  line-height: 1.55;
}
a { color: var(--tomato); text-decoration: none; }
a:hover { text-decoration: underline; }
h1, h2, h3, h4 { font-family: var(--serif); font-weight: 700; line-height: 1.15; color: var(--ink); }

.container { max-width: 1060px; margin: 0 auto; padding: 0 20px; }
.narrow { max-width: 680px; }

/* ---- Top nav -------------------------------------------------------------*/
.nav {
  background: var(--ink);
  color: var(--cream);
  border-bottom: 3px solid var(--gold);
}
.nav .container {
  display: flex; align-items: center; gap: 12px;
  min-height: 62px; padding-top: 6px; padding-bottom: 6px;
  /* NOT flex-wrap: wrap. With wrapping on, a full set of links pushed the
     brand onto its own line above them; the links wrap among themselves
     instead, so the brand always sits on the same row. */
  flex-wrap: nowrap;
}
.brand {
  font-family: var(--serif); font-size: 1.35rem; font-weight: 700;
  color: var(--cream); display: flex; align-items: center; gap: 9px;
}
.brand:hover { text-decoration: none; color: #fff; }
.brand .dot { color: var(--tomato); }
/* The links sit immediately to the RIGHT of the brand, rather than being
   pushed to the far edge of the bar -- so the logo and name read as the
   start of one group instead of two things at opposite ends. `margin-right:
   auto` keeps whatever follows (nothing, currently) hard right. */
.nav-links {
  display: flex; gap: 4px; margin-right: auto; align-items: center;
  flex-wrap: wrap;
  /* min-width:0 lets this shrink inside a nowrap row rather than forcing
     the brand out of it. */
  min-width: 0;
}
/* Never let the brand be squeezed or wrapped below the links. */
.brand { flex: 0 0 auto; }
.nav-links a, .nav-links form button {
  color: var(--cream); padding: 6px 9px; border-radius: 6px; font-size: 0.88rem;
  white-space: nowrap;
  background: none; border: none; cursor: pointer; font-family: var(--sans);
}
.nav-links a:hover { background: rgba(255,255,255,0.1); text-decoration: none; }
.nav-links .credits {
  background: var(--tomato); color: #fff; padding: 5px 11px; border-radius: 999px;
  font-weight: 600; font-size: 0.85rem;
}

/* ---- Buttons -------------------------------------------------------------*/
.btn {
  display: inline-block; padding: 11px 20px; border-radius: 8px; border: none;
  font-size: 1rem; font-weight: 600; cursor: pointer; font-family: var(--sans);
  background: var(--tomato); color: #fff; transition: background .15s, transform .05s;
}
.btn:hover { background: var(--tomato-dark); text-decoration: none; color: #fff; }
.btn:active { transform: translateY(1px); }
.btn.ghost { background: transparent; color: var(--tomato); border: 2px solid var(--tomato); }
.btn.ghost:hover { background: var(--tomato); color: #fff; }
.btn.olive { background: var(--olive); }
.btn.olive:hover { background: #43571f; }
.btn.big { padding: 15px 30px; font-size: 1.12rem; }
.btn.block { display: block; width: 100%; text-align: center; }
.btn.small { padding: 6px 12px; font-size: 0.85rem; }

/* ---- Cards / panels ------------------------------------------------------*/
.panel {
  background: #fff; border: 1px solid var(--line); border-radius: 12px;
  padding: 24px; box-shadow: var(--shadow); margin-bottom: 22px;
}
.grid { display: grid; gap: 18px; }
.grid.cols-2 { grid-template-columns: repeat(2, 1fr); }
.grid.cols-3 { grid-template-columns: repeat(3, 1fr); }
.grid.cols-4 { grid-template-columns: repeat(4, 1fr); }
/* cols-2 drops straight to one column at the same breakpoint the nav
   collapses at: a two-up grid squeezed into two ~350px columns is exactly
   where a folder picker or a data table stops being readable, so there is
   no in-between width worth keeping it side-by-side for. cols-3/cols-4
   still step down through two columns first -- those hold shorter tiles
   that a halfway width suits fine. */
@media (max-width: 760px) { .grid.cols-2 { grid-template-columns: 1fr; } }
@media (max-width: 760px) { .grid.cols-3, .grid.cols-4 { grid-template-columns: 1fr 1fr; } }
@media (max-width: 480px) { .grid.cols-2, .grid.cols-3, .grid.cols-4 { grid-template-columns: 1fr; } }

/* ---- Hero ----------------------------------------------------------------*/
.hero {
  text-align: center; padding: 70px 20px 50px;
  background: linear-gradient(180deg, var(--parchment), var(--cream));
  border-bottom: 1px solid var(--line);
}
.hero h1 { font-size: 3rem; margin: 0 0 10px; }
.hero .tag { font-size: 1.3rem; color: var(--olive); font-family: var(--serif); font-style: italic; }
.hero .sub { max-width: 620px; margin: 18px auto 26px; color: #5b4c3a; font-size: 1.08rem; }
.hero .cta-row { display: flex; gap: 14px; justify-content: center; flex-wrap: wrap; }

/* ---- Menu (checkout & pricing) ------------------------------------------*/
.menu-sheet {
  background: var(--parchment); border: 2px solid var(--gold); border-radius: 10px;
  padding: 34px 40px; box-shadow: var(--shadow); position: relative;
}
.menu-sheet::before, .menu-sheet::after {
  content: ""; position: absolute; left: 24px; right: 24px; height: 2px; background: var(--gold);
}
.menu-sheet::before { top: 14px; } .menu-sheet::after { bottom: 14px; }
.menu-head { text-align: center; margin: 12px 0 26px; }
.menu-head h2 { font-size: 2.2rem; margin: 0; letter-spacing: 1px; }
.menu-head .flourish { color: var(--tomato); letter-spacing: 6px; }
.menu-section-title {
  font-family: var(--serif); font-style: italic; color: var(--olive);
  border-bottom: 1px dashed var(--line); padding-bottom: 4px; margin: 26px 0 14px;
  font-size: 1.15rem;
}
.menu-item { display: flex; align-items: baseline; gap: 10px; margin: 16px 0; }
.menu-item .name { font-family: var(--serif); font-size: 1.25rem; font-weight: 700; }
.menu-item .dots { flex: 1; border-bottom: 2px dotted var(--line); transform: translateY(-4px); }
.menu-item .price { font-family: var(--serif); font-size: 1.25rem; font-weight: 700; color: var(--tomato); }
.menu-item .desc { color: #6b5c48; font-size: 0.95rem; margin: 2px 0 0; font-style: italic; }
.menu-item-block { margin: 22px 0; }

/* ---- Forms ---------------------------------------------------------------*/
label { display: block; font-weight: 600; margin: 14px 0 5px; font-size: 0.95rem; }
input[type=text], input[type=email], input[type=password], textarea, select {
  width: 100%; padding: 11px 13px; border: 1px solid var(--line); border-radius: 8px;
  font-size: 1rem; font-family: var(--sans); background: #fff; color: var(--ink);
}
textarea { min-height: 140px; resize: vertical; }
input:focus, textarea:focus, select:focus { outline: 2px solid var(--gold); border-color: var(--gold); }
.hint { color: #7a6a54; font-size: 0.86rem; margin-top: 4px; }

/* ---- Flash messages ------------------------------------------------------*/
.flashes { margin: 16px 0 0; }
.flash {
  padding: 12px 16px; border-radius: 8px; margin-bottom: 10px; border-left: 5px solid;
  background: #fff;
}
.flash.success { border-color: var(--basil); }
.flash.danger  { border-color: var(--tomato); }
.flash.warning { border-color: var(--gold); }
.flash.info    { border-color: var(--olive); }

/* ---- Trading-card visuals -----------------------------------------------*/
.card-tile {
  background: #fff; border: 1px solid var(--line); border-radius: 10px; overflow: hidden;
  box-shadow: var(--shadow); display: flex; flex-direction: column;
}
.card-tile img { width: 100%; aspect-ratio: 63/88; object-fit: cover; background: #efe6d2; display: block; }
.card-tile .meta { padding: 10px 12px; }
.card-tile .meta .cname { font-weight: 700; font-size: 0.98rem; }
.card-tile .meta .csub { color: #7a6a54; font-size: 0.82rem; }
.card-tile .meta .cprice { color: var(--tomato); font-weight: 700; margin-top: 4px; }
.badge {
  display: inline-block; background: var(--gold); color: #fff; font-size: 0.7rem;
  padding: 2px 8px; border-radius: 999px; font-weight: 700; letter-spacing: .5px;
}

/* ---- Profile -------------------------------------------------------------*/
.chef-header { text-align: center; padding: 20px 0; }
.avatar {
  width: 130px; height: 130px; border-radius: 50%; object-fit: cover;
  border: 4px solid var(--gold); background: var(--parchment); box-shadow: var(--shadow);
  /* Hard square pixels, never smoothed: uploads are deliberately crushed to
     a 24px grid so they sit alongside the hand-drawn pixel faces, and the
     browser would otherwise blur that work straight back out. */
  image-rendering: pixelated;
}
.motto { font-family: var(--serif); font-style: italic; color: var(--olive); font-size: 1.25rem; margin-top: 8px; }

/* ---- Stat pills ----------------------------------------------------------*/
.stats { display: flex; gap: 16px; flex-wrap: wrap; justify-content: center; }
.stat { background: #fff; border: 1px solid var(--line); border-radius: 10px; padding: 14px 22px; text-align: center; box-shadow: var(--shadow); }
.stat .n { font-family: var(--serif); font-size: 1.9rem; font-weight: 700; color: var(--tomato); }
.stat .l { font-size: 0.8rem; text-transform: uppercase; letter-spacing: 1px; color: #7a6a54; }

/* ---- Tables --------------------------------------------------------------*/
table.tidy { width: 100%; border-collapse: collapse; }
table.tidy th, table.tidy td { text-align: left; padding: 9px 10px; border-bottom: 1px solid var(--line); font-size: 0.92rem; }
table.tidy th { color: #7a6a54; text-transform: uppercase; font-size: 0.74rem; letter-spacing: 1px; }

/* ---- Footer --------------------------------------------------------------*/
footer.site {
  background: var(--ink); color: var(--parchment); margin-top: 50px; padding: 34px 0;
  font-size: 0.9rem; border-top: 3px solid var(--gold);
}
footer.site a { color: var(--gold); }
footer.site .cols { display: flex; gap: 40px; flex-wrap: wrap; justify-content: space-between; }

.muted { color: #7a6a54; }
.center { text-align: center; }
.mt { margin-top: 22px; } .mb { margin-bottom: 22px; }
section.pad { padding: 40px 0; }

/* ---- Spinning linguine + the nom nom tab ---------------------------------*/
/* Same spirit as the local tool's #site-logo-spin: a slow, endless 3D twirl
   of the brand plate. Kept subtle (no scaleX stretch here -- it's inline in
   a nav bar, not a hero banner). */
@keyframes logo-spin {
  from { transform: perspective(240px) rotateY(0deg); }
  to   { transform: perspective(240px) rotateY(360deg); }
}
#site-logo-spin {
  display: inline-block;
  animation: logo-spin 8s linear infinite;
}
@media (prefers-reduced-motion: reduce) {
  #site-logo-spin { animation: none; }
}

.nav-links a.nomnom {
  font-weight: 700;
  color: var(--tomato, #c0392b);
}

/* ---- Step progress line ---------------------------------------------------*/
.steps-line {
  display: flex; align-items: center; gap: 10px;
  flex-wrap: wrap; padding: 12px 0;
}
.step-node {
  display: inline-flex; align-items: center; gap: 8px;
  text-decoration: none; color: inherit; opacity: .55;
}
.step-node .dot {
  display: inline-flex; align-items: center; justify-content: center;
  width: 30px; height: 30px; border-radius: 50%;
  border: 2px solid var(--line, #d8c9ae); font-weight: 700; font-size: .8rem;
  background: var(--parchment, #f6efe2);
}
.step-node .lbl { font-size: .85rem; }
.step-node .cnt {
  background: var(--tomato, #c0392b); color: #fff; border-radius: 999px;
  font-size: .72rem; font-weight: 700; padding: 1px 8px;
}
.step-node.current { opacity: 1; }
.step-node.current .dot {
  border-color: var(--basil, #2e7d32); background: var(--basil, #2e7d32);
  color: #fff;
}
.step-node.current .lbl { font-weight: 700; }
.step-bar { flex: 1; min-width: 18px; height: 2px; background: var(--line, #d8c9ae); }

/* Set-window comparison block: yours stacked over the match's at equal
   height so the symbols line up; scrolls sideways rather than squishing. */
.setwins { display: flex; flex-direction: column; gap: 6px; overflow-x: auto; max-width: 420px; }
.setwins img { max-width: none; }

/* Compact 2a review table: everything on screen, no horizontal scroll. */
table.tidy.compact th, table.tidy.compact td { padding: 5px 6px; font-size: 0.82rem; }
table.tidy.compact td { vertical-align: middle; }

/* ========================= THE GALAXY 90s THEME =========================
   GeoCities energy: deep-space starfield with shooting stars, Comic Sans
   headings, beveled panels in tacky-but-charming pastels, grainy clip-art
   (SVG feTurbulence -- our stock photos), zero emojis. Appended last so it
   overrides the base styles above. */
/* Base: every page lives in the End -- near-black violet void, heavy
   static grain, the rink carpet barely-there in the distance. */
body {
  color: #1a1a1a;
  font-family: Verdana, Geneva, sans-serif;
  background-color: #05030a;
  background-image:
    url("../img/noise.svg"),
    url("../img/carpet-dim.svg");
  background-size: 180px 180px, 150px 150px;
}

/* Home page only: the full galaxy. Blacker-than-blue void with the rink
   carpet as ONE squiggle overlay up front + the dense rainbow starfield
   behind it. The squiggle carpet SCROLLS with the page; the stars are the
   distant sky, pinned to the viewport so they don't move on scroll. */
body.galaxy {
  background-color: #030109;
  background-image:
    url("../img/noise.svg");
  background-size: 180px 180px;
  background-position: 0 0;
}
body.galaxy { position: relative; }

/* The squiggle field: ONE layer holding the whole tiled pattern, so the
   entire arrangement turns together rather than each shape spinning in
   place. Oversized (200%, offset -50%) so a rotated square still covers the
   viewport corners; fixed so it can never add scrollbars. Rotation and the
   scroll offset are driven together from one rAF loop in base.html. */
.squiggle-field {
  position: fixed; top: -50%; left: -50%; width: 200%; height: 200%;
  z-index: 0; pointer-events: none;
  background: url("../img/carpet-mid.svg") repeat;
  background-size: 120px 120px;
  /* Own compositor layer: the field only ever changes transform, so it can
     be moved without repainting the tiled bitmap underneath it. */
  will-change: transform;
  backface-visibility: hidden;
  contain: strict;
}

/* Offsets are exact multiples of each squiggle tile (150px and 110px) so the
   loop is seamless -- layer 1 drifts right/up, layer 2 left/down. */
@media (prefers-reduced-motion: reduce) {
  body.galaxy { animation: none; }
}

/* ---- The Collection: gold cursor, and money falls out of it ------------ */
body.ep-main-collection_archive,
body.ep-main-collection_archive a,
body.ep-main-collection_archive button,
body.ep-main-collection_archive .btn {
  cursor: url("../img/cursor-gold.svg") 3 3, auto;
}
.money-bill {
  position: fixed; width: 40px; pointer-events: none; z-index: 3;
  image-rendering: pixelated;
}

/* Per-tab themed backdrops: the pasted 90s stock photos, dimmed to the
   void so panels stay readable, grain always on top. */
/* nom nom linguine: the dingy 90s Italian kitchen -- warm greasy-brown
   dim over the kitchen photo. */
body.bp-scan {
  background-image: url("../img/noise.svg"), url("../img/noise.svg");
  background-size: 180px 180px, 97px 97px;
  background-position: center;
}
/* The Pass keeps its own service photo. */
body.ep-main-collection {
  background-image: url("../img/noise.svg"), url("../img/noise.svg");
  background-size: 180px 180px, 97px 97px;
  background-position: center;
}
/* The Collection: the bank. Money-counting backdrop, banker's green and
   vault gold on every accent. */
body.ep-main-collection_archive {
  /* The whole backdrop moved to the fixed ::before below --
     `background-attachment: fixed` repaints the entire backdrop on every
     scroll frame, which is what made this page crawl; a fixed-position
     layer is composited once instead. Only the image list is reset --
     background-color stays whatever the base body rule sets, because
     overriding it here tinted the whole page green. */
  background-image: none;
}
body.ep-main-collection_archive::before {
  content: ""; position: fixed; inset: 0; z-index: -1; pointer-events: none;
  /* Grain listed FIRST so it stays on top of the photo -- a negative-z layer
     paints over the body's own background, so the noise has to live here too
     or the page loses its 90s grain entirely. */
  background-image:
    url("../img/noise.svg"),
    url("../img/noise.svg"),
    linear-gradient(rgba(6,26,12,.84), rgba(3,16,8,.92)),
    url("../img/collection-bank.jpg");
  background-size: 180px 180px, 97px 97px, cover, cover;
  background-position: center;
  background-repeat: repeat, repeat, no-repeat, no-repeat;
}

body.ep-main-collection_archive h1 { text-shadow: 2px 2px 0 #0a5c2e, 4px 4px 0 #032410; }
body.ep-main-collection_archive .panel,
body.ep-main-collection_archive .stat { border-color: #d4af37; background: #f6f2e4; }
body.ep-main-collection_archive table.tidy { border-color: #0a5c2e; }
body.ep-main-collection_archive table.tidy th { background: #0a5c2e; border-color: #032410; }
body.ep-main-collection_archive .stat .n { color: #0a5c2e; }
body.ep-main-collection_archive .badge { background: #d4af37; border-color: #0a5c2e; }
body.ep-main-collection_archive .btn, body.ep-main-collection_archive button {
  background: #d4af37 !important; border-color: #f2e2a0 !important; color: #032410 !important;
}
body.ep-main-collection_archive .steps-line { border-color: #d4af37; }
/* Dining Hall (and A La Carte inside it): candlelit table service. */
body.bp-dining_hall, body.bp-a_la_carte {
  /* Only the tiling noise on the body. The photo moved to the composited
     layer below -- `background-attachment: fixed` re-rasterises a full-screen
     image on every scroll frame, which is the juddering. Every other page was
     converted for this reason; this one was missed. */
  background-image: url("../img/noise.svg"), url("../img/noise.svg");
  background-size: 180px 180px, 97px 97px;
  background-position: center;
}
body.bp-dining_hall::before, body.bp-a_la_carte::before {
  content: ""; position: fixed; inset: 0; z-index: -1; pointer-events: none;
  background-image: linear-gradient(rgba(30,10,6,.8), rgba(20,6,4,.9)),
                    url("../img/dininghall.jpg");
  background-size: cover; background-position: center;
  will-change: transform; transform: translateZ(0);
}
/* The back office: brick-and-mortar clutter, plus cobwebs in the corners. */
body.bp-back_office {
  /* Only the tiling noise stays on the body. The office photo moved to the
     composited ::before below: `background-attachment: fixed` on a
     full-screen photo re-rasterises it on every scroll frame, which is what
     made this page judder while every other page had already been fixed. */
  background-image: url("../img/noise.svg"), url("../img/noise.svg");
  background-size: 180px 180px, 97px 97px;
  background-position: center;
}
body.bp-back_office::before {
  content: ""; position: fixed; inset: 0; z-index: -1; pointer-events: none;
  background-image: linear-gradient(rgba(18,8,38,.84), rgba(18,8,38,.92)),
                    url("../img/office.jpg");
  background-size: cover; background-position: center;
  will-change: transform; transform: translateZ(0);
}
/* Both cobwebs share one pseudo-element now that ::before carries the
   backdrop -- two background layers, the right-hand one pre-mirrored in its
   own file because a CSS transform cannot be applied per layer. */
body.bp-back_office::after {
  content: ""; position: fixed; inset: 0; z-index: 2; pointer-events: none;
  background-image: url("../img/cobweb.svg"), url("../img/cobweb-right.svg");
  background-repeat: no-repeat, no-repeat;
  background-position: top left, top right;
  background-size: 150px 150px, 150px 150px;
}
/* ---------------- whimsy chrome (galaxy era) + kept banner titles -------- */
body { font-family: Verdana, Geneva, sans-serif; }

h1, h2, h3, h4, .brand, summary {
  font-family: "Comic Sans MS", "Comic Sans", "Chalkboard SE", cursive;
}
/* the banner h1 stays -- explicitly liked */
h1 {
  background: #000080; color: #ffe12d;
  border: 3px ridge #c0c0c0;
  padding: 8px 12px; font-size: 1.5rem;
  text-shadow: 2px 2px 0 #000;
}
h2, h3 { color: #14004a; }
section.pad > p.muted, section.pad > .muted { color: #d9d2ff; }
.muted { color: #555; }
a { color: #7ad0ff; }
a:visited { color: #d0a9ff; }
.panel a, table a { color: #0000cc; }
.panel a:visited { color: #551a8b; }

/* Header bar: a flat late-90s/early-2000s title bar with beveled tab
   buttons -- squared corners, outset ridges, not the rounded pills. */
.nav {
  background: #000080;               /* classic navy title bar */
  border-bottom: 3px solid #c0c0c0;
  box-shadow: inset 0 -2px 0 #2a2aa0;
}
.brand { color: #fff !important; text-shadow: 1px 1px 0 #000; }
.nav-links a, .brand { color: #fff !important; }
.nav-links a {
  border: 2px outset #d8d8f0;
  border-radius: 0 !important;
  background: #2a2ab0;
  color: #fff !important;
  padding: 5px 10px;
  font-family: "MS Sans Serif", Tahoma, Geneva, sans-serif;
  font-size: 0.82rem;
}
.nav-links a:hover { background: #4a4ad0; text-decoration: none; }
.nav-links a:active { border-style: inset; }
.nav-links a.nomnom { color: #ffe12d !important; font-weight: 700; background: #6a2dff; }
.nav-links .credits {
  background: #ff2dd6; border: 2px outset #ffb3f2; border-radius: 0 !important;
  color: #fff; font-family: "Courier New", monospace;
}

/* Hamburger toggle: hidden entirely on desktop, where .nav-links already
   fits on one row. Below the breakpoint it takes over -- .nav-links stops
   being an inline row of links and becomes a collapsed panel the checkbox
   shows/hides, so six-plus nav links on a phone are one tap away instead
   of pushing the whole page down two or three rows' worth. Checkbox-driven
   on purpose: it opens and closes with zero JS, so there's no script that
   can fail to attach and leave the menu stuck. */
.navtoggle-input { position: absolute; opacity: 0; pointer-events: none; }
.navtoggle { display: none; }
@media (max-width: 760px) {
  .nav .container { flex-wrap: wrap; }
  .navtoggle {
    display: flex; flex-direction: column; justify-content: center; gap: 4px;
    width: 34px; height: 30px; padding: 4px; margin-left: auto;
    cursor: pointer; flex: 0 0 auto;
  }
  .navtoggle span {
    display: block; height: 3px; width: 100%; background: #fff;
    border-radius: 2px;
  }
  .nav-links {
    flex-basis: 100%; max-height: 0; overflow: hidden;
    flex-direction: column; align-items: stretch; gap: 2px;
    margin-right: 0; transition: max-height .2s ease;
  }
  .navtoggle-input:checked ~ .nav-links {
    max-height: 60vh; overflow-y: auto; margin-top: 8px;
  }
  .nav-links a, .nav-links form button, .nav-links .credits {
    width: 100%; box-sizing: border-box; text-align: left;
  }
}

.panel, .stat, .card-tile, .flash {
  background: #fffbe6;
  border: 3px ridge #b48cff;
  border-radius: 0 !important;
  box-shadow: 4px 4px 0 rgba(0,0,0,.45) !important;
}
.stat .n { color: #6a2dff; font-family: "Courier New", monospace; font-weight: 700; }
.stat .l { color: #333; }

.btn, button, .nav-links form button {
  background: #ffe12d !important;
  color: #14004a !important;
  border: 3px outset #fff6b3 !important;
  border-radius: 0 !important;
  font-family: "Comic Sans MS", "Comic Sans", "Chalkboard SE", cursive;
  font-size: 0.85rem;
  box-shadow: none !important;
}
.btn:active, button:active { border-style: inset !important; }
.btn:hover, button:hover { background: #ffd02d !important; }

/* The one button that leaves for eBay. Blue so it reads as "off to
   somewhere else" against the yellow everything-here buttons. Needs
   !important to beat the blanket .btn rule directly above. */
.btn.ebay, a.btn.ebay {
  background: #0064d2 !important;   /* eBay blue */
  color: #fff !important;
  border: 3px outset #6ba9e8 !important;
}
.btn.ebay:hover, a.btn.ebay:hover { background: #0053ad !important; }

input, select, textarea {
  background: #fff; color: #000;
  border: 2px inset #b48cff !important; border-radius: 0 !important;
  font-family: Verdana, Geneva, sans-serif;
}

table.tidy { border: 2px solid #6a2dff; background: #fff; }
table.tidy th {
  background: #6a2dff; color: #fff;
  font-family: Verdana, Geneva, sans-serif;
  letter-spacing: 0; text-transform: none;
  border: 1px solid #14004a;
}
table.tidy td { border: 1px solid #d8c9ff; background: #fff; color: #1a1a1a; }
table.tidy td.muted, table.tidy .muted { color: #555; }

.steps-line { background: #14004a; border: 3px ridge #ff2dd6; padding: 8px; }
.step-node { color: #fff; opacity: .6; }
.step-node .lbl { color: #fff; }
.step-node .dot {
  border-radius: 0; border: 2px outset #b48cff; background: #2a0a6e; color: #ffe12d;
  font-family: "Courier New", monospace;
}
.step-node.current { opacity: 1; }
.step-node.current .dot { background: #ff2dd6; color: #fff; }
.step-node .cnt { border-radius: 0; background: #ff2d2d; font-family: "Courier New", monospace; }
.step-bar { background: #6a2dff; height: 3px; }

.badge { background: #ffe12d; color: #14004a; border: 1px solid #6a2dff; border-radius: 0; }
.flash { font-family: Verdana, Geneva, sans-serif; color: #14004a; }
.flash.success { background: #d6ffd6; }
.flash.danger { background: #ffd6d6; }
.flash.warning { background: #fff6c0; }
.flash.info { background: #d6ecff; }

img { border-radius: 0 !important; }
.card-tile img { border: 2px solid #6a2dff; }
hr { border: none; height: 10px;
     background: url("../img/divider.svg") repeat-x; background-size: auto 100%; }

footer.site { background: #14004a; color: #d9d2ff; border-top: 4px ridge #ff2dd6; }
footer.site a { color: #7ad0ff; }

.liq { font-weight: 700; font-family: "Courier New", monospace; }
.liq-high { color: #0a7a1e; }
.liq-medium { color: #b07800; }
.liq-low { color: #c01414; }
.verified { color: #0a7a1e; font-family: "Courier New", monospace; font-size: .8rem; }

#bar { background: #ff2dd6 !important; border-radius: 0 !important; }

/* back-office rats: JS-spawned runners along the floor (see the rat
   theater script on the back-office page) */
body.bp-back_office .rat {
  position: fixed; bottom: 4px; left: -140px; height: 34px;
  z-index: 2; pointer-events: none;
}

/* rainbow divider strip usable anywhere */
.divider90 { display: block; width: 100%; height: 10px;
  background: url("../img/divider.svg") repeat-x; background-size: auto 100%; }

/* grainy kitchen art */
.kitchen-art { display: block; width: 100%; max-width: 460px;
  border: 3px ridge #b48cff; image-rendering: auto; }

/* --------- shooting stars (spawned by JS at random spots) --------- */
.shoot90 {
  /* The star travels toward its own right edge, so the BRIGHT end has to be
     on the right: head first, trail streaming out behind. It was the other
     way round, which read as flying backwards. */
  position: fixed; width: 130px; height: 2px;
  background: linear-gradient(90deg, transparent, currentColor);
  pointer-events: none; z-index: 0; opacity: 0;
}
/* the UFO keeps its distance: small and half-faded like the far stars */
.ufo90 {
  /* Part of the sky: sits back with the starfield, behind page content,
     and position:fixed so it never moves with the scroll.
     This is now a WRAPPER that carries the flight path; the <img> inside
     carries the stumble. Two elements because both are transforms, and one
     element can only run one transform animation at a time. */
  position: fixed; width: 64px; opacity: .92; z-index: 0;
  pointer-events: none;
}
/* only the saucer itself is clickable, and only where page content isn't
   sitting on top of it (it flies behind the UI, by your earlier choice) */
.ufo90 img {
  width: 100%; display: block; pointer-events: auto; cursor: pointer;
}
/* Keep page content above the sky layers. `header` matters: the signed-out
   landing hero is a <header>, and without it here the squiggle field
   (z-index 0) painted straight over the headline and buttons. */
.nav, .container, section, header, footer { position: relative; z-index: 1; }

/* low-res 90s photos: let them scale up chunky, never smooth */
.kitchen-art, .card-tile img, body img { image-rendering: auto; }
.kitchen-art { image-rendering: pixelated; }

/* the footer is the end of the page: body fills the viewport, footer sits
   flush at the bottom, nothing scrolls past it */
body { display: flex; flex-direction: column; min-height: 100vh; }
body > footer.site { margin-top: auto; }

/* NEW! starburst beside the brand */
.new-burst { height: 32px; margin-left: 4px; vertical-align: middle;
  animation: new-pulse 1.8s ease-in-out infinite; }
@keyframes new-pulse {
  50% { transform: scale(1.12) rotate(-7deg); }
}
@media (prefers-reduced-motion: reduce) { .new-burst { animation: none; } }

/* the Collection vault floor: tiny pixelated card sprites piled at the
   bottom of the viewport, behind the page content */
.card-pile {
  position: fixed; left: 0; right: 0; bottom: 0; height: 120px;
  pointer-events: none; z-index: 0; overflow: visible;
}
.card-pile img {
  position: absolute; width: 24px; height: auto;
  image-rendering: pixelated;
  border: 1px solid rgba(0, 0, 0, .45);
  box-shadow: 1px 1px 0 rgba(0, 0, 0, .35);
}

/* ---- The Collection's big oak doors (the paywall gate) ---------------- */
.oak-doors {
  position: relative; display: flex; max-width: 1180px; height: 250px;
  margin: 24px auto; border: 18px ridge #6b4a24;
  background: #2a1a0c; box-shadow: 10px 10px 0 rgba(0,0,0,.55);
  image-rendering: pixelated;
}
.oak-door {
  flex: 1; position: relative;
  background:
    repeating-linear-gradient(90deg,
      #7a4f22 0, #7a4f22 10px, #6b4520 10px, #6b4520 20px,
      #814f1f 20px, #814f1f 30px, #6f471e 30px, #6f471e 40px);
  border: 3px solid #4a3115;
}
.oak-door.left { border-right-width: 1px; }
.oak-door.right { border-left-width: 1px; }
.oak-panel {
  height: 30%; margin: 6% 10% 0; border: 10px groove #4a3115;
  background: rgba(0,0,0,.12);
}
.oak-knob {
  position: absolute; top: 50%; width: 28px; height: 28px; border-radius: 50%;
  background: radial-gradient(#ffe9a8, #b8860b); border: 2px solid #6b4a24;
}
.oak-door.left  .oak-knob { right: 8px; }
.oak-door.right .oak-knob { left: 8px; }
/* The label sits ABOVE the doors on its own. It used to be absolutely
   positioned inside them, which stopped working once .oak-door gained a
   z-index for the glow -- the plaque ended up painting behind the wood. */
/* The vault doors proper break out of .container (max-width 1060px), which
   was silently clipping them to ~1020px however large max-width got. Negative
   side margins stretch them to the full viewport -- as wide as they can go
   without running off screen. `width:auto` rather than 100vw, because 100vw
   includes the scrollbar and would add a horizontal scrollbar.
   The small doors on the profile / back office keep the default size. */
.oak-doors.full {
  /* Two thirds of the previous half-viewport width, centered. Still breaks
     out of .container's ~1020px cap on wide screens, but door-shaped. */
  max-width: none;
  width: 33.33vw;
  min-width: 320px;
  height: 280px;
  margin-left: auto;
  margin-right: auto;
}

.oak-plaque {
  display: block; width: max-content; max-width: 90%;
  margin: 24px auto -6px; text-align: center;
  background: #c8a24a; color: #2a1a0c; padding: 4px 20px;
  border: 2px outset #ffe9a8; font-family: "Times New Roman", Georgia, serif;
  font-weight: 700; font-size: 1.15rem; letter-spacing: 1px; white-space: nowrap;
}

/* things the saucer beams up */
.abductee {
  /* travels with the saucer, same depth as the sky */
  position: fixed; width: 58px; pointer-events: none; z-index: 0;
  image-rendering: pixelated;
}

/* ---- Profile: a slowly shifting rainbow, not the galaxy ---------------- */
body.ep-profile-me, body.ep-profile-public {
  background-color: #1a0033;
  background-image: linear-gradient(120deg,
    #ff5a5a, #ff9a2d, #ffe12d, #3ddc48, #2dffe1, #2db8ff, #b48cff, #ff2dd6, #ff5a5a);
  background-size: 400% 400%;
  background-attachment: fixed;
  animation: rainbow-shift 28s ease-in-out infinite;
}
@keyframes rainbow-shift {
  0%   { background-position: 0% 50%; }
  50%  { background-position: 100% 50%; }
  100% { background-position: 0% 50%; }
}
@media (prefers-reduced-motion: reduce) {
  body.ep-profile-me, body.ep-profile-public { animation: none; }
}

/* The house motto, in moving rainbow. Background-clip paints the gradient
   through the glyphs; the animation slides it so the colors travel. */
.motto-rainbow {
  font-weight: 700;
  background-image: linear-gradient(90deg,
    #ff5a5a, #ff9a2d, #ffe12d, #3ddc48, #2dffe1, #2db8ff, #b48cff, #ff2dd6, #ff5a5a);
  background-size: 300% 100%;
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
  -webkit-text-fill-color: transparent;
  animation: motto-rainbow-slide 6s linear infinite;
}
@keyframes motto-rainbow-slide {
  from { background-position: 0% 50%; }
  to   { background-position: 300% 50%; }
}
@media (prefers-reduced-motion: reduce) { .motto-rainbow { animation: none; } }

/* ---- Doors you can push open (only once the vault is unlocked) --------- */
.oak-doors.enterable { cursor: pointer; text-decoration: none; display: flex; }
.oak-doors.enterable:hover { text-decoration: none; }
/* heavy oak: slow to swing, slow to settle */
.oak-door { transition: transform 1.5s cubic-bezier(.22,.61,.28,1);
            position: relative; z-index: 1; }
.oak-doors.enterable .oak-door.left { transform-origin: left center; }

/* Only the LEFT door opens -- the right one stays shut, so the light spills
   out of a single gap the way a real vault door would. */
.oak-doors.enterable:hover .oak-door.left {
  transform: perspective(900px) rotateY(-46deg);
  box-shadow: 8px 0 18px rgba(0,0,0,.55);
}
.oak-doors.enterable:hover .oak-plaque { color: #2a1a0c; background: #ffe9a8; }

/* The lit interior, sitting behind the doors and revealed as one swings. */
.oak-glow {
  position: absolute; inset: 0; z-index: 0; pointer-events: none;
  background:
    radial-gradient(120% 90% at 22% 50%,
      rgba(255,236,170,.95) 0%, rgba(255,203,92,.75) 28%,
      rgba(190,130,35,.45) 55%, rgba(40,24,8,.9) 85%);
  opacity: 0; transition: opacity 1.5s ease;
}
.oak-doors.enterable:hover .oak-glow { opacity: 1; }
@media (prefers-reduced-motion: reduce) { .oak-glow { transition: none; } }
/* A locked .oak-doors has no .enterable, so hovering it does nothing at all. */
@media (prefers-reduced-motion: reduce) {
  .oak-door { transition: none; }
}

/* the collection-value figure reacting to a delivery */
.value-pop { animation: value-pop .6s ease; }
@keyframes value-pop {
  0%   { transform: scale(1);   color: inherit; }
  35%  { transform: scale(1.25); color: #3ddc48; }
  100% { transform: scale(1);   color: inherit; }
}
.party-rat {
  position: fixed; bottom: 4px; left: -160px; height: 34px;
  z-index: 4; pointer-events: none; image-rendering: pixelated;
}
.party-bill {
  position: fixed; width: 34px; pointer-events: none; z-index: 5;
  image-rendering: pixelated;
}

/* the saucer's tractor beam, used when it delivers money to the vault box */
/* A proper green tractor beam: a widening cone under the saucer, brightest
   at the emitter, with a soft glow so cargo reads as being INSIDE it. */
.tractor-beam {
  /* Sky layer, same depth as the saucer that emits it -- the cargo is
     appended after the beam, so it paints on top of the light without
     needing a z-index that would float it over page content. */
  position: fixed; width: 68px; z-index: 0; pointer-events: none;
  background: linear-gradient(rgba(120,255,140,.75), rgba(60,230,90,.30) 55%,
                              rgba(40,210,70,.06));
  clip-path: polygon(40% 0, 60% 0, 100% 100%, 0 100%);
  filter: drop-shadow(0 0 8px rgba(90,255,120,.55));
}
/* The profile's delivery beam is the exception: it reaches down into a
   stats box that lives in page content, so it has to clear it. */
.tractor-beam.deliver { z-index: 3; }

/* the gold cursor catches the light now and then */
.cursor-shine {
  position: fixed; width: 26px; height: 26px; z-index: 6; pointer-events: none;
  background: radial-gradient(closest-side, rgba(255,240,180,.95), rgba(255,211,77,.55) 45%, rgba(255,211,77,0) 75%);
}
.cursor-shine::before, .cursor-shine::after {
  content: ""; position: absolute; left: 50%; top: 50%;
  background: linear-gradient(90deg, rgba(255,247,205,0), rgba(255,247,205,.95), rgba(255,247,205,0));
  transform: translate(-50%, -50%);
}
.cursor-shine::before { width: 34px; height: 2px; }
.cursor-shine::after  { width: 2px; height: 34px; }

/* ---------------------------------------------------------------- scrolling
   `background-attachment: fixed` asks the browser to re-rasterise the whole
   backdrop on every scroll frame. On a big photo backdrop that misses frames,
   which is the juddering you see while scrolling -- the page catches up in
   steps instead of gliding. The Collection was already converted to a fixed
   ::before layer for exactly this reason; this does the same for the rest.

   A fixed-position layer is painted once and then composited, so scrolling
   costs almost nothing. -1 z-index keeps it behind content; the squiggle
   field sits above it and is animated on the compositor via transform. */
body.bp-scan::after,
body.ep-main-collection::after {
  content: "";
  position: fixed;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  background-size: cover;
  background-position: center;
  /* Its own compositor layer, so scrolling never repaints it. */
  will-change: transform;
  transform: translateZ(0);
}
body.bp-scan::after {
  background-image: linear-gradient(rgba(38,20,6,.82), rgba(24,12,4,.9)),
                    url("../img/kitchen-photo.jpg");
}
body.ep-main-collection::after {
  background-image: linear-gradient(rgba(18,8,38,.85), rgba(18,8,38,.92)),
                    url("../img/thepass.jpg");
}

/* Same idea for the pinned starfield. */
body.galaxy::after {
  content: "";
  position: fixed;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  background-image: url("../img/stars.svg");
  background-size: 320px 320px;
  will-change: transform;
  transform: translateZ(0);
}

/* ---- The service tiers on the menu ---------------------------------------
   The Collection, then the Vintage Picture Service, then the House Stamp:
   each only means anything while the one above it is running. Showing that
   as indentation with an arrow says "this hangs off that" far faster than a
   sentence does, and the greyer card separates the subscriptions from the
   one-off scan packs above them. */
.menu-item-block.tier {
  background: rgba(120, 120, 130, 0.14);
  border: 1px solid rgba(120, 120, 130, 0.28);
  border-radius: 10px;
  padding: 12px 16px;
  margin: 10px 0;
}
.menu-item-block.tier .menu-item { margin: 6px 0; }
.menu-item-block.tier .name { color: #5d5d68; }
.menu-item-block.tier .price { color: #6f6f7c; }
.menu-item-block.tier .desc { color: #6b6b76; }

/* Each step in is one indent, with an arrow drawn in the gutter. */
.menu-item-block.tier-2 { margin-left: 30px; }
.menu-item-block.tier-3 { margin-left: 60px; }
.menu-item-block.tier-2, .menu-item-block.tier-3 { position: relative; }
.menu-item-block.tier-2::before,
.menu-item-block.tier-3::before {
  content: "↳";
  position: absolute;
  left: -22px;
  top: 14px;
  font-size: 1.3rem;
  color: rgba(120, 120, 130, 0.75);
  line-height: 1;
}
@media (max-width: 560px) {
  /* On a phone the indents cost more width than the hierarchy is worth. */
  .menu-item-block.tier-2 { margin-left: 16px; }
  .menu-item-block.tier-3 { margin-left: 32px; }
  .menu-item-block.tier-2::before,
  .menu-item-block.tier-3::before { left: -14px; }
}

/* Locks marking how many tiers deep a service sits: one for The Collection,
   two for the Vintage Picture Service, three for the House Stamp. */
.menu-item .locks { display: inline-flex; gap: 2px; align-items: center; }
.menu-item .locks img {
  width: 15px; height: 15px; image-rendering: pixelated; display: block;
}

/* A service you already own: no lock, and the name in green. */
.menu-item .name.owned { color: #2e7d32; }
.menu-item .name.owned::after {
  content: " ✓"; font-size: .85em; color: #2e7d32;
}

/* The back office chart popup. Closed unless .open -- driven by a class, not
   the hidden attribute, because an inline display value overrides `hidden`
   and the modal ended up permanently on screen with no way past it. */
.chart-modal { display: none; }
.chart-modal.open { display: flex; }
