/* The palette is the feature's own: the amber of a stroke in progress, the green of one that has
   been dealt with, on the navy the game draws its outlines in. Status is never colour alone —
   every state also carries a word. */

:root {
  --ground:      #eaeef4;
  --surface:     #ffffff;
  --surface-2:   #f2f5f9;
  --ink:         #14203a;
  --ink-soft:    #4a5772;
  --ink-faint:   #7b87a3;
  --rule:        #ccd6e2;
  --rule-soft:   #e1e8f0;

  --open:      #a86a05;
  --open-bg:   #f8ecd2;
  --resolved:  #1c7a45;
  --resolved-bg:#d8efe2;
  --wontfix:   #6b7280;
  --wontfix-bg:#e5e7eb;
  --danger:    #b23c0c;

  /* The three tags. Distinct from the status colours above on purpose — a note's kind and a
     note's state are two different questions, and a badge that borrowed amber from `open` would
     read as a third status. */
  --tag-bug:      #b23c0c;
  --tag-bug-bg:   #fbe2d8;
  --tag-idea:     #6d43c0;
  --tag-idea-bg:  #e9e2fa;
  --tag-other:    #4a5772;
  --tag-other-bg: #e5e9f1;

  /* Easy, middling, hard — a three-step scale of its own rather than the status colours, which
     answer a different question on the same screen. */
  --tier-easy: #2e9e5b;
  --tier-mid:  #e0a010;
  --tier-hard: #d23f22;

  --focus: #2563eb;

  --sans: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", system-ui, sans-serif;
  --mono: ui-monospace, "SF Mono", "JetBrains Mono", Menlo, monospace;

  --sidebar: 16rem;
  --panel: 23rem;
}

@media (prefers-color-scheme: dark) {
  :root {
    --ground:     #0c1423;
    --surface:    #131e33;
    --surface-2:  #1a2740;
    --ink:        #dde5f2;
    --ink-soft:   #9fadc4;
    --ink-faint:  #6d7c96;
    --rule:       #2b3a56;
    --rule-soft:  #22304a;

    --open:       #eab040;
    --open-bg:    #3a2a0c;
    --resolved:   #56cf87;
    --resolved-bg:#0f2f1e;
    --wontfix:    #9aa3b2;
    --wontfix-bg: #232a36;
    --danger:     #f08050;

    --tag-bug:      #f08050;
    --tag-bug-bg:   #3a1c10;
    --tag-idea:     #b39bf0;
    --tag-idea-bg:  #241b3d;
    --tag-other:    #9fadc4;
    --tag-other-bg: #222d43;

    --tier-easy: #4cc47c;
    --tier-mid:  #f0bb44;
    --tier-hard: #f0603c;

    --focus: #7aa5ff;
  }
}

* { box-sizing: border-box; }

/* The UA sheet's [hidden]{display:none} is the same specificity as any class here, and author
   styles win over it — so `.curtain { display: grid }` silently defeated `el.login.hidden = true`
   and left the sign-in card sitting on top of a working app. Anything the code hides must stay
   hidden regardless of what it is otherwise displayed as. */
[hidden] { display: none !important; }

html, body { height: 100%; }

body {
  margin: 0;
  background: var(--ground);
  color: var(--ink);
  font-family: var(--sans);
  font-size: 15px;
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
}

:focus-visible {
  outline: 2px solid var(--focus);
  outline-offset: 2px;
}

h1, h2, h3 { margin: 0; font-weight: 650; letter-spacing: -0.01em; }
h1 { font-size: 1.35rem; }
h2 { font-size: 1rem; }
p { margin: 0; }

code { font-family: var(--mono); font-size: 0.85em; }

.muted { color: var(--ink-faint); }
.small { font-size: 0.82rem; }
.error { color: var(--danger); font-size: 0.86rem; }

/* ---------------------------------------------------------------- curtains */

.curtain {
  position: fixed;
  inset: 0;
  display: grid;
  place-items: center;
  background: var(--ground);
  padding: 1.5rem;
  z-index: 10;
}

.card {
  background: var(--surface);
  border: 1px solid var(--rule);
  border-radius: 10px;
  padding: 1.75rem;
  width: min(26rem, 100%);
  display: flex;
  flex-direction: column;
  gap: 0.85rem;
  box-shadow: 0 1px 2px rgb(0 0 0 / 0.04), 0 8px 24px rgb(0 0 0 / 0.06);
}

.card label {
  font-size: 0.8rem;
  font-weight: 600;
  letter-spacing: 0.03em;
  text-transform: uppercase;
  color: var(--ink-faint);
  margin-bottom: -0.5rem;
}

input[type="password"], input[type="text"] {
  font: inherit;
  font-family: var(--mono);
  font-size: 0.9rem;
  padding: 0.6rem 0.7rem;
  border: 1px solid var(--rule);
  border-radius: 6px;
  background: var(--surface-2);
  color: var(--ink);
  width: 100%;
}

button {
  font: inherit;
  font-weight: 600;
  cursor: pointer;
  border-radius: 6px;
  border: 1px solid transparent;
  padding: 0.55rem 0.9rem;
  background: var(--ink);
  color: var(--surface);
  transition: filter 0.12s ease;
}

