/* DexAI — translucent glass over a near-black ground.
   Single visual world on purpose: the design commits to black so the generated banners
   (the actual content) read as the only bright objects on the page.
   Note for anyone changing the ground: on black, controls are LIGHT glass and the accent
   is light. Both would have to flip if the ground ever became a light one — the accent is
   worn by the wordmark and by the $TICKER label inside a control pill. */
:root{
  --bg:#07080B;
  --mesh:
    radial-gradient(70vw 70vh at  6%  -8%, rgba( 44, 62, 92,.85), transparent 62%),
    radial-gradient(66vw 72vh at 100%  4%, rgba( 62, 48, 96,.75), transparent 64%),
    radial-gradient(80vw 70vh at 88% 108%, rgba( 30, 44, 58,.85), transparent 62%),
    radial-gradient(60vw 60vh at -4% 100%, rgba( 22, 46, 54,.75), transparent 62%),
    radial-gradient(70vw 60vh at 48%  50%, rgba( 18, 22, 32,.60), transparent 72%);

  --ink:#F3F6FA;
  --muted:rgba(255,255,255,.72);
  --faint:rgba(255,255,255,.48);
  --glass:rgba(255,255,255,.055);
  --glass-2:rgba(255,255,255,.09);
  --edge:rgba(255,255,255,.13);
  --edge-2:rgba(255,255,255,.34);
  --ctl:rgba(255,255,255,.08);
  --ctl-2:rgba(255,255,255,.15);
  --ctl-edge:rgba(255,255,255,.16);
  --ctl-edge-2:rgba(255,255,255,.38);
  --toast:rgba(16,18,24,.86);
  --accent:#8AF0E2;
  --ember:#F2A25C;
  --bad:#FF8B7E;
  --ok:#86E3B9;
  --grain:.04;
  --r:18px;
  --blur:saturate(170%) blur(22px);
}
*{box-sizing:border-box}
/* The hidden attribute only carries display:none from the UA sheet, so ANY class rule
   that sets display beats it. .lb, .tokenbar and .stats all do, which meant hiding them
   silently did nothing. This makes the attribute authoritative, as it reads. */
[hidden]{display:none!important}
/* The mesh lives on the ROOT, not on body. The root's background is what paints the
   canvas, which includes the strip revealed by macOS rubber-band scrolling: with a flat
   colour there, overscrolling past the top exposed a hard black bar against a page top
   that the gradient had lifted to #1A1C2B. Painted here, the canvas continues the
   gradient and the bounce shows nothing. */
html{
  background:var(--mesh), var(--bg);
  background-attachment:fixed;
}
body{
  margin:0;min-height:100vh;color:var(--ink);
  font-family:Sora,ui-sans-serif,system-ui,sans-serif;line-height:1.55;
  -webkit-font-smoothing:antialiased;text-rendering:optimizeLegibility;
  /* No background here: it would sit on top of the root's and stop the canvas, and the
     overscroll strip, from being painted. */
  background:transparent;
}
/* Faint grain so the large flat areas do not band. */
body::after{
  content:"";position:fixed;inset:0;z-index:1;pointer-events:none;opacity:var(--grain);
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='140' height='140'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.85' numOctaves='3'/%3E%3C/filter%3E%3Crect width='140' height='140' filter='url(%23n)'/%3E%3C/svg%3E");
}
.mono{font-family:"IBM Plex Mono",ui-monospace,monospace;font-variant-numeric:tabular-nums}
/* Sticky footer. A column that is at least as tall as the viewport, with the active view
   allowed to grow: on a short page the footer is pushed to the bottom instead of leaving a
   dead band under it, and on a long one it simply follows the content.
   dvh rather than vh so a phone's collapsing address bar does not leave a gap. */
.wrap{max-width:1040px;margin:0 auto;padding:0 24px 44px;position:relative;z-index:2;
  display:flex;flex-direction:column;min-height:100vh;min-height:100dvh}
/* 1 0 auto, never 1 1 auto: the view may grow to fill the height but must never be
   compressed below its content when the banners make it taller than the screen. */
#view-generate,#view-submit,#admin-out{flex:1 0 auto}
a{color:inherit}

/* ── glass primitive ─────────────────────────────────────────────────────── */
.glass{
  background:var(--glass);
  backdrop-filter:var(--blur);-webkit-backdrop-filter:var(--blur);
  border:1px solid var(--edge);border-radius:var(--r);
  box-shadow:inset 0 1px 0 rgba(255,255,255,.16),
             inset 0 -1px 0 rgba(255,255,255,.05),
             0 24px 60px -28px rgba(6,26,40,.55);
}

