/* PIM Automation panel.
   Sibling to the PriceHunt terminal: same dark instrument vocabulary, same
   three faces, same radius and pill idiom. Its own accent, because lime is
   PriceHunt's signature and it collides with the green that means "ok" here.

   The part number is the headline. RCO-6000-RPL-2E16-P is the most
   characteristic thing in this product's world, so it is set as display type
   rather than as a label above the real title. */

:root {
  --bg:        #0a0b0d;
  --bg-grid:   rgba(255, 255, 255, 0.022);
  --panel:     #14161a;
  --panel-2:   #101216;
  --panel-hi:  #1b1e24;
  --line:      #23262d;
  --line-2:    #333843;

  --text:  #eef1f4;
  --muted: #8b929c;
  --faint: #5b6270;

  --accent:      #3ba9ff;
  --accent-dim:  #2b74ae;
  --accent-ink:  #04121e;
  --accent-glow: rgba(59, 169, 255, 0.20);

  --ok:      #4cd6a4;
  --ok-bg:   rgba(76, 214, 164, 0.12);
  --warn:    #f5b544;
  --warn-bg: rgba(245, 181, 68, 0.12);
  --fault:   #ff6a6a;
  --fault-bg: rgba(255, 106, 106, 0.11);

  --radius:    0.8rem;
  --radius-sm: 0.56rem;
  --gutter: clamp(1rem, 2.4vw, 2rem);
  --maxw: 72rem;

  --font-display: "Chakra Petch", system-ui, sans-serif;
  --font-body: "Archivo", system-ui, "Segoe UI", sans-serif;
  --font-mono: "JetBrains Mono", ui-monospace, "Cascadia Mono", Consolas, monospace;
}

* { box-sizing: border-box; }
::selection { background: var(--accent); color: var(--accent-ink); }

body {
  margin: 0;
  color: var(--text);
  font-family: var(--font-body);
  font-size: 0.94rem;
  line-height: 1.5;
  background:
    radial-gradient(66vw 28vw at 88% -10%, rgba(59, 169, 255, 0.06), transparent 60%),
    linear-gradient(var(--bg-grid) 1px, transparent 1px) 0 0 / 100% 2.1rem,
    linear-gradient(90deg, var(--bg-grid) 1px, transparent 1px) 0 0 / 2.1rem 100%,
    var(--bg);
  -webkit-font-smoothing: antialiased;
}

/* Film grain, so the flat panels read as a surface rather than as swatches. */
body::before {
  content: ""; position: fixed; inset: 0; pointer-events: none; z-index: 1; opacity: 0.5;
  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.85' numOctaves='2' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)' opacity='0.035'/%3E%3C/svg%3E");
}

a { color: var(--accent); text-decoration: none; }
a:hover { text-decoration: underline; text-underline-offset: 3px; }

main {
  position: relative;
  z-index: 2;
  max-width: var(--maxw);
  margin: 0 auto;
  padding: 1.75rem var(--gutter) 5rem;
}

section { margin-bottom: 2.5rem; }

/* --- nameplate ----------------------------------------------------------- */

.nameplate {
  position: sticky;
  top: 0;
  z-index: 30;
  display: flex;
  align-items: baseline;
  gap: 0.85rem;
  padding: 0.9rem var(--gutter);
  background: linear-gradient(180deg, rgba(10, 11, 13, 0.94), rgba(10, 11, 13, 0.74));
  backdrop-filter: blur(14px) saturate(1.1);
  border-bottom: 1px solid var(--line);
}

/* The hairline under the bar picks up the accent in the middle only, so the
   chrome has a light source rather than a border. */
.nameplate::after {
  content: ""; position: absolute; left: 0; right: 0; bottom: -1px; height: 1px;
  background: linear-gradient(90deg, transparent, var(--accent-glow) 30%, transparent 70%);
}

.nameplate .mark {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 1.05rem;
  letter-spacing: 0.5px;
  text-transform: uppercase;
  color: var(--text);
  text-decoration: none;
}

.nameplate .mark::first-letter { color: var(--accent); }
.nameplate .mark:hover { text-decoration: none; }

.nameplate .legend { margin: 0; }

/* The two halves of the app, plus a way back to the scrapers. Pushed right so
   the wordmark keeps the left edge to itself; the current place is marked by
   the accent underline rather than a filled pill, which at this size reads as
   a button and invites a second click. */
.nameplate .places {
  display: flex;
  gap: 0.15rem;
  margin-left: auto;
  flex-wrap: wrap;
}

.nameplate .places a {
  padding: 0.3rem 0.6rem;
  border-radius: 6px;
  font-size: 0.83rem;
  font-weight: 500;
  color: var(--muted);
  text-decoration: none;
  border-bottom: 2px solid transparent;
  white-space: nowrap;
}

.nameplate .places a:hover {
  color: var(--text);
  background: var(--panel-hi);
  text-decoration: none;
}

.nameplate .places a[aria-current="page"] {
  color: var(--text);
  border-bottom-color: var(--accent);
}

/* The scrapers are a separate tool behind the same front door, so its link is
   marked as leaving rather than styled like the two that stay here. */
