/* PLAKACIK — a darkroom for posters.

   The chrome is graphite and unlit so the proof sheet is the only bright
   object on screen: the same reason photo tools go dark, applied literally.
   One accent — safelight amber — and it only ever appears where the tool is
   telling you something (active state, trim, low resolution, export).

   The UI is set in the app's own bundled type: the wordmark is Archivo Black,
   the default poster face, and every number is Space Mono. Both ship with the
   renderer, and body copy falls to the system UI face, so the interface loads
   no font from anywhere but this server.

   Contrast: every text colour below is checked against the darkest and the
   lightest surface it lands on, to WCAG 2.1 AA (4.5:1 body, 3:1 for the lines
   that identify a control). Worst cases are noted next to each token. UI text
   has an 11px floor; only the badges overlaid on the poster preview go smaller,
   and those sit at 13:1 or better. */

/* Display + readout faces come from the bundled poster library (src/main/
   resources/web/fonts) under their own names, independent of the @font-face
   rules app.js injects for the poster preview. */
@font-face {
  font-family: "PR Display";
  src: url("/fonts/ArchivoBlack-Regular.ttf") format("truetype");
  font-weight: 400; font-display: swap;
}
@font-face {
  font-family: "PR Mono";
  src: url("/fonts/SpaceMono-Bold.ttf") format("truetype");
  font-weight: 700; font-display: swap;
}

:root {
  /* surfaces, darkest to lightest */
  --void:     #0a0a0b;
  --panel:    #121214;
  --raise:    #191a1d;
  --raise-2:  #232429;
  --menu:     #17181b;

  /* lines. --hair/--hair-up are decorative separators; --edge is reserved for
     anything whose job is to show you where a control is (3.1:1 on --panel) */
  --hair:    rgba(255, 255, 255, 0.075);
  --hair-up: rgba(255, 255, 255, 0.17);
  --edge:    rgba(255, 255, 255, 0.34);

  /* type — worst-case ratio against the lightest surface each one is used on */
  --text:  #eceae6;   /* 12.9:1 on --raise-2 */
  --dim:   #b0aca2;   /*  6.8:1 on --raise-2 */
  --faint: #949086;   /*  4.9:1 on --raise-2 */

  /* safelight — 5.3:1 on --raise, 5.7:1 on --panel, 6.0:1 on --void */
  --sl:      #ff4e22;
  --sl-lit:  #ff7043;
  --sl-wash: rgba(255, 78, 34, 0.13);
  --sl-ink:  #140d09;  /* 5.8:1 on --sl, 7.0:1 on --sl-lit */

  /* ink drawn *on* the proof sheet, which stays light whatever the chrome does */
  --sheet-ink: #16130f;

  /* The interface face is the reader's own system UI font. It used to be
     Schibsted Grotesk off the Google Fonts CDN, which sent every visitor's IP
     address to a third party in the US before they had touched anything — the
     exact arrangement LG München I fined in 3 O 17493/20. A system stack costs
     no request, no third party and no consent, and lands on a neutral grotesque
     on every platform anyway. */
  --ui: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
  --display: "PR Display", "Helvetica Neue", sans-serif;
  --mono: "PR Mono", ui-monospace, "SF Mono", Menlo, monospace;

  --ease: cubic-bezier(.2, .7, .3, 1);
}

* { box-sizing: border-box; margin: 0; }

html, body { height: 100%; }

body {
  font-family: var(--ui);
  font-size: 13px;
  color: var(--text);
  background: var(--void);
  overflow: hidden;
  -webkit-font-smoothing: antialiased;
}

/* film grain — keeps the large flat darks from banding. Kept low: at higher
   opacity it eats into the contrast of the small type underneath. */
body::after {
  content: "";
  position: fixed; inset: 0;
  z-index: 100;
  pointer-events: none;
  opacity: 0.03;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='140' height='140'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='2'/%3E%3C/filter%3E%3Crect width='140' height='140' filter='url(%23n)'/%3E%3C/svg%3E");
}

::selection { background: var(--sl); color: var(--sl-ink); }

/* Every link is styled here rather than per-component, because the UA's default
   link blue and visited purple are both unreadable on graphite — a link that
   misses its component rule has to still land on a palette colour. Colour comes
   from the text greys and the rule underneath carries the affordance; hover is
   the only place a link borrows the safelight. */
a {
  color: var(--text);
  text-decoration-color: var(--edge);
  text-decoration-thickness: 1px;
  text-underline-offset: 2px;
  transition: color 0.15s var(--ease), text-decoration-color 0.15s var(--ease);
}
a:hover { color: var(--sl-lit); text-decoration-color: var(--sl-lit); }

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

#sidebar::-webkit-scrollbar, #photobar::-webkit-scrollbar, #fontMenu::-webkit-scrollbar { width: 11px; }
#sidebar::-webkit-scrollbar-thumb, #photobar::-webkit-scrollbar-thumb, #fontMenu::-webkit-scrollbar-thumb {
  background: rgba(255, 255, 255, 0.2);
  border: 4px solid transparent;
  background-clip: content-box;
  border-radius: 99px;
}
#sidebar::-webkit-scrollbar-thumb:hover, #photobar::-webkit-scrollbar-thumb:hover { background: rgba(255, 255, 255, 0.34); background-clip: content-box; }

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

header {
  height: 62px;
  display: flex; align-items: center; justify-content: space-between;
  padding: 0 22px 0 24px;
  background: var(--panel);
  border-bottom: 1px solid var(--hair);
  position: relative; z-index: 10;
}

.brand { display: flex; align-items: baseline; gap: 11px; }

.wordmark {
  font-family: var(--display);
  font-size: 17px;
  letter-spacing: 0.015em;
  text-transform: uppercase;
}

