/**
 * Black Chancery — the wordmark face, taken from ravenswebsite so the two sites
 * share a title. Public domain, per the font's own name table: outlines by Earl
 * Allen and Doug Miles. public/fonts/BlackChancery-README.txt is the author's
 * original readme, kept as the provenance, and the footer carries the credit.
 */
@font-face {
  font-family: "Black Chancery";
  src: url("/fonts/BlackChancery.woff2") format("woff2");
  font-weight: 100 900;
  font-style: normal;
  font-display: swap;
}

/*
 * An homage to Dress to Impress. The palette is lifted from DTI's own design
 * constants (app/assets/stylesheets/partials/clean/_constants.sass) so the site
 * reads as a sibling rather than an imitation: dark green on white, soft green
 * module fills, generous borders.
 *
 * Delicious is DTI's header face. We ask for it and fall back gracefully rather
 * than pulling a webfont — nobody should wait on a font to try on a helmet.
 */
:root {
  --bg: #fff;
  --text: #040;
  --soft-text: #484;
  --link: #262;
  --module-bg: #efe;
  --module-border: #060;
  --soft-border: #ada;
  --input-border: #cec;
  --marked: #0b61a4;

  --notice-color: #264409;
  --notice-bg: #e6efc2;
  --notice-border: #c6d880;
  --warning-color: #514721;
  --warning-bg: #fff6bf;
  --warning-border: #ffd324;
  --error-color: #8a1f11;
  --error-bg: #fbe3e4;
  --error-border: #fbc2c4;

  --header-font: Delicious, "Trebuchet MS", "Segoe UI", system-ui, sans-serif;
  /* The wordmark, shared with ravenswebsite so the two sites read as a pair.
     Cinzel is not loaded here, so the fallback is a real serif rather than a
     font nobody has. */
  --brand-font: "Black Chancery", Cinzel, "Palatino Linotype", Palatino, Georgia, serif;
  --main-font: system-ui, -apple-system, "Segoe UI", sans-serif;

  --object-img-size: 80px;
  --object-width: 100px;
}

* { box-sizing: border-box; }

body {
  margin: 0;
  background: var(--bg);
  color: var(--text);
  font: 14px/1.5 var(--main-font);
}

a { color: var(--link); }

h1, h2, h3 { font-family: var(--header-font); font-weight: normal; margin: 0; }

/* ── chrome ─────────────────────────────────────────────────────────────── */
.masthead {
  border-bottom: 1px solid var(--soft-border);
  padding: 14px 20px;
  display: flex;
  align-items: baseline;
  gap: 14px;
  flex-wrap: wrap;
}
.masthead h1.brand {
  font-family: var(--brand-font);
  /* Black Chancery's capitals run small for their em, so it needs more size
     than a text face at the same optical weight. */
  font-size: 34px;
  line-height: 1.1;
  letter-spacing: 0.5px;
}
.masthead h1.brand a { text-decoration: none; color: var(--text); }
.masthead .tagline { color: var(--soft-text); font-size: 13px; }
.masthead nav { margin-left: auto; font-size: 13px; display: flex; gap: 14px; }

main {
  display: grid;
  grid-template-columns: minmax(320px, 660px) minmax(300px, 1fr);
  gap: 24px;
  padding: 20px;
  align-items: start;
  max-width: 1240px;
  margin: 0 auto;
}

/* ── modules ────────────────────────────────────────────────────────────── */
.module {
  background: var(--module-bg);
  border: 1px solid var(--module-border);
  border-radius: 6px;
  padding: 12px;
  margin-bottom: 16px;
}
.module > h2 {
  font-size: 16px;
  margin-bottom: 8px;
  display: flex;
  align-items: center;
  gap: 8px;
}
.module > h2 .count { color: var(--soft-text); font-size: 12px; font-family: var(--main-font); }