.nameplate .places a.away::after {
  content: " \2197";
  color: var(--muted);
}

.nameplate .places + .ghost { margin-left: 0.4rem; }
.nameplate form { margin-left: 0.4rem; }

/* A tile that is the page you are on: same shape, accent edge. */
.tiles a[aria-current="page"] { border-color: var(--accent); }
.tiles a[aria-current="page"] .part { color: var(--accent); }

/* --- micro-labels -------------------------------------------------------- */

.legend {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: 0.15rem 0.5rem;
  font-family: var(--font-mono);
  font-size: 0.68rem;
  font-weight: 600;
  letter-spacing: 1.1px;
  text-transform: uppercase;
  color: var(--muted);
  margin: 0 0 0.75rem;
}

.legend .hint {
  font-weight: 500;
  letter-spacing: 0.3px;
  text-transform: none;
  color: var(--faint);
}

.part { font-family: var(--font-mono); }

.title {
  font-family: var(--font-mono);
  font-size: clamp(1.35rem, 3.2vw, 1.75rem);
  font-weight: 700;
  letter-spacing: -0.6px;
  margin: 0 0 0.35rem;
}

.note {
  color: var(--muted);
  font-size: 0.85rem;
  max-width: 62ch;
  margin: 0 0 0.75rem;
}

.note code {
  font-family: var(--font-mono);
  font-size: 0.8rem;
  color: var(--text);
  background: var(--panel-2);
  border: 1px solid var(--line);
  border-radius: 0.3rem;
  padding: 0.05rem 0.35rem;
}

.lead { margin: 0 0 1.5rem; font-size: 1rem; }

/* The part number and the things you can do to it, on one line. */
.masthead {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 0.75rem 1.25rem;
  margin-bottom: 1.75rem;
}

.masthead .title { margin: 0; }

.actions { display: flex; flex-wrap: wrap; gap: 0.6rem; align-items: center; }
.actions .ghost, .actions .primary { margin: 0; }

/* --- status strip -------------------------------------------------------- */

.strip {
  position: relative;
  z-index: 2;
  list-style: none;
  margin: 0;
  padding: 0;
  background: var(--panel);
  border-bottom: 1px solid var(--line);
}

.msg {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  max-width: var(--maxw);
  margin: 0 auto;
  padding: 0.7rem var(--gutter);
  font-size: 0.88rem;
}

.msg + .msg { border-top: 1px solid var(--line); }
.msg.ok    { color: var(--ok); }
.msg.warn  { color: var(--warn); }
.msg.fault { color: var(--fault); }

.led {
  flex: none;
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--faint);
}

.ok    .led, .led.ok    { background: var(--ok);    box-shadow: 0 0 8px var(--ok-bg); }
.warn  .led, .led.warn  { background: var(--warn);  box-shadow: 0 0 8px var(--warn-bg); }
.fault .led, .led.fault { background: var(--fault); box-shadow: 0 0 8px var(--fault-bg); }

/* --- the bay: the one place this panel raises its voice ------------------ */

.bays {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(17rem, 1fr));
  gap: 1.25rem;
  margin-bottom: 2.5rem;
}

.bays > form { min-width: 0; }

.bay {
  position: relative;
  display: block;
  padding: 2.4rem 1.5rem;
  text-align: center;
  background:
    linear-gradient(180deg, var(--panel), var(--panel-2));
  border: 1px dashed var(--line-2);
  border-radius: var(--radius);
  cursor: pointer;
  overflow: hidden;
  transition: border-color 140ms ease, box-shadow 140ms ease, background 140ms ease;
}

.bay.slim { padding: 1.6rem 1.5rem; }

.bay input[type="file"] {
  position: absolute;
  width: 1px;
  height: 1px;
  opacity: 0;
}

.bay .prompt {
  display: block;
  font-weight: 500;
  color: var(--text);
}

.bay .prompt .part { color: var(--accent); }

.bay .sub {
  display: block;
  margin-top: 0.3rem;
  font-family: var(--font-mono);
  font-size: 0.7rem;
  letter-spacing: 0.4px;
  text-transform: uppercase;
  color: var(--faint);
}

.bay:hover {
  border-color: var(--accent-dim);
  background: linear-gradient(180deg, var(--panel-hi), var(--panel));
}

/* Drag-over is the one orchestrated motion in the panel: the bay goes solid,
   lights, and a single sweep crosses it. It answers an action, so it is not
   the ambient kind of animation that reads as decoration. */
.bay.energised {
  border-style: solid;
  border-color: var(--accent);
  background: linear-gradient(180deg, #0d1a26, var(--panel-2));
  box-shadow: 0 0 0 3px var(--accent-glow), inset 0 0 40px rgba(59, 169, 255, 0.10);
}

.bay.energised::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(100deg, transparent 35%, var(--accent-glow) 50%, transparent 65%);
  animation: sweep 900ms ease-in-out infinite;
}

@keyframes sweep {
  from { transform: translateX(-100%); }
  to   { transform: translateX(100%); }
}

.bay.sending { border-color: var(--line-2); opacity: 0.55; }
.bay.sending .prompt::after { content: " — sending…"; color: var(--muted); }