/* the safelight: the only thing on the page that moves on its own */
.lamp {
  align-self: center;
  width: 7px; height: 7px; border-radius: 50%;
  background: var(--sl);
  box-shadow: 0 0 0 3px var(--sl-wash), 0 0 14px 1px rgba(255, 78, 34, 0.7);
  animation: breathe 3.4s ease-in-out infinite;
}
@keyframes breathe {
  50% { box-shadow: 0 0 0 5px var(--sl-wash), 0 0 22px 2px rgba(255, 78, 34, 0.45); opacity: 0.85; }
}

/* a readout, not a menu: every item here is a fact about the sheet, so nothing
   in it is clickable and nothing else belongs in it */
.specline { display: flex; align-items: center; }
.specline span {
  font-family: var(--mono);
  font-size: 11px; letter-spacing: 0.1em; text-transform: uppercase;
  color: var(--faint);
}
.specline span + span { position: relative; margin-left: 18px; }
.specline span + span::before {
  content: ""; position: absolute; left: -11px; top: 50%;
  width: 3px; height: 3px; margin-top: -1.5px;
  background: var(--hair-up);
}
.specline .live { color: var(--sl); }

/* ---------- drafts ---------- */

/* document-scope control, so it sits in the header rather than in either rail */
.drafts { position: relative; display: flex; align-items: stretch; gap: 8px; }

#draftBtn {
  display: flex; align-items: center; gap: 9px;
  font-family: var(--ui);
  font-size: 12.5px; font-weight: 500;
  color: var(--text);
  background: var(--raise);
  border: 1px solid var(--edge);
  border-radius: 5px;
  padding: 8px 12px;
  min-width: 210px;
  cursor: pointer;
  transition: border-color 0.15s, background 0.15s;
}
#draftBtn:hover { border-color: rgba(255, 255, 255, 0.55); background: var(--raise-2); }
#draftBtn[aria-expanded="true"] { border-color: var(--sl); box-shadow: 0 0 0 3px var(--sl-wash); }
#draftName { flex: 1; text-align: left; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }

/* a stacked-sheets mark */
.dIcon {
  flex: none;
  width: 11px; height: 13px;
  border: 1.5px solid var(--dim);
  border-radius: 1px;
  position: relative;
}
.dIcon::before {
  content: ""; position: absolute;
  left: 1.5px; top: -4px; right: -3px; height: 3px;
  border: 1.5px solid var(--dim); border-bottom: 0;
  border-radius: 1px 1px 0 0;
}
#draftBtn:hover .dIcon, #draftBtn:hover .dIcon::before { border-color: var(--text); }

/* save state as a button: loud while there are edits to keep, a quiet disabled
   "Saved" once they're in the draft, absent while there is nothing at all */
#saveBtn {
  flex: none;
  font-family: var(--ui);
  font-size: 11px; font-weight: 700;
  letter-spacing: 0.1em; text-transform: uppercase;
  color: var(--sl-ink); background: var(--sl);
  border: 1px solid transparent;
  border-radius: 5px;
  padding: 0 14px;
  cursor: pointer;
  transition: background 0.15s, color 0.15s, border-color 0.15s;
}
#saveBtn:hover:not(:disabled) { background: var(--sl-lit); }
#saveBtn:disabled {
  color: var(--faint); background: transparent;
  border-color: var(--edge);
  cursor: default;
}
#saveBtn[hidden] { display: none; }

.dCaret {
  flex: none;
  width: 5px; height: 5px; margin-top: -3px;
  border-right: 1.5px solid var(--dim); border-bottom: 1.5px solid var(--dim);
  transform: rotate(45deg);
}

#draftMenu {
  position: absolute; z-index: 30;
  left: 0; top: calc(100% + 6px);
  width: 320px;
  padding: 5px;
  background: var(--menu);
  border: 1px solid var(--edge);
  border-radius: 6px;
  box-shadow: 0 18px 44px rgba(0, 0, 0, 0.7), 0 2px 6px rgba(0, 0, 0, 0.5);
  max-height: 62vh; overflow-y: auto;
  animation: drop 0.16s var(--ease);
}
#draftMenu[hidden] { display: none; }

.dEmpty {
  padding: 12px 10px;
  font-size: 11.5px;
  color: var(--faint);
  text-align: center;
}

.dRow { display: flex; align-items: stretch; gap: 2px; border-radius: 4px; }
.dRow:hover { background: rgba(255, 255, 255, 0.07); }
.dRow.on { background: var(--sl-wash); box-shadow: inset 0 0 0 1px rgba(255, 78, 34, 0.45); }

