/* Flux site - one stylesheet for every page.
   Deliberately dark-only: this is the app's own face, and the app is dark.
   Every surface paints itself; nothing borrows a host background. */

/* The palette, fonts, radii and the SHELF components (tile, chips,
   verdict, buttons, empty state, avatar, stats, breadcrumb) come from
   flux-ui.css, the sheet shared with the app (a copy - edit it in the app
   repo, then run its sync script). This site's own components follow
   just below. The names below are this file's OLD names, kept as
   aliases while its remaining rules migrate onto the shared ones; new
   rules use the shared names directly. */
/* ---------- the site's own components ----------
   These came out of flux-ui.css on 2026-09-10, when the shared sheet was
   cut down to what the app and the site genuinely both show (the shelf
   pieces). Fields, forms, the danger button, dialogs, toasts, the progress
   bar, the drop zone, thumbnails and the tabs are this site's alone. They
   still use the shared tokens, which load first. */

.fx-seg:focus-visible,
.fx-field:focus-visible {
  outline: none;
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 40%, transparent);
}

/* ---------- tabs (the app's segmented control) ---------- */

/* The active pill lives on ::before and FADES in, because a gradient
   cannot cross-fade with none. While held, ::after covers the original
   footprint so a press near an edge still counts (the click fix). */
.fx-segmented {
  display: inline-flex;
  gap: 3px;
  padding: 3px;
  border: 1px solid var(--edge);
  border-radius: var(--fx-r-2);
  background: var(--fx-field);
}
.fx-seg {
  appearance: none;
  border: none;
  background: transparent;
  position: relative;
  z-index: 0;
  color: var(--text-muted);
  font: var(--fx-label);
  font-size: 11px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  padding: 7px 12px;
  border-radius: var(--fx-r-1);
  cursor: pointer;
  transition:
    color 180ms ease,
    box-shadow 180ms ease,
    transform 170ms var(--fx-ease);
}
.fx-seg:hover { color: var(--text); }
.fx-seg::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  border-radius: inherit;
  background: linear-gradient(100deg, var(--accent-hot), var(--accent-2-deep));
  opacity: 0;
  transition: opacity 180ms ease;
}
.fx-seg.active::before { opacity: 1; }
.fx-seg.active { color: #fff; box-shadow: 0 0 14px var(--glow); }
.fx-seg:active { transform: scale(0.94); transition-duration: 60ms; }
.fx-seg:active::after {
  content: "";
  position: absolute;
  inset: -6px;
  clip-path: polygon(
    evenodd,
    0 0, 100% 0, 100% 100%, 0 100%, 0 0,
    6px 6px, 6px calc(100% - 6px), calc(100% - 6px) calc(100% - 6px), calc(100% - 6px) 6px, 6px 6px
  );
}

/* ---------- fields and forms ---------- */

/* The app's dark-field recipe: dark fill, faint border, accent halo on
   focus. A form on the web looks like a form in Settings. */
.fx-form { display: grid; gap: 14px; }
.fx-form[hidden] { display: none; }
.fx-form-row { display: grid; gap: 6px; }
.fx-field {
  appearance: none;
  width: 100%;
  min-width: 0;
  background: var(--fx-field);
  border: 1px solid var(--edge);
  border-radius: var(--fx-r-2);
  color: var(--text);
  font: inherit;
  font-size: 14px;
  line-height: 1.4;
  padding: 9px 12px;
  transition: border-color 160ms ease, box-shadow 160ms ease;
}
.fx-field::placeholder { color: var(--text-faint); }
.fx-field:focus {
  outline: none;
  border-color: var(--accent);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 16%, transparent);
}
.fx-field:disabled { opacity: 0.5; cursor: default; }
.fx-field.bad { border-color: color-mix(in srgb, var(--bad) 60%, transparent); }
textarea.fx-field { min-height: 96px; resize: vertical; line-height: 1.5; }
select.fx-field { cursor: pointer; }
.fx-help { font-size: 12.5px; color: var(--text-faint); line-height: 1.45; }
.fx-help.bad { color: var(--bad); }
.fx-help.good { color: var(--good); }
/* A fixed prefix in front of a field ("@" before a handle). */
.fx-field-prefix { display: flex; align-items: stretch; }
.fx-field-prefix > span {
  display: flex;
  align-items: center;
  padding: 0 10px 0 12px;
  border: 1px solid var(--edge);
  border-right: 0;
  border-radius: var(--fx-r-2) 0 0 var(--fx-r-2);
  background: rgba(255, 255, 255, 0.03);
  color: var(--text-faint);
  font: var(--fx-label);
  font-size: 12px;
}
.fx-field-prefix > .fx-field { border-radius: 0 var(--fx-r-2) var(--fx-r-2) 0; }

/* The danger button: only for things that cannot be undone. */
.fx-btn-danger {
  background: linear-gradient(135deg, color-mix(in srgb, var(--bad) 75%, white), var(--bad));
  background-origin: border-box;
  border-color: transparent;
  color: #fff;
  box-shadow: 0 0 18px color-mix(in srgb, var(--bad) 45%, transparent);
}
.fx-btn-danger:hover:not(:disabled) {
  background: linear-gradient(135deg, color-mix(in srgb, var(--bad) 75%, white), var(--bad));
  border-color: transparent;
  box-shadow: 0 0 28px color-mix(in srgb, var(--bad) 60%, transparent);
}

/* The door to a destructive step - it only opens an "are you sure?" box -
   is a calm red outline, so it never looks like the page's main action.
   The bright danger button above is kept for the final confirm inside that
   box (2026-09-23). */
.fx-btn-danger-quiet {
  background: transparent;
  border-color: color-mix(in srgb, var(--bad) 45%, transparent);
  color: color-mix(in srgb, var(--bad) 75%, white);
  box-shadow: none;
}
.fx-btn-danger-quiet:hover:not(:disabled) {
  background: color-mix(in srgb, var(--bad) 10%, transparent);
  border-color: color-mix(in srgb, var(--bad) 70%, transparent);
  color: color-mix(in srgb, var(--bad) 60%, white);
  box-shadow: none;
}

/* ---------- toast ---------- */

/* The app's toast: a pill at the top, a coloured dot, in for a moment and
   gone. Put one .fx-toast-host on the page; fxToast() in the page script
   (or the app) appends and removes .fx-toast pills inside it. */
.fx-toast-host {
  position: fixed;
  top: 48px;
  left: 0;
  right: 0;
  z-index: 90;
  display: grid;
  justify-items: center;
  gap: 8px;
  pointer-events: none;
}
.fx-toast {
  display: inline-flex;
  align-items: center;
  gap: 11px;
  max-width: 78%;
  padding: 10px 18px 10px 14px;
  border-radius: 999px;
  background: var(--panel-hover);
  border: 1px solid var(--edge-strong);
  backdrop-filter: blur(14px);
  box-shadow: 0 12px 38px rgba(0, 0, 0, 0.5), 0 0 26px color-mix(in srgb, var(--accent) 18%, transparent);
  color: var(--text);
  font-size: 13px;
  pointer-events: auto;
  animation: fxToastIn 280ms var(--fx-ease) both;
}
.fx-toast::before {
  content: "";
  width: 8px;
  height: 8px;
  border-radius: 50%;
  flex: none;
  background: var(--warn);
  box-shadow: 0 0 10px var(--warn);
}
.fx-toast.good::before { background: var(--good); box-shadow: 0 0 10px var(--good); }
.fx-toast.bad::before { background: var(--bad); box-shadow: 0 0 10px var(--bad); }
.fx-toast.out { animation: fxToastOut 220ms ease-in both; }
@keyframes fxToastIn { from { opacity: 0; transform: translateY(-8px); } to { opacity: 1; transform: none; } }
@keyframes fxToastOut { to { opacity: 0; transform: translateY(-6px); } }