.bay input[type="file"]:focus-visible + .prompt {
  outline: 2px solid var(--accent);
  outline-offset: 4px;
  border-radius: 0.3rem;
}

.replace {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  margin-top: 0.75rem;
  font-size: 0.82rem;
  color: var(--muted);
  cursor: pointer;
}

.replace input {
  appearance: none;
  width: 1.05rem;
  height: 1.05rem;
  flex: none;
  border: 1.5px solid var(--line-2);
  border-radius: 0.3rem;
  background: var(--panel-2);
  display: grid;
  place-content: center;
  cursor: pointer;
  transition: background 120ms, border-color 120ms;
}

.replace input:checked { background: var(--accent); border-color: var(--accent); }

.replace input:checked::after {
  content: "";
  width: 0.28rem;
  height: 0.55rem;
  border: solid var(--accent-ink);
  border-width: 0 2px 2px 0;
  transform: rotate(45deg) translateY(-1px);
}

/* --- file listing -------------------------------------------------------- */

.spec {
  width: 100%;
  border-collapse: collapse;
  background: linear-gradient(180deg, var(--panel), var(--panel-2));
  border: 1px solid var(--line);
  border-radius: var(--radius);
  overflow: hidden;
}

.spec td {
  padding: 0.6rem 1rem;
  border-bottom: 1px solid var(--line);
  vertical-align: baseline;
}

.spec tr:last-child td { border-bottom: 0; }
.spec tr:hover td { background: var(--panel-hi); }

.spec .name {
  font-family: var(--font-mono);
  font-size: 0.82rem;
  word-break: break-all;
}

.spec .name .led { margin-right: 0.6rem; display: inline-block; }

.spec .flag {
  font-family: var(--font-mono);
  font-size: 0.62rem;
  font-weight: 600;
  letter-spacing: 0.4px;
  text-transform: uppercase;
  color: var(--warn);
  background: var(--warn-bg);
  border-radius: 999px;
  padding: 0.15rem 0.5rem;
  margin-left: 0.6rem;
  white-space: nowrap;
}

.spec .meta {
  font-family: var(--font-mono);
  font-size: 0.75rem;
  color: var(--faint);
  text-align: right;
  white-space: nowrap;
  padding-left: 1rem;
}

/* --- series tiles -------------------------------------------------------- */

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

.tiles a {
  position: relative;
  display: block;
  padding: 0.9rem 1rem;
  background: linear-gradient(180deg, var(--panel), var(--panel-2));
  border: 1px solid var(--line);
  border-radius: var(--radius);
  color: var(--text);
  text-decoration: none;
  overflow: hidden;
  transition: border-color 160ms, transform 100ms, box-shadow 160ms;
}

.tiles a::before {
  content: "";
  position: absolute;
  top: 0; left: 0; right: 0;
  height: 2px;
  background: var(--accent);
  opacity: 0;
  transition: opacity 160ms;
}

.tiles a:hover {
  border-color: var(--accent-dim);
  transform: translateY(-3px);
  box-shadow: 0 14px 30px rgba(0, 0, 0, 0.45);
  text-decoration: none;
}

.tiles a:hover::before { opacity: 1; }
.tiles a:hover .part { color: var(--accent); }

.tiles .part {
  display: block;
  font-size: 0.9rem;
  font-weight: 600;
  letter-spacing: -0.2px;
  transition: color 120ms;
}

.tiles .count {
  display: block;
  font-family: var(--font-mono);
  font-size: 0.65rem;
  letter-spacing: 0.5px;
  text-transform: uppercase;
  color: var(--faint);
  margin-top: 0.3rem;
}

/* --- controls ------------------------------------------------------------ */

.row { display: flex; gap: 0.75rem; flex-wrap: wrap; align-items: center; }
.row input.part, .row input.token { flex: 1 1 18rem; width: auto; }
.gate form:not(.stack) input.part,
.gate form:not(.stack) input.token { flex: 1 1 12rem; width: auto; }

/* Width, never flex-basis, on the element itself. In the stacked sign-in form
   the container is a column, where a flex-basis of 18rem becomes the input's
   HEIGHT and the two fields render as tall empty boxes. */
input.part,
input.token {
  width: 100%;
  padding: 0.7rem 0.85rem;
  background: var(--panel-2);
  border: 1px solid var(--line-2);
  border-radius: var(--radius-sm);
  color: var(--text);
  font-family: var(--font-mono);
  font-size: 0.9rem;
  outline: none;
  transition: border-color 140ms, box-shadow 140ms, background 140ms;
}

input.part::placeholder,
input.token::placeholder { color: var(--faint); }

input.part:focus,
input.token:focus {
  border-color: var(--accent);
  background: #0b1219;
  box-shadow: 0 0 0 3px var(--accent-glow);
}

.primary,
.ghost {
  font-family: var(--font-mono);
  font-weight: 700;
  font-size: 0.72rem;
  letter-spacing: 0.6px;
  text-transform: uppercase;
  padding: 0.7rem 1.35rem;
  border-radius: var(--radius-sm);
  border: 1px solid transparent;
  cursor: pointer;
  text-decoration: none;
  display: inline-block;
  transition: filter 140ms, border-color 140ms, background 140ms, color 140ms;
}