header.top{display:flex;justify-content:space-between;align-items:center;gap:16px;
  padding:20px 0;flex-wrap:wrap}
/* The wallet chip sits next to a button. Default flex stretch made it as tall as the
   button while keeping chip padding, so the pill read as an oversized box around small
   type. Centre the row and give the chip the button's own vertical metrics. */
.top .row{align-items:center}
.top .chip{padding:11px 16px;font-size:12.5px}
/* Logotype. Instrument Sans caps, tight, with AI set as a filled badge: the badge is the
   mark, so the name still carries an identity when it stands alone. The colour is inverted
   inside it (ground on accent) rather than accent-on-dark, which is what makes it read as
   an object rather than as two coloured words. */
.logo{display:inline-flex;align-items:center;gap:9px;
  font-family:"Instrument Sans",Sora,sans-serif;font-weight:700;
  font-size:20px;letter-spacing:-.04em;text-transform:uppercase;
  text-decoration:none;color:inherit;line-height:1}
/* The mascot is a detailed illustration, not a glyph: below about 34px the brush and the
   palette turn to mush, so it is sized in px rather than em and never shrinks with the type. */
.mascot{width:34px;height:34px;flex:none;display:block}
.logo i{font-style:normal;background:var(--accent);color:var(--bg);
  border-radius:.3em;padding:.03em .23em .07em;margin-left:.04em}
.logo:focus-visible{outline:2px solid var(--accent);outline-offset:4px;border-radius:6px}

.hero{padding:64px 0 28px}
h1{margin:0;font-size:clamp(34px,6vw,60px);font-weight:700;letter-spacing:-.035em;text-shadow:0 2px 30px rgba(0,0,0,.22);
  line-height:1.02;text-wrap:balance}
h1 em{font-style:normal;color:rgba(255,255,255,.52)}
.lede{max-width:54ch;color:rgba(255,255,255,.84);margin:18px 0 0;font-size:17px}

/* ── controls ────────────────────────────────────────────────────────────── */
.btn{
  font-family:inherit;font-size:14.5px;font-weight:600;color:var(--ink);cursor:pointer;
  text-decoration:none;                 /* .btn is worn by <a> as well as <button> */
  padding:11px 20px;border-radius:999px;
  background:var(--ctl);border:1px solid var(--ctl-edge);
  backdrop-filter:var(--blur);-webkit-backdrop-filter:var(--blur);
  box-shadow:inset 0 1px 0 rgba(255,255,255,.22);
  transition:border-color .18s,background .18s,transform .12s;
}
.btn:hover:not(:disabled){background:var(--ctl-2);border-color:var(--ctl-edge-2)}
.btn:active:not(:disabled){transform:translateY(1px)}
.btn:disabled{opacity:.4;cursor:not-allowed}
.btn:focus-visible{outline:2px solid var(--accent);outline-offset:3px}
.btn.pri{background:rgba(255,255,255,.90);color:#0A0F18;border-color:rgba(255,255,255,.7);
  box-shadow:0 0 0 1px rgba(255,255,255,.18),0 14px 40px -18px rgba(255,255,255,.5)}