/* ---------- dialog ---------- */

/* The app's one generic dialog: eyebrow, title, a line of detail, two
   buttons on the right. The overlay blurs the page behind it. */
.fx-dialog {
  position: fixed;
  inset: 0;
  z-index: 60;
  display: grid;
  /* one column the width of the screen: without it the column grows to the
     card's own width and max-width: 100% measures against that, so a
     wide card (the report form) spilled off a phone. A card taller than the
     screen scrolls from its top instead of being cut off at both ends. */
  grid-template-columns: minmax(0, 1fr);
  place-items: center;
  place-items: safe center;
  overflow-y: auto;
  padding: 24px;
  background: color-mix(in srgb, var(--bg) 78%, transparent);
  backdrop-filter: blur(8px);
  animation: fxFade 140ms ease-out both;
}
.fx-dialog[hidden] { display: none; }
.fx-dialog-card {
  width: 400px;
  max-width: 100%;
  display: grid;
  gap: 12px;
  padding: 22px;
  border-radius: var(--fx-r-4);
  background: linear-gradient(180deg, var(--panel-hover), var(--panel-solid));
  border: 1px solid var(--edge-strong);
  box-shadow: 0 24px 70px rgba(0, 0, 0, 0.6), 0 0 40px color-mix(in srgb, var(--accent) 16%, transparent);
  animation: fxRise 160ms ease-out both;
}
/* a card that sets its own display must still honour hidden (one dialog can
   hold several cards and show one at a time) */
.fx-dialog-card[hidden] { display: none; }
@media (max-width: 520px) { .fx-dialog { padding: 16px; } .fx-dialog-card { padding: 18px; } }
.fx-dialog-title { font: 650 17px / 1.3 var(--fx-display); color: var(--text); margin: 0; }
.fx-dialog-text { color: var(--text-muted); font-size: 13.5px; line-height: 1.5; margin: 0; white-space: pre-line; overflow-wrap: anywhere; }
.fx-dialog-actions { display: flex; justify-content: flex-end; gap: 8px; margin-top: 8px; }
@keyframes fxFade { from { opacity: 0; } }
@keyframes fxRise { from { opacity: 0; transform: translateY(8px) scale(0.98); } }

/* ---------- progress ---------- */

/* The app's progress bar: a thin track, a two-tone fill with a glow. */
.fx-progress {
  width: 100%;
  height: 4px;
  border-radius: 999px;
  background: rgba(255, 255, 255, 0.07);
  overflow: hidden;
}
.fx-progress > span {
  display: block;
  height: 100%;
  width: 0;
  border-radius: 999px;
  background: linear-gradient(90deg, var(--accent), var(--accent-2));
  box-shadow: 0 0 12px var(--glow);
  transition: width 180ms ease-out;
}

/* ---------- drop zone ---------- */

/* The app's dashed drop area, for the publish page's file and pictures. */
.fx-dropzone {
  display: grid;
  place-items: center;
  gap: 8px;
  text-align: center;
  padding: 34px 20px;
  border: 1px dashed var(--edge-strong);
  border-radius: var(--fx-r-4);
  background: rgba(255, 255, 255, 0.015);
  color: var(--text-muted);
  cursor: pointer;
  transition: border-color 160ms ease, background 160ms ease, box-shadow 200ms ease;
}
.fx-dropzone:hover, .fx-dropzone.over {
  border-color: var(--accent);
  background: color-mix(in srgb, var(--accent) 6%, transparent);
  box-shadow: 0 0 40px color-mix(in srgb, var(--accent) 14%, transparent);
}
.fx-dropzone strong {
  font: var(--fx-label);
  font-size: 12px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--text);
}
.fx-dropzone small { font-size: 12.5px; color: var(--text-faint); }
.fx-dropzone:focus-visible { outline: none; box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 40%, transparent); }

/* ---------- small picture thumbnails with a remove button ---------- */