.primary {
  background: var(--accent);
  color: var(--accent-ink);
}

.primary:hover { filter: brightness(1.08); text-decoration: none; }
.primary:disabled { opacity: 0.5; cursor: default; }

.ghost {
  background: var(--panel-2);
  border-color: var(--line-2);
  color: var(--text);
  padding: 0.5rem 1rem;
}

.ghost:hover { border-color: var(--accent-dim); background: var(--panel-hi); text-decoration: none; }

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

/* --- gate ---------------------------------------------------------------- */

.gate {
  max-width: 24rem;
  margin: 5rem auto;
  padding: 1.75rem;
  background: linear-gradient(180deg, var(--panel), var(--panel-2));
  border: 1px solid var(--line);
  border-radius: var(--radius);
}

.gate form { display: flex; gap: 0.75rem; margin-bottom: 1rem; }
.gate form.stack { flex-direction: column; align-items: stretch; }
.gate form.stack .primary { align-self: flex-start; margin-top: 0.25rem; }
.gate .note { margin-bottom: 0; }

/* --- produktinfo desk ---------------------------------------------------- */

.desk {
  display: grid;
  grid-template-columns: 1fr 20rem;
  gap: 1.5rem;
  align-items: start;
}

/* The sheet grows with its content, so the readout would scroll away from a
   long produktinfo without this. */
.desk > :last-child { position: sticky; top: 4.5rem; }

@media (max-width: 60rem) {
  .desk { grid-template-columns: 1fr; }
  .desk > :last-child { position: static; }
}

.sheet {
  width: 100%;
  min-height: 60vh;
  padding: 1rem 1.1rem;
  margin-bottom: 0.75rem;
  background: var(--panel-2);
  color: var(--text);
  border: 1px solid var(--line-2);
  border-radius: var(--radius);
  font-family: var(--font-mono);
  font-size: 0.8rem;
  line-height: 1.7;
  resize: vertical;
  outline: none;
  transition: border-color 140ms, box-shadow 140ms;
  /* A pasted vendor blob is one line of many hundreds of characters. Under
     `pre` it ran off the right edge and read as text the editor had dropped.
     wrap="soft" on the textarea keeps the saved bytes free of the wrap. */
  white-space: pre-wrap;
  overflow-wrap: anywhere;
  overflow-x: hidden;
}

.sheet:focus { border-color: var(--accent); box-shadow: 0 0 0 3px var(--accent-glow); }

.readout {
  list-style: none;
  margin: 0 0 1rem;
  padding: 0.85rem 0.9rem;
  background: linear-gradient(180deg, var(--panel), var(--panel-2));
  border: 1px solid var(--line);
  border-radius: var(--radius);
}

.readout .item {
  display: flex;
  gap: 0.55rem;
  align-items: baseline;
  padding: 0.3rem 0;
  font-size: 0.8rem;
  color: var(--text);
}

/* A run log is a machine trace: the action in mono so the eye can run down the
   column, the argument dimmed beside it. Long commands wrap rather than
   scrolling the page sideways. */
.readout .doing {
  font-family: var(--font-mono);
  font-size: 0.75rem;
  font-weight: 600;
  color: var(--accent);
  flex: none;
}

.readout .said {
  color: var(--muted);
  overflow-wrap: anywhere;
  white-space: pre-wrap;
}

.readout .fault ~ .said,
.readout .item .led.fault ~ .doing { color: var(--fault); }

/* The run log is the one list that can grow to hundreds of rows. */
[data-runlog] { max-height: 34rem; overflow-y: auto; scrollbar-color: var(--line-2) transparent; }

/* --- reduced motion, small screens --------------------------------------- */

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { transition: none !important; animation: none !important; }
  .tiles a:hover { transform: none; }
}

@media (max-width: 34rem) {
  main { padding: 1.25rem 1rem 4rem; }
  .spec td { padding: 0.55rem 0.7rem; }
  .spec .meta { font-size: 0.68rem; }
  .nameplate { flex-wrap: wrap; gap: 0.5rem 0.85rem; }
}

/* --- light ---------------------------------------------------------------
   Same structure, inverted ground. Kept to token overrides so the rules above
   stay the single definition of every component. */