.dOpen {
  flex: 1; min-width: 0;
  display: flex; flex-direction: column; align-items: flex-start; gap: 3px;
  font-family: var(--ui);
  background: transparent; border: 0; border-radius: 4px;
  padding: 9px 10px;
  cursor: pointer;
  text-align: left;
}
.dLabel {
  font-size: 12.5px; font-weight: 600; color: var(--text);
  max-width: 100%; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.dTime { font-family: var(--mono); font-size: 11px; color: var(--faint); }

.dRm {
  flex: none; width: 30px;
  background: transparent; border: 0; border-radius: 4px;
  color: var(--faint);
  font-size: 15px; line-height: 1;
  cursor: pointer;
  transition: color 0.14s, background 0.14s;
}
.dRm:hover { color: var(--sl-ink); background: var(--sl); }

.dSep { height: 1px; background: var(--hair); margin: 5px 4px; }

.dAction {
  display: block; width: 100%;
  font-family: var(--ui);
  font-size: 11.5px; font-weight: 600;
  color: var(--dim);
  background: transparent; border: 0; border-radius: 4px;
  padding: 9px 10px;
  text-align: left;
  cursor: pointer;
  transition: color 0.14s, background 0.14s;
}
.dAction:hover { color: var(--text); background: rgba(255, 255, 255, 0.07); }
.dAction.dQuiet { color: var(--faint); }

.dNew { display: flex; gap: 5px; padding: 4px; }
.dNew input {
  flex: 1; min-width: 0;
  font-family: var(--ui);
  font-size: 12px;
  color: var(--text);
  background: var(--raise);
  border: 1px solid var(--edge);
  border-radius: 4px;
  padding: 8px 10px;
  outline: none;
}
.dNew input::placeholder { color: var(--faint); }
.dNew input:focus { border-color: var(--sl); box-shadow: 0 0 0 3px var(--sl-wash); }
.dNew button {
  flex: none;
  font-family: var(--ui);
  font-size: 11px; font-weight: 700;
  letter-spacing: 0.1em; text-transform: uppercase;
  color: var(--sl-ink); background: var(--sl);
  border: 0; border-radius: 4px;
  padding: 0 14px;
  cursor: pointer;
  transition: background 0.15s;
}
.dNew button:hover { background: var(--sl-lit); }

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

main { display: flex; height: calc(100% - 62px); }

#sidebar, #photobar {
  flex: none;
  background: var(--panel);
  display: flex; flex-direction: column;
  overflow-y: auto; overflow-x: hidden;
  position: relative; z-index: 5;
}
#sidebar { width: 344px; border-right: 1px solid var(--hair); }
#photobar { width: 274px; border-left: 1px solid var(--hair); }

.panel {
  padding: 20px 24px 24px;
  border-bottom: 1px solid var(--hair);
  animation: rise 0.55s calc(var(--i) * 70ms) var(--ease) backwards;
}
@keyframes rise { from { opacity: 0; transform: translateY(12px); } }

.panel h2 {
  display: flex; align-items: center; gap: 10px;
  font-size: 11px; font-weight: 600;
  letter-spacing: 0.18em; text-transform: uppercase;
  color: var(--text);
  margin-bottom: 16px;
}
.panel h2 .num {
  font-family: var(--mono);
  font-size: 11px; letter-spacing: 0.04em;
  color: var(--sl);
}
.panel h2 i { flex: 1; height: 1px; background: var(--hair); }

/* ---------- text fields ---------- */

input[type=text] {
  width: 100%;
  font-family: var(--ui);
  font-size: 13px;
  color: var(--text);
  background: var(--raise);
  border: 1px solid var(--edge);
  border-radius: 4px;
  padding: 11px 12px;
  margin-bottom: 8px;
  outline: none;
  transition: border-color 0.16s, background 0.16s, box-shadow 0.16s;
}
input[type=text]:hover { border-color: rgba(255, 255, 255, 0.5); }
input[type=text]:focus {
  border-color: var(--sl);
  background: #1d1e21;
  box-shadow: 0 0 0 3px var(--sl-wash);
}
#title { font-weight: 700; letter-spacing: 0.18em; text-transform: uppercase; font-size: 14px; }
#subtitle { font-size: 12.5px; letter-spacing: 0.04em; margin-bottom: 0; }
input::placeholder { color: var(--faint); letter-spacing: inherit; }

/* ---------- control rows ---------- */

.layoutRow {
  display: flex; align-items: center; gap: 12px;
  margin-top: 12px;
}
.lbl {
  flex: none; width: 44px;
  font-size: 11px; font-weight: 600;
  letter-spacing: 0.1em; text-transform: uppercase;
  color: var(--dim);
}

/* segmented */
.seg {
  flex: 1;
  display: flex;
  gap: 2px;
  padding: 2px;
  background: var(--raise);
  border: 1px solid var(--edge);
  border-radius: 5px;
}
.seg button {
  flex: 1;
  font-family: var(--ui);
  font-size: 11px; font-weight: 600;
  letter-spacing: 0.08em; text-transform: uppercase;
  color: var(--faint);
  background: transparent;
  border: 0; border-radius: 3px;
  padding: 7px 0;
  cursor: pointer;
  transition: background 0.18s var(--ease), color 0.18s;
}
.seg button:hover { color: var(--text); background: rgba(255, 255, 255, 0.06); }
.seg button.active {
  background: var(--raise-2);
  color: var(--text);
  box-shadow: inset 0 0 0 1px var(--edge), 0 1px 3px rgba(0, 0, 0, 0.5);
}

/* ---------- font picker ---------- */

/* names render in their own face, which a native <select> cannot do */
.fontPicker { flex: 1; position: relative; min-width: 0; }
.fontPicker::after {
  content: "";
  position: absolute; right: 11px; top: 50%;
  width: 5px; height: 5px; margin-top: -4px;
  border-right: 1.5px solid var(--dim); border-bottom: 1.5px solid var(--dim);
  transform: rotate(45deg);
  pointer-events: none;
  transition: border-color 0.15s;
}
.fontPicker:hover::after { border-color: var(--text); }
#fontBtn {
  width: 100%;
  font-size: 14px;
  color: var(--text);
  background: var(--raise);
  border: 1px solid var(--edge);
  border-radius: 5px;
  padding: 8px 26px 8px 11px;
  text-align: left;
  line-height: 1.35;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  outline: none;
  cursor: pointer;
  transition: border-color 0.15s, background 0.15s;
}
#fontBtn:hover { border-color: rgba(255, 255, 255, 0.5); }
#fontBtn[aria-expanded="true"] { border-color: var(--sl); box-shadow: 0 0 0 3px var(--sl-wash); }