button:hover:not(:disabled) { filter: brightness(1.18); }
button:disabled { opacity: 0.45; cursor: default; }

button.ghost {
  background: transparent;
  color: var(--ink-soft);
  border-color: var(--rule);
  padding: 0.4rem 0.7rem;
  font-size: 0.85rem;
}

button.ghost:hover:not(:disabled) { background: var(--surface-2); filter: none; }

button.danger {
  background: transparent;
  color: var(--danger);
  border-color: color-mix(in srgb, var(--danger) 40%, transparent);
}

button.danger:hover:not(:disabled) { background: color-mix(in srgb, var(--danger) 10%, transparent); filter: none; }

/* ---------------------------------------------------------------- topbar */

.topbar {
  height: 3.25rem;
  border-bottom: 1px solid var(--rule);
  background: var(--surface);
  display: flex;
  align-items: center;
  gap: 1rem;
  padding: 0 1rem;
  position: sticky;
  top: 0;
  z-index: 5;
}

.brand { display: flex; align-items: center; gap: 0.5rem; font-weight: 650; white-space: nowrap; }
.mark { font-size: 1.1rem; }

.filters { display: flex; gap: 0.3rem; margin-left: auto; }

.chip {
  background: transparent;
  color: var(--ink-soft);
  border: 1px solid var(--rule);
  padding: 0.3rem 0.75rem;
  font-size: 0.85rem;
  border-radius: 999px;
}

.chip.is-on {
  background: var(--ink);
  color: var(--surface);
  border-color: var(--ink);
}

.chip:hover:not(.is-on) { background: var(--surface-2); filter: none; }

.topbar-end { display: flex; align-items: center; gap: 0.5rem; }

/* ---------------------------------------------------------------- layout */

main {
  display: grid;
  grid-template-columns: var(--sidebar) minmax(0, 1fr) var(--panel);
  height: calc(100vh - 3.25rem);
}

nav#levels {
  border-right: 1px solid var(--rule);
  background: var(--surface);
  overflow-y: auto;
  padding: 0.6rem;
}

.level {
  width: 100%;
  text-align: left;
  background: transparent;
  color: var(--ink);
  border: 1px solid transparent;
  padding: 0.5rem 0.6rem;
  border-radius: 6px;
  display: flex;
  align-items: center;
  gap: 0.5rem;
  font-weight: 500;
}

.level:hover { background: var(--surface-2); filter: none; }
.level.is-on { background: var(--surface-2); border-color: var(--rule); font-weight: 650; }

.level .idx {
  font-family: var(--mono);
  font-size: 0.75rem;
  color: var(--ink-faint);
  min-width: 1.8rem;
}

.level .nm { flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

.badge {
  font-size: 0.72rem;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  padding: 0.05rem 0.4rem;
  border-radius: 999px;
  background: var(--open-bg);
  color: var(--open);
}

.badge.zero { background: var(--surface-2); color: var(--ink-faint); }

.nav-head {
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ink-faint);
  padding: 0.6rem 0.6rem 0.3rem;
}

/* ---------------------------------------------------------------- stage */

#stage { display: flex; flex-direction: column; min-width: 0; }

#stage-head {
  padding: 0.7rem 1rem;
  border-bottom: 1px solid var(--rule-soft);
}

#viewport {
  flex: 1;
  position: relative;
  overflow: hidden;
  padding: 1rem;
  display: grid;
  place-items: center;
  background:
    linear-gradient(45deg, var(--rule-soft) 25%, transparent 25%, transparent 75%, var(--rule-soft) 75%),
    linear-gradient(45deg, var(--rule-soft) 25%, transparent 25%, transparent 75%, var(--rule-soft) 75%);
  background-size: 22px 22px;
  background-position: 0 0, 11px 11px;
}

#plate {
  position: relative;
  line-height: 0;
  box-shadow: 0 2px 12px rgb(0 0 0 / 0.16);
  background: var(--surface);
  max-width: 100%;
  transform-origin: 0 0;
}

#art-missing {
  display: grid;
  place-content: center;
  gap: 0.4rem;
  text-align: center;
  padding: 2rem;
  line-height: 1.5;
  border: 1px dashed var(--rule);
  color: var(--ink-soft);
}

#overlay { position: absolute; inset: 0; width: 100%; height: 100%; cursor: crosshair; touch-action: none; }
#overlay.is-panning { cursor: grabbing; }

/* ---------------------------------------------------------------- panel */

#panel {
  border-left: 1px solid var(--rule);
  background: var(--surface);
  overflow-y: auto;
  display: flex;
  flex-direction: column;
}

#note-list-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  padding: 0.8rem 0.9rem 0.5rem;
  border-bottom: 1px solid var(--rule-soft);
  position: sticky;
  top: 0;
  background: var(--surface);
}

ol#note-list { list-style: none; margin: 0; padding: 0.4rem; display: flex; flex-direction: column; gap: 0.25rem; }