@media (prefers-color-scheme: light) {
  :root {
    --bg:       #f4f5f2;
    --bg-grid:  rgba(0, 0, 0, 0.028);
    --panel:    #ffffff;
    --panel-2:  #f8f9f6;
    --panel-hi: #eef0ec;
    --line:     #dcdfd8;
    --line-2:   #c3c7bf;

    --text:  #16191c;
    --muted: #5a6169;
    --faint: #838a92;

    --accent:      #0a6dc4;
    --accent-dim:  #6ba7dd;
    --accent-ink:  #ffffff;
    --accent-glow: rgba(10, 109, 196, 0.18);

    --ok:    #12805a;
    --warn:  #9a6300;
    --fault: #b3261e;
  }

  body {
    background:
      radial-gradient(66vw 28vw at 88% -10%, rgba(10, 109, 196, 0.05), transparent 60%),
      linear-gradient(var(--bg-grid) 1px, transparent 1px) 0 0 / 100% 2.1rem,
      linear-gradient(90deg, var(--bg-grid) 1px, transparent 1px) 0 0 / 2.1rem 100%,
      var(--bg);
  }

  body::before { opacity: 0.22; }

  .nameplate {
    background: linear-gradient(180deg, rgba(244, 245, 242, 0.94), rgba(244, 245, 242, 0.74));
  }

  .bay.energised { background: linear-gradient(180deg, #e8f2fb, var(--panel-2)); }
  input.part:focus, input.token:focus { background: #fff; }
  .tiles a:hover { box-shadow: 0 12px 26px rgba(0, 0, 0, 0.10); }
}

/* --- the media picker ----------------------------------------------------
   Slots on the left in file order, the vendor's shots on the right. Arming a
   slot is what makes a click on a shot mean something, so the armed state is
   the loudest thing in this section. */

.picker {
  display: grid;
  grid-template-columns: minmax(18rem, 24rem) 1fr;
  gap: 1.5rem;
  align-items: start;
  margin-top: 2.5rem;
}

@media (max-width: 60rem) {
  .picker { grid-template-columns: 1fr; }
}

.slots {
  list-style: none;
  margin: 0;
  padding: 0.5rem;
  background: linear-gradient(180deg, var(--panel), var(--panel-2));
  border: 1px solid var(--line);
  border-radius: var(--radius);
}

.slot { padding: 0.15rem 0; }

.slot-face {
  display: flex;
  align-items: baseline;
  gap: 0.55rem;
  width: 100%;
  padding: 0.5rem 0.6rem;
  background: transparent;
  border: 1px solid transparent;
  border-radius: var(--radius-sm);
  color: var(--text);
  font: inherit;
  text-align: left;
  cursor: pointer;
  transition: background 120ms, border-color 120ms;
}

.slot-face:hover:not(:disabled) { background: var(--panel-hi); }
.slot-face:disabled { cursor: default; opacity: 0.55; }

.slot.armed .slot-face {
  border-color: var(--accent);
  background: var(--panel-hi);
  box-shadow: 0 0 0 3px var(--accent-glow);
}

.slot-what { display: flex; flex-direction: column; gap: 0.1rem; min-width: 0; flex: 1; }

.slot-label {
  font-family: var(--font-mono);
  font-size: 0.74rem;
  font-weight: 700;
  letter-spacing: 0.4px;
  text-transform: uppercase;
}

.slot-where {
  font-size: 0.78rem;
  color: var(--muted);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.slot-holds {
  flex: none;
  max-width: 9rem;
  font-family: var(--font-mono);
  font-size: 0.68rem;
  color: var(--faint);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.slot.filled .slot-holds { color: var(--ok); }

.slot-value,
.slot-alt {
  width: calc(100% - 1.2rem);
  margin: 0.1rem 0.6rem 0.3rem;
  padding: 0.35rem 0.5rem;
  background: var(--panel-2);
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  color: var(--text);
  font-family: var(--font-body);
  font-size: 0.78rem;
  outline: none;
}

.slot-value::placeholder,
.slot-alt::placeholder { color: var(--faint); }

.slot-value:focus,
.slot-alt:focus { border-color: var(--accent); box-shadow: 0 0 0 3px var(--accent-glow); }

/* The value is the line itself, so it is set in the face of the file. */
.slot-value {
  font-family: var(--font-mono);
  font-size: 0.72rem;
  margin-bottom: 0.25rem;
}

.shelf {
  /* A six-page series offers around ninety shots. Uncapped, the grid pushes
     the editor off the bottom of the page. */
  max-height: 34rem;
  overflow-y: auto;
  scrollbar-color: var(--line-2) transparent;
  padding: 0.9rem;
  background: linear-gradient(180deg, var(--panel), var(--panel-2));
  border: 1px solid var(--line);
  border-radius: var(--radius);
}

.shelf .note { margin-bottom: 0; }

.shots {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(8.5rem, 1fr));
  gap: 0.6rem;
}

.shot {
  display: flex;
  flex-direction: column;
  gap: 0.35rem;
  padding: 0.4rem;
  background: var(--panel-2);
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  cursor: pointer;
  transition: border-color 140ms, transform 100ms;
}

.shot:hover { border-color: var(--accent); transform: translateY(-2px); }

/* The vendor's product shots are on white, so they need a light plate to sit
   on rather than being punched out of a dark panel. */
.shot img {
  width: 100%;
  aspect-ratio: 4 / 3;
  object-fit: contain;
  background: #fff;
  border-radius: 0.25rem;
}

.shot-name {
  font-family: var(--font-mono);
  font-size: 0.62rem;
  color: var(--muted);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* --- the activity drawer -------------------------------------------------
   Pinned to the bottom of every page. It is the answer to "what just
   happened", so it sits above the content rather than at the end of it. */

[hidden] { display: none !important; }

body.drawer-open main { padding-bottom: 46vh; }

.drawer {
  position: fixed;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 40;
  display: flex;
  flex-direction: column;
  max-height: 42vh;
  padding: 0.75rem var(--gutter) 0.9rem;
  background: linear-gradient(180deg, rgba(16, 18, 22, 0.97), rgba(10, 11, 13, 0.99));
  backdrop-filter: blur(14px) saturate(1.1);
  border-top: 1px solid var(--line-2);
  box-shadow: 0 -18px 40px rgba(0, 0, 0, 0.45);
}

.drawer-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 1rem;
  max-width: var(--maxw);
  width: 100%;
  margin: 0 auto 0.4rem;
}

.drawer-head .legend { margin: 0; }

.drawer .activity {
  max-width: var(--maxw);
  width: 100%;
  margin: 0 auto;
  overflow-y: auto;
  scrollbar-color: var(--line-2) transparent;
  background: transparent;
  border: 0;
  padding: 0;
}

.activity .item {
  gap: 0.6rem;
  padding: 0.22rem 0;
  border-bottom: 1px solid rgba(255, 255, 255, 0.03);
}

/* The clock column is fixed-width so the eye can run down the messages. */
.activity .when {
  flex: none;
  width: 3.1rem;
  font-family: var(--font-mono);
  font-size: 0.7rem;
  color: var(--faint);
}

.activity .doing {
  flex: none;
  max-width: 11rem;
  font-family: var(--font-mono);
  font-size: 0.7rem;
  color: var(--accent);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.activity .said { color: var(--text); font-size: 0.8rem; }
.activity .fault ~ .said { color: var(--fault); }

/* ==========================================================================
   The application shell
   --------------------------------------------------------------------------
   One rail down the left, one working area to its right. Both halves of the
   job -- what the suppliers publish, and the sheets built from it -- live
   inside this, so moving between them is a click in the same page furniture
   rather than a hop to a different-looking tool.
   ========================================================================== */

body.shell {
  display: grid;
  grid-template-columns: 15.5rem minmax(0, 1fr);
  min-height: 100vh;
}

body.shell .frame { min-width: 0; }        /* let grid children actually shrink */

.rail {
  position: sticky;
  top: 0;
  align-self: start;
  height: 100vh;
  display: flex;
  flex-direction: column;
  gap: 0.35rem;
  padding: 1.1rem 0.75rem 0.9rem;
  background: linear-gradient(180deg, rgba(16, 18, 22, 0.92), rgba(10, 11, 13, 0.92));
  border-right: 1px solid var(--line);
  backdrop-filter: blur(14px) saturate(1.1);
}

.rail .brand {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  padding: 0.2rem 0.55rem 1rem;
  text-decoration: none;
}

.rail .brand:hover { text-decoration: none; }

/* A mark rather than a logo: three stacked planes, which is what this does --
   supplier data, the sheet built from it, the shop it ends up in. */
.rail .glyph {
  width: 1.6rem;
  height: 1.6rem;
  flex: none;
  border-radius: 0.45rem;
  background:
    linear-gradient(160deg, var(--accent), var(--accent-dim));
  box-shadow: 0 0 0 1px rgba(59, 169, 255, 0.35), 0 6px 16px var(--accent-glow);
  position: relative;
}

.rail .glyph::after {
  content: "";
  position: absolute;
  inset: 0.42rem 0.34rem;
  border-radius: 0.14rem;
  border: 1.5px solid var(--accent-ink);
  border-bottom-width: 4px;
  opacity: 0.75;
}

.rail .wordmark {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 1.02rem;
  letter-spacing: 0.6px;
  text-transform: uppercase;
  color: var(--text);
}

.rail-nav { display: flex; flex-direction: column; gap: 0.12rem; overflow-y: auto; }

.rail-title {
  margin: 0.9rem 0 0.3rem;
  padding: 0 0.6rem;
  font-family: var(--font-mono);
  font-size: 0.63rem;
  font-weight: 600;
  letter-spacing: 1.4px;
  text-transform: uppercase;
  color: var(--faint);
}

.rail-nav a {
  display: flex;
  align-items: center;
  gap: 0.66rem;
  padding: 0.52rem 0.6rem;
  border-radius: var(--radius-sm);
  color: var(--muted);
  font-size: 0.88rem;
  font-weight: 500;
  text-decoration: none;
  border: 1px solid transparent;
}

.rail-nav a svg { width: 1.05rem; height: 1.05rem; flex: none; }

.rail-nav a:hover {
  background: var(--panel-hi);
  color: var(--text);
  text-decoration: none;
}

/* The current page is marked by a filled plate and an accent edge, which
   survives being read at a glance far better than colour alone. */
.rail-nav a[aria-current="page"] {
  background: linear-gradient(90deg, var(--accent-glow), transparent 70%);
  border-color: rgba(59, 169, 255, 0.28);
  color: var(--text);
}

.rail-nav a[aria-current="page"] svg { color: var(--accent); }

/* Leaving the application proper. */
.rail-nav a.away span::after { content: " \2197"; color: var(--faint); }

/* Something is running right now. */
.pip {
  margin-left: auto;
  width: 0.42rem;
  height: 0.42rem;
  border-radius: 50%;
  background: var(--ok);
  box-shadow: 0 0 0 3px rgba(76, 214, 164, 0.16);
  animation: pip 1.8s ease-in-out infinite;
}

@keyframes pip { 50% { opacity: 0.35; } }

.rail-foot {
  margin-top: auto;
  display: flex;
  gap: 0.4rem;
  padding-top: 0.7rem;
  border-top: 1px solid var(--line);
}

.rail-foot form { margin: 0; }
.rail-foot .ghost { width: 100%; }

/* --- the working area ---------------------------------------------------- */

.topbar {
  display: flex;
  align-items: flex-end;
  gap: 1rem;
  flex-wrap: wrap;
  padding: 1.5rem var(--gutter) 0.9rem;
}

.topbar .titling { min-width: 0; }

.topbar h1 {
  margin: 0;
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 1.75rem;
  letter-spacing: 0.2px;
  color: var(--text);
}

.topbar .legend, .topbar .note { margin: 0.25rem 0 0; }
.topbar .actions { margin-left: auto; display: flex; gap: 0.5rem; flex-wrap: wrap; }

/* --- figures ------------------------------------------------------------- */

.figures {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(11rem, 1fr));
  gap: 0.8rem;
  list-style: none;
  margin: 0 0 1.4rem;
  padding: 0;
}

.figures li {
  padding: 1rem 1.1rem;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: linear-gradient(180deg, var(--panel), var(--panel-2));
}

.figures .value {
  display: block;
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 1.9rem;
  line-height: 1.1;
  color: var(--text);
  font-variant-numeric: tabular-nums;
}

.figures .of {
  display: block;
  margin-top: 0.2rem;
  font-size: 0.78rem;
  color: var(--muted);
}

.figures .warn .value { color: var(--warn); }
.figures .live .value { color: var(--ok); }

/* --- supplier cards ------------------------------------------------------ */

.producers {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(19rem, 1fr));
  gap: 0.9rem;
  list-style: none;
  margin: 0;
  padding: 0;
}

.producers li {
  display: flex;
  flex-direction: column;
  gap: 0.7rem;
  padding: 1.05rem 1.15rem;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: linear-gradient(180deg, var(--panel), var(--panel-2));
}

.producers .head { display: flex; align-items: baseline; gap: 0.6rem; }

.producers .who {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 1.05rem;
  color: var(--text);
}

.producers .counts {
  display: flex;
  gap: 1.3rem;
  font-variant-numeric: tabular-nums;
}

.producers .counts b {
  display: block;
  font-family: var(--font-display);
  font-size: 1.25rem;
  font-weight: 700;
  color: var(--text);
}

.producers .counts span {
  font-size: 0.72rem;
  color: var(--muted);
  text-transform: uppercase;
  letter-spacing: 0.7px;
}

/* --- state badges -------------------------------------------------------- */

.badge {
  margin-left: auto;
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
  padding: 0.16rem 0.5rem;
  border-radius: 999px;
  font-family: var(--font-mono);
  font-size: 0.66rem;
  font-weight: 600;
  letter-spacing: 0.8px;
  text-transform: uppercase;
}

.badge.running { background: var(--ok-bg); color: var(--ok); }
.badge.done    { background: rgba(255, 255, 255, 0.05); color: var(--muted); }
.badge.stale   { background: var(--warn-bg); color: var(--warn); }
.badge.unknown { background: rgba(255, 255, 255, 0.04); color: var(--faint); }

.bar {
  height: 0.32rem;
  border-radius: 999px;
  background: var(--panel-hi);
  overflow: hidden;
}

.bar i {
  display: block;
  height: 100%;
  border-radius: 999px;
  background: linear-gradient(90deg, var(--accent-dim), var(--accent));
}

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

@media (max-width: 60rem) {
  body.shell { grid-template-columns: minmax(0, 1fr); }

  .rail {
    position: static;
    height: auto;
    flex-direction: row;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.3rem;
    border-right: 0;
    border-bottom: 1px solid var(--line);
    padding: 0.7rem var(--gutter);
  }

  .rail .brand { padding: 0 0.5rem 0 0; }
  .rail-nav { flex-direction: row; flex-wrap: wrap; overflow: visible; }
  .rail-title { display: none; }
  .rail-foot { margin-left: auto; border-top: 0; padding-top: 0; }
  .topbar { padding-top: 1.1rem; }
}

/* --- the merged product list --------------------------------------------
   One row per product, whichever half it exists in. The state column is the
   reason this list exists, so it is fixed-width and the eye can run down it.
   ------------------------------------------------------------------------ */

.ledger {
  list-style: none;
  margin: 0;
  padding: 0;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  overflow: hidden;
  background: var(--panel-2);
}

.ledger li {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  padding: 0.6rem 0.9rem;
  border-bottom: 1px solid var(--line);
}

.ledger li:last-child { border-bottom: 0; }
.ledger li:hover { background: var(--panel); }

/* Something the supplier has changed since the sheet was built is the one
   row that must not be scrolled past. */
.ledger li.drifted { background: linear-gradient(90deg, var(--warn-bg), transparent 55%); }

.ledger .what { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; }
.ledger .what .part { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ledger .what .hint { font-size: 0.74rem; }

.ledger .state {
  flex: none;
  width: 9.5rem;
  font-family: var(--font-mono);
  font-size: 0.7rem;
  font-weight: 600;
  letter-spacing: 0.5px;
  text-transform: uppercase;
  color: var(--faint);
}

.ledger .state.drifted { color: var(--warn); }
.ledger .state.built { color: var(--ok); }
.ledger .state.imported { color: var(--accent); }

.ledger .doing { flex: none; }
.ledger .doing form { margin: 0; }

.ledger .doing .ghost {
  padding: 0.32rem 0.7rem;
  font-size: 0.78rem;
  text-decoration: none;
  display: inline-block;
}

/* The filter row in the legend. */
.legend .hint a { color: var(--muted); text-decoration: none; }
.legend .hint a:hover { color: var(--text); }
.legend .hint a[aria-current="page"] { color: var(--accent); }

@media (max-width: 46rem) {
  .ledger li { flex-wrap: wrap; }
  .ledger .state { width: auto; order: 3; }
  .ledger .doing { margin-left: auto; order: 4; }
}

/* A control that ends something. Not red until hovered: a Stop button that
   shouts from the page invites the eye to it, which is the opposite of what
   a destructive action wants. */
.ghost.danger:hover {
  border-color: var(--fault);
  color: var(--fault);
  background: var(--fault-bg);
}

/* The per-vendor controls sit inside a card, so they need their own row
   rather than the topbar's right-alignment. */
.producers .actions {
  display: flex;
  gap: 0.4rem;
  flex-wrap: wrap;
  margin-left: 0;
}

.producers .actions form { margin: 0; }
.producers .actions .ghost { padding: 0.3rem 0.62rem; font-size: 0.78rem; }

/* How many things are waiting for a person. A count, not a dot: "6" and "1"
   are different amounts of work and the rail should say which. */
.tally {
  margin-left: auto;
  min-width: 1.25rem;
  padding: 0 0.32rem;
  border-radius: 999px;
  background: var(--warn-bg);
  color: var(--warn);
  font-family: var(--font-mono);
  font-size: 0.68rem;
  font-weight: 700;
  line-height: 1.25rem;
  text-align: center;
}

/* Queue stages, borrowing the ledger's state column. */
.ledger .state.changed      { color: var(--warn); }
.ledger .state.needs-media  { color: var(--accent); }
.ledger .state.to-publish   { color: var(--ok); }
.ledger .state.to-run,
.ledger .state.to-import    { color: var(--faint); }

.ledger li.changed { background: linear-gradient(90deg, var(--warn-bg), transparent 55%); }

/* --- the media picker ---------------------------------------------------- */

/* What a slot holds, as a picture. A filename is not recognisable; forty
   pixels of the actual shot is. */
.slot-shot {
  width: 2.4rem;
  height: 2.4rem;
  flex: none;
  object-fit: contain;
  border-radius: var(--radius-sm);
  background: var(--panel-hi);
  border: 1px solid var(--line);
}

/* A candidate already placed in a slot. Dimmed rather than hidden: on the
   fifth pick you want to see the four you have already made, not a grid that
   silently shrinks under you. */
.shot.used { opacity: 0.38; }

.shot.used::after {
  content: "\2713";
  position: absolute;
  top: 0.3rem;
  right: 0.4rem;
  width: 1.1rem;
  height: 1.1rem;
  border-radius: 50%;
  background: var(--ok);
  color: var(--accent-ink);
  font-size: 0.72rem;
  font-weight: 700;
  line-height: 1.1rem;
  text-align: center;
}

.shot { position: relative; }

/* Filling the rest in order is the common case and four identical clicks.
   Kept beside the grid's legend rather than in the toolbar, because it acts
   on the grid. */
.fill-rest {
  margin-left: auto;
  font-size: 0.78rem;
  padding: 0.28rem 0.62rem;
}

/* --- picking several products -------------------------------------------- */

.tiles.pickable li {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  padding: 0.5rem 0.8rem;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  background: var(--panel-2);
}

.tiles.pickable li.done { opacity: 0.5; }

.tiles.pickable .tick {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  flex: 1 1 auto;
  min-width: 0;
  cursor: pointer;
}

.tiles.pickable li.done .tick { cursor: default; }
.tiles.pickable .tick input { flex: none; accent-color: var(--accent); }
.tiles.pickable .tick .part { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.tiles.pickable .tick .count { margin-left: auto; }

/* The way to a single product, kept small: the list is for choosing many. */
.tiles.pickable .peek {
  flex: none;
  font-size: 0.76rem;
  color: var(--muted);
  text-decoration: none;
}

.tiles.pickable .peek:hover { color: var(--accent); }

.legend .hint .ghost { padding: 0.18rem 0.5rem; font-size: 0.72rem; }

/* --- an embedded tool, full width --------------------------------------- */

/* The Files page is one tool that wants the whole working area, not a column
   of text, so its main drops the reading width. */
main:has(> .embedded) {
  max-width: none;
  padding-bottom: 1rem;
}

.embedded {
  display: block;
  width: 100%;
  height: calc(100vh - 8.5rem);
  min-height: 24rem;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--bg);
}