#fontMenu {
  position: absolute; z-index: 20;
  left: 0; right: 0; top: calc(100% + 5px);
  padding: 4px;
  background: var(--menu);
  border: 1px solid var(--edge);
  border-radius: 6px;
  box-shadow: 0 18px 44px rgba(0, 0, 0, 0.7), 0 2px 6px rgba(0, 0, 0, 0.5);
  max-height: 306px; overflow-y: auto;
  animation: drop 0.16s var(--ease);
}
@keyframes drop { from { opacity: 0; transform: translateY(-5px); } }
#fontMenu[hidden] { display: none; }
#fontMenu button {
  display: block; width: 100%;
  font-size: 16px;
  color: var(--text);
  background: transparent;
  border: 0; border-radius: 4px;
  padding: 9px 10px;
  text-align: left;
  line-height: 1.3;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  cursor: pointer;
  outline: none;
}
#fontMenu button:hover, #fontMenu button:focus { background: rgba(255, 255, 255, 0.09); }
#fontMenu button[aria-selected="true"] { background: var(--sl); color: var(--sl-ink); }

/* ---------- sliders ---------- */

.layoutRow input[type=range] {
  flex: 1; min-width: 0;
  -webkit-appearance: none; appearance: none;
  height: 18px;
  background: transparent;
  cursor: pointer;
}
/* The unfilled track stays dark so the amber fill reads 4.4:1 against it; the
   1px ring is what carries the 3:1 that makes the control identifiable. A
   light track would satisfy the ring but flatten the fill to 1.8:1. */
.layoutRow input[type=range]::-webkit-slider-runnable-track {
  height: 4px; border-radius: 2px;
  background: linear-gradient(to right,
    var(--sl) 0 var(--p, 26%), rgba(255, 255, 255, 0.10) var(--p, 26%) 100%);
  box-shadow: 0 0 0 1px var(--edge);
}
.layoutRow input[type=range]::-webkit-slider-thumb {
  -webkit-appearance: none;
  width: 14px; height: 14px; margin-top: -5px;
  border-radius: 50%;
  background: var(--text);
  box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.7), 0 2px 5px rgba(0, 0, 0, 0.6);
  transition: transform 0.14s var(--ease), box-shadow 0.14s;
}
.layoutRow input[type=range]:hover::-webkit-slider-thumb { transform: scale(1.18); }
.layoutRow input[type=range]:active::-webkit-slider-thumb { box-shadow: 0 0 0 5px var(--sl-wash), 0 0 0 1px rgba(0, 0, 0, 0.7); }
.layoutRow input[type=range]::-moz-range-track { height: 4px; border-radius: 2px; background: rgba(255, 255, 255, 0.10); box-shadow: 0 0 0 1px var(--edge); }
.layoutRow input[type=range]::-moz-range-progress { height: 4px; border-radius: 2px; background: var(--sl); }
.layoutRow input[type=range]::-moz-range-thumb { width: 14px; height: 14px; border: 0; border-radius: 50%; background: var(--text); }

.val {
  flex: none; width: 42px;
  text-align: right;
  font-family: var(--mono);
  font-size: 11px; letter-spacing: 0.01em;
  color: var(--dim);
  font-variant-numeric: tabular-nums;
}

/* ---------- ink & paper ---------- */

.inkRow { display: flex; gap: 12px; margin-top: 16px; }
.inkField {
  flex: 1;
  display: flex; align-items: center; gap: 8px;
  cursor: pointer;
}
.inkField .lbl { width: auto; }
.swatch {
  flex: none;
  width: 32px; height: 24px;
  border-radius: 3px;
  overflow: hidden;
  box-shadow: inset 0 0 0 1px var(--edge), 0 1px 3px rgba(0, 0, 0, 0.5);
  transition: box-shadow 0.15s, transform 0.15s var(--ease);
}
.inkField:hover .swatch { transform: translateY(-1px); box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.6), 0 3px 8px rgba(0, 0, 0, 0.6); }
.swatch input[type=color] {
  -webkit-appearance: none; appearance: none;
  display: block; width: 100%; height: 100%;
  border: 0; padding: 0; background: none;
  cursor: pointer;
}
.swatch input[type=color]::-webkit-color-swatch-wrapper { padding: 0; }
.swatch input[type=color]::-webkit-color-swatch { border: 0; }
.hex {
  font-family: var(--mono);
  font-size: 11px; letter-spacing: 0.02em;
  color: var(--faint);
}

/* ---------- template cards ---------- */

#templateCards { display: grid; grid-template-columns: repeat(3, 1fr); gap: 9px; }