.note {
  width: 100%;
  text-align: left;
  background: transparent;
  color: var(--ink);
  border: 1px solid transparent;
  border-left: 3px solid var(--rule);
  border-radius: 5px;
  padding: 0.5rem 0.6rem;
  display: grid;
  grid-template-columns: 1.5rem 1fr;
  gap: 0.1rem 0.5rem;
  font-weight: 400;
}

.note:hover { background: var(--surface-2); filter: none; }
.note.is-on { background: var(--surface-2); border-color: var(--rule); }
.note.s-open     { border-left-color: var(--open); }
.note.s-resolved { border-left-color: var(--resolved); }
.note.s-wontfix  { border-left-color: var(--wontfix); }

.note .n {
  font-family: var(--mono);
  font-size: 0.72rem;
  font-weight: 700;
  color: var(--ink-faint);
  padding-top: 0.15rem;
}

.note .txt {
  font-size: 0.88rem;
  line-height: 1.35;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

.note .meta { grid-column: 2; font-size: 0.74rem; color: var(--ink-faint); }

.empty { padding: 2rem 1rem; text-align: center; color: var(--ink-faint); font-size: 0.88rem; }

/* ---------------------------------------------------------------- detail */

#detail {
  border-top: 1px solid var(--rule);
  padding: 0.9rem;
  display: flex;
  flex-direction: column;
  gap: 0.75rem;
  position: sticky;
  bottom: 0;
  background: var(--surface);
  box-shadow: 0 -6px 18px rgb(0 0 0 / 0.06);
}

.state {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  align-self: flex-start;
  font-size: 0.74rem;
  font-weight: 700;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  padding: 0.15rem 0.5rem;
  border-radius: 999px;
}

/* A tag badge. The same pill as a status, deliberately — they answer different questions but
   they are read in the same glance, and two shapes for one glance is one shape too many. */
.tag {
  display: inline-flex;
  align-items: center;
  align-self: flex-start;
  font-size: 0.68rem;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  padding: 0.1rem 0.4rem;
  border-radius: 4px;
}

.tag.t-bug   { background: var(--tag-bug-bg);   color: var(--tag-bug); }
.tag.t-idea  { background: var(--tag-idea-bg);  color: var(--tag-idea); }
.tag.t-other { background: var(--tag-other-bg); color: var(--tag-other); }

/* In a list row the badge rides on the meta line, so it never pushes the text out of its two
   clamped lines. */
.note .meta .tag { margin-right: 0.35rem; vertical-align: baseline; }

/* The detail's status pill and tag badge sit on one line rather than stacking two pills. */
#detail .heads { display: flex; align-items: center; gap: 0.4rem; flex-wrap: wrap; }

.state.s-open     { background: var(--open-bg);     color: var(--open); }
.state.s-resolved { background: var(--resolved-bg); color: var(--resolved); }
.state.s-wontfix  { background: var(--wontfix-bg);  color: var(--wontfix); }

#detail .body { font-size: 0.95rem; line-height: 1.5; white-space: pre-wrap; overflow-wrap: anywhere; }

.shot {
  display: block;
  width: 100%;
  max-height: 40vh;
  object-fit: contain;
  object-position: top;
  background: var(--surface-2);
  border: 1px solid var(--rule);
  border-radius: 4px;
  cursor: zoom-in;
}

dl.facts {
  margin: 0;
  display: grid;
  grid-template-columns: auto 1fr;
  gap: 0.15rem 0.7rem;
  font-size: 0.78rem;
}

dl.facts dt { color: var(--ink-faint); }
dl.facts dd { margin: 0; font-family: var(--mono); font-size: 0.75rem; overflow-wrap: anywhere; }

.actions { display: flex; flex-wrap: wrap; gap: 0.4rem; }
.actions button { padding: 0.4rem 0.7rem; font-size: 0.85rem; }

/* ---------------------------------------------------------------- narrow */

@media (max-width: 60rem) {
  main {
    grid-template-columns: 1fr;
    grid-template-rows: auto minmax(18rem, 1fr) auto;
    height: auto;
  }

  nav#levels { border-right: none; border-bottom: 1px solid var(--rule); max-height: 11rem; }
  #panel { border-left: none; border-top: 1px solid var(--rule); }
  .topbar { flex-wrap: wrap; height: auto; padding: 0.5rem 0.75rem; }
  .topbar-end { flex-wrap: wrap; max-width: 100%; }
}

/* ---------------------------------------------------------------- approvals

   The queue is one column of cards rather than the notes dashboard's three panes: there is no
   level art to draw on and nothing to select — every card is complete, and the only thing to do
   with it is decide. A single measured column is what keeps the words the thing being read, since
   the words are what the decision is made on. */

.queue-main {
  display: block;
  height: auto;
  max-width: 46rem;
  margin: 0 auto;
  padding: 1.25rem 1rem 4rem;
}

#queue-head h2 { margin: 0 0 0.15rem; font-size: 1.05rem; }
#queue-head p { margin: 0 0 1rem; }

#queue { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 0.85rem; }