.btn.pri:hover:not(:disabled){background:#fff}

.panel{background:var(--glass);
  backdrop-filter:var(--blur);-webkit-backdrop-filter:var(--blur);
  border:1px solid var(--edge);border-radius:var(--r);
  box-shadow:inset 0 1px 0 rgba(255,255,255,.16),0 24px 60px -28px rgba(6,26,40,.55);
  padding:24px;margin:24px 0 0}
.row{display:flex;gap:11px;flex-wrap:wrap}
input[type=text]{
  flex:1 1 240px;min-width:0;font-family:"IBM Plex Mono",monospace;font-size:14px;color:var(--ink);
  background:var(--ctl);border:1px solid var(--ctl-edge);border-radius:999px;padding:13px 20px;
  backdrop-filter:blur(8px);
}
input::placeholder{color:rgba(255,255,255,.55)}
input:focus{outline:none;border-color:var(--accent);
  box-shadow:0 0 0 3px rgba(127,231,220,.14)}
.hint{margin:13px 0 0;font-size:13px;color:rgba(255,255,255,.82)}
/* The dev banner has to be impossible to miss and impossible to mistake for chrome. */
.hint.warn{color:var(--ember);font-family:"IBM Plex Mono",monospace;font-size:12px}
/* Toasts. Errors used to print into the panel, directly under a hint that often said
   the same thing — the quota message appeared twice. They live outside the flow now. */
.toasts{position:fixed;right:22px;bottom:22px;z-index:20;display:flex;flex-direction:column;
  gap:10px;align-items:flex-end;pointer-events:none}
.toast{display:flex;align-items:flex-start;gap:11px;max-width:min(30rem,calc(100vw - 44px));
  padding:13px 17px;border-radius:14px;font-size:13.5px;line-height:1.45;color:#fff;
  background:var(--toast);border:1px solid var(--ctl-edge);
  box-shadow:0 14px 40px rgba(0,20,35,.35);pointer-events:auto;
  backdrop-filter:saturate(160%) blur(18px);-webkit-backdrop-filter:saturate(160%) blur(18px);
  animation:toast-in .26s cubic-bezier(.2,.9,.3,1)}
.toast::before{content:"";width:7px;height:7px;border-radius:50%;margin-top:6px;flex:none;background:#FF7A6B}
.toast.ok::before{background:#5BE8A8}
.toast.leaving{animation:toast-out .2s ease forwards}
@keyframes toast-in{from{opacity:0;transform:translateY(10px) scale(.97)}}
@keyframes toast-out{to{opacity:0;transform:translateY(6px) scale(.98)}}
@media (prefers-reduced-motion:reduce){.toast,.toast.leaving{animation:none}}

/* Two-step confirm on a destructive-ish button: arming it in place beats a native dialog. */
.btn.arm{background:var(--ember);border-color:transparent;color:#2A1405}

.chip{font-family:"IBM Plex Mono",monospace;font-size:11.5px;padding:7px 14px;border-radius:999px;
  color:var(--ink);background:var(--ctl);border:1px solid var(--ctl-edge);
  backdrop-filter:blur(12px);-webkit-backdrop-filter:blur(12px)}

/* ── coin header ─────────────────────────────────────────────────────────── */
.coin{display:flex;gap:18px;align-items:flex-start;flex-wrap:wrap}
.coin img{width:84px;height:84px;border-radius:16px;object-fit:cover;
  border:1px solid var(--edge);background:rgba(0,0,0,.25);
  box-shadow:0 14px 34px -16px rgba(6,26,40,.6)}
.coin h2{margin:0;font-size:26px;font-weight:700;letter-spacing:-.03em}
.coin p{margin:4px 0 0;color:rgba(255,255,255,.78);font-size:14px}
.narr{margin:16px 0 0;font-size:15.5px;color:rgba(255,255,255,.92);max-width:70ch}

/* ── results grid ────────────────────────────────────────────────────────── */
.grid{display:grid;gap:16px;margin:26px 0 0;grid-template-columns:repeat(3,minmax(0,1fr))}
@media(max-width:900px){.grid{grid-template-columns:repeat(2,minmax(0,1fr))}}
@media(max-width:560px){.grid{grid-template-columns:1fr}}

.v{position:relative;margin:0;border-radius:14px;overflow:hidden;
  border:1px solid var(--edge);background:rgba(255,255,255,.04);
  box-shadow:0 20px 46px -24px rgba(6,26,40,.6);
  transition:transform .2s,border-color .2s}
.v:hover{transform:translateY(-3px);border-color:var(--edge-2)}
.v img{display:block;width:100%;height:auto}
.v .bar{position:absolute;inset:auto 0 0 0;display:flex;justify-content:flex-end;padding:11px;
  opacity:0;transition:opacity .18s;
  background:linear-gradient(transparent,rgba(4,7,12,.82))}
.v:hover .bar,.v:focus-within .bar{opacity:1}
.v .dl{font-size:12.5px;padding:8px 15px;
  /* No backdrop-filter here. The bar fades in by animating opacity on the ancestor, and a
     backdrop-filtered child inside a fading ancestor gets its backdrop re-sampled mid
     transition, which reads as a black-to-white flash. The banner behind is opaque, so
     blurring it bought nothing anyway. */
  backdrop-filter:none;-webkit-backdrop-filter:none;
  background:rgba(6,10,16,.72);border-color:rgba(255,255,255,.3)}
.v .dl:hover{background:rgba(6,10,16,.9);border-color:rgba(255,255,255,.55)}
.v.fail{padding:20px;color:var(--bad);font-size:12.5px;aspect-ratio:3/1;
  display:grid;place-items:center;text-align:center}

/* ── loading ─────────────────────────────────────────────────────────────── */
.skl{border-radius:14px;aspect-ratio:3/1;border:1px solid var(--edge);
  background:linear-gradient(100deg,rgba(255,255,255,.04) 30%,rgba(255,255,255,.13) 50%,rgba(255,255,255,.04) 70%);
  background-size:280% 100%;animation:sh 1.6s ease-in-out infinite}
@keyframes sh{0%{background-position:180% 0}100%{background-position:-80% 0}}

.prog{display:grid;gap:10px;margin:2px 0 22px}
.st{display:flex;align-items:center;gap:13px;font-size:13.5px;color:rgba(255,255,255,.5);
  transition:color .35s}
.st.on{color:var(--ink)}
.st.done{color:rgba(255,255,255,.8)}
.st .ic{width:17px;height:17px;flex:none;border-radius:50%;position:relative;
  border:1.5px solid currentColor}
.st.on .ic{border-color:var(--accent);border-top-color:transparent;animation:sp .8s linear infinite}
.st.done .ic{border-color:var(--ok);background:var(--ok)}
.st.done .ic::after{content:"";position:absolute;left:5px;top:2px;width:4px;height:8px;
  border:solid #060910;border-width:0 1.7px 1.7px 0;transform:rotate(45deg)}
@keyframes sp{to{transform:rotate(360deg)}}
.st .sub{font-family:"IBM Plex Mono",monospace;font-size:11px;color:rgba(255,255,255,.6);margin-left:auto}

/* ── project identity ────────────────────────────────────────────────────────
   The X link and our own mint, rendered twice: once under the hero copy, once in
   the footer. The mint pill is allowed to shrink — `min-width:0` on both the pill
   and the address lets the address ellipsis instead of forcing the row wider than
   a phone screen. Cropping the tail is fine; the copy button gives the full value. */
.tokenbar{display:flex;gap:11px;flex-wrap:wrap;align-items:center;margin:4px 0 0}
/* One address, two possible homes. Below 900px the header has no room for a 44-character
   key next to the logo and the wallet button, so it sits under the hero instead. */
.hd-right{align-items:center;gap:12px;min-width:0}
#headbar{display:none;margin:0;min-width:0}
@media(min-width:900px){
  #headbar{display:flex}
  #tokenbar{display:none}
  #headbar .tk{padding:8px 13px}
  #headbar .tk-v{font-size:11.5px}
}
.tk{display:inline-flex;align-items:center;gap:9px;max-width:100%;min-width:0;overflow:hidden;
  padding:9px 15px;border-radius:999px;font:inherit;font-size:13px;color:#fff;
  text-decoration:none;cursor:pointer;text-align:left;
  background:var(--ctl);border:1px solid var(--ctl-edge);
  backdrop-filter:blur(12px);-webkit-backdrop-filter:blur(12px);
  transition:background .16s ease,border-color .16s ease}
.tk:hover{background:var(--ctl-2);border-color:var(--ctl-edge-2)}
.tk:active{transform:translateY(1px)}
.tk svg{flex:none;opacity:.72}
.tk:hover svg{opacity:1}
.tk-k{font-family:"IBM Plex Mono",monospace;font-size:11.5px;color:var(--accent);flex:none}
.tk-v{font-size:12px;color:rgba(255,255,255,.86);min-width:0;
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.tk-i{display:inline-flex;flex:none;align-items:center}
.tk .ck,.tk.copied .cp{display:none}
.tk.copied .ck{display:inline-flex;color:#5BE8A8;opacity:1}
.tk.copied{border-color:rgba(134,227,185,.5);background:rgba(6,50,38,.45)}

.foot{display:flex;flex-wrap:wrap;gap:18px;align-items:center;justify-content:space-between;
  margin:72px 0 0;padding:26px 0 0;border-top:1px solid rgba(255,255,255,.18)}
.foot .logo{font-size:17px;opacity:.9}
.foot .mascot{width:28px;height:28px}
.foot .tokenbar{min-width:0;flex:1 1 auto;justify-content:flex-end}

/* ── stats strip ─────────────────────────────────────────────────────────── */
.stats{display:grid;grid-template-columns:repeat(auto-fit,minmax(165px,1fr));gap:8px 20px;align-items:start;
  padding:26px 24px}
.stat{display:flex;flex-direction:column;gap:5px;padding:6px 0;min-width:0}
.stat-v{font-size:26px;font-weight:700;letter-spacing:-.03em;line-height:1.1;
  font-variant-numeric:tabular-nums;overflow-wrap:anywhere}
.stat-k{font-family:"IBM Plex Mono",monospace;font-size:11px;letter-spacing:.04em;
  color:rgba(255,255,255,.62)}
/* "last banner" is a phrase, not a figure: set smaller so it holds one line beside the counts. */
.stats .stat:last-child .stat-v{font-size:19px;line-height:1.35}

/* ── narrow screens ──────────────────────────────────────────────────────── */
@media(max-width:620px){
  .wrap{padding:0 16px 72px}
  .hero{padding:40px 0 22px}
  .panel{padding:18px}
  /* The mint is longer than any phone is wide: give it its own row and let the
     address itself ellipsis rather than pushing the whole page sideways. */
  .tk{width:100%;justify-content:flex-start}
  .stats{grid-template-columns:repeat(2,minmax(0,1fr));gap:14px}
  .stat-v{font-size:22px}
  /* "last banner" carries words, not a figure — it gets the full row rather than half of one. */
  .stats .stat:last-child{grid-column:1 / -1}
  .foot{flex-direction:column;align-items:flex-start}
  .foot .tokenbar{flex:1 1 100%;width:100%;justify-content:flex-start}
  .foot{margin:48px 0 0}
}


/* ── background meme wall ────────────────────────────────────────────────────
   Sits between the ground and the grain: z-index 0, under body::after (1) and the
   content column (2). Masked out in the middle so it never competes with the headline
   or with the generated banners, which are the only things that should hold the eye. */
.memes{
  position:fixed;inset:0;z-index:0;overflow:hidden;pointer-events:none;
  display:flex;flex-direction:column;justify-content:space-around;
  opacity:.3;
  /* Blurred as a single composited layer rather than per image: 250 tiles each with
     their own filter would be 250 layers to rasterise every frame. */
  filter:blur(2.4px);
  /* The centre is dimmed, not cut: a fully transparent middle left a dead zone exactly
     where most of the page is. rgba keeps a quarter of the wall behind the copy. */
  -webkit-mask-image:radial-gradient(126% 102% at 50% 40%, rgba(0,0,0,.26) 16%, #000 66%);
          mask-image:radial-gradient(126% 102% at 50% 40%, rgba(0,0,0,.26) 16%, #000 66%);
}
.mrow{display:flex;width:max-content;gap:46px;align-items:center;will-change:transform;
  animation:memeslide linear infinite}
.mrow.rev{animation-direction:reverse}
.mrow img{width:70px;height:70px;flex:none;border-radius:14px;object-fit:cover;
  user-select:none}
@keyframes memeslide{from{transform:translateX(0)}to{transform:translateX(-50%)}}

/* Motion is the whole point of the wall, so with reduced motion it stops rather than
   disappears: the logos stay as a static texture. */
@media (prefers-reduced-motion:reduce){.mrow{animation:none}}
@media(max-width:620px){.memes{opacity:.2}.mrow{gap:34px}.mrow img{width:52px;height:52px}}

/* ── lightbox ────────────────────────────────────────────────────────────────
   Above the toasts (20) so a toast fired mid-view cannot land on top of the image. */
.v img{cursor:pointer}
body.lb-on{overflow:hidden}
.lb{position:fixed;inset:0;z-index:40;display:flex;align-items:center;justify-content:center;
  gap:10px;padding:22px;background:rgba(4,6,10,.86);
  backdrop-filter:saturate(140%) blur(14px);-webkit-backdrop-filter:saturate(140%) blur(14px);
  animation:lb-in .18s ease}
@keyframes lb-in{from{opacity:0}}
.lb-stage{max-width:min(1500px,calc(100vw - 140px));display:flex}
.lb-img{display:block;width:100%;height:auto;max-height:calc(100vh - 140px);object-fit:contain;
  border-radius:12px;border:1px solid var(--ctl-edge);
  box-shadow:0 40px 90px -30px rgba(0,0,0,.8)}
.lb-x{position:absolute;top:18px;right:20px;width:40px;height:40px;border-radius:50%;
  font-size:26px;line-height:1;color:var(--ink);cursor:pointer;
  background:var(--ctl);border:1px solid var(--ctl-edge)}
.lb-nav{width:46px;height:46px;flex:none;border-radius:50%;font-size:28px;line-height:1;
  color:var(--ink);cursor:pointer;background:var(--ctl);border:1px solid var(--ctl-edge);
  transition:background .15s ease,border-color .15s ease}
.lb-x:hover,.lb-nav:hover{background:var(--ctl-2);border-color:var(--ctl-edge-2)}
.lb-x:focus-visible,.lb-nav:focus-visible,.lb-dl:focus-visible{outline:2px solid var(--accent);outline-offset:3px}
.lb-foot{position:absolute;left:0;right:0;bottom:20px;display:flex;justify-content:center;
  align-items:center;gap:14px}
.lb-n{font-size:12px;color:rgba(255,255,255,.7);font-variant-numeric:tabular-nums}
@media (prefers-reduced-motion:reduce){.lb{animation:none}}
@media(max-width:700px){
  /* No room for side arrows on a phone: they move down beside the counter. */
  .lb{flex-wrap:wrap;padding:14px}
  .lb-stage{max-width:100%;order:-1;flex-basis:100%;justify-content:center}
  .lb-nav{order:2}
  .lb-foot{position:static;flex-basis:100%;order:3}
}

/* ── selection ───────────────────────────────────────────────────────────────
   Clicking a card picks it; the magnifier is the only way into the lightbox, so
   hiding it below 700px is what disables enlarging on a phone, where the banner
   already spans the screen and there is nothing to enlarge. */
.v{cursor:pointer}
.v:focus-visible{outline:2px solid var(--accent);outline-offset:3px}
.v.on{border-color:var(--accent);box-shadow:0 0 0 2px var(--accent),0 18px 40px -20px rgba(0,0,0,.7)}
.v.on:hover{border-color:var(--accent)}

.tick{position:absolute;top:9px;left:9px;width:22px;height:22px;border-radius:50%;
  background:var(--accent);opacity:0;transform:scale(.6);transition:opacity .16s ease,transform .16s ease}
.tick::after{content:"";position:absolute;left:7px;top:4px;width:6px;height:11px;
  border:2px solid var(--bg);border-top:0;border-left:0;transform:rotate(42deg)}
.v.on .tick{opacity:1;transform:scale(1)}

.zoom{position:absolute;top:9px;right:9px;width:30px;height:30px;border-radius:50%;
  display:grid;place-items:center;cursor:pointer;color:var(--ink);
  background:rgba(6,10,16,.62);border:1px solid var(--ctl-edge);
  backdrop-filter:blur(8px);-webkit-backdrop-filter:blur(8px);
  opacity:0;transition:opacity .16s ease,background .16s ease}
.v:hover .zoom,.v:focus-within .zoom{opacity:1}
.zoom:hover{background:rgba(6,10,16,.85)}
.zoom:focus-visible{opacity:1;outline:2px solid var(--accent);outline-offset:2px}

.pick{display:flex;align-items:center;justify-content:flex-end;gap:16px;margin:18px 0 0;
  animation:pick-in .2s ease}
.pick-n{font-size:12.5px;color:rgba(255,255,255,.72)}
@keyframes pick-in{from{opacity:0;transform:translateY(-6px)}}

.lb-pick{font-size:13px;padding:9px 16px}

@media (prefers-reduced-motion:reduce){.tick,.pick{transition:none;animation:none}}
@media(max-width:700px){
  .zoom{display:none}
  .pick{justify-content:stretch}
  .pick .btn{flex:1}
}

/* ── account menu ────────────────────────────────────────────────────────────
   One control in the header: the address IS the button, and it opens the menu. */
.acct{position:relative}
.acct-btn{display:inline-flex;align-items:center;gap:8px;font-size:12.5px;padding:11px 15px}
.acct-btn .chev{opacity:.6;transition:transform .16s ease,opacity .16s ease}
.acct-btn.open .chev{transform:rotate(180deg);opacity:1}
.acct-btn.open{background:var(--ctl-2);border-color:var(--ctl-edge-2)}
.menu{position:absolute;top:calc(100% + 8px);right:0;z-index:30;min-width:170px;
  display:flex;flex-direction:column;padding:6px;border-radius:14px;
  background:var(--toast);border:1px solid var(--ctl-edge);
  backdrop-filter:saturate(160%) blur(16px);-webkit-backdrop-filter:saturate(160%) blur(16px);
  box-shadow:0 18px 44px -20px rgba(0,0,0,.75);animation:menu-in .14s ease}
@keyframes menu-in{from{opacity:0;transform:translateY(-5px)}}
.menu-item{font-family:inherit;font-size:13.5px;color:var(--ink);text-align:left;cursor:pointer;
  padding:10px 12px;border-radius:9px;background:transparent;border:0;width:100%}
.menu-item:hover{background:rgba(255,255,255,.09)}
.menu-item:focus-visible{outline:2px solid var(--accent);outline-offset:-2px}
@media (prefers-reduced-motion:reduce){.menu{animation:none}.acct-btn .chev{transition:none}}

/* ── navigation ──────────────────────────────────────────────────────────────*/
/* position:relative anchors the burger menu; without it the absolutely positioned
   panel resolved against .wrap and its top:100% landed below the whole page. */
.brand{position:relative;display:flex;align-items:center;gap:22px;min-width:0}
.nav{display:flex;gap:4px}
.nav a{position:relative;padding:8px 13px;border-radius:999px;font-size:13.5px;font-weight:600;
  color:rgba(255,255,255,.62);text-decoration:none;transition:color .15s ease,background .15s ease}
.nav a:hover{color:var(--ink);background:rgba(255,255,255,.06)}
.nav a.on{color:var(--bg);background:var(--accent)}
.nav a:focus-visible{outline:2px solid var(--accent);outline-offset:3px}

/* ── submit form ─────────────────────────────────────────────────────────────*/
.sub-hero{padding:44px 0 20px}
.form{display:flex;flex-direction:column;gap:0;padding:28px}
.field{display:flex;flex-direction:column;gap:9px;margin:0 0 24px}
.lbl{font-size:13.5px;font-weight:600;color:var(--ink)}
.form input[type=text],.form input[type=url],.form textarea{
  /* flex:none is load-bearing. The global input[type=text] rule carries flex:1 1 240px for
     the generate row; inside these column-direction fields that basis is applied to the
     MAIN axis, which is vertical, and the address box grew to 240px tall. */
  flex:none;
  font-family:"IBM Plex Mono",monospace;font-size:13.5px;color:var(--ink);
  background:var(--ctl);border:1px solid var(--ctl-edge);border-radius:12px;padding:13px 15px;
  width:100%;min-width:0}
.form textarea{font-family:Sora,sans-serif;font-size:14.5px;line-height:1.6;resize:vertical;
  border-radius:14px}
.form input:focus,.form textarea:focus{outline:none;border-color:var(--accent);
  box-shadow:0 0 0 3px rgba(127,231,220,.14)}
.help{margin:0;font-size:12.5px;line-height:1.55;color:rgba(255,255,255,.58)}
.sec{margin:14px 0 14px;font-size:20px;font-weight:700;letter-spacing:-.02em}
.sub{margin:26px 0 6px;font-size:14px;font-weight:600}
.opt{font-size:12.5px;font-weight:400;color:rgba(255,255,255,.5)}

.links{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:11px;margin:0 0 8px}
.link-add{justify-content:center;width:100%;padding:15px 12px;font-weight:500}
.link-open{flex-wrap:nowrap;gap:8px;min-width:0}
/* Specificity matters here: .form input[type=url] carries flex:none and width:100%, and it
   outranks a plain .link-open input, so the first attempt at this rule silently lost and the
   Remove button kept spilling out of its cell. Matched at the same depth to win. */
.form .link-open input[type=url],
.form .extra-row input[type=url]{flex:1 1 auto;min-width:0;width:auto}
.link-del{flex:none;font-size:12.5px;padding:9px 13px}
.extra-row{flex-wrap:nowrap;gap:8px;margin:0 0 9px;min-width:0;width:100%}

.imgs{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:26px;margin:0 0 10px}
.spec{margin:0 0 12px;padding-left:18px;font-size:12.5px;line-height:1.7;
  color:rgba(255,255,255,.58)}
.drop-btn{display:inline-flex;justify-content:center;width:100%;cursor:pointer;font-size:13.5px}
.drop-has{display:flex;align-items:center;gap:13px;margin:0 0 11px;padding:11px;
  border:1px solid var(--edge);border-radius:14px;background:var(--glass)}
.drop-has img{width:96px;height:auto;border-radius:8px;flex:none;background:#000}
.drop-has>div{min-width:0;flex:1}
.drop-name{margin:0;font-size:13px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.drop-dim{margin:3px 0 0;font-size:11.5px;color:rgba(255,255,255,.55)}
.drop-clear{flex:none;font-size:12.5px;padding:8px 13px}
.err-line{margin:9px 0 0;font-size:12.5px;color:var(--bad)}

/* Standalone adders sit at their natural width; only the link grid stretches. */
/* Adders need air above them: they read as attached to the help text otherwise. */
#add-extra{align-self:flex-start;margin:14px 0 0;font-size:13px;padding:10px 16px}
.submit-bar{display:flex;justify-content:flex-end;margin:30px 0 0;
  padding:22px 0 0;border-top:1px solid var(--edge)}

@media(max-width:760px){
  .brand{gap:12px}
  .nav a{padding:7px 11px;font-size:12.5px}
  .links,.imgs{grid-template-columns:1fr}
  .form{padding:20px}
  .submit-bar .btn{flex:1}
}

/* ── burger (narrow screens) ─────────────────────────────────────────────────
   The inline tabs and the burger are the same navigation at two widths: below 760px the
   header cannot hold the logo, two tabs and the wallet control at once. */
.burger{display:none;flex-direction:column;justify-content:center;gap:4px;
  width:34px;height:34px;padding:0 8px;border-radius:10px;cursor:pointer;
  background:var(--ctl);border:1px solid var(--ctl-edge)}
.burger span{display:block;height:2px;border-radius:2px;background:var(--ink);
  transition:transform .18s ease,opacity .18s ease}
.burger.open span:nth-child(1){transform:translateY(6px) rotate(45deg)}
.burger.open span:nth-child(2){opacity:0}
.burger.open span:nth-child(3){transform:translateY(-6px) rotate(-45deg)}
.burger:focus-visible{outline:2px solid var(--accent);outline-offset:3px}
.nav-menu{left:0;right:auto;min-width:150px}
.nav-menu a{display:block;text-decoration:none}
.nav-menu a.on{color:var(--bg);background:var(--accent)}
.nav-menu a.on:hover{background:var(--accent)}

@media(max-width:760px){
  .burger{display:flex}
  .brand>.nav{display:none}
}
@media (prefers-reduced-motion:reduce){.burger span{transition:none}}

/* ── submissions panel ───────────────────────────────────────────────────────*/
.tabs{gap:8px;margin:22px 0 4px}
.tab{font-size:13px;padding:9px 15px}
.tab.on{background:var(--accent);color:var(--bg);border-color:transparent}
.sub{padding:0;overflow:hidden;margin:16px 0 0}
.sub-head{display:flex;flex-wrap:wrap;gap:12px;align-items:center;justify-content:space-between;
  padding:16px 18px}
.sub-id{display:flex;flex-wrap:wrap;gap:8px;align-items:center}
.sub-when{font-size:11.5px;color:rgba(255,255,255,.5)}
.sub-acts{gap:8px}
.sub-acts .btn{font-size:12.5px;padding:8px 13px}
.chip.st-new{color:#B9FFDC;border-color:rgba(160,255,214,.4)}
.chip.st-done{color:rgba(255,255,255,.55)}
.chip.st-rejected{color:#FFC9C2;border-color:rgba(255,180,170,.38)}
.chip.st-dup{color:var(--ember);border-color:rgba(242,162,92,.4)}

.sub-token{display:flex;align-items:center;gap:14px;padding:0 18px 16px;flex-wrap:wrap}
.sub-icon{width:52px;height:52px;border-radius:13px;object-fit:cover;flex:none;background:#000}
.sub-icon.empty{background:rgba(255,255,255,.06)}
.sub-names{min-width:0}
.sub-names h3{margin:0;font-size:19px;font-weight:700;letter-spacing:-.02em}
.sub-names p{margin:2px 0 0;font-size:13px;color:rgba(255,255,255,.6)}
.sub-mint{margin-left:auto;max-width:100%}

/* One labelled block per field: the moderator's eye lands in the same place every card. */
.blk{display:grid;grid-template-columns:120px minmax(0,1fr);gap:18px;align-items:start;
  padding:16px 18px;border-top:1px solid var(--edge)}
.blk-l{font-family:"IBM Plex Mono",monospace;font-size:11px;letter-spacing:.05em;
  color:rgba(255,255,255,.5);padding-top:3px}
.blk-b{min-width:0}
.blk-b .dim{margin:0;font-size:13px;color:rgba(255,255,255,.4)}
.sub-header{display:block;width:100%;height:auto;border-radius:10px;border:1px solid var(--edge)}
.sub-icon-big{width:88px;height:88px;border-radius:16px;object-fit:cover;background:#000;
  border:1px solid var(--edge)}
.sub-desc{margin:0;font-size:13.5px;line-height:1.65;white-space:pre-wrap;overflow-wrap:anywhere}
.sub-desc.dim{color:rgba(255,255,255,.45)}
.sub-links{display:flex;flex-wrap:wrap;gap:8px}
.sub-links .chip{text-decoration:none}
.sub-links .chip:hover{border-color:var(--ctl-edge-2)}
.sub-wallet{margin:0;font-size:11px;color:rgba(255,255,255,.4);overflow-wrap:anywhere}
@media(max-width:620px){
  .blk{grid-template-columns:1fr;gap:8px;padding:14px}
  .sub-mint{margin-left:0;width:100%}
}
.submit-why{margin:0;margin-right:auto;font-size:13px;color:rgba(255,255,255,.72);max-width:44ch}