.fx-thumbs { display: grid; grid-template-columns: repeat(auto-fill, minmax(120px, 1fr)); gap: 10px; }
.fx-thumb {
  position: relative;
  aspect-ratio: 16 / 9;
  border-radius: var(--fx-r-2);
  border: 1px solid var(--edge);
  background-size: cover;
  background-position: center;
  background-clip: padding-box;
  background-color: var(--fx-field);
  overflow: hidden;
}
.fx-thumb button {
  position: absolute;
  top: 6px;
  right: 6px;
  width: 24px;
  height: 24px;
  border-radius: var(--fx-r-1);
  border: 1px solid var(--edge-strong);
  background: color-mix(in srgb, var(--bg) 70%, transparent);
  color: var(--text);
  font: 700 13px/1 var(--fx-mono);
  cursor: pointer;
  transition: background 140ms ease, color 140ms ease, transform 170ms var(--fx-ease);
}
.fx-thumb button:hover { background: color-mix(in srgb, var(--bad) 30%, transparent); color: #fff; }
.fx-thumb button:active { transform: scale(0.9); transition-duration: 60ms; }

/* ---------- this site's own layout ---------- */

:root {
  --surface: var(--fx-surface);
  --surface-2: color-mix(in srgb, var(--fx-surface) 92%, #fff);
  --muted: var(--text-muted);
  --faint: var(--text-faint);
  --hot: var(--accent-hot);
  --display: var(--fx-display);
  --body: var(--fx-body);
  --mono: var(--fx-mono);
  color-scheme: dark;
}

* { box-sizing: border-box; }

html { scroll-behavior: smooth; scroll-padding-top: 72px; }

::selection { background: color-mix(in srgb, var(--accent) 35%, transparent); color: #fff; }

/* The app's focus ring (R12): a soft halo that follows the control's own
   corners. The old hard outline also forced 4px corners on whatever it
   wrapped, which matched nothing. */
a:focus-visible,
button:focus-visible,
input:focus-visible,
select:focus-visible {
  outline: none;
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 40%, transparent);
}

body {
  margin: 0;
  background: var(--bg);
  color: var(--text);
  font-family: var(--body);
  font-size: var(--fx-text);
  line-height: var(--fx-lh);
}

/* A faint warm glow up top, the app's own corner-light trick. */
body::before {
  content: "";
  position: fixed;
  inset: 0;
  pointer-events: none;
  background: radial-gradient(900px 420px at 18% -10%, color-mix(in srgb, var(--accent) 12%, transparent), transparent 62%);
}

/* And the app's grid material, fading out down the page exactly like it
   does in the app - same 46px cell, same whisper of white. */
body::after {
  content: "";
  position: fixed;
  inset: 0;
  pointer-events: none;
  background-image:
    linear-gradient(rgba(255, 255, 255, 0.021) 1px, transparent 1px),
    linear-gradient(90deg, rgba(255, 255, 255, 0.021) 1px, transparent 1px);
  background-size: 46px 46px;
  mask-image: radial-gradient(circle at 50% 0%, #000 0%, transparent 75%);
}

.wrap { max-width: 980px; margin: 0 auto; padding: 0 22px; position: relative; }
/* Shelf pages (library, creator) run wider so three tiles fit a row (R15). */
body.wide .wrap { max-width: 1120px; }
/* The header and footer hold ONE width on every page (2026-09-23): the
   library, mod and creator pages widen their content for side ads or a
   third tile, and the header used to widen with them - the logo and menu
   jumped sideways up to 200px from page to page. 1120 lines up with the
   library and creator content column. The body prefix outranks the
   per-page .wrap rules below. */
body header.site .wrap, body footer.site .wrap { max-width: 1120px; }
/* A short page has no scrollbar; without the reserved gutter everything
   on it sat 7px off from the page before. */
html { scrollbar-gutter: stable; }

/* ---------- header ---------- */

header.site {
  border-bottom: 1px solid var(--edge);
  background: color-mix(in srgb, var(--bg) 70%, transparent);
  backdrop-filter: blur(10px);
  position: sticky;
  top: 0;
  z-index: 10;
}
header.site .wrap {
  display: flex;
  align-items: center;
  gap: 12px;
  padding-top: 14px;
  padding-bottom: 14px;
}
.brand { display: flex; align-items: center; gap: 10px; text-decoration: none; color: var(--text); }
.brand .mark { width: 22px; height: 22px; display: block; filter: drop-shadow(0 0 8px var(--glow)); }
/* The wordmark is the app's own drawn FLUX - same grid as the mark, and
   the X is two strokes whose overlap is hollowed out on purpose. Never
   retype it in a font; the split X is the identity. */
.brand .wordmark {
  height: 13px;
  width: auto;
  display: block;
  fill: var(--text);
}
nav { margin-left: auto; display: flex; align-items: center; gap: 22px; }
nav a {
  color: var(--muted);
  text-decoration: none;
  font-family: var(--mono);
  font-size: 12px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  transition: color 150ms ease;
}
nav a:hover, nav a[aria-current] { color: var(--text); }
nav a[aria-current] { color: var(--hot); }
nav .cta {
  color: #fff;
  background: linear-gradient(100deg, var(--accent-hot), var(--accent) 50%, var(--accent-2-deep));
  padding: 8px 16px;
  border-radius: var(--fx-r-2);
  box-shadow: 0 0 18px var(--glow);
}
nav .cta:hover { color: #fff; filter: brightness(1.08); }

/* ---------- shared type ---------- */

.eyebrow {
  font-family: var(--mono);
  font-size: 12px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--accent-hot);
}
h1 {
  font-family: var(--display);
  font-weight: 700;
  font-size: clamp(34px, 5.5vw, 54px);
  line-height: 1.04;
  letter-spacing: -0.01em;
  margin: 12px 0 16px;
  text-wrap: balance;
}
h2 {
  font-family: var(--display);
  font-weight: 600;
  font-size: 26px;
  line-height: 1.15;
  margin: 0 0 10px;
  text-wrap: balance;
}
h3 { font-family: var(--display); font-weight: 600; font-size: 17px; margin: 0 0 6px; }
.lead { color: var(--muted); font-size: 18px; max-width: 58ch; margin: 0; }
p { margin: 0; }
section { padding: 56px 0; }
section + section { border-top: 1px solid var(--edge); }
/* The home hero wears a champion's splash art (index.html, dressHero):
   full strength on the right, fading out under the words on the left and
   into the page at the bottom, like League's own client. On a phone the
   words span the width, so the art sits further back. Two layers take
   turns so one splash crossfades slowly into the next (2026-09-23). */
.hero { position: relative; isolation: isolate; overflow: hidden; padding: 84px 0 80px; }
.hero-art {
  position: absolute; inset: 0; z-index: -1;
  background-size: cover; background-position: 72% 18%;
  opacity: 0; transition: opacity 2500ms ease-in-out;
  -webkit-mask-image: linear-gradient(90deg, transparent 18%, #000 64%), linear-gradient(180deg, #000 55%, transparent);
  -webkit-mask-composite: source-in;
  mask-image: linear-gradient(90deg, transparent 18%, #000 64%), linear-gradient(180deg, #000 55%, transparent);
  mask-composite: intersect;
}
.hero-art.shown { opacity: 0.6; }
.hero-credit {
  position: absolute; right: 22px; bottom: -58px; margin: 0;
  font-family: var(--mono); font-size: 10.5px; letter-spacing: 0.08em; text-transform: uppercase;
  color: var(--faint);
  transition: opacity 450ms ease;
}
.hero-credit.swap { opacity: 0; }
.hero-credit a { color: var(--muted); text-decoration: none; }
.hero-credit a:hover { color: var(--text); text-decoration: underline; }
@media (max-width: 700px) {
  .hero { padding: 64px 0 76px; }
  .hero-art { background-position: 64% 12%; }
  .hero-art.shown { opacity: 0.3; }
}
@media (prefers-reduced-motion: reduce) { .hero-art, .hero-credit { transition: none; } }
.sub { color: var(--muted); max-width: 64ch; }
/* No default underlines anywhere - the hot colour already says 'link',
   and the browser's underline was striking through the pack labels and
   arrow links (user report, 2026-09-05). Prose links get theirs back on
   hover, where it means something. */
a { color: var(--hot); text-decoration: none; }
main p a:hover {
  text-decoration: underline;
  text-underline-offset: 3px;
  text-decoration-thickness: 1px;
}

/* ---------- components ---------- */

.cards { display: grid; grid-template-columns: repeat(3, 1fr); gap: 14px; margin-top: 26px; }
.card {
  background: var(--surface);
  border: 1px solid var(--edge);
  border-radius: 12px;
  padding: 20px 20px 22px;
  display: flex;
  flex-direction: column;
  gap: 9px;
}
.card p { color: var(--muted); font-size: 14.5px; }

.stamp {
  display: inline-flex;
  gap: 8px;
  align-items: baseline;
  font-family: var(--mono);
  font-size: 12.5px;
  letter-spacing: 0.07em;
  color: var(--good);
  border: 1px solid color-mix(in srgb, var(--good) 40%, transparent);
  border-radius: var(--fx-r-1);
  padding: 4px 10px;
  white-space: nowrap;
}
/* on a phone a long verdict wraps instead of pushing the page sideways */
@media (max-width: 600px) { .stamp { white-space: normal; } }
.stamp.bad { color: var(--bad); border-color: color-mix(in srgb, var(--bad) 40%, transparent); }
.stamp.dim { color: var(--faint); border-color: var(--edge-strong); }

.badge-example {
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 0.12em;
  color: var(--warn);
  border: 1px dashed color-mix(in srgb, var(--warn) 50%, transparent);
  border-radius: var(--fx-r-1);
  padding: 3px 10px;
  text-transform: uppercase;
}

/* Buttons are .fx-btn from flux-ui.css (two sizes, the app's hover glow
   and press). The site's own pair lived here until 2026-09-07. */

table { border-collapse: collapse; width: 100%; font-size: 14.5px; }
th {
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--faint);
  font-weight: 500;
  text-align: left;
  padding: 0 16px 9px 0;
}
td { border-top: 1px solid var(--edge); padding: 10px 16px 10px 0; color: var(--muted); vertical-align: top; }
td:first-child { color: var(--text); font-family: var(--mono); font-size: 13.5px; white-space: nowrap; }
.tablewrap { overflow-x: auto; }
.num { font-variant-numeric: tabular-nums; }

/* ---------- steps (guide) ---------- */

ol.steps { margin: 26px 0 0; padding: 0; list-style: none; counter-reset: step; display: flex; flex-direction: column; gap: 14px; }
ol.steps li {
  counter-increment: step;
  display: grid;
  grid-template-columns: 46px 1fr;
  background: var(--surface);
  border: 1px solid var(--edge);
  border-radius: 12px;
  overflow: hidden;
}
ol.steps li::before {
  content: counter(step);
  font-family: var(--mono);
  font-size: 17px;
  color: var(--accent);
  background: var(--surface-2);
  border-right: 1px solid var(--edge);
  display: grid;
  place-items: center;
}
ol.steps .step-body { padding: 15px 18px 16px; }
ol.steps p { color: var(--muted); font-size: 14.5px; max-width: 62ch; }
code {
  font-family: var(--mono);
  font-size: 0.9em;
  background: rgba(255, 255, 255, 0.06);
  border: 1px solid var(--edge);
  border-radius: var(--fx-r-1);
  padding: 1px 6px;
}

.note {
  border: 1px solid color-mix(in srgb, var(--warn) 35%, transparent);
  background: color-mix(in srgb, var(--warn) 6%, transparent);
  border-radius: var(--fx-r-3);
  padding: 15px 18px;
  color: var(--muted);
  font-size: 14.5px;
  max-width: 68ch;
}
.note strong { color: var(--text); }

/* ---------- looks page ---------- */

.packshots { display: grid; grid-template-columns: 1fr 1fr; gap: 14px; margin-top: 24px; }
/* Only LINKED packshots lift on hover - a hover state on a plain image
   would promise a click that does nothing, the app's own rule. */
a.packshot {
  transition:
    transform 200ms cubic-bezier(0.22, 1, 0.36, 1),
    border-color 200ms ease,
    box-shadow 200ms ease;
}
a.packshot:hover {
  transform: translateY(-3px);
  border-color: var(--edge-strong);
  box-shadow: 0 14px 34px rgba(0, 0, 0, 0.45);
}
.packshot {
  position: relative;
  border: 1px solid var(--edge);
  border-radius: var(--fx-r-3);
  overflow: hidden;
  min-height: 230px;
  background-size: cover;
  background-position: center;
  /* The art stops at the INSIDE of the border. By default it runs under
     it, and a translucent border over a bright image reads as a coloured
     rim - purple on Minecraft, cyan on Terraria (user report, 2026-09-06). */
  background-clip: padding-box;
  display: flex;
  align-items: flex-end;
}
.packshot .label {
  width: 100%;
  padding: 40px 18px 14px;
  background: linear-gradient(transparent, rgba(0, 0, 0, 0.82));
}
.packshot b { font-family: var(--display); font-size: 17px; }
.packshot span { display: block; color: rgba(255, 255, 255, 0.75); font-size: 13px; }

.swatches { display: grid; grid-template-columns: repeat(5, 1fr); gap: 10px; margin-top: 24px; }
/* The theme cards, matched to the app card for card: the swatch is the
   app's own four-stop ramp (accent2 in the middle is what makes a
   two-tone theme read as two-tone), and the material overlay is copied
   verbatim - grid, dots and weave draw their lattice, mesh and plain are
   the gradient alone, exactly as in-app. */
.swatch {
  background: var(--surface);
  border: 1px solid var(--edge);
  border-radius: var(--fx-r-3);
  padding: 7px 7px 8px;
}
.swatch .ramp {
  height: 38px;
  border-radius: var(--fx-r-1);
  border: 1px solid rgba(255, 255, 255, 0.07);
  position: relative;
  overflow: hidden;
}
.ramp::after {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
}
.ramp[data-material="grid"]::after {
  background-image:
    linear-gradient(rgba(255, 255, 255, 0.16) 1px, transparent 1px),
    linear-gradient(90deg, rgba(255, 255, 255, 0.16) 1px, transparent 1px);
  background-size: 9px 9px;
}
.ramp[data-material="dots"]::after {
  background-image: radial-gradient(circle, rgba(255, 255, 255, 0.26) 1px, transparent 1.3px);
  background-size: 7px 7px;
}
.ramp[data-material="weave"]::after {
  background-image: repeating-linear-gradient(
    45deg,
    rgba(255, 255, 255, 0.16) 0 1px,
    transparent 1px 5px
  );
}
.swatch b {
  display: block;
  font-size: 12.5px;
  font-weight: 650;
  padding: 7px 3px 0;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.swatch span {
  display: block;
  font-family: var(--mono);
  font-size: 10.5px;
  color: var(--faint);
  padding: 1px 3px 0;
}

/* The pack rail: two visible, the rest a page to the right. Native
   scroll with snap does the moving; the arrows are the affordance, so
   the scrollbar stays hidden. */
.rail-head { display: flex; align-items: center; gap: 8px; margin-top: 24px; }
.rail-spacer { flex: 1; }
.rail-btn {
  appearance: none;
  width: 36px;
  height: 36px;
  border: 1px solid var(--edge-strong);
  border-radius: var(--fx-r-2);
  background: var(--surface);
  color: var(--text);
  font-size: 20px;
  line-height: 1;
  cursor: pointer;
  transition: border-color 150ms ease, background 150ms ease, opacity 150ms ease;
}
.rail-btn:hover:not(:disabled) { border-color: var(--accent); background: color-mix(in srgb, var(--accent) 12%, transparent); box-shadow: 0 0 16px color-mix(in srgb, var(--accent) 22%, transparent); }
.rail-btn:disabled { opacity: 0.35; cursor: default; }
.pack-rail {
  display: flex;
  gap: 14px;
  margin-top: 12px;
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  scrollbar-width: none;
}
.pack-rail::-webkit-scrollbar { display: none; }
.pack-rail .packshot {
  flex: 0 0 calc(50% - 7px);
  scroll-snap-align: start;
}
@media (max-width: 640px) {
  .pack-rail .packshot { flex-basis: 100%; }
}
/* The one pack whose art stays off the web: its card wears the pack's
   own gold instead, and says why. */
.tag-inapp {
  position: absolute;
  top: 10px;
  right: 10px;
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: 0.12em;
  color: #f0dfae;
  border: 1px solid rgba(240, 223, 174, 0.45);
  border-radius: var(--fx-r-1);
  padding: 3px 8px;
  background: rgba(1, 10, 19, 0.5);
}

/* ---------- footer ---------- */

footer.site {
  border-top: 1px solid var(--edge);
  padding: 30px 0 44px;
  color: var(--faint);
  font-size: 13px;
}
footer.site .wrap { display: flex; flex-direction: column; gap: 8px; }
footer.site .mono { font-family: var(--mono); font-size: 11.5px; letter-spacing: 0.06em; }
footer.site .foot-legal a { color: var(--text-muted); text-decoration: none; }
footer.site .foot-legal a:hover { color: var(--text); text-decoration: underline; }

/* The legal pages (terms.html, privacy.html): plain sections read best
   in a narrower column, with room between headings. */
.legal { max-width: 820px; }
.legal h2 { margin-top: 34px; }
.prose-list { margin: 0 0 10px; padding-left: 22px; display: grid; gap: 6px; }

/* ---------- responsive ---------- */

@media (max-width: 760px) {
  .cards { grid-template-columns: 1fr; }
  .packshots { grid-template-columns: 1fr; }
  .swatches { grid-template-columns: repeat(3, 1fr); }
  /* The links stay - they shrink instead of vanishing. A nav that hides
     its pages on a phone is a nav that lies about the site. */
  header.site nav { gap: 12px; }
  header.site nav a { font-size: 10.5px; letter-spacing: 0.06em; }
  header.site nav .cta { padding: 7px 11px; }
}

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  * { transition-duration: 0.01ms !important; }
}

/* --- the library (library.html, mod.html) --------------------------------
   Art first, words second: a tile is its cover with the name and the
   patch-check chip resting on it, and everything else lives on the skin's
   own page. The chip is the whole point - only Flux can say whether a skin
   still applies after a patch, so it sits on every tile and every page. */

/* The library page's frame: the three columns and the bar above the
   shelf. The rail itself, the gallery, the search box and the select are
   shared pieces in flux-ui.css (since 2026-09-14, when the app's Browse
   view took the same rail). */
/* Filters | shelf, with the shared side-ads frame around them (2026-09-18,
   replacing the old right-hand-only column). The content keeps 1120px -
   the filter rail plus three tiles - and the page widens only for the
   sides: 120x600 from 1400px (so they exist on ordinary screens), 160
   from 1600px, 300 from 1860px. The in-grid and bottom units carry the
   page below that. The page header rides INSIDE the frame's centre
   column (.lib-col, 2026-09-20): the side columns belong to the ads from
   the very top, and no text ever sits above or beside them. */
.lib-col { min-width: 0; }
body.library .wrap { max-width: 1120px; }
@media (min-width: 1400px) {
  body.library .wrap { max-width: 1408px; }
  body.library .adframe { grid-template-columns: 120px minmax(0, 1fr) 120px; }
  body.library .adframe > .side-ads { display: block; }
  body.library .side-ads .ad { width: 120px; }
}
@media (min-width: 1600px) {
  body.library .wrap { max-width: 1488px; }
  body.library .adframe { grid-template-columns: 160px minmax(0, 1fr) 160px; }
  body.library .side-ads .ad { width: 160px; }
}
@media (min-width: 1860px) {
  body.library .wrap { max-width: 1768px; }
  body.library .adframe { grid-template-columns: 300px minmax(0, 1fr) 300px; }
  body.library .side-ads .ad { width: 300px; }
}
.lib { display: grid; grid-template-columns: 260px minmax(0, 1fr); gap: 28px; margin-top: 26px; align-items: start; }
/* the shared rail (flux-ui.css), kept beside the shelf while it scrolls */
.lib .fx-rail { position: sticky; top: 84px; max-height: calc(100vh - 100px); overflow: hidden auto; overscroll-behavior: contain; padding: 2px 0 12px; }
.lib-field { display: grid; gap: 6px; }
.lib-field > span { font-family: var(--mono); font-size: 11px; letter-spacing: 0.1em; text-transform: uppercase; color: var(--faint); }
.lib-field > span { display: flex; align-items: center; gap: 6px; }
.mod-who { display: flex; align-items: center; gap: 10px; }
.mod-face { width: 44px; height: 44px; border-radius: 50%; object-fit: cover; border: 2px solid var(--edge-strong); box-shadow: 0 4px 14px rgba(0, 0, 0, 0.4); }
.mod-cover.stock { background-position: center 22%; }
/* the champion's splash, blurred and faded, behind the top of the page */
/* Ads at both sides (mod pages 2026-09-18, creator pages same day). The
   content column keeps its 980px - still three tiles on a creator's
   shelf - and the page runs wider only to hold the side columns, giving
   them up below 1360px, where the in-content units carry the page. */
body.adsides .wrap { max-width: 980px; }
.adframe { display: grid; grid-template-columns: minmax(0, 1fr); gap: 24px; align-items: start; }
.adframe > .side-ads { display: none; }
.side-ads { position: sticky; top: 84px; }
.side-ads .ad { width: 160px; height: 600px; }
/* Scoped to body.adsides: the library shares .adframe but runs its own
   wider-content breakpoints below, and these must not reach it. */
@media (min-width: 1360px) {
  body.adsides .wrap { max-width: 1352px; }
  body.adsides .adframe { grid-template-columns: 160px minmax(0, 1fr) 160px; }
  body.adsides .adframe > .side-ads { display: block; }
}
@media (min-width: 1760px) {
  body.adsides .wrap { max-width: 1640px; }
  body.adsides .adframe { grid-template-columns: 300px minmax(0, 1fr) 300px; }
  body.adsides .side-ads .ad { width: 300px; }
}
.mod-backdrop { position: fixed; left: 0; right: 0; top: 0; height: 70vh; z-index: -1; pointer-events: none; background-size: cover; background-position: center 25%; opacity: 0.34; filter: blur(7px) saturate(1.05); -webkit-mask-image: linear-gradient(180deg, #000 25%, transparent); mask-image: linear-gradient(180deg, #000 25%, transparent); }
.lib-active .fx-chip svg { width: 12px; height: 12px; }
/* the bar above the shelf: the count, the filters in force, per page, view */
.lib-bar { display: flex; align-items: center; gap: 10px 14px; flex-wrap: wrap; min-height: 40px; }
.lib-bar .count { margin-left: 0; }
.lib-active { flex: 1 1 auto; }
.lib-active .fx-chip .x { font-size: 13px; line-height: 1; opacity: 0.7; }
.lib-field.inline { display: flex; align-items: center; gap: 8px; margin-left: auto; }
.lib-field.inline .fx-select { width: auto; padding: 8px 32px 8px 10px; background-position: right 11px center; }
.lib-view .fx-seg { display: inline-flex; align-items: center; padding: 6px 9px; }
.lib-view svg { width: 15px; height: 15px; display: block; fill: none; stroke: currentColor; stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; }
.lib-main .shelf { margin-top: 16px; grid-template-columns: repeat(auto-fill, minmax(250px, 1fr)); }
.lib-main .fx-empty .fx-btn { margin-top: 14px; }
.lib-main .fx-list .fx-tile.invite { flex-direction: column; }
.sr-only { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0; }

/* --- ad slots (ads.js) -------------------------------------------------
   Every public page reserves its ad room now, though ads come last (plan,
   phase 3). Standard sizes, so any network fits: 300x250, 300x600,
   160x600, 970x90. Locally a slot is a labelled placeholder; live, the
   network fills it; off, it takes no room at all. Placeholders are quiet
   on purpose - the page must still read as content-first. */
.ad { box-sizing: border-box; position: relative; }
.ad.is-placeholder {
  display: grid; align-content: center; justify-items: center; gap: 4px;
  border: 1px dashed var(--edge-strong);
  border-radius: var(--fx-r-2);
  background: repeating-linear-gradient(135deg, transparent 0 10px, rgba(255, 255, 255, 0.025) 10px 20px);
  color: var(--faint);
  font-family: var(--mono); font-size: 10.5px; letter-spacing: 0.12em; text-transform: uppercase; text-align: center;
  overflow: hidden;
}
.ad.is-placeholder::after { content: attr(data-size); opacity: 0.6; letter-spacing: 0.06em; }
.ad[data-slot="rail-mod"] { width: 300px; max-width: 100%; height: 250px; margin: 18px auto 0; }
.ad[data-slot="bottom"] { width: 100%; max-width: 970px; height: 90px; margin: 26px auto 0; }
@media (max-width: 760px) { .ad[data-slot="bottom"] { max-width: 336px; height: 280px; } }
.ad-tile { min-height: 290px; }
.fx-list .ad-tile { min-height: 120px; }
.count { font-family: var(--mono); font-size: 12px; letter-spacing: 0.1em; color: var(--faint); text-transform: uppercase; margin-left: auto; }

.shelf { margin-top: 18px; }
/* Tiles are .fx-tile (flux-ui.css); the old .skin-tile rules lived here until 2026-09-08. */
.shelf-empty { grid-column: 1 / -1; color: var(--muted); padding: 40px 0; text-align: center; }

/* the skin's own page */
.crumbs { display: flex; justify-content: flex-start; margin-left: 0; gap: 10px; align-items: center; flex-wrap: wrap; font-family: var(--mono); font-size: 12px; letter-spacing: 0.08em; text-transform: uppercase; color: var(--faint); }
.crumbs a { color: var(--muted); }
.crumbs a:hover { color: var(--text); }
.crumbs .cur { color: var(--text); }
/* Three areas, so a phone gets cover, then the buttons, then the words -
   nobody should scroll past the description to find Open in Flux. */
.mod-grid {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 340px;
  grid-template-areas: "art rail" "main rail";
  /* The rail spans both rows; without this its height would be shared out
     to the cover's row too, leaving a hole under the cover. */
  grid-template-rows: auto 1fr;
  gap: 28px; margin-top: 22px; align-items: start;
}
.mod-art { grid-area: art; min-width: 0; }
.mod-main { grid-area: main; min-width: 0; }
.mod-rail { grid-area: rail; }
.mod-cover {
  aspect-ratio: 16 / 9;
  border-radius: 16px;
  background-size: cover; background-position: center;
  background-clip: padding-box;
  border: 1px solid var(--edge);
  box-shadow: 0 30px 80px rgba(0, 0, 0, 0.5);
}
.mod-main h2 { margin-top: 0; }
.mod-main p { color: var(--muted); max-width: 66ch; }
.mod-main p + p { margin-top: 12px; }
.features { margin: 16px 0 0; padding: 0; list-style: none; display: grid; grid-template-columns: 1fr 1fr; gap: 8px 18px; }
.features li { color: var(--muted); font-size: 14.5px; padding-left: 20px; position: relative; }
.features li::before { content: "✓"; position: absolute; left: 0; color: var(--good); font-family: var(--mono); }
.creator {
  margin-top: 34px;
  display: flex; gap: 14px; align-items: center;
  background: var(--surface);
  border: 1px solid var(--edge);
  border-radius: var(--fx-r-3);
  padding: 16px 18px;
}
.creator b { font-family: var(--display); font-size: 16px; display: block; }
.creator span { color: var(--muted); font-size: 14px; }

.mod-rail {
  position: sticky; top: 84px;
  background: var(--surface);
  border: 1px solid var(--edge);
  border-radius: 16px;
  padding: 20px;
  display: flex; flex-direction: column; gap: 14px;
}
.mod-rail h1 { font-size: 28px; margin: 0; line-height: 1.1; }
.mod-rail .by { color: var(--muted); font-size: 14px; margin-top: -6px; }
.mod-rail .by b { color: var(--text); }
.actions { display: flex; flex-direction: column; gap: 8px; }
.actions .fx-btn { width: 100%; }
.actions small { color: var(--faint); font-size: 12px; text-align: center; }
/* The note under Open in Flux when nothing answered the click (mod.html,
   wireOpenHelp). */
.open-help { margin: 2px 0 0; padding: 10px 12px; border: 1px solid color-mix(in srgb, var(--warn) 35%, transparent); background: color-mix(in srgb, var(--warn) 6%, transparent); border-radius: var(--fx-r-2); color: var(--muted); font-size: 13px; line-height: 1.5; }
.open-help strong { color: var(--text); }
.open-help[hidden] { display: none; }
.meta { display: grid; grid-template-columns: auto minmax(0, 1fr); gap: 6px 14px; font-size: 13.5px; margin: 0; }
.meta dt { color: var(--faint); font-family: var(--mono); font-size: 11.5px; letter-spacing: 0.08em; text-transform: uppercase; padding-top: 2px; }
.meta dd { margin: 0; color: var(--text); overflow-wrap: anywhere; }
/* The verdict box is .fx-verdict-box (flux-ui.css). */
.steps-mini { margin: 0; padding: 0; list-style: none; display: grid; gap: 6px; font-size: 12.5px; color: var(--faint); }
.steps-mini li { display: flex; gap: 10px; }
/* under the buttons it explains (2026-09-23; it used to sit below the ad) */
.actions .steps-mini { margin: 4px 0 0; padding: 0 2px; }
.steps-mini li i { font-style: normal; font-family: var(--mono); color: var(--muted); }
/* Report this skin (mod.html, 2026-09-23): a quiet link under the facts,
   and the form's reasons as a list of rows. */
.report-link { appearance: none; display: inline-flex; align-items: center; gap: 7px; justify-self: start; margin-top: 2px; padding: 4px 2px; border: 0; background: none; color: var(--faint); font: inherit; font-size: 12.5px; cursor: pointer; transition: color 140ms ease; }
.report-link:hover { color: var(--text); text-decoration: underline; }
.report-link[hidden] { display: none; }
.report-link svg { width: 14px; height: 14px; fill: none; stroke: currentColor; stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; }
.report-card { width: 480px; }
.report-reasons { display: grid; gap: 6px; }
.report-reason { display: flex; align-items: flex-start; gap: 10px; padding: 9px 11px; border: 1px solid var(--edge); border-radius: var(--fx-r-2); background: var(--fx-field); cursor: pointer; font-size: 14px; transition: border-color 140ms ease; }
.report-reason:hover { border-color: var(--edge-strong); }
.report-reason:has(input:checked) { border-color: var(--accent); background: color-mix(in srgb, var(--accent) 8%, var(--fx-field)); }
.report-reason input { margin-top: 3px; accent-color: var(--accent); }

@media (max-width: 860px) {
  .mod-grid { grid-template-columns: 1fr; grid-template-rows: auto; grid-template-areas: "art" "rail" "main"; }
  .mod-rail { position: static; }
  .shelf { grid-template-columns: 1fr 1fr; }
}
/* Five links plus the button no longer fit beside the logo on a phone. The
   links take a full row of their own under it - never a horizontal
   scrollbar, never "Patch day" broken across two lines. Since 2026-09-23
   this is only the fallback for scripts being off: with them on, the
   phone header below takes over. */
@media (max-width: 600px) {
  header.site .wrap { flex-wrap: wrap; row-gap: 10px; padding-top: 12px; padding-bottom: 12px; }
  header.site nav { width: 100%; margin-left: 0; justify-content: space-between; gap: 6px 10px; flex-wrap: wrap; }
  header.site nav a { font-size: 11px; letter-spacing: 0.06em; white-space: nowrap; }
  header.site nav .cta { padding: 7px 12px; }
}
/* ---------- the phone header (2026-09-23) ----------
   The wrapped links above ran to two rows (three, signed in) with tap
   targets 18px tall. Up to 700px wide the header is the logo and a Menu
   button (auth.js makes it and adds .has-menu); the links drop down as
   full-width rows under the header, Download last as a full button. */
.nav-toggle { display: none; }
@media (max-width: 700px) {
  header.site.has-menu .wrap { flex-wrap: nowrap; padding-top: 12px; padding-bottom: 12px; }
  header.site.has-menu .nav-toggle {
    display: inline-flex; align-items: center; gap: 8px;
    margin-left: auto; min-height: 42px; padding: 0 14px;
    border: 1px solid var(--edge); border-radius: var(--fx-r-2);
    background: var(--fx-surface); color: var(--text);
    font: 600 12px / 1 var(--mono); letter-spacing: 0.1em; text-transform: uppercase;
    cursor: pointer;
  }
  .nav-toggle svg { width: 18px; height: 18px; fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round; }
  header.site.has-menu nav {
    display: none; position: absolute; top: 100%; left: 0; right: 0;
    width: auto; margin: 0; flex-direction: column; align-items: stretch; gap: 0;
    padding: 6px 22px 18px;
    background: var(--bg); border-bottom: 1px solid var(--edge);
    box-shadow: 0 22px 44px rgba(0, 0, 0, 0.55);
  }
  header.site.has-menu.menu-open nav { display: flex; }
  header.site.has-menu nav a {
    display: flex; align-items: center; gap: 10px;
    min-height: 50px; padding: 0 2px;
    border-bottom: 1px solid var(--edge);
    font-size: 13px; letter-spacing: 0.1em; white-space: nowrap;
  }
  header.site.has-menu nav .cta {
    justify-content: center; margin-top: 14px; min-height: 48px;
    border-bottom: 0; padding: 0 16px;
  }
  header.site.has-menu .nav-me-name { display: inline; }
}
@media (max-width: 560px) {
  .shelf { grid-template-columns: 1fr; }
  .features { grid-template-columns: 1fr; }
  .count { margin-left: 0; }
}

/* --- home: the shelf preview and the three steps ----------------------- */
.rail-head h2 { margin: 0; }
.rail-head .more { font-family: var(--mono); font-size: 12px; letter-spacing: 0.1em; text-transform: uppercase; }
.how { display: grid; grid-template-columns: repeat(3, 1fr); gap: 14px; margin-top: 26px; }
.how .n { font-family: var(--mono); font-size: 12px; letter-spacing: 0.14em; color: var(--accent-hot); }
@media (max-width: 720px) { .how { grid-template-columns: 1fr; } }

/* --- creator page (creator.html) ------------------------------------------
   Who they are up top, the numbers, then their shelf. Every piece is a
   shared component from flux-ui.css; this block only arranges them. */
.creator-hero { display: grid; grid-template-columns: auto minmax(0, 1fr); gap: 26px; align-items: start; margin-top: 22px; }
.creator-kicker { display: flex; gap: 12px; align-items: center; flex-wrap: wrap; }
.creator-id h1 { font-size: clamp(30px, 4.5vw, 42px); margin: 8px 0 2px; }
.creator-handle { font-family: var(--mono); font-size: 13px; letter-spacing: 0.06em; color: var(--faint); }
.creator-bio { color: var(--muted); font-size: 17px; max-width: 60ch; margin-top: 12px; }
.creator-links { margin-top: 16px; }
.creator-stats { margin-top: 24px; }
.creator-tabs { display: flex; align-items: center; gap: 14px; margin-top: 36px; }
.creator-mods { margin-top: 18px; }
.tile-tags { color: var(--faint); font-family: var(--mono); font-size: 11px; letter-spacing: 0.06em; text-transform: uppercase; }
.creator-about { display: grid; grid-template-columns: minmax(0, 1fr) 340px; gap: 16px; margin-top: 18px; align-items: start; }
/* Both panels set their own display, which defeats the hidden attribute
   unless said outright (the app hits this trap too). */
.creator-mods[hidden], .creator-about[hidden] { display: none; }
.creator-about-card { display: flex; flex-direction: column; gap: 12px; }
.creator-about-card p { color: var(--muted); max-width: 66ch; }
@media (max-width: 860px) {
  .creator-about { grid-template-columns: 1fr; }
}
@media (max-width: 600px) {
  .creator-hero { grid-template-columns: 1fr; gap: 16px; }
  .creator-hero .fx-avatar-lg { --fx-avatar-size: 72px; }
  .creator-tabs { flex-wrap: wrap; }
}

/* --- account page (account.html) and the header's signed-in chip ------- */
.nav-me { display: inline-flex; align-items: center; gap: 8px; }
.nav-me .fx-avatar { font-size: 10px; }
.account-grid { display: grid; gap: 16px; margin-top: 26px; }
.account-card { display: grid; gap: 14px; padding: 22px; align-content: start; }
.account-card.danger { border-color: color-mix(in srgb, var(--bad) 30%, transparent); }
/* The card sets its own display, which defeats the hidden attribute unless
   said outright (the app documents this trap; the publish page steps hit it). */
.account-card[hidden] { display: none; }
.avatar-row { display: flex; gap: 18px; align-items: center; flex-wrap: wrap; }
.avatar-actions { display: flex; gap: 8px; flex-wrap: wrap; align-items: center; flex: 1; }
.avatar-actions .fx-help { flex-basis: 100%; }
.links-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 10px; }
.link-row { display: grid; gap: 4px; }
.link-kind { font: var(--fx-label); font-size: 11px; letter-spacing: 0.1em; text-transform: uppercase; color: var(--text-faint); }
.form-actions { display: flex; gap: 12px; align-items: center; flex-wrap: wrap; margin-top: 4px; }
.devices { display: grid; }
.device { display: flex; align-items: center; justify-content: space-between; gap: 12px; padding: 10px 0; border-top: 1px solid var(--edge); font-size: 14px; }
.device:first-child { border-top: 0; padding-top: 0; }
.device b { font-weight: 600; }
@media (max-width: 600px) {
  .links-grid { grid-template-columns: 1fr; }
  .nav-me-name { display: none; }
}

/* --- the shelf on the shared tile (stage 2) ---------------------------- */
.fx-tile-featured { grid-column: span 2; }
.fx-tile-featured .fx-tile-art { aspect-ratio: 21 / 9; }
.fx-tile-featured h3 { font-size: 27px; }
.fx-tile.invite {
  border-style: dashed;
  align-items: center; justify-content: center; text-align: center;
  padding: 28px 22px; gap: 8px; min-height: 220px;
  color: var(--muted);
}
.fx-tile.invite:hover { transform: none; box-shadow: none; border-color: var(--edge-strong); }
.fx-tile.invite .plus { font-family: var(--display); font-size: 36px; color: var(--faint); line-height: 1; }
.fx-tile.invite b { font-family: var(--display); font-size: 17px; color: var(--text); }
.fx-tile.invite span { font-size: 13.5px; max-width: 26ch; }
/* pages under the shelf (library.html) */
.lib-pager { display: flex; justify-content: center; align-items: center; gap: 6px; margin-top: 26px; flex-wrap: wrap; }
.lib-pager button { appearance: none; min-width: 36px; height: 36px; padding: 0 10px; border-radius: var(--fx-r-2); border: 1px solid var(--edge); background: var(--surface); color: var(--muted); font-family: var(--mono); font-size: 12px; cursor: pointer; transition: border-color 160ms ease, color 160ms ease; }
.lib-pager button:hover:not(:disabled) { color: var(--text); border-color: var(--edge-strong); }
.lib-pager button[aria-current="page"] { border-color: var(--accent); color: var(--accent-hot); background: color-mix(in srgb, var(--accent) 14%, transparent); }
.lib-pager button:disabled { opacity: 0.35; cursor: default; }
.lib-pager .gap { color: var(--faint); padding: 0 2px; }
@media (max-width: 900px) {
  .lib { grid-template-columns: 1fr; gap: 18px; }
  .lib-field.inline { margin-left: 0; }
}

/* ---------- the phone rail (2026-09-23) ----------
   Where the rail stacks above the shelf, five tall filter boxes buried
   the first skin a whole screen down. There the rail folds to the search
   box and one Filters button; the button opens the whole rail as a
   full-screen panel with a Show button that counts the results. */
.lib-filters-btn, .lib-sheet-head, .lib-sheet-foot { display: none; }
@media (max-width: 900px) {
  .lib .fx-rail { position: static; max-height: none; overflow: visible; padding: 0; }
  .lib .fx-rail:not(.open) > :not(.fx-rail-search):not(.lib-filters-btn) { display: none; }
  .lib-filters-btn {
    display: flex; align-items: center; justify-content: center; gap: 10px;
    min-height: 48px; padding: 0 16px;
    border: 1px solid var(--edge); border-radius: var(--fx-r-3);
    background: var(--fx-surface); color: var(--text);
    font: 600 15px / 1 var(--fx-display); cursor: pointer;
  }
  .lib-filters-btn:hover { border-color: var(--edge-strong); }
  .lib-filters-btn svg { width: 18px; height: 18px; fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round; }
  .lib-filters-n {
    min-width: 22px; height: 22px; padding: 0 6px; border-radius: 999px;
    display: inline-grid; place-items: center;
    background: linear-gradient(100deg, var(--accent-hot), var(--accent-2-deep));
    color: #fff; font: 700 12px / 1 var(--mono);
  }
  .lib-filters-n[hidden] { display: none; }

  /* the panel: the same rail, lifted over the page */
  .lib .fx-rail.open {
    position: fixed; inset: 0; z-index: 70;
    max-height: none; overflow: auto; overscroll-behavior: contain;
    padding: 0 16px; background: var(--bg);
    align-content: start;
  }
  .lib .fx-rail.open .lib-filters-btn { display: none; }
  .lib .fx-rail.open .lib-sheet-head {
    display: flex; align-items: center; justify-content: space-between;
    position: sticky; top: 0; z-index: 2;
    margin: 0 -16px; padding: 12px 16px;
    background: var(--bg); border-bottom: 1px solid var(--edge);
    font: 600 18px / 1 var(--fx-display); color: var(--text);
  }
  .lib-sheet-x {
    width: 44px; height: 44px; display: grid; place-items: center;
    border: 1px solid var(--edge); border-radius: var(--fx-r-2);
    background: var(--fx-surface); color: var(--text); cursor: pointer;
  }
  .lib-sheet-x svg { width: 18px; height: 18px; fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round; }
  .lib .fx-rail.open .lib-sheet-foot {
    display: block; position: sticky; bottom: 0; z-index: 2;
    margin: 4px -16px 0; padding: 12px 16px 16px;
    background: linear-gradient(transparent, var(--bg) 22%);
  }
  .lib-sheet-foot .fx-btn { width: 100%; }
  /* flux-ui.css caps every rail child at 100%; these two reach edge to edge */
  .lib .fx-rail.open .lib-sheet-head, .lib .fx-rail.open .lib-sheet-foot { max-width: none; }
}
/* the page behind the panel stays put while the panel scrolls */
html.sheet-open, html.sheet-open body { overflow: hidden; }
@media (max-width: 560px) {
  .fx-tile-featured { grid-column: auto; }
  .fx-tile-featured .fx-tile-art { aspect-ratio: 16 / 9; }
}

/* --- the mod page (stage 2) --------------------------------------------- */
.versions { display: grid; }
.version { display: flex; align-items: center; justify-content: space-between; gap: 14px; padding: 12px 0; border-top: 1px solid var(--edge); font-size: 14.5px; }
.version:first-child { border-top: 0; padding-top: 0; }
.mono-sm { font-family: var(--mono); font-size: 12px; }
a.creator { text-decoration: none; color: var(--text); transition: border-color 160ms ease, background 160ms ease; }
a.creator:hover { border-color: var(--edge-strong); background: color-mix(in srgb, var(--accent) 5%, var(--surface)); }

/* --- publish and review pages ------------------------------------------- */
/* align-items: start - a field with a hint under it must not stretch its
   neighbour into a taller box */
/* the line under Submit and Publish this version (2026-09-23) */
.terms-line { margin: 12px 0 0; font-size: 12.5px; color: var(--text-faint); line-height: 1.5; }
.terms-line a { color: var(--text-muted); }
.two-up { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; align-items: start; }
.pictures { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: 18px; align-items: start; }
.pictures > div { display: grid; gap: 8px; }
.cover-preview { aspect-ratio: 16 / 9; padding: 0; background-size: cover; background-position: center; background-clip: padding-box; }
.cover-preview.has { border-style: solid; color: #fff; }
.cover-preview.has strong, .cover-preview.has small { text-shadow: 0 1px 6px rgba(0, 0, 0, 0.8); }
.cover-preview.has strong { color: #fff; }
.fx-dropzone.small { padding: 18px 14px; }
/* Placing the cover (publish.html, 2026-09-25): the frame is the tile's own
   16:9 with the tile's own shade and title, so it shows what the shelf will. */
.cover-card { width: 640px; }
.cover-crop {
  position: relative;
  aspect-ratio: 16 / 9;
  overflow: hidden;
  border-radius: var(--fx-r-2);
  border: 1px solid var(--edge-strong);
  background: #050203;
  cursor: grab;
  touch-action: none;
  user-select: none;
  outline: none;
}
.cover-crop.dragging { cursor: grabbing; }
.cover-crop:focus-visible { box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 40%, transparent); }
.cover-crop img { position: absolute; left: 0; top: 0; max-width: none; pointer-events: none; }
.cover-crop-shade { position: absolute; inset: 0; pointer-events: none; background: linear-gradient(180deg, transparent 50%, color-mix(in srgb, var(--fx-surface) 96%, transparent)); }
.cover-crop-title {
  position: absolute;
  left: 16px;
  right: 16px;
  bottom: 12px;
  font: 700 20px / 1.15 var(--fx-display);
  letter-spacing: -0.01em;
  color: var(--text);
  pointer-events: none;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.cover-zoom { display: flex; align-items: center; gap: 12px; }
.cover-zoom input { flex: 1; accent-color: var(--accent); }
.cover-tools { display: flex; gap: 8px; margin-top: 8px; }
.cover-tools:empty { display: none; }
.tag-picks .fx-chip.active { background: color-mix(in srgb, var(--accent) 18%, transparent); border-color: var(--accent); color: var(--accent-hot); }
.review-item { display: grid; grid-template-columns: 220px minmax(0, 1fr); gap: 18px; padding: 18px; }
.review-cover { aspect-ratio: 16 / 9; border-radius: var(--fx-r-2); border: 1px solid var(--edge); background-size: cover; background-position: center; background-clip: padding-box; background-color: var(--fx-field); }
.review-body h3 { margin: 6px 0 2px; font-size: 20px; }
/* Reports on the review page (2026-09-23): one card per reported skin, the
   reports inside it; urgent ones (stolen work, a creator asking back) edged
   in red, a creator's own ask in amber. */
.report-skin { display: grid; grid-template-columns: 220px minmax(0, 1fr); gap: 18px; padding: 18px; }
.report-skin.urgent { border-color: color-mix(in srgb, var(--bad) 45%, transparent); }
.report-list { list-style: none; margin: 12px 0 0; padding: 0; display: grid; gap: 10px; }
.report-row { display: grid; gap: 4px; justify-items: start; padding: 10px 12px; border: 1px solid var(--edge); border-radius: var(--fx-r-2); background: var(--fx-field); }
.report-row.from-owner { border-color: color-mix(in srgb, var(--warn) 45%, transparent); }
.report-words { margin: 0; white-space: pre-wrap; font-size: 14px; color: var(--muted); }
.review-section { margin-top: 44px; display: flex; align-items: baseline; gap: 12px; }
.my-mod-cover { width: 64px; aspect-ratio: 16 / 9; border-radius: var(--fx-r-1); border: 1px solid var(--edge); background-size: cover; background-position: center; background-clip: padding-box; background-color: var(--fx-field); flex: none; }
.my-mod { justify-content: flex-start; }
.my-mod-text { flex: 1; min-width: 0; }
@media (max-width: 720px) {
  .two-up, .pictures { grid-template-columns: 1fr; }
  .review-item { grid-template-columns: 1fr; }
  .report-skin { grid-template-columns: 1fr; }
}

/* --- QA pass (2026-09-08): long text wraps, a locked drop zone, quieter loading --- */
.mod-rail h1, .mod-main p, .creator-bio, .creator-id h1, .review-body h3, .device b, .fx-help { overflow-wrap: anywhere; }
.fx-dropzone.locked { border-style: solid; cursor: default; opacity: 0.9; pointer-events: none; }
.fx-empty.loading { border-style: solid; border-color: var(--edge); color: var(--text-faint); }
.fx-empty.loading strong { color: var(--text-muted); }

/* --- QA pass, phone: rows that carry buttons wrap instead of overflowing --- */
@media (max-width: 600px) {
  .device { flex-wrap: wrap; }
  .device:not(.my-mod) > div:first-child { flex: 1 1 100%; }
  .my-mod-text { flex: 1 1 calc(100% - 80px); }
  .my-mod .fx-btn { flex: 1 1 40%; }
  .version { flex-wrap: wrap; }
}