.ask {
  background: var(--surface);
  border: 1px solid var(--rule);
  border-left: 3px solid var(--rule);
  border-radius: 8px;
  padding: 0.85rem 0.95rem;
  display: flex;
  flex-direction: column;
  gap: 0.55rem;
}

/* The stripe is what the action is, at a glance down the column — a delete should not look like
   a resolve until you have read it. It is never the only signal: the badge says the word too. */
.ask.t-resolved { border-left-color: var(--resolved); }
.ask.t-wontfix  { border-left-color: var(--wontfix); }
.ask.t-open     { border-left-color: var(--open); }
.ask.t-danger   { border-left-color: var(--danger); }

.ask.is-decided { opacity: 0.62; }

.ask-head { display: flex; align-items: center; gap: 0.55rem; flex-wrap: wrap; }
.ask-head .stamp { margin-left: auto; }

.state.s-danger { background: color-mix(in srgb, var(--danger) 14%, transparent); color: var(--danger); }

.asker { font-size: 0.88rem; color: var(--ink-soft); }

.ask .body {
  margin: 0;
  font-size: 0.95rem;
  line-height: 1.5;
  white-space: pre-wrap;
  overflow-wrap: anywhere;
}

.ask .reason {
  margin: 0;
  font-size: 0.88rem;
  color: var(--ink-soft);
  border-left: 2px solid var(--rule-soft);
  padding-left: 0.6rem;
}

.ask p { margin: 0; }

.ask .shot { max-height: 22rem; }

/* The link back to the notes dashboard sits among the ghost buttons and has to look like one. */
a.ghost {
  display: inline-flex;
  align-items: center;
  font: inherit;
  font-weight: 600;
  font-size: 0.85rem;
  text-decoration: none;
  color: var(--ink-soft);
  border: 1px solid var(--rule);
  border-radius: 6px;
  padding: 0.4rem 0.7rem;
}

a.ghost:hover { background: var(--surface-2); }

/* The count of what is waiting, worn by the link to this page from the notes dashboard. */
#approvals-link { position: relative; }
#approvals-link .badge { margin-left: 0.4rem; }

@media (max-width: 60rem) {
  .queue-main { padding: 0.9rem 0.75rem 3rem; }
}

/* The archive's level filter sits among the ghost buttons and has to look like one. */
select.ghost {
  font: inherit;
  font-weight: 600;
  font-size: 0.85rem;
  color: var(--ink-soft);
  background: transparent;
  border: 1px solid var(--rule);
  border-radius: 6px;
  padding: 0.35rem 0.5rem;
  max-width: 12rem;
}

/* Paging is one button at the end of the list rather than an infinite scroll: this is a record
   somebody is reading deliberately, and a list that grows as you look at it is one you cannot
   keep your place in. */
.more { display: flex; justify-content: center; padding: 1.1rem 0 0; }

/* ---------------------------------------------------------------- level art

   The art manager is one column of plates rather than the notes dashboard's three panes: nothing
   here is selected and nothing is drawn on, so there is no stage to keep and no sidebar to keep
   it beside. Each plate is one question — what is this taking up, what is going up, what is
   already there — and they are answered in that order because the first one decides the second. */

.queue-main.wide { max-width: 62rem; }

.plate {
  background: var(--surface);
  border: 1px solid var(--rule);
  border-radius: 8px;
  padding: 0.9rem 1rem 1rem;
  margin-bottom: 1rem;
}

.plate-head {
  display: flex;
  align-items: baseline;
  gap: 0.6rem;
  margin-bottom: 0.7rem;
}

.plate-head h2 { font-size: 1rem; }
.plate-head > :last-child { margin-left: auto; }

.retired-levels > summary { cursor: pointer; font-weight: 600; }
.retired-levels > p { margin: 0.75rem 0; }
.retired-list { list-style: none; padding: 0; margin: 0; }
.retired-list > li { display: flex; flex-wrap: wrap; align-items: center; gap: 0.5rem; padding: 0.5rem 0; }
.retired-list > li + li { border-top: 1px solid var(--rule-soft); }
.retired-list button { margin-left: auto; }

.headline { margin: 0 0 0.5rem; font-size: 0.95rem; color: var(--ink-soft); }
.headline strong { color: var(--ink); font-variant-numeric: tabular-nums; }

/* The bar is the whole disk, not this service's share of it: how much room is left is the number
   worth reading, and the breakdown below is the part anything can be done about. Every segment
   also appears in the legend with its figure — the colours are an at-a-glance summary and never
   the only way to tell one part from another. */
.bar {
  display: flex;
  height: 0.85rem;
  border-radius: 999px;
  overflow: hidden;
  background: var(--surface-2);
  border: 1px solid var(--rule-soft);
}

.bar-part { display: block; min-width: 2px; }

.bar-part.is-art   { background: var(--open); }
.bar-part.is-db    { background: var(--focus); }
.bar-part.is-img   { background: var(--open); filter: saturate(0.5); }
.bar-part.is-audit { background: var(--wontfix); }
.bar-part.is-items { background: var(--resolved); }
.bar-part.is-other { background: var(--rule); }
.bar-part.is-free  { background: transparent; }