/* ── preview ────────────────────────────────────────────────────────────── */
.preview-frame {
  background: #fff;
  border: 1px solid var(--soft-border);
  border-radius: 4px;
  width: 100%;
  /* Size comes from JS (sizeFrame), which holds the character's scale constant
     and lets the box widen for pets instead. max-width is the belt to that
     braces: the box must never spill past its module, whatever JS computes. */
  max-width: 100%;
  margin: 0 auto;
  overflow: hidden;
  position: relative;
}
/* ⚠️ Target the PLAYER, not every child. A bare `> *` also matched the status
   overlay and stretched it to fill the frame — an opaque panel sitting on top
   of the avatar, which reads exactly like "the renderer drew nothing". */
.preview-frame ruffle-player { width: 100%; height: 100%; display: block; }

.preview-status {
  position: absolute;
  inset: auto 8px 8px 8px;
  font-size: 12px;
  padding: 6px 8px;
  border-radius: 4px;
  background: var(--notice-bg);
  border: 1px solid var(--notice-border);
  color: var(--notice-color);
}
.preview-status[data-level="warning"] {
  background: var(--warning-bg); border-color: var(--warning-border); color: var(--warning-color);
}
.preview-status[data-level="error"] {
  background: var(--error-bg); border-color: var(--error-border); color: var(--error-color);
}
.preview-status[hidden] { display: none; }

/* ── controls ───────────────────────────────────────────────────────────── */
.control-row {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-bottom: 8px;
  flex-wrap: wrap;
}
.control-row > label { min-width: 74px; color: var(--soft-text); font-size: 13px; }