.tcard {
  background: var(--raise);
  border: 1px solid var(--edge);
  border-radius: 5px;
  padding: 9px 8px 8px;
  cursor: pointer;
  text-align: center;
  transition: transform 0.18s var(--ease), border-color 0.18s, background 0.18s, box-shadow 0.18s;
}
.tcard:hover {
  transform: translateY(-2px);
  border-color: rgba(255, 255, 255, 0.55);
  background: var(--raise-2);
  box-shadow: 0 8px 18px rgba(0, 0, 0, 0.55);
}
.tcard.active {
  border-color: var(--sl);
  background: rgba(255, 78, 34, 0.08);
  box-shadow: 0 0 0 1px var(--sl), 0 8px 22px -8px rgba(255, 78, 34, 0.5);
}
.tcard svg { width: 100%; display: block; }
.tcard svg .sheet { fill: rgba(255, 255, 255, 0.04); stroke: rgba(255, 255, 255, 0.28); }
.tcard svg .c { fill: rgba(255, 255, 255, 0.38); transition: fill 0.18s; }
.tcard svg .tbar { stroke: rgba(255, 255, 255, 0.5); }
.tcard:hover svg .c { fill: rgba(255, 255, 255, 0.5); }
.tcard.active svg .c { fill: var(--sl); }
.tcard.active svg .tbar { stroke: var(--sl-lit); }
.tcard .tname {
  font-size: 11px; font-weight: 600; letter-spacing: 0.02em;
  color: var(--faint);
  margin-top: 8px;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.tcard.active .tname { color: var(--text); }
.tcard .tcount { font-family: var(--mono); font-size: 11px; color: var(--faint); margin-top: 3px; }

/* ---------- dropzone ---------- */

#dropzone {
  display: flex; flex-direction: column; align-items: center; gap: 8px;
  padding: 20px 14px 18px;
  border: 1px dashed var(--edge);
  border-radius: 6px;
  background: var(--raise);
  text-align: center;
  cursor: pointer;
  transition: border-color 0.18s, background 0.18s, transform 0.18s var(--ease);
}
#dropzone:hover { border-color: rgba(255, 255, 255, 0.55); background: var(--raise-2); }
#dropzone strong { font-size: 12.5px; font-weight: 600; color: var(--text); letter-spacing: 0.01em; }
#dropzone .dzSub { font-size: 11.5px; line-height: 1.5; color: var(--faint); }
/* aperture-ish mark, drawn rather than shipped as an asset */
#dropzone .dzIcon {
  width: 22px; height: 22px; border-radius: 50%;
  border: 1.5px solid var(--dim);
  position: relative;
  transition: border-color 0.18s, transform 0.3s var(--ease);
}
#dropzone .dzIcon::before, #dropzone .dzIcon::after {
  content: ""; position: absolute; background: var(--dim);
  left: 50%; top: 50%;
  transition: background 0.18s;
}
#dropzone .dzIcon::before { width: 9px; height: 1.5px; margin: -0.75px 0 0 -4.5px; }
#dropzone .dzIcon::after { width: 1.5px; height: 9px; margin: -4.5px 0 0 -0.75px; }
#dropzone:hover .dzIcon { border-color: var(--text); transform: rotate(90deg); }
#dropzone:hover .dzIcon::before, #dropzone:hover .dzIcon::after { background: var(--text); }
#dropzone.over {
  border-color: var(--sl);
  border-style: solid;
  background: rgba(255, 78, 34, 0.1);
  transform: scale(1.015);
}
#dropzone.over .dzIcon { border-color: var(--sl); transform: rotate(90deg) scale(1.1); }
#dropzone.over .dzIcon::before, #dropzone.over .dzIcon::after { background: var(--sl); }
/* busy: the aperture ring becomes the spinner — one edge lit, the plus hidden */
#dropzone.uploading {
  pointer-events: none;
  border-style: solid;
  background: var(--raise-2);
}
#dropzone.uploading .dzIcon {
  border-color: var(--edge);
  border-top-color: var(--sl);
  animation: whirl 0.8s linear infinite;
}
#dropzone.uploading .dzIcon::before, #dropzone.uploading .dzIcon::after { display: none; }
@keyframes whirl { to { transform: rotate(360deg); } }

/* ---------- tray ---------- */

#tray {
  margin-top: 12px;
  display: grid; grid-template-columns: repeat(3, 1fr); gap: 7px;
}

.tphoto {
  position: relative;
  aspect-ratio: 1;
  background: var(--raise-2);
  border-radius: 4px;
  cursor: grab;
  overflow: hidden;
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.5);
  animation: pop 0.28s var(--ease) backwards;
  transition: transform 0.16s var(--ease), box-shadow 0.16s;
}
@keyframes pop { from { opacity: 0; transform: scale(0.86); } }
.tphoto:hover { transform: translateY(-2px); box-shadow: 0 6px 14px rgba(0, 0, 0, 0.65); }
.tphoto:active { cursor: grabbing; }
.tphoto img { width: 100%; height: 100%; object-fit: cover; display: block; }
/* spare photos read as inactive by desaturation, not by dimming the label */
.tphoto.unused img { opacity: 0.45; filter: grayscale(1); }

/* rights + retention small print under the dropzone */
.legal {
  margin: 8px 0 0;
  font-size: 10px; line-height: 1.55;
  color: var(--faint);
}
/* one bright word in a paragraph set at the faintest grey: --dim sat too close
   to the surrounding copy to read as the clickable part at 10px */
.legal a { color: var(--text); text-decoration-color: var(--hair-up); }
.legal a:hover { color: var(--sl-lit); text-decoration-color: var(--sl-lit); }

/* destructive but rare: a quiet text button that only turns red on approach */
#purgeBtn {
  margin-top: 10px;
  font-family: var(--ui);
  font-size: 10.5px; font-weight: 600;
  letter-spacing: 0.06em; text-transform: uppercase;
  color: var(--faint);
  background: transparent;
  border: 1px solid var(--edge);
  border-radius: 4px;
  padding: 7px 10px;
  cursor: pointer;
  transition: color 0.15s, border-color 0.15s, background 0.15s;
}
#purgeBtn:hover { color: var(--sl); border-color: rgba(255, 78, 34, 0.55); background: var(--sl-wash); }

/* expired photograph in the tray: filename on a dashed tile instead of pixels */
.tphoto.missing { border: 1px dashed rgba(255, 78, 34, 0.55); background: var(--raise); }
.tphoto.missing .ghost {
  width: 100%; height: 100%;
  display: flex; align-items: center; justify-content: center;
  padding: 4px;
}
.tphoto.missing .ghostName {
  font-family: var(--mono);
  font-size: 9.5px;
  color: var(--dim);
  text-align: center;
  overflow: hidden; text-overflow: ellipsis;
  display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical;
  word-break: break-all;
}