.legend {
  list-style: none;
  margin: 0.75rem 0 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(15rem, 1fr));
  gap: 0.3rem 1.2rem;
}

.legend li { display: flex; align-items: center; gap: 0.5rem; font-size: 0.88rem; }

.swatch {
  width: 0.7rem;
  height: 0.7rem;
  border-radius: 3px;
  flex: none;
}

.swatch.is-art   { background: var(--open); }
.swatch.is-db    { background: var(--focus); }
.swatch.is-img   { background: var(--open); filter: saturate(0.5); }
.swatch.is-audit { background: var(--wontfix); }
.swatch.is-items { background: var(--resolved); }
.swatch.is-other { background: var(--rule); }

.legend-label { color: var(--ink-soft); }
.legend-value { margin-left: auto; font-variant-numeric: tabular-nums; font-weight: 650; }

.note-line { margin: 0.75rem 0 0; }

/* ---- publishing */

.fields { display: flex; gap: 0.75rem; margin-bottom: 0.8rem; flex-wrap: wrap; }

.field { display: flex; flex-direction: column; gap: 0.25rem; }
.field.grow { flex: 1; min-width: 12rem; }

.field span {
  font-size: 0.75rem;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--ink-faint);
}

.field input {
  font: inherit;
  color: var(--ink);
  background: var(--surface-2);
  border: 1px solid var(--rule);
  border-radius: 6px;
  padding: 0.45rem 0.6rem;
}

.field input[type="number"] { width: 7rem; }

/* Dropping is the gesture this expects, because the thing being published is a file sitting on
   somebody's Desktop. Clicking does the same thing for anyone who would rather not drag. */
.drop {
  border: 1.5px dashed var(--rule);
  border-radius: 8px;
  padding: 1.4rem 1rem;
  text-align: center;
  cursor: pointer;
  background: var(--surface-2);
}

.drop:hover, .drop.is-over {
  border-color: var(--focus);
  background: color-mix(in srgb, var(--focus) 7%, var(--surface-2));
}

.drop p { margin: 0; }
.drop-main { font-weight: 600; margin-bottom: 0.25rem !important; }

/* What will be stored, made by the same pipeline that will store it — so the preview cannot be
   showing something other than what the button sends. */
.preview {
  display: flex;
  gap: 1rem;
  margin-top: 0.9rem;
  padding-top: 0.9rem;
  border-top: 1px solid var(--rule-soft);
}

.preview img {
  width: 11rem;
  height: 11rem;
  object-fit: contain;
  background: var(--surface-2);
  border: 1px solid var(--rule);
  border-radius: 6px;
}

.preview-facts { flex: 1; min-width: 0; }
.preview-facts .strong { font-weight: 650; margin: 0 0 0.4rem; overflow-wrap: anywhere; }

/* ---- the published levels */

.level-grid {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 0.6rem;
}

.art-card {
  display: flex;
  align-items: center;
  gap: 0.9rem;
  border: 1px solid var(--rule);
  border-radius: 8px;
  padding: 0.6rem 0.75rem;
  background: var(--surface);
}

/* A level with notes and nothing to draw them on is the one thing on this page to act on, so it
   is marked — dimmed thumbnail and a soft stripe, never colour alone: the card says "No picture"
   where the picture would be. */
.art-card.is-bare { border-left: 3px solid var(--open); }

.thumb {
  width: 5.5rem;
  height: 5.5rem;
  flex: none;
  border-radius: 6px;
  overflow: hidden;
  background: var(--surface-2);
  border: 1px solid var(--rule-soft);
  display: grid;
  place-items: center;
}

.thumb img { width: 100%; height: 100%; object-fit: cover; display: block; }

.art-body { flex: 1; min-width: 0; }
.art-body p { margin: 0; }
.art-head { margin-bottom: 0.15rem !important; display: flex; align-items: baseline; gap: 0.4rem; }
.art-head .idx { font-family: var(--mono); font-size: 0.78rem; color: var(--ink-faint); }

.art-actions { display: flex; flex-wrap: wrap; gap: 0.35rem; justify-content: flex-end; flex: none; }
.art-actions button, .art-actions a { font-size: 0.82rem; padding: 0.32rem 0.6rem; }

@media (max-width: 46rem) {
  .art-card { flex-wrap: wrap; }
  .art-actions { width: 100%; justify-content: flex-start; }
  .preview { flex-direction: column; }
  .preview img { width: 100%; height: 12rem; }
}

/* A button that has to read as a link, because it sits inside a sentence. */
.linky {
  background: none;
  border: none;
  padding: 0;
  font: inherit;
  color: var(--focus);
  text-decoration: underline;
  cursor: pointer;
}

.linky:hover { filter: none; opacity: 0.8; }

/* A level published in pieces rather than as one flattening. Marked because it is the difference
   between a note that can be read on the board as it stood and one that cannot — and the mark is
   a word, never only the stripe. */
.art-card.is-scene { border-left: 3px solid var(--resolved); }

