/* Palette sampled from static/logo.png:
     #6c4818 bronze  -- the star-anise fan (the logo's dominant colour)
     #26262f charcoal -- the BADIANE lettering
     #7a3a18 sienna  -- the red letterforms
   Bronze is used for brand, links, focus and warm surfaces. It is deliberately
   NOT used for chips or nav pills: it sits too close to the amber "need check"
   state, and the two must never be confusable side by side. */
:root {
  --bg: #faf8f4;          /* warm paper, tinted towards the bronze */
  --panel: #ffffff;
  --ink: #26262f;         /* logo lettering */
  --muted: #6e685e;
  --line: #e8e1d5;        /* warm hairline */
  --accent: #8a5f24;      /* logo bronze */
  --accent-soft: #f4ece0;
  --sienna: #7a3a18;      /* logo's red letterforms; used sparingly */

  /* One source of truth for the sticky header height: the top bar and the
     sticky table head both derive from it, so they cannot drift apart when the
     logo changes size. */
  --topbar-h: 68px;

  /* Annotation states. Desaturated on purpose: a wall of 120 saturated
     borders is exhausting to look at, but these still read instantly at a
     glance and stay distinguishable for red/green colour blindness because
     they differ in lightness as well as hue. */
  --ok: #4a7d55;
  --ok-bg: #edf4ee;
  --check: #c07818;
  --check-bg: #fdf2e2;
  /* Corrected: a blue, chosen to sit clearly apart from both the green and the
     orange in hue AND lightness, so the three states stay separable for
     red/green colour blindness. */
  --fixed: #4a6da8;
  --fixed-bg: #eef1f8;
}

* { box-sizing: border-box; }

body {
  margin: 0;
  background: var(--bg);
  color: var(--ink);
  font: 15px/1.5 -apple-system, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", sans-serif;
}

/* ------------------------------------------------------------ chrome */
.topbar {
  display: flex; align-items: center; gap: 1.5rem;
  height: var(--topbar-h); padding: 0 1.5rem;
  background: var(--panel);
  border-bottom: 1px solid var(--line);
  position: sticky; top: 0; z-index: 10;
}
.brand { display: flex; align-items: center; text-decoration: none; }
.brand img {
  /* The logo is a near-square lockup (fan above, wordmark below), so the
     wordmark only gets a third of the height -- it needs ~52px overall before
     "BADIANE" is legible. The negative margin trims its internal whitespace so
     the nav does not sit adrift. */
  height: 52px; width: auto; display: block;
  margin: 0 -0.5rem;
}
.topbar-meta { margin-left: auto; font-size: 0.82rem; color: var(--muted); }
.topbar-meta a { color: var(--accent); text-decoration: none; }
.topbar-meta a:hover { text-decoration: underline; }