.tphoto .slot {
  position: absolute; left: 4px; top: 4px;
  min-width: 18px; height: 18px; padding: 0 4px;
  border-radius: 3px;
  background: rgba(8, 7, 6, 0.88);
  color: var(--text);
  font-family: var(--mono);
  font-size: 10px;
  display: flex; align-items: center; justify-content: center;
}

.tphoto .rm {
  position: absolute; right: 4px; top: 4px;
  width: 18px; height: 18px;
  border: 0; border-radius: 50%;
  background: var(--sl); color: var(--sl-ink);
  font-size: 12px; line-height: 1; font-weight: 700;
  display: flex; align-items: center; justify-content: center;
  cursor: pointer;
  opacity: 0; transform: scale(0.7);
  transition: opacity 0.14s, transform 0.14s var(--ease);
}
.tphoto:hover .rm, .tphoto .rm:focus-visible { opacity: 1; transform: scale(1); }

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

footer.panel { margin-top: auto; border-bottom: 0; }

/* outer ring rather than inset: .meter clips its fill, and children paint over
   an inset shadow, which would hide the outline exactly where the bar is full */
.meter {
  height: 4px; border-radius: 2px;
  background: rgba(255, 255, 255, 0.10);
  box-shadow: 0 0 0 1px var(--edge);
  overflow: hidden;
  margin-bottom: 13px;
}
.meter i {
  display: block; height: 100%; width: 0;
  background: var(--dim);
  border-radius: 2px;
  transition: width 0.4s var(--ease), background 0.3s, box-shadow 0.3s;
}
.meter.ready i { background: var(--sl); box-shadow: 0 0 12px rgba(255, 78, 34, 0.7); }

#fillStatus {
  font-size: 12.5px; line-height: 1.5;
  color: var(--dim);
  margin-bottom: 12px;
}
#fillStatus b { color: var(--text); font-weight: 600; font-variant-numeric: tabular-nums; }

#exportBtn {
  width: 100%;
  position: relative; overflow: hidden;
  font-family: var(--ui);
  font-size: 12px; font-weight: 700;
  letter-spacing: 0.13em; text-transform: uppercase;
  padding: 15px 0;
  border: 0; border-radius: 5px;
  cursor: pointer;
  background: var(--sl); color: var(--sl-ink);
  transition: background 0.18s, box-shadow 0.18s, transform 0.12s var(--ease), color 0.18s;
}
#exportBtn:hover:not(:disabled) {
  background: var(--sl-lit);
  transform: translateY(-1px);
  box-shadow: 0 8px 24px -8px rgba(255, 78, 34, 0.75);
}
#exportBtn:active:not(:disabled) { transform: translateY(0); }
/* disabled still has to be readable — it names the action you are waiting for */
#exportBtn:disabled {
  background: var(--raise-2);
  color: var(--faint);
  box-shadow: inset 0 0 0 1px var(--edge);
  cursor: not-allowed;
}
/* a transform sweep rather than a scrolling gradient — background-position on a
   diagonal repeating gradient can't loop seamlessly at an arbitrary angle */
#exportBtn.busy::after {
  content: "";
  position: absolute; inset: 0;
  background: linear-gradient(100deg, transparent 18%, rgba(255, 255, 255, 0.45) 50%, transparent 82%);
  transform: translateX(-100%);
  animation: sweep 1.15s linear infinite;
}
@keyframes sweep { to { transform: translateX(100%); } }

.hint {
  margin-top: 12px;
  font-size: 11.5px; line-height: 1.55;
  color: var(--faint);
}

/* The only navigation in the app. It sits at the foot of the rail because a
   privacy notice has to stay reachable from every screen, and quiet because
   nobody came here to read it — the rule underneath is what marks it as a link
   without borrowing the safelight, which is reserved for the tool talking. */
.legalLink {
  display: inline-block;
  margin-top: 14px;
  font-size: 11px;
  color: var(--dim);
  text-decoration: none;
  border-bottom: 1px solid var(--edge);
  padding-bottom: 1px;
  transition: color 0.15s var(--ease), border-color 0.15s var(--ease);
}
.legalLink:hover { color: var(--sl-lit); border-bottom-color: var(--sl-lit); }

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

#stage {
  flex: 1;
  display: flex; flex-direction: column;
  align-items: center; justify-content: center;
  padding: 44px 48px 26px;
  min-width: 0;
  position: relative;
  /* enlarger light overhead, safelight bleeding in from the top-left corner */
  background:
    radial-gradient(78% 62% at 50% 40%, rgba(255, 255, 255, 0.052), transparent 72%),
    radial-gradient(46% 40% at 2% -4%, rgba(255, 78, 34, 0.11), transparent 70%),
    var(--void);
}
/* bench grid, barely there */
#stage::before {
  content: "";
  position: absolute; inset: 0;
  pointer-events: none;
  background-image: radial-gradient(rgba(255, 255, 255, 0.055) 1px, transparent 1px);
  background-size: 26px 26px;
  mask-image: radial-gradient(70% 60% at 50% 45%, #000 20%, transparent 78%);
  -webkit-mask-image: radial-gradient(70% 60% at 50% 45%, #000 20%, transparent 78%);
}

#proof {
  position: relative;
  animation: settle 0.8s 0.2s var(--ease) backwards;
}
@keyframes settle { from { opacity: 0; transform: translateY(16px) scale(0.985); } }

#page {
  position: relative;
  background: #fff;
  box-shadow:
    0 0 0 1px rgba(255, 255, 255, 0.07),
    0 2px 8px rgba(0, 0, 0, 0.55),
    0 34px 90px -22px rgba(0, 0, 0, 0.9),
    0 0 140px -40px rgba(255, 140, 90, 0.28);
}