.pill {
  font-size: 0.7rem;
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  padding: 0.05rem 0.4rem;
  border-radius: 4px;
  background: var(--resolved-bg);
  color: var(--resolved);
}

/* The board — pieces or a flat picture — painted in screen pixels over wherever the plate is. */
#scene {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  pointer-events: none;
}

/* The stage head becomes a row once there is a control in it. */
#stage-head { display: flex; align-items: baseline; gap: 0.75rem; flex-wrap: wrap; }
#stage-head #board-state { margin-left: auto; }
#stage-head #fit, #stage-head #focus { align-self: center; padding: 0.2rem 0.6rem; font-size: 0.78rem; }
#stage-head #board-state[hidden] + #fit { margin-left: auto; }
#stage-head .chip { padding: 0.2rem 0.6rem; font-size: 0.78rem; }

/* ---------------------------------------------------------------- the level viewer

   Full-height and its own scroll: this is the one page that is a tool rather than a document.
   The board fills what is left after the topbar, the list scrolls beside it, and the page itself
   never scrolls — a board you have to scroll the window to see is a board you cannot pan. */

.viewer-shell {
  display: flex;
  height: calc(100vh - 3.25rem);
  min-height: 0;
}

.viewer-list {
  width: 19rem;
  flex: none;
  border-right: 1px solid var(--rule);
  background: var(--surface);
  display: flex;
  flex-direction: column;
  min-height: 0;
}

.viewer-search {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.6rem 0.75rem;
  border-bottom: 1px solid var(--rule-soft);
}

.viewer-search input {
  flex: 1;
  min-width: 0;
  padding: 0.35rem 0.5rem;
  border: 1px solid var(--rule);
  border-radius: 6px;
  background: var(--surface-2);
  color: var(--ink);
  font: inherit;
  font-size: 0.85rem;
}

/* The two orders, under the search box. Chips rather than a select: there are two of them, the
   choice is made and remade constantly, and a dropdown hides the option you are not in. */
.viewer-sort {
  display: flex;
  gap: 0.3rem;

  /* Off the rule above it. The row sat against the search box's border, which read as the two
     belonging to one control rather than as a filter under a search. */
  padding: 0.6rem 0.75rem 0.5rem;
}

.viewer-sort { flex-wrap: wrap; }
.viewer-sort .chip { padding: 0.2rem 0.6rem; font-size: 0.78rem; }

/* The metric picker sits with the orders it drives, and takes the rest of the row. */
.viewer-sort select {
  flex: 1;
  min-width: 7rem;
  font-size: 0.78rem;
  padding: 0.2rem 0.4rem;
}
.viewer-sort .chip:disabled { opacity: 0.45; cursor: not-allowed; }

/* One third of the board at a time, under the two orders. Each chip carries the dot the rows in
   it carry, so the filter and the thing it filters are named by the same mark. */
.viewer-tiers {
  display: flex;
  gap: 0.3rem;
  padding: 0 0.75rem 0.65rem;
  border-bottom: 1px solid var(--rule-soft);
}

.viewer-tiers .chip {
  display: flex;
  align-items: center;
  gap: 0.3rem;
  padding: 0.2rem 0.55rem;
  font-size: 0.78rem;
}

.viewer-tiers .chip:disabled { opacity: 0.45; cursor: not-allowed; }

.viewer-tiers .dot { width: 0.5rem; height: 0.5rem; border-radius: 50%; flex: none; }
.viewer-tiers .dot.is-easy { background: var(--tier-easy); }
.viewer-tiers .dot.is-mid  { background: var(--tier-mid); }
.viewer-tiers .dot.is-hard { background: var(--tier-hard); }

.item-list {
  list-style: none;
  margin: 0;
  padding: 0 0 1rem;
  overflow-y: auto;
  flex: 1;
  min-height: 0;
}

.item-group {
  padding: 0.6rem 0.75rem 0.25rem;
  font-size: 0.72rem;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--ink-faint);
  position: sticky;
  top: 0;
  background: var(--surface);
}

.item-row {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.3rem 0.75rem;
  cursor: pointer;
  font-size: 0.85rem;
  border-left: 3px solid transparent;
}

.item-row:hover { background: var(--surface-2); }

.item-row.is-selected {
  background: var(--surface-2);
  border-left-color: var(--focus);
  font-weight: 600;
}

/* The swatch is square and small on purpose: it is the key to the outline on the board, not a
   status light, and anything rounder starts reading as one. */