button, .btn {
  font: inherit;
  color: var(--text);
  background: #fff;
  border: 1px solid var(--module-border);
  border-radius: 4px;
  padding: 5px 11px;
  cursor: pointer;
}
button:hover { background: #f6fff6; }
button[aria-pressed="true"] { background: var(--marked); border-color: var(--marked); color: #fff; }
button:disabled { opacity: 0.5; cursor: not-allowed; }

input[type="text"], input[type="search"] {
  font: inherit;
  color: var(--text);
  background: #fff;
  border: 1px solid var(--input-border);
  border-radius: 4px;
  padding: 6px 8px;
  width: 100%;
}
input[type="text"]:focus, input[type="search"]:focus {
  outline: 2px solid var(--soft-border);
  outline-offset: -1px;
}
input[type="color"] {
  width: 34px; height: 26px; padding: 0;
  border: 1px solid var(--input-border); border-radius: 4px;
  background: #fff; cursor: pointer;
}

.color-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 8px; }
.color-grid label { display: flex; align-items: center; gap: 6px; font-size: 12px; color: var(--soft-text); }

/* ── slots ──────────────────────────────────────────────────────────────── */
.slot-list { list-style: none; margin: 0; padding: 0; }
.slot {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 7px 0;
  border-bottom: 1px solid var(--soft-border);
}
.slot:last-child { border-bottom: 0; }
.slot-name { width: 68px; font-size: 13px; color: var(--soft-text); flex: none; }
.slot-item { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.slot-item.empty { color: var(--soft-text); font-style: italic; }
.slot .tag {
  font-size: 10px; padding: 1px 5px; border-radius: 3px;
  border: 1px solid var(--soft-border); background: #fff; color: var(--soft-text);
  margin-left: 6px; vertical-align: 1px;
}

/* ── item grid (DTI's object dimensions) ────────────────────────────────── */
.results {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  list-style: none;
  margin: 10px 0 0;
  padding: 0;
}
.object {
  /* ⚠️ MUST be a block. These are <a> as well as <button>, and an inline
     element's border breaks across line boxes — which renders as stray green
     fragments floating beside each card rather than a box around it. */
  display: block;
  width: var(--object-width);
  text-align: center;
  cursor: pointer;
  border: 1px solid transparent;
  border-radius: 4px;
  padding: 4px;
  background: none;
  font: inherit;
  color: inherit;
}
.object:hover { border-color: var(--soft-border); background: #fff; }
.object[aria-pressed="true"] { border-color: var(--marked); background: #fff; }
.object .thumb {
  width: var(--object-img-size);
  height: var(--object-img-size);
  margin: 0 auto 4px;
  border: 1px solid var(--input-border);
  border-radius: 4px;
  background: #fff center/contain no-repeat;
  display: grid;
  place-items: center;
  color: var(--soft-border);
  font-size: 22px;
  overflow: hidden;
}
.object .thumb img {
  max-width: 100%;
  max-height: 100%;
  object-fit: contain;   /* item art is rarely square; never distort it */
}
.object .label { font-size: 11px; line-height: 1.3; word-break: break-word; }

.empty-state {
  color: var(--soft-text);
  font-size: 13px;
  padding: 10px 2px;
}
.banner {
  border-radius: 4px;
  padding: 8px 10px;
  font-size: 13px;
  margin-bottom: 12px;
  background: var(--warning-bg);
  border: 1px solid var(--warning-border);
  color: var(--warning-color);
}
.banner[hidden] { display: none; }
.banner code { background: rgba(0,0,0,0.06); padding: 0 3px; border-radius: 2px; }

.share { display: flex; gap: 8px; align-items: center; }
.share input { font-family: ui-monospace, monospace; font-size: 12px; }

footer p { margin: 0 0 6px; }
footer .stat { color: var(--soft-text); }
footer {
  max-width: 1240px;
  margin: 0 auto;
  padding: 8px 20px 32px;
  color: var(--soft-text);
  font-size: 12px;
}

@media (max-width: 860px) {
  main { grid-template-columns: 1fr; }
}

/* ── homepage ───────────────────────────────────────────────────────────── */
.masthead h1 a { color: inherit; text-decoration: none; }

main.home {
  display: block;
  max-width: 900px;
  margin: 0 auto;
  padding: 24px 20px;
}

.hero { text-align: center; margin-bottom: 32px; }
.hero h2 {
  font-size: 26px;
  margin-bottom: 8px;
}
.hero .lede {
  color: var(--soft-text);
  max-width: 52ch;
  margin: 0 auto;
  font-size: 13px;
}

/* A silhouette rather than a screenshot: it needs no asset, and it cannot go
   stale when the renderer changes. */
/* A head-and-shoulders mark, drawn in CSS so there is no asset to fetch and
   nothing to go stale when the renderer changes. */
.hero-art {
  width: 84px;
  height: 84px;
  margin: 0 auto 10px;
  border-radius: 50%;
  background: var(--module-bg);
  border: 1px solid var(--module-border);
  position: relative;
  overflow: hidden;
}
.hero-art::before,
.hero-art::after {
  content: "";
  position: absolute;
  background: var(--module-border);
}
.hero-art::before {              /* head */
  width: 26px; height: 26px;
  border-radius: 50%;
  left: 50%; top: 18px;
  transform: translateX(-50%);
}
.hero-art::after {               /* shoulders */
  width: 54px; height: 34px;
  border-radius: 27px 27px 0 0;
  left: 50%; bottom: -4px;
  transform: translateX(-50%);
}

/* DTI leads with the one thing most people came to do — type a name — rather
   than with an explanation. The prose sits underneath, where it is available
   without being in the way. */
.load-character { margin: 0 auto 10px; max-width: 480px; }
.load-character label {
  display: block;
  font-family: var(--header-font);
  font-size: 15px;
  margin-bottom: 6px;
}
.hero-row { display: flex; gap: 8px; flex-wrap: wrap; }
.hero-row input { flex: 1; min-width: 180px; font-size: 15px; padding: 7px 9px; }
.hero-row button { font-size: 15px; padding: 7px 14px; }
.from-scratch {
  display: flex;
  gap: 8px;
  justify-content: center;
  align-items: center;
  flex-wrap: wrap;
  margin: 0 0 18px;
  font-size: 13px;
  color: var(--soft-text);
}
.from-scratch .or { color: var(--soft-text); }

.form-status {
  margin: 8px 0 0;
  font-size: 13px;
  padding: 6px 8px;
  border-radius: 4px;
  background: var(--notice-bg);
  border: 1px solid var(--notice-border);
  color: var(--notice-color);
}
.form-status[data-level="warning"] {
  background: var(--warning-bg); border-color: var(--warning-border); color: var(--warning-color);
}
.form-status[data-level="error"] {
  background: var(--error-bg); border-color: var(--error-border); color: var(--error-color);
}
.form-status[hidden] { display: none; }

.sections {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  /* ⚠️ FIXED COLUMNS, NOT auto-fit, and the COUNT HAS TO MATCH THE CARDS. With
     four cards and auto-fit this landed three across and orphaned the fourth;
     the fix then was two columns. There are three cards now — the same three
     destinations DTI puts across its front page — so three columns it is, and
     adding a fourth card without revisiting this line brings the orphan back. */
  grid-template-columns: 1fr;
  gap: 16px;
  align-items: stretch;
}
@media (min-width: 620px) {
  .sections { grid-template-columns: repeat(2, 1fr); }
}
@media (min-width: 860px) {
  .sections { grid-template-columns: repeat(3, 1fr); }
}
.sections > li { display: flex; flex-direction: column; }
.sections > li > :last-child { margin-bottom: 0; }
.sections h3 { font-size: 17px; margin-bottom: 2px; }
.sections h3 a { text-decoration: none; }
.sections h3 a:hover { text-decoration: underline; }
.sections .section-tagline {
  margin: 0 0 8px;
  font-weight: 600;
  color: var(--soft-text);
  font-size: 13px;
}
.sections p { margin: 0 0 10px; font-size: 13px; }
.sections .muted { color: var(--soft-text); font-style: italic; }
.sections .stat { color: var(--soft-text); font-size: 12px; }
form.mini { display: flex; gap: 6px; }
form.mini input { flex: 1; min-width: 0; }

@media (max-width: 560px) {
  .hero h2 { font-size: 21px; }
}

select {
  font: inherit;
  color: var(--text);
  background: #fff;
  border: 1px solid var(--input-border);
  border-radius: 4px;
  padding: 5px 8px;
}

/* ── example sets ───────────────────────────────────────────────────────── */
.latest-strip { margin: 28px 0 0; }
.latest-strip h3 { font-size: 17px; margin-bottom: 8px; }
.latest-strip h3 a { text-decoration: none; }
.latest-strip h3 a:hover { text-decoration: underline; }

.example-sets { margin: 0 0 28px; }
.example-sets h3 { font-size: 17px; margin-bottom: 8px; }
/* A grid rather than the item flex-row: these cards carry two lines of text,
   which overflowed the fixed item width and spilled outside their own border. */
.example-sets .results {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(170px, 1fr));
  gap: 12px;
}
.object.example {
  width: auto;
  padding: 10px;
  text-decoration: none;
  color: inherit;
  border: 1px solid var(--soft-border);
  border-radius: 6px;
  background: var(--module-bg);
}
.object.example:hover { border-color: var(--module-border); background: #fff; }
.object.example .thumb {
  font-family: var(--header-font);
  font-size: 30px;
  color: var(--module-border);
  background: #fff;
}
.object.example .label { font-size: 13px; font-weight: 600; }
.object.example .note {
  font-size: 11px;
  color: var(--soft-text);
  margin-top: 2px;
  overflow-wrap: anywhere;   /* two-word notes must not widen the card */
}

label.checkbox {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  min-width: 0;
  font-size: 13px;
  color: var(--soft-text);
  cursor: pointer;
}

/* ── in-game nameplate ──────────────────────────────────────────────────────
   The live client's plate, not the character page's. "Mini 7" is the client's
   own pixel font — the same file charbot renders with — so this is the actual
   typeface rather than a lookalike. Size and position are set in JS from the
   character's measurements (see drawNameplate). */
@font-face {
  font-family: "Mini 7";
  src: url("/fonts/Mini7.ttf") format("truetype");
  font-display: swap;
}
.nameplate-overlay {
  position: absolute;
  transform: translate(-50%, -100%);
  pointer-events: none;
  text-align: center;
  white-space: nowrap;
  line-height: 1;
  font-family: "Mini 7", ui-monospace, monospace;
  /* A pixel font must not be smoothed, or the grid turns to mush. */
  -webkit-font-smoothing: none;
  font-smooth: never;
  /* The client draws a black GlowFilter behind the text; at pixel sizes a ring
     of hard 1px offsets reads the same and stays crisp. */
  text-shadow:
     1px  0   0 #000, -1px  0   0 #000,
     0    1px 0 #000,  0   -1px 0 #000,
     1px  1px 0 #000, -1px  1px 0 #000,
     1px -1px 0 #000, -1px -1px 0 #000;
}
.nameplate-overlay[hidden] { display: none; }
.nameplate-overlay .np-name { color: var(--np-color, #fff); }
/* The client keeps the guild tag white however the name is coloured. */
.nameplate-overlay .np-guild { color: #fff; }

/* ⚠️ IN THE VIEWPORT, NOT OFF-SCREEN. The export renders here, and a player
   parked at left:-10000px never advances its timeline — the capture then
   records the same frame over and over and the GIF comes out motionless while
   looking perfectly valid. Keeping it on-screen but invisible (and behind
   everything, ignoring input) is what actually lets it play. */
#export-stage {
  position: fixed;
  left: 0;
  top: 0;
  opacity: 0.01;
  pointer-events: none;
  z-index: -1;
  overflow: hidden;
}


/* The backdrop: its own player behind the avatar's, filling the frame. */
.background-layer {
  position: absolute;
  inset: 0;
  z-index: 0;
}
.background-layer[hidden] { display: none; }
.background-layer ruffle-player { width: 100%; height: 100%; display: block; }
/* The scene's own zoom and offset ride on top of Ruffle's cover fit. */
/* ⚠️ `hidden` LOSES TO `display: flex`. .control-row sets display, so the
   attribute alone left this row on screen with no scene behind it. */
.control-row[hidden] { display: none; }
.scene-adjust input[type="range"] { flex: 1; min-width: 70px; }
.scene-adjust button { padding: 4px 10px; font-size: 12px; }
.preview-frame ruffle-player { position: relative; z-index: 1; }
.nameplate-overlay { z-index: 2; }
.preview-status { z-index: 3; }

/* The legal page: long prose, so it gets a measure rather than the full width. */
main.legal { max-width: 760px; }
main.legal h2 { font-size: 24px; margin-bottom: 14px; }
main.legal h3 { font-size: 17px; margin: 22px 0 6px; }
main.legal p, main.legal li { font-size: 13.5px; }
main.legal ul { margin: 0; padding-left: 20px; }
main.legal li { margin-bottom: 10px; }

/* ── the account slot and saved sets ─────────────────────────────────────── */
.account-slot {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  margin-left: 4px;
}
.account-slot .avatar { border-radius: 50%; vertical-align: middle; }
.account-slot .who { color: var(--soft-text); }
.link-button {
  background: none;
  border: none;
  padding: 0;
  font: inherit;
  color: var(--link);
  text-decoration: underline;
  cursor: pointer;
}
.link-button:hover { color: var(--text); }
.link-button[disabled] { opacity: 0.5; cursor: default; }

.saved-sets { list-style: none; margin: 10px 0 0; padding: 0; }
.saved-sets li {
  display: flex;
  align-items: baseline;
  gap: 8px;
  padding: 4px 0;
  border-top: 1px solid var(--input-border);
  font-size: 13px;
}
.saved-sets li > a { flex: 1; min-width: 0; }
#save-set input { flex: 1; min-width: 0; }
#save-status[data-level="error"] { color: var(--error-color); }
#save-status[data-level="notice"] { color: var(--soft-text); }
#closet-status[data-level="error"] { color: var(--error-color); }
#closet-status[data-level="notice"] { color: var(--soft-text); }
.saved-sets .muted { font-size: 12px; white-space: nowrap; }