.topnav { display: flex; align-self: center; gap: 0.25rem; }
.topnav a {
  display: flex; align-items: center; gap: 0.4rem;
  padding: 0.4rem 0.8rem;
  font-size: 0.85rem; font-weight: 600; text-decoration: none;
  color: var(--muted); border-radius: 8px;
  transition: background-color 0.12s ease, color 0.12s ease;
}
.topnav a:hover { color: var(--ink); background: var(--bg); }
.topnav a.active { color: #fff; background: var(--ink); }
.topnav a.active .navcount { background: rgba(255, 255, 255, 0.24); color: #fff; }
.navcount {
  font-variant-numeric: tabular-nums; font-weight: 700;
  font-size: 0.72rem; padding: 0.05rem 0.4rem; border-radius: 999px;
  background: var(--line); color: var(--muted);
}
/* Validated/Need-check nav items pick up their state colour when active, so
   the same green/orange language used throughout the app (borders, chips)
   carries into the navigation. */
.topnav a.nav-validated.active { background: var(--ok);    color: #fff; }
.topnav a.nav-needcheck.active { background: var(--check); color: #fff; }
.topnav a.nav-corrected.active { background: var(--fixed); color: #fff; }
.topnav a.nav-validated.active .navcount,
.topnav a.nav-needcheck.active .navcount,
.topnav a.nav-corrected.active .navcount { background: rgba(255, 255, 255, 0.28); color: #fff; }

.topuser { display: flex; align-items: center; gap: 0.6rem; margin-left: 1.2rem; }
.topuser .who {
  font-size: 0.8rem; font-weight: 600; color: var(--accent);
  padding: 0.2rem 0.6rem; border-radius: 999px; background: var(--accent-soft);
}
.topuser form { margin: 0; }
.topuser button {
  background: none; border: 0; cursor: pointer; padding: 0.2rem 0.1rem;
  font: inherit; font-size: 0.78rem; color: var(--muted);
  text-decoration: underline; text-underline-offset: 2px;
}
.topuser button:hover { color: var(--ink); }

main { padding: 1.5rem; max-width: 1600px; margin: 0 auto; }

/* ------------------------------------------------------------ login */
.login-body {
  display: flex; align-items: center; justify-content: center;
  min-height: 100vh; padding: 1.5rem; background: var(--bg);
}
.login-card {
  width: 100%; max-width: 360px; padding: 2rem 2rem 1.75rem;
  background: var(--panel); border: 1px solid var(--line);
  border-radius: 14px; box-shadow: 0 2px 16px rgba(38, 38, 47, 0.07);
  text-align: center;
}
.login-logo { height: 96px; width: auto; margin: 0 auto 0.25rem; display: block; }
.login-card h1 {
  margin: 0 0 1.4rem; font-size: 0.95rem; font-weight: 600;
  color: var(--muted); letter-spacing: 0.02em;
}
.lfld { display: block; margin-bottom: 0.85rem; text-align: left; }
.lfld span {
  display: block; margin-bottom: 0.3rem;
  font-size: 0.7rem; text-transform: uppercase; letter-spacing: 0.06em;
  color: var(--muted);
}
.lfld input {
  width: 100%; padding: 0.6rem 0.75rem; font: inherit; font-size: 0.95rem;
  color: var(--ink); background: var(--bg);
  border: 1px solid var(--line); border-radius: 8px;
}
.lfld input:focus {
  outline: none; border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--accent-soft);
}
.login-btn {
  width: 100%; margin-top: 0.5rem; padding: 0.7rem 1rem;
  font: inherit; font-size: 0.95rem; font-weight: 700; color: #fff;
  background: var(--accent); border: 0; border-radius: 9px; cursor: pointer;
}
.login-btn:hover { filter: brightness(1.08); }
.login-err {
  margin: 0 0 1rem; padding: 0.55rem 0.7rem; border-radius: 8px;
  background: #fbeae6; color: var(--sienna);
  border: 1px solid #f0d3cc; font-size: 0.83rem;
}

.page-head h1 { margin: 0 0 0.3rem; font-size: 1.3rem; font-weight: 600; }
.note { margin: 0; color: var(--muted); font-size: 0.85rem; }
.note code { background: var(--accent-soft); padding: 0.05em 0.35em; border-radius: 3px; }

/* ------------------------------------------------------------ home */
.filter {
  margin-top: 0.9rem; width: 100%; max-width: 380px;
  padding: 0.55rem 0.8rem; font-size: 0.9rem;
  border: 1px solid var(--line); border-radius: 8px; background: var(--panel);
  color: var(--ink);
}
.filter:focus {
  outline: none;
  border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--accent-soft);
}

table.chars {
  width: 100%; margin-top: 1.2rem;
  border-collapse: collapse; background: var(--panel);
  border: 1px solid var(--line); border-radius: 10px;
  font-variant-numeric: tabular-nums;
  /* No overflow:hidden here -- it would make this table the scroll container
     and break the sticky thead below. */
}
table.chars th, table.chars td {
  padding: 0.5rem 0.9rem; text-align: left;
  border-bottom: 1px solid var(--line);
}
table.chars tbody tr:last-child td { border-bottom: 0; }
table.chars thead th {
  position: sticky; top: var(--topbar-h);
  background: var(--panel); z-index: 5;
  font-size: 0.72rem; text-transform: uppercase; letter-spacing: 0.06em;
  color: var(--muted); font-weight: 600;
}
table.chars tbody tr:hover { background: var(--bg); }
.num { text-align: right !important; }
.strong { font-weight: 600; }
.muted { color: var(--muted); }
.cp { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 0.8rem; color: var(--muted); }

.prog { font-variant-numeric: tabular-nums; font-size: 0.82rem; }
.prog.none { color: #b3aca1; }
.prog.some { color: var(--check); font-weight: 600; }
.prog.done { color: var(--ok); font-weight: 600; }

.glyph { font-size: 1.7rem; line-height: 1; font-family: "Songti SC", "STSong", serif; }
.glyph a { color: var(--ink); text-decoration: none; border-bottom: 2px solid var(--accent); }
.glyph a:hover { color: var(--accent); }
tr.disabled .glyph span { color: #b3aca1; }

/* ------------------------------------------------------------ char page */
.charhead { display: flex; align-items: center; gap: 1.2rem; }
.bigglyph {
  font-size: 3.4rem; line-height: 1;
  font-family: "Songti SC", "STSong", serif;
  color: var(--ink);
  padding: 0.5rem 1.1rem; background: var(--panel);
  border: 1px solid var(--line); border-radius: 10px;
  box-shadow: 0 1px 2px rgba(38, 38, 47, 0.05);
}

.controls { display: flex; flex-wrap: wrap; gap: 1.6rem; margin: 1.1rem 0 0.6rem; }
.ctl { display: flex; align-items: center; gap: 0.35rem; }
.lbl {
  font-size: 0.7rem; text-transform: uppercase; letter-spacing: 0.06em;
  color: var(--muted); margin-right: 0.2rem;
}
.chip {
  display: inline-block; padding: 0.25rem 0.7rem;
  font-size: 0.8rem; text-decoration: none; color: var(--muted);
  background: var(--panel); border: 1px solid var(--line); border-radius: 999px;
  transition: background-color 0.12s ease, color 0.12s ease, border-color 0.12s ease;
}
.chip:hover { color: var(--ink); border-color: #d8cfbe; }
.chip.on { background: var(--accent); border-color: var(--accent); color: #fff; }
.chip .n { font-variant-numeric: tabular-nums; opacity: 0.65; font-size: 0.74rem; }
.chip.on .n { opacity: 0.8; }

/* The View row is the primary control now, so it gets state colours rather
   than the generic cinnabar. */
.view-ctl .chip { font-weight: 600; }
.chip.st-valid.on      { background: var(--ok);    border-color: var(--ok); }
.chip.st-need_check.on { background: var(--check); border-color: var(--check); }
.chip.st-corrected.on  { background: var(--fixed); border-color: var(--fixed); }
.hint { font-size: 0.72rem; color: #a49c90; font-style: italic; }

.pager {
  display: flex; align-items: center; gap: 1rem;
  margin: 0.9rem 0; padding: 0.55rem 0.9rem;
  background: var(--panel); border: 1px solid var(--line); border-radius: 10px;
  font-size: 0.85rem;
}
.pager.bottom { margin-top: 1.4rem; }
.pager a { color: var(--accent); text-decoration: none; font-weight: 600; }
.pager .off { color: #c4bdb2; font-weight: 600; }
.pageinfo { margin-left: auto; color: var(--muted); font-variant-numeric: tabular-nums; }

.grid {
  display: flex; flex-wrap: wrap; gap: 0.6rem;
  align-items: flex-end;
}
.crop {
  margin: 0; padding: 0.4rem;
  background: var(--panel);
  border: 2px solid var(--line); border-radius: 6px;
  transition: border-color 0.12s ease, background-color 0.12s ease;
}
.crop img { display: block; border-radius: 3px; background: #eee; }
.crop:hover { box-shadow: 0 0 0 2px var(--accent-soft); }

/* Annotation state. 2px is the sweet spot -- 1px disappears against the
   thumbnails, 3px starts to dominate the grid. The faint background tint does
   most of the work at a glance; the border does it up close. */
.crop.st-valid      { border-color: var(--ok);    background: var(--ok-bg); }
.crop.st-need_check { border-color: var(--check); background: var(--check-bg); }
.crop.st-corrected  { border-color: var(--fixed); background: var(--fixed-bg); }
figcaption {
  margin-top: 0.3rem; max-width: 160px;
  font-size: 0.62rem; line-height: 1.3; color: var(--muted);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
figcaption .pg { display: block; color: var(--ink); font-weight: 600; }

.empty { color: var(--muted); padding: 2rem 0; }

/* ------------------------------------------------------------ char lists (/validated, /need-check) */
.progress-cell { display: flex; align-items: center; gap: 0.6rem; }
.progress-bar {
  position: relative; display: inline-block;
  width: 140px; height: 9px;
  /* Neutral track: the fill colour now varies per page, so a tinted track
     would fight with it. */
  background: #efe9dd; border: 1px solid var(--line);
  border-radius: 999px; overflow: hidden;
}
.progress-bar > span { position: absolute; inset: 0; right: auto; }
.progress-bar .fill-valid     { background: var(--ok); }
.progress-bar .fill-check     { background: var(--check); }
.progress-bar .fill-corrected { background: var(--fixed); }
.pct {
  min-width: 3.2ch; text-align: right;
  font-variant-numeric: tabular-nums; font-weight: 600; font-size: 0.82rem;
}

/* ------------------------------------------------------------ lightbox */
body.lb-open { overflow: hidden; }

.lb {
  position: fixed; inset: 0; z-index: 100;
  display: flex; flex-direction: column;
  background: rgba(18, 16, 14, 0.94);
  color: #f2efe9;
}
.lb[hidden] { display: none; }

.lb-bar, .lb-foot {
  display: flex; align-items: center; gap: 1rem;
  padding: 0.6rem 1rem; flex: 0 0 auto;
  font-size: 0.8rem;
}
.lb-bar { border-bottom: 1px solid rgba(255, 255, 255, 0.12); }
.lb-foot {
  border-top: 1px solid rgba(255, 255, 255, 0.12);
  color: #a8a196; font-variant-numeric: tabular-nums;
}

/* Big, high-contrast buttons anchored at the very bottom of the lightbox --
   the most reachable zone for a mouse resting near the window edge -- so
   marking a crop is one easy click, not a small target you have to aim for.
   This is the primary interaction of the whole app, so it gets the most
   prominent, easiest-to-hit control on the page. */
.lb-actionbar {
  display: flex; justify-content: center; gap: 1rem;
  flex: 0 0 auto; padding: 0.9rem 1.25rem 1.1rem;
  border-top: 1px solid rgba(255, 255, 255, 0.12);
  background: rgba(0, 0, 0, 0.18);
}
.act.big {
  display: flex; align-items: center; justify-content: center; gap: 0.6rem;
  flex: 1 1 0; max-width: 320px; min-width: 160px;
  padding: 1rem 1.4rem;
  font-size: 1.05rem; font-weight: 700; color: #fff;
  border: none; border-radius: 12px; cursor: pointer;
  transition: filter 0.12s ease, transform 0.06s ease;
}
.act.big .ico { font-size: 1.25rem; line-height: 1; }
.act.big kbd {
  padding: 0.1em 0.5em; border-radius: 5px;
  background: rgba(0, 0, 0, 0.18); border: 1px solid rgba(255, 255, 255, 0.3);
  font: inherit; font-size: 0.78rem; font-weight: 600; opacity: 0.9;
}
.act.big:hover:not(:disabled) { filter: brightness(1.1); }
.act.big:active:not(:disabled) { transform: scale(0.97); }
/* Disabled = "already in this state" -- it reads as status, not as breakage. */
.act.big:disabled { opacity: 0.35; cursor: default; }

.act-ok.big  { background: var(--ok); }
.act-pen.big { background: var(--check); }
.lb-title { font-weight: 600; font-size: 0.9rem; }

.lb-status {
  padding: 0.15rem 0.55rem; border-radius: 999px;
  font-size: 0.75rem; font-weight: 600;
  border: 1px solid transparent;
}
.lb-status.st-valid      { color: #9fdcac; border-color: #9fdcac44; background: #4f8a5b26; }
.lb-status.st-need_check { color: #f0c37a; border-color: #f0c37a44; background: #c8871f26; }
.lb-status.st-corrected  { color: #a8c2ee; border-color: #a8c2ee44; background: #4a6da826; }

/* "?" -- only rendered when the crop actually has a correction history. */
.lb-help {
  width: 1.55rem; height: 1.55rem; flex: 0 0 auto;
  border-radius: 50%; cursor: pointer;
  border: 1px solid rgba(255, 255, 255, 0.35);
  background: rgba(255, 255, 255, 0.08); color: #f2efe9;
  font: inherit; font-weight: 700; font-size: 0.85rem; line-height: 1;
}
.lb-help:hover { background: var(--fixed); border-color: var(--fixed); }

/* ---- correction history panel ---- */
.lb-hist {
  position: absolute; top: 0.85rem; right: 0.85rem; z-index: 4;
  width: min(420px, 46vw); max-height: 70%; overflow: auto;
  background: #1f1d1a; color: #f2efe9;
  border: 1px solid rgba(255, 255, 255, 0.18); border-radius: 10px;
  box-shadow: 0 8px 32px rgba(0, 0, 0, 0.55);
}
.hist-head {
  display: flex; align-items: center; justify-content: space-between;
  padding: 0.6rem 0.85rem; font-weight: 700; font-size: 0.85rem;
  border-bottom: 1px solid rgba(255, 255, 255, 0.14);
}
.hist-x {
  background: none; border: 0; color: #cfc8bd;
  font-size: 1.2rem; line-height: 1; cursor: pointer;
}
.hist-list { margin: 0; padding: 0.5rem 0.85rem 0.75rem 1.9rem; }
.hist-list li { margin: 0.5rem 0; font-size: 0.85rem; }
.hist-list .h-from, .hist-list .h-to {
  font-family: "Songti SC", "STSong", serif; font-size: 1.35rem;
  vertical-align: middle;
}
.hist-list .h-from { color: #d8b0a8; }
.hist-list .h-to   { color: #a8c2ee; font-weight: 600; }
.hist-list .h-arrow { margin: 0 0.4rem; color: #8b8377; }
.hist-list .h-meta { display: block; color: #9d958a; font-size: 0.72rem; }

/* ---- correction form ---- */
.lb-form {
  position: absolute; z-index: 5;
  top: 50%; left: 50%; transform: translate(-50%, -50%);
  width: min(380px, 88vw); padding: 1.1rem 1.25rem 0.9rem;
  background: #1f1d1a; color: #f2efe9;
  border: 1px solid rgba(255, 255, 255, 0.2); border-radius: 12px;
  box-shadow: 0 12px 48px rgba(0, 0, 0, 0.6);
}
.form-head { font-weight: 700; margin-bottom: 0.8rem; }
.fld { display: block; margin-bottom: 0.7rem; }
.fld span {
  display: block; margin-bottom: 0.25rem;
  font-size: 0.7rem; text-transform: uppercase; letter-spacing: 0.06em;
  color: #a8a196;
}
.fld input {
  width: 100%; padding: 0.5rem 0.65rem;
  font-family: "Songti SC", "STSong", serif; font-size: 1.5rem; line-height: 1.2;
  text-align: center;
  border-radius: 8px; border: 1px solid rgba(255, 255, 255, 0.25);
  background: rgba(255, 255, 255, 0.06); color: #f2efe9;
}
.fld input:focus { outline: 2px solid var(--fixed); outline-offset: 1px; }
#fld-current { opacity: 0.65; cursor: default; }
#fld-new::placeholder { font-family: inherit; font-size: 0.8rem; color: #7d766c; }
.form-err {
  margin: 0 0 0.6rem; padding: 0.4rem 0.6rem; border-radius: 6px;
  background: #7a2f22; color: #ffd9d2; font-size: 0.78rem;
}
.form-actions { display: flex; gap: 0.6rem; justify-content: flex-end; }
.btn-cancel, .btn-save {
  padding: 0.5rem 1rem; border-radius: 8px; cursor: pointer;
  font: inherit; font-weight: 600; font-size: 0.85rem;
}
.btn-cancel { background: none; color: #cfc8bd; border: 1px solid rgba(255, 255, 255, 0.25); }
.btn-cancel:hover { background: rgba(255, 255, 255, 0.1); }
.btn-save { background: var(--fixed); color: #fff; border: 1px solid var(--fixed); }
.btn-save:hover { filter: brightness(1.12); }
.form-note { margin: 0.7rem 0 0; font-size: 0.7rem; color: #8b8377; }
.form-note kbd, .form-note code {
  border: 1px solid rgba(255, 255, 255, 0.22); border-radius: 4px;
  padding: 0.02em 0.3em; font-size: 0.9em;
}

.lb-toast {
  position: absolute; z-index: 6; left: 50%; bottom: 1rem;
  transform: translateX(-50%);
  padding: 0.5rem 1rem; border-radius: 999px;
  background: var(--fixed); color: #fff;
  font-size: 0.85rem; font-weight: 600;
  box-shadow: 0 4px 16px rgba(0, 0, 0, 0.4);
}
.lb-toast[hidden] { display: none; }

.lb-keys { margin-left: auto; color: #a8a196; }
.lb-keys kbd {
  display: inline-block; padding: 0.05em 0.4em; margin: 0 0.1em;
  border: 1px solid rgba(255, 255, 255, 0.25); border-radius: 4px;
  font: inherit; font-size: 0.72rem; color: #e8e3da;
}
.lb-x {
  background: none; border: 0; color: #cfc8bd;
  font-size: 1.5rem; line-height: 1; cursor: pointer; padding: 0 0.2rem;
}
.lb-x:hover { color: #fff; }
#lb-zoom { margin-left: auto; }

.lb-stage {
  /* min-height:0 is load-bearing: without it a flex item won't shrink below
     its content size, so adding the action bar below would push the stage
     (and the whole lightbox) taller than the viewport instead of the stage
     giving up the space. */
  flex: 1 1 auto; min-height: 0; overflow: hidden;
  display: flex; align-items: center; justify-content: center;
  padding: 0.5rem;
  position: relative;   /* anchors .lb-glyph */
}

/* Reference glyph, top-left of the image area: the printed form of the
   character, black on white, to compare a doubtful rubbing against. */
.lb-glyph {
  position: absolute; top: 0.85rem; left: 0.85rem; z-index: 2;
  width: 84px; height: 84px;
  display: flex; align-items: center; justify-content: center;
  background: #fff; color: #000;
  font-family: "Songti SC", "STSong", "SimSun", serif;
  font-size: 62px; line-height: 1;
  border-radius: 4px;
  box-shadow: 0 2px 12px rgba(0, 0, 0, 0.55);
  /* Purely informational: must never intercept a click meant to close the
     lightbox, nor a drag meant to pan the zoomed image beneath it. */
  pointer-events: none; user-select: none;
}
.lb-stage.zoomed { cursor: grab; }
.lb-stage.zoomed:active { cursor: grabbing; }
.lb-stage img {
  /* object-fit:contain scales a crop BOTH up (the common case -- crops are
     only ~150-350px native, "enlarge" was the whole point) and down (a huge
     Sichuan mosaic), to fill whatever room the stage actually has. That means
     it stays correct automatically as the layout changes (e.g. the action bar
     below taking a row) instead of a hand-tuned vh number that has to be
     re-derived every time. */
  width: 100%; height: 100%; object-fit: contain;
  transform-origin: center center;
  user-select: none; -webkit-user-drag: none;
}