.item-swatch { width: 0.7rem; height: 0.7rem; border-radius: 2px; flex: none; }
.item-id { font-family: var(--mono); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

.item-flag {
  margin-left: auto;
  font-size: 0.65rem;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  padding: 0.05rem 0.35rem;
  border-radius: 4px;
  background: var(--wontfix-bg);
  color: var(--wontfix);
}

.viewer-stage {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
}

/* One row, always. A tool bar that wraps moves the buttons under the mouse between one window
   size and the next; scrolling sideways keeps every control where it was put. */
.viewer-tools {
  display: flex;
  align-items: center;
  gap: 0.85rem;
  flex-wrap: nowrap;
  overflow-x: auto;
  scrollbar-width: thin;
  padding: 0.5rem 0.9rem;
  border-bottom: 1px solid var(--rule);
  background: var(--surface);
  font-size: 0.82rem;
}

.viewer-tools label { display: flex; align-items: center; gap: 0.3rem; white-space: nowrap; }
.viewer-tools .tool-gap { flex: 1; }
.viewer-tools .tool-slider input { width: 6rem; }
.viewer-tools button { padding: 0.25rem 0.6rem; font-size: 0.82rem; }

/* The board's own ground is deliberately darker than the page: an item isolated out of its scene
   is mostly transparent, and it has to be looked at against something that is not white. */
.viewer-board {
  position: relative;
  flex: 1;
  min-height: 0;
  overflow: hidden;
  background:
    repeating-conic-gradient(var(--surface-2) 0% 25%, var(--ground) 0% 50%) 50% / 24px 24px;
  touch-action: none;
  cursor: default;
}

.viewer-board canvas { display: block; position: absolute; inset: 0; }

/* ---- the phone simulator: the board beside it, the phone at whatever height the stage allows */
.viewer-stage-body { flex: 1; min-height: 0; display: flex; }
.viewer-stage-body .viewer-board { min-width: 0; }

.phone-sim {
  flex: none;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.5rem;
  padding: 0.6rem 0.9rem;
  border-left: 1px solid var(--rule);
  background: var(--surface);
  overflow: auto;
}

.phone-sim[hidden] { display: none; }

.viewer-tools #t-phone.is-on { background: var(--ink); color: var(--surface); }

.phone-head {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: 0.3rem 0.7rem;
  font-size: 0.8rem;
}

.phone-head label { display: flex; align-items: center; gap: 0.3rem; white-space: nowrap; }
.phone-head button { padding: 0.2rem 0.55rem; font-size: 0.8rem; }

.phone-frame {
  padding: 9px;
  background: #0d0f14;
  border-radius: 46px;
  box-shadow: 0 0 0 2px #3a3f4b, 0 10px 30px rgba(0, 0, 0, 0.35);
}

.phone-frame canvas {
  display: block;
  border-radius: 37px;
  background: #000;
  touch-action: none;
  cursor: grab;
}

.phone-frame canvas:active { cursor: grabbing; }
.phone-sim #phone-zoom { margin: 0; }
.viewer-board .empty { position: relative; max-width: 30rem; margin: 0 auto; }

.viewer-footer {
  border-top: 1px solid var(--rule);
  background: var(--surface);
  padding: 0.4rem 0.9rem;
  font-family: var(--mono);
  font-size: 0.76rem;
  white-space: nowrap;
  overflow-x: auto;
}

.field.inline { flex-direction: row; align-items: center; }
.field.inline select { max-width: 22rem; }

.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;
}

/* The find-order picker sits with the toggles; the dataset name is long and the row is not, so it
   is the one control allowed to shrink. */
.viewer-tools .tool-stats select { max-width: 12rem; }
.viewer-tools > * { flex: none; }

/* The rank badge on a row, and the shape flag beside it. Both live at the right-hand end, which
   is why the flag stops claiming all of the space the moment a rank is there too. */
/* The board cut in three, on the row beside the id. Round against the category swatch's square,
   because one is which thing this is and the other is how it went. */
.item-tier {
  width: 0.55rem;
  height: 0.55rem;
  border-radius: 50%;
  flex: none;
  margin-left: 0.15rem;
}

.item-tier.is-easy { background: var(--tier-easy); }
.item-tier.is-mid  { background: var(--tier-mid); }
.item-tier.is-hard { background: var(--tier-hard); }

/* The right-hand end of a row: the measurement, the shape flag, the place. One box, pushed over
   together, so a row with two of them spaces them rather than stacking margins. */
.item-end {
  margin-left: auto;
  display: flex;
  align-items: center;
  gap: 0.35rem;
  flex: none;
}

/* The number itself, quiet: the row is read by its id, and this is what the id is worth. */
.item-value {
  font-family: var(--mono);
  font-size: 0.7rem;
  color: var(--ink-faint);
  white-space: nowrap;
}

.item-row.is-selected .item-value { color: var(--ink-soft); }

/* Dark rather than coloured, like the second line of the label it matches: which object this is
   and how it behaved are two different statements. */
/* Under **All** every row carries its rank; the marked ones stay dark so "which are the problem"
   is still one glance rather than a comparison of numbers. */
.item-hard.is-muted { background: var(--rule-soft); color: var(--ink-faint); }

.item-hard {
  font-family: var(--mono);
  font-size: 0.7rem;
  padding: 0.05rem 0.35rem;
  border-radius: 4px;
  background: var(--ink);
  color: var(--surface);
}

/* ---------------------------------------------------------------- the analysis panel

   Sits above the list because it is read on the way in: what is wrong with this board, before
   anything is asked about one object. Collapsible, since after that it is in the way. */

.analysis {
  border-bottom: 1px solid var(--rule-soft);
  background: var(--surface-2);
}