/* trim outline — solid safelight rather than a tint: at 45% alpha it was
   1.8:1 against white paper, under the 3:1 a guide line needs */
#page::after {
  content: "";
  position: absolute;
  inset: var(--bpx);
  border: 1px dashed var(--sl);
  pointer-events: none;
}

/* crop marks, drawn on the sheet (positions set inline from JS) */
.mark { position: absolute; background: var(--sheet-ink); opacity: 0.85; }

/* dimension callouts, drawn on the bench */
.dim {
  position: absolute;
  font-family: var(--mono);
  font-size: 11px; letter-spacing: 0.1em;
  color: var(--faint);
  white-space: nowrap;
}
.dim::before, .dim::after {
  content: "";
  position: absolute; top: 50%;
  width: 46px; height: 1px;
  background: linear-gradient(to right, transparent, var(--hair-up));
}
.dim::before { right: calc(100% + 10px); }
.dim::after { left: calc(100% + 10px); transform: scaleX(-1); }
.dim-w { top: -36px; left: 50%; transform: translateX(-50%); }
.dim-h { left: -38px; top: 50%; transform: translate(-50%, -50%) rotate(-90deg); }

#proofCaption {
  margin-top: 26px;
  font-family: var(--mono);
  font-size: 11px; letter-spacing: 0.11em; text-transform: uppercase;
  color: var(--faint);
}

/* ---------- cells ---------- */

/* An empty cell is an editor placeholder, not part of the print, so it is
   allowed to look like one: the outline carries the 3:1 that makes the layout
   legible, and the hatch is left faint so it doesn't fight the number. */
.cell {
  position: absolute;
  overflow: hidden;
  background:
    repeating-linear-gradient(-45deg, transparent 0 6px, rgba(22, 19, 15, 0.035) 6px 7px),
    #f3f1ec;
  box-shadow: inset 0 0 0 1px rgba(22, 19, 15, 0.5);
}
.cell img {
  position: absolute;
  max-width: none;
  display: block;
  cursor: zoom-in;
  -webkit-user-drag: element;
}

.cell .idx {
  position: absolute; inset: 0;
  display: flex; align-items: center; justify-content: center;
  font-family: var(--mono);
  font-size: 13px; letter-spacing: 0.06em;
  color: rgba(22, 19, 15, 0.62);
  pointer-events: none;
}

/* badges sit on unpredictable photo content, so both carry their own opaque
   plate — 13:1 and 5.8:1 against it respectively */
.cell .zbadge {
  position: absolute; right: 5px; top: 5px;
  border-radius: 3px;
  background: rgba(8, 7, 6, 0.88);
  color: #fff;
  font-family: var(--mono);
  font-size: 10px; letter-spacing: 0.02em;
  padding: 3px 6px;
  pointer-events: none;
}

.cell .lowres {
  position: absolute; left: 5px; bottom: 5px;
  border-radius: 3px;
  background: var(--sl); color: var(--sl-ink);
  font-family: var(--mono);
  font-size: 10px; letter-spacing: 0.03em;
  padding: 3px 6px;
  pointer-events: none;
  box-shadow: 0 2px 8px rgba(255, 78, 34, 0.45);
}

.cell.droptarget {
  outline: 2px solid var(--sl);
  outline-offset: -2px;
  filter: brightness(1.06);
}

/* crop affordance: appears on hover so the poster stays clean while idle */
.cell .cellHint {
  position: absolute; left: 50%; bottom: 8px;
  transform: translate(-50%, 4px);
  border-radius: 3px;
  background: rgba(8, 7, 6, 0.88);
  color: #fff;
  font-family: var(--mono);
  font-size: 10px; letter-spacing: 0.03em;
  padding: 3px 7px;
  pointer-events: none;
  white-space: nowrap;
  opacity: 0;
  transition: opacity 0.15s, transform 0.15s;
}
.cell:hover .cellHint { opacity: 1; transform: translate(-50%, 0); }

/* expired photograph: the cell keeps its place in the layout but shows the
   filename it is waiting for instead of pixels */
.cell.missing {
  cursor: pointer;
  box-shadow: inset 0 0 0 1px rgba(255, 78, 34, 0.6);
}
.cell.missing .ghost {
  position: absolute; inset: 0;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 4px;
  padding: 6px;
  text-align: center;
  background: repeating-linear-gradient(-45deg, transparent 0 6px, rgba(255, 78, 34, 0.06) 6px 7px);
}
.cell.missing .ghostName {
  font-family: var(--mono);
  font-size: 11px;
  color: rgba(22, 19, 15, 0.75);
  max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.cell.missing .ghostHint {
  font-family: var(--mono);
  font-size: 9px; letter-spacing: 0.08em; text-transform: uppercase;
  color: var(--sl);
}

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

#confirmOverlay {
  position: fixed; inset: 0; z-index: 60;
  background: rgba(6, 5, 5, 0.76);
  backdrop-filter: blur(6px);
  display: flex; align-items: center; justify-content: center;
  animation: fade 0.18s ease;
}
#confirmOverlay[hidden] { display: none; }