.analysis > summary {
  display: flex;
  align-items: baseline;
  gap: 0.5rem;
  padding: 0.55rem 0.75rem;
  cursor: pointer;
  font-weight: 600;
  font-size: 0.85rem;
  list-style: none;
}

.analysis > summary::-webkit-details-marker { display: none; }
.analysis > summary::before { content: '▸'; color: var(--ink-faint); font-size: 0.7rem; }
.analysis[open] > summary::before { content: '▾'; }
.analysis > summary > .muted { margin-left: auto; font-family: var(--mono); }

#analysis-body { padding: 0 0.75rem 0.7rem; }

.analysis-scores { display: flex; gap: 0.5rem; }

.analysis-score {
  flex: 1;
  background: var(--surface);
  border: 1px solid var(--rule-soft);
  border-radius: 6px;
  padding: 0.4rem 0.5rem;
  text-align: center;
}

.analysis-score p { margin: 0; }
.analysis-score-value { font-size: 1.35rem; font-weight: 650; line-height: 1.1; }
.analysis-score-label { font-size: 0.7rem; text-transform: uppercase; letter-spacing: 0.04em; color: var(--ink-soft); }

.analysis-parts { margin: 0.45rem 0 0; line-height: 1.45; }

.analysis-flags { list-style: none; margin: 0.5rem 0 0; padding: 0; display: grid; gap: 0.3rem; }

/* A flag is a way into the board, so it behaves like one: it looks pressable and it selects. */
.analysis-flag {
  padding: 0.35rem 0.45rem;
  border-radius: 6px;
  background: var(--surface);
  border: 1px solid var(--rule-soft);
  cursor: pointer;
}

.analysis-flag:hover { border-color: var(--focus); }
.analysis-flag p { margin: 0; }
.analysis-flag-head { display: flex; align-items: center; gap: 0.4rem; }

/* The four diagnoses are not four severities — they are four different jobs — so they are told
   apart by hue rather than ranked by one. */
.analysis-tag {
  margin-left: auto;
  font-size: 0.65rem;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  padding: 0.05rem 0.4rem;
  border-radius: 4px;
  white-space: nowrap;
  background: var(--wontfix-bg);
  color: var(--wontfix);
}

.analysis-tag.is-wall { background: var(--tag-bug-bg); color: var(--tag-bug); }
.analysis-tag.is-invisible { background: var(--open-bg); color: var(--open); }
.analysis-tag.is-confusable { background: var(--tag-idea-bg); color: var(--tag-idea); }
.analysis-tag.is-unrecognised { background: var(--resolved-bg); color: var(--resolved); }

/* ---------------------------------------------------------------- levels at risk */

/* The bar is behind the number rather than beside it: the column is read as numbers, and the fill
   is for the eye running down it. */
.risk-bar {
  display: inline-block;
  width: 4rem;
  height: 0.5rem;
  margin-right: 0.4rem;
  border-radius: 3px;
  background: var(--rule-soft);
  background-image: linear-gradient(var(--tier-hard), var(--tier-hard));
  background-size: var(--fill, 0%) 100%;
  background-repeat: no-repeat;
  vertical-align: middle;
}

.risk-spike.is-high { color: var(--tier-hard); font-weight: 650; }

.risk-flag {
  display: inline-block;
  margin: 0.1rem 0.25rem 0.1rem 0;
  text-decoration: none;
  font-family: var(--mono);
  text-transform: none;
  letter-spacing: 0;
}

.risk-flag:hover { filter: brightness(1.05); text-decoration: underline; }

#risk-table td { vertical-align: middle; }

/* ---- the notch check
 *
 * One colour ramp, used by the badge on a row, the dot on the filter chip and the bands over the
 * board — so "kırpılmış" is the same orange wherever it is said. Deliberately not the tier
 * colours: a tier is a third of a board and this is a defect, and two scales sharing a palette
 * would be read as one scale. */

:root {
  --notch-buried:  #d23f22;
  --notch-clipped: #e0a010;
  --notch-hidden:  #c8a828;
}

@media (prefers-color-scheme: dark) {
  :root {
    --notch-buried:  #f0603c;
    --notch-clipped: #f0bb44;
    --notch-hidden:  #d8c05c;
  }
}

.notch-summary {
  margin: 0 0 0.5rem;
  font-size: 0.78rem;
  line-height: 1.35;
  color: var(--ink-faint);
  border-left: 3px solid var(--notch-hidden);
  padding-left: 0.5rem;
  cursor: help;
  white-space: pre-line;
}

.notch-summary.is-bad {
  color: var(--notch-buried);
  border-left-color: var(--notch-buried);
}

.viewer-tiers .dot.is-notch { background: var(--notch-buried); }

.item-notch {
  font-size: 0.65rem;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  padding: 0.05rem 0.35rem;
  border-radius: 4px;
  white-space: nowrap;
  color: #fff;
}

.item-notch.is-buried  { background: var(--notch-buried); }
.item-notch.is-clipped { background: var(--notch-clipped); color: #241a00; }
.item-notch.is-hidden  { background: var(--notch-hidden); color: #241a00; }