#confirmPanel {
  background: var(--panel);
  border: 1px solid var(--edge);
  border-radius: 8px;
  box-shadow: 0 30px 90px rgba(0, 0, 0, 0.8);
  padding: 18px;
  width: min(88vw, 380px);
  animation: settle 0.3s var(--ease) backwards;
}
#confirmMsg {
  margin: 0 0 16px;
  font-size: 13px; line-height: 1.55;
  color: var(--text);
}
.confirmActions { display: flex; justify-content: flex-end; gap: 10px; }
.confirmActions button {
  font-family: var(--ui);
  font-size: 11.5px; font-weight: 700;
  letter-spacing: 0.1em; text-transform: uppercase;
  padding: 11px 16px;
  border: 0; border-radius: 5px;
  cursor: pointer;
  background: var(--sl); color: var(--sl-ink);
  transition: background 0.16s, color 0.16s, border-color 0.16s;
}
#confirmYes:hover { background: var(--sl-lit); }
.confirmActions button.ghost {
  background: transparent; color: var(--dim);
  border: 1px solid var(--edge);
}
.confirmActions button.ghost:hover { color: var(--text); border-color: rgba(255, 255, 255, 0.55); background: rgba(255, 255, 255, 0.06); }

/* ---------- crop overlay ---------- */

#cropOverlay {
  position: fixed; inset: 0; z-index: 50;
  background: rgba(6, 5, 5, 0.76);
  backdrop-filter: blur(6px);
  display: flex; align-items: center; justify-content: center;
  animation: fade 0.18s ease;
}
@keyframes fade { from { opacity: 0; } }
#cropOverlay[hidden] { display: none; }

#cropPanel {
  background: var(--panel);
  border: 1px solid var(--edge);
  border-radius: 8px;
  box-shadow: 0 30px 90px rgba(0, 0, 0, 0.8);
  padding: 16px 18px 15px;
  max-width: min(80vw, 1100px);
  animation: settle 0.3s var(--ease) backwards;
}

.cropHead {
  display: flex; justify-content: space-between; align-items: baseline;
  gap: 20px;
  margin-bottom: 13px;
}
#cropName { font-size: 13px; font-weight: 600; color: var(--text); }
#cropDpi {
  font-family: var(--mono);
  font-size: 11px; letter-spacing: 0.06em; text-transform: uppercase;
  color: var(--dim);
  padding: 5px 9px; border-radius: 3px;
  background: var(--raise);
}
/* #ffc043 is 9.9:1 on --raise; the amber at AA-safe lightness */
#cropDpi.warn { color: #ffc043; background: rgba(255, 192, 67, 0.12); }
#cropDpi.bad { color: var(--sl); background: var(--sl-wash); }

#cropStage {
  position: relative;
  line-height: 0;
  user-select: none;
  touch-action: none;
  border-radius: 4px;
  overflow: hidden;
}
#cropImg { display: block; max-width: min(76vw, 1050px); max-height: 62vh; }

/* The crop edge lands on unknown photo content, so it is banded light-on-dark:
   whichever the photo is, one of the two rings clears 3:1 against it. */
#cropRect {
  position: absolute;
  border: 1.5px solid rgba(255, 255, 255, 0.95);
  box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.6), 0 0 0 9999px rgba(6, 5, 5, 0.62);
  cursor: move;
}
/* rule-of-thirds guides */
#cropRect::before, #cropRect::after {
  content: ""; position: absolute; pointer-events: none;
  border: 0 solid rgba(255, 255, 255, 0.4);
}
#cropRect::before { inset: 0 33.33%; border-width: 0 1px; }
#cropRect::after { inset: 33.33% 0; border-width: 1px 0; }

#cropRect .h {
  position: absolute;
  width: 12px; height: 12px;
  border-radius: 2px;
  background: var(--sl);
  border: 1.5px solid #fff;
  box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.6), 0 1px 4px rgba(0, 0, 0, 0.5);
}
.h[data-c=tl] { left: -7px; top: -7px; cursor: nwse-resize; }
.h[data-c=tr] { right: -7px; top: -7px; cursor: nesw-resize; }
.h[data-c=bl] { left: -7px; bottom: -7px; cursor: nesw-resize; }
.h[data-c=br] { right: -7px; bottom: -7px; cursor: nwse-resize; }

.cropActions {
  display: flex; align-items: center; gap: 10px;
  margin-top: 15px;
}
.cropHint {
  flex: 1; text-align: center;
  font-family: var(--mono);
  font-size: 11px; letter-spacing: 0.06em;
  color: var(--faint);
}

.cropActions button {
  font-family: var(--ui);
  font-size: 11.5px; font-weight: 700;
  letter-spacing: 0.1em; text-transform: uppercase;
  padding: 12px 18px;
  border: 0; border-radius: 5px;
  cursor: pointer;
  background: var(--sl); color: var(--sl-ink);
  transition: background 0.16s, box-shadow 0.16s, color 0.16s, border-color 0.16s;
}
#cropApply:hover { background: var(--sl-lit); box-shadow: 0 8px 22px -8px rgba(255, 78, 34, 0.75); }
.cropActions button.ghost {
  background: transparent; color: var(--dim);
  border: 1px solid var(--edge);
}
.cropActions button.ghost:hover { color: var(--text); border-color: rgba(255, 255, 255, 0.55); background: rgba(255, 255, 255, 0.06); }

/* ---------- poster typography overlays ---------- */

/* family and colour are set inline from the selected bundled font; weight stays
   normal because each cut is single-weight (faux-bold would diverge from the
   embedded outlines) */
.ptitle, .psub {
  position: absolute;
  text-align: center;
  white-space: pre;
  font-weight: normal;
  text-transform: uppercase;
  pointer-events: none;
  line-height: 1;
}

/* ---------- narrower screens ---------- */

@media (max-width: 1440px) {
  #sidebar { width: 322px; }
  #photobar { width: 262px; }
  .panel { padding: 18px 20px 20px; }
  #stage { padding: 36px 40px 22px; }
}

/* the middle spec chips are redundant with the caption under the proof */
@media (max-width: 1320px) {
  #draftBtn { min-width: 168px; }
}
@media (max-width: 1180px) {
  .specline span:nth-child(2), .specline span:nth-child(3) { display: none; }
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }
}
