:root {
  --bg: #f5f6f8;
  --surface: #ffffff;
  --line: #d9dde3;
  --ink: #14181d;
  --muted: #5b6675;
  --accent: #1f4e79;
  --accent-soft: #e8eff6;
  --ok: #1d6b3f;
  --ok-soft: #e6f2ea;
  --warn: #8a5a00;
  --warn-soft: #fdf3e0;
  --stop: #9b2323;
  --stop-soft: #fbeaea;
  --radius: 10px;
  --shadow: 0 1px 2px rgba(20, 24, 29, .06), 0 4px 14px rgba(20, 24, 29, .05);
}

@media (prefers-color-scheme: dark) {
  :root {
    --bg: #14181d; --surface: #1c2229; --line: #2e3742; --ink: #eef2f6; --muted: #9aa7b6;
    --accent: #6ea8dc; --accent-soft: #1d2b3a; --ok: #6cc48f; --ok-soft: #16281e;
    --warn: #e0ac5a; --warn-soft: #2b2113; --stop: #e58b8b; --stop-soft: #2c1a1a;
    --shadow: 0 1px 2px rgba(0, 0, 0, .4);
  }
}

* { box-sizing: border-box; }
body {
  margin: 0; background: var(--bg); color: var(--ink);
  font: 15px/1.55 system-ui, -apple-system, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
}
a { color: var(--accent); }
h1, h2, h3 { line-height: 1.25; margin: 0 0 .5rem; }
h1 { font-size: 1.15rem; }
h2 { font-size: 1rem; letter-spacing: .01em; }
h3 { font-size: .82rem; text-transform: uppercase; letter-spacing: .07em; color: var(--muted); }

.banner {
  background: var(--accent-soft); border-bottom: 1px solid var(--line);
  padding: .4rem 1.25rem; font-size: .8rem; color: var(--muted);
}
header.app {
  display: flex; flex-wrap: wrap; gap: 1rem; align-items: center; justify-content: space-between;
  padding: .85rem 1.25rem; background: var(--surface); border-bottom: 1px solid var(--line);
}
.brand { display: flex; align-items: baseline; gap: .6rem; }
.brand strong { font-size: 1.05rem; }
.brand span { color: var(--muted); font-size: .82rem; }
.who { display: flex; align-items: center; gap: .75rem; font-size: .85rem; color: var(--muted); }

/* Full width, deliberately. The function bar spans the window, so boxing the content it belongs to
   into a narrow column left a wide monitor mostly empty while the planner — the widest thing in the
   Demo — was the view being squeezed. Padding scales with the viewport instead of a fixed column. */
main { max-width: none; margin: 0; padding: 1.25rem clamp(1rem, 2.5vw, 2.5rem); }

/* Horizontal function bar, following the prototype. It is the demonstration's spine: every role
   sees its own functions in the order a panel actually travels through them. */
nav.functions {
  position: sticky; top: 0; z-index: 20;
  display: flex; gap: .25rem; align-items: stretch;
  padding: 0 1.25rem; background: var(--surface); border-bottom: 1px solid var(--line);
  overflow-x: auto; scrollbar-width: thin; -webkit-overflow-scrolling: touch;
}
nav.functions a {
  flex: 0 0 auto; white-space: nowrap; text-decoration: none;
  padding: .7rem .85rem; border-bottom: 2px solid transparent;
  color: var(--muted); font-size: .9rem; font-weight: 500;
}
nav.functions a:hover { color: var(--ink); background: var(--bg); }
nav.functions a[aria-current="page"] {
  color: var(--ink); border-bottom-color: var(--accent); font-weight: 650;
}
nav.functions a:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }

@media (max-width: 860px) {
  /* Scrolls rather than wraps: a wrapping bar changes height as the role changes and pushes the
     content around. Momentum scrolling keeps every function reachable one thumb-swipe away. */
  nav.functions { padding: 0 .5rem; }
  nav.functions a { padding: .7rem .6rem; font-size: .85rem; }
  main { padding: .9rem .75rem; }

  /* Keep the header to one line. Left alone, the role label wraps and the sign-out button drops to
     a third row, so the nav starts a third of the way down a phone screen. */
  header.app { padding: .6rem .75rem; gap: .5rem; flex-wrap: nowrap; }
  /* The brand never shrinks or clips; the identity beside it is what gives way. */
  .brand { flex: 0 0 auto; }
  .brand strong { font-size: .88rem; white-space: nowrap; }
  /* The subtitle goes: at this width it collides with the identity on its right, and the page
     title already carries it. */
  .brand span { display: none; }
  .who { flex: 1 1 auto; min-width: 0; gap: .5rem; font-size: .78rem; justify-content: flex-end; }
  .who #who-label { flex: 0 1 auto; min-width: 0;
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
  .who .btn { padding: .35rem .6rem; white-space: nowrap; }
}

.unauthorised { text-align: center; padding: 2.5rem 1rem; }
.unauthorised .code { font-size: 2.6rem; font-weight: 700; color: var(--stop); line-height: 1; }
.unauthorised h2 { margin-top: .75rem; font-size: 1.15rem; }
.unauthorised .options { display: flex; flex-wrap: wrap; gap: .5rem; justify-content: center;
  margin-top: 1.1rem; }

.card {
  background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius);
  padding: 1.1rem 1.25rem; box-shadow: var(--shadow); margin-bottom: 1.1rem;
}
.card > :last-child { margin-bottom: 0; }

.identity { display: flex; flex-wrap: wrap; gap: .5rem 1.5rem; align-items: center; }
.identity .code { font-size: 1.35rem; font-weight: 650; letter-spacing: .01em; }
.identity dl { display: flex; gap: 1.25rem; margin: 0; flex-wrap: wrap; }
.identity dt { font-size: .7rem; text-transform: uppercase; letter-spacing: .06em; color: var(--muted); }
.identity dd { margin: 0; font-size: .92rem; }

.chip {
  display: inline-flex; align-items: center; gap: .35rem; padding: .18rem .6rem; border-radius: 999px;
  font-size: .78rem; font-weight: 600; border: 1px solid transparent; white-space: nowrap;
}
.chip.state { background: var(--accent-soft); color: var(--accent); border-color: var(--accent); }
.chip.ok { background: var(--ok-soft); color: var(--ok); border-color: var(--ok); }
.chip.warn { background: var(--warn-soft); color: var(--warn); border-color: var(--warn); }
.chip.stop { background: var(--stop-soft); color: var(--stop); border-color: var(--stop); }
.chip.prepared { background: var(--warn-soft); color: var(--warn); border-color: var(--warn); }

table { width: 100%; border-collapse: collapse; font-size: .88rem; }
th, td { text-align: left; padding: .45rem .6rem; border-bottom: 1px solid var(--line); vertical-align: top; }
th { font-size: .72rem; text-transform: uppercase; letter-spacing: .05em; color: var(--muted); font-weight: 600; }
tr:last-child td { border-bottom: 0; }
.scroll { overflow-x: auto; }

button.action, .btn {
  font: inherit; text-decoration: none; display: inline-block; font-weight: 600; padding: .5rem .95rem; border-radius: 8px; cursor: pointer;
  border: 1px solid var(--accent); background: var(--accent); color: #fff;
}
button.action.secondary, .btn.secondary { background: transparent; color: var(--accent); }
button.action:disabled { opacity: .45; cursor: not-allowed; }
button.action.danger { background: var(--stop); border-color: var(--stop); }

label { display: block; font-size: .8rem; color: var(--muted); margin: .7rem 0 .2rem; }
input, select, textarea {
  font: inherit; width: 100%; padding: .45rem .6rem; border: 1px solid var(--line);
  border-radius: 8px; background: var(--bg); color: var(--ink);
}
.row { display: flex; gap: .75rem; flex-wrap: wrap; align-items: flex-end; }
.row > * { flex: 1 1 180px; }
.row > .fit { flex: 0 0 auto; }

.notice { padding: .7rem .9rem; border-radius: 8px; font-size: .87rem; border: 1px solid; margin: .8rem 0; }
.notice.info { background: var(--accent-soft); border-color: var(--accent); color: var(--accent); }
.notice.ok { background: var(--ok-soft); border-color: var(--ok); color: var(--ok); }
.notice.warn { background: var(--warn-soft); border-color: var(--warn); color: var(--warn); }
.notice.stop { background: var(--stop-soft); border-color: var(--stop); color: var(--stop); }

.timeline { list-style: none; margin: 0; padding: 0; }
.timeline li { display: grid; grid-template-columns: 150px 1fr; gap: .8rem; padding: .55rem 0;
  border-bottom: 1px solid var(--line); font-size: .88rem; }
.timeline li:last-child { border-bottom: 0; }
.timeline time { color: var(--muted); font-variant-numeric: tabular-nums; font-size: .8rem; }
.timeline .who { font-size: .78rem; color: var(--muted); display: block; }
@media (max-width: 620px) { .timeline li { grid-template-columns: 1fr; gap: .15rem; } }

.qr { display: flex; gap: 1.5rem; align-items: center; flex-wrap: wrap; }
.qr > div:last-child { flex: 1 1 300px; }
.qr canvas, .qr img, .qr svg {
  background: #fff; padding: 10px; border-radius: 8px; border: 1px solid var(--line);
}
.muted { color: var(--muted); font-size: .85rem; }
td.src { color: var(--muted); font-size: .78rem; text-transform: uppercase; letter-spacing: .05em; }
#qr-target { flex: 0 0 auto; }
.qr svg { display: block; width: 168px; height: 168px; }
.stat-row { display: flex; gap: 1.5rem; flex-wrap: wrap; }
.stat { min-width: 120px; }
.stat .v { font-size: 1.5rem; font-weight: 650; }
.stat .k { font-size: .72rem; text-transform: uppercase; letter-spacing: .06em; color: var(--muted); }
details.limits { border: 1px solid var(--line); border-radius: var(--radius); padding: .7rem .9rem;
  background: var(--surface); }
details.limits summary { cursor: pointer; font-weight: 600; font-size: .9rem; }
details.limits ul { margin: .6rem 0 0; padding-left: 1.1rem; font-size: .86rem; color: var(--muted); }
.centre { max-width: 520px; margin: 12vh auto; text-align: center; padding: 1.5rem; }

/* --- STORY-005: project header, to-scale casting beds --- */
.project-head { display: flex; gap: 1rem; align-items: flex-start; justify-content: space-between;
  flex-wrap: wrap; }
hr.rule { border: 0; border-top: 1px solid var(--line); margin: 1rem 0; }
.chosen-row > td { background: var(--accent-soft); }

/* The beds are drawn at the scale the server documents, so they scroll rather than shrink:
   fitting them to the viewport would break the single scale the whole planner depends on. */
.beds { display: flex; flex-direction: column; gap: 1.1rem; margin: .8rem 0; overflow-x: auto; }
.bed-wrap { min-width: min-content; }
.bed-figure {
  position: relative; background: var(--bg); border: 2px solid var(--line); border-radius: 6px;
  background-image: linear-gradient(to right, var(--line) 1px, transparent 1px),
                    linear-gradient(to bottom, var(--line) 1px, transparent 1px);
  /* One grid square is one metre. --metre is set per bed from the same conversion that sizes the
     bed and its panels, so the grid stays exactly one metre at any zoom. A fixed 30px here silently
     became a lie the moment zoom was added. */
  --metre: 30px;
  background-size: var(--metre) var(--metre);
}
.bed-figure.placeable { cursor: crosshair; }
.bed-panel {
  position: absolute; box-sizing: border-box; overflow: hidden;
  background: var(--accent-soft); border: 1px solid var(--accent); border-radius: 3px;
  font-size: .68rem; line-height: 1.1; color: var(--accent); padding: 2px 3px;
  cursor: grab; touch-action: none; user-select: none;
}
.bed-panel.genuine { background: var(--ok-soft); border-color: var(--ok); color: var(--ok); }
.bed-panel.chosen { outline: 2px solid var(--ink); outline-offset: 1px; z-index: 2; }
.bed-panel span { display: block; white-space: nowrap; }
.bed-panel-dim { font-size: .6rem; opacity: .75; font-variant-numeric: tabular-nums; }

/* A ruler under each bed. Without a reference of known length, a to-scale drawing and an arbitrary
   one look identical — which is exactly the feedback this answers. */
.bed-ruler { position: relative; height: 1.15rem; margin-top: 2px; }
.bed-tick {
  position: absolute; top: 0; width: 1px; height: 4px; background: var(--line);
}
.bed-tick.major { height: 8px; background: var(--muted); }
.bed-tick i {
  position: absolute; left: 2px; top: 6px; font-style: normal;
  font-size: .6rem; color: var(--muted); white-space: nowrap;
}

.zoom-control { display: flex; align-items: center; gap: .3rem; margin: .5rem 0; flex-wrap: wrap; }
.zoom-btn {
  font: inherit; font-size: .72rem; padding: .15rem .45rem; cursor: pointer;
  background: var(--bg); color: var(--ink);
  border: 1px solid var(--line); border-radius: 4px;
}
.zoom-btn.on { background: var(--accent-soft); border-color: var(--accent); color: var(--accent); }

/* Deliveries collapse to one line per project; the panels are one click away. */
.delivery-group { border-bottom: 1px solid var(--line); }
.delivery-group summary {
  display: flex; align-items: center; gap: .5rem; flex-wrap: wrap;
  padding: .45rem .2rem; cursor: pointer;
}
.delivery-group summary::-webkit-details-marker { color: var(--muted); }
.dg-code { font-weight: 600; }
.dg-due { margin-left: auto; display: flex; align-items: center; gap: .3rem; font-size: .8rem; }

/* ------------------------------------------------------------------ dashboard */

/* A second band of figures under the headline row, separated rather than merged: the top row is
   "how many", this one is "how many, when". */
.stat-row.bordered {
  border-top: 1px solid var(--line); margin-top: .9rem; padding-top: .9rem;
}
.stat .v.ok { color: var(--ok); }
.stat .v.stop { color: var(--stop); }

.project-list { display: grid; gap: .6rem; margin: .6rem 0 0; }

.project {
  border: 1px solid var(--line); border-radius: var(--radius); padding: .7rem .85rem;
  background: var(--bg);
}
.project header {
  display: flex; gap: .75rem; align-items: flex-start; justify-content: space-between;
  flex-wrap: wrap;
}
.project .name { font-size: .95rem; text-transform: none; letter-spacing: 0; color: var(--ink);
  margin: 0; }
.project p.muted { margin: .1rem 0 0; font-size: .8rem; }
.project .marks { display: flex; gap: .35rem; flex-wrap: wrap; }

/* The stage-group strip. Deliberately not a table: it is a shape to be scanned, and a group with
   no panels in it is left out rather than shown as a zero. */
.project .groups { display: flex; flex-wrap: wrap; gap: .35rem .9rem; margin-top: .55rem; }
.project .groups .group { font-size: .82rem; color: var(--muted); white-space: nowrap; }
.project .groups .group b { color: var(--ink); font-size: .95rem; margin-right: .15rem; }

details.more { margin-top: .9rem; }
details.more > summary {
  cursor: pointer; font-size: .85rem; color: var(--muted); padding: .3rem 0;
}
details.more > summary:hover { color: var(--ink); }

@media (max-width: 620px) {
  .project header { flex-direction: column; gap: .35rem; }
  .project .groups { gap: .3rem .7rem; }
}

/* Sub-headings inside a dashboard card need air above them: the figures they follow are a
   different thought, and without the gap the label rows run together. */
.card h3 { margin-top: 1.35rem; }
.card > h3:first-child { margin-top: 0; }

/* ------------------------------------------------------- register panel cards */

/* The prototype's card, which is the shape RMK's own people built and recognise: identity and
   stage on the top line, physical facts under it, progress, then actions. */
.pcards { display: grid; gap: .6rem; margin-top: .8rem; }

.pcard {
  border: 1px solid var(--line); border-radius: var(--radius); padding: .7rem .85rem;
  background: var(--bg);
}
.pcard .phead { display: flex; flex-wrap: wrap; align-items: center; gap: .45rem; }
.pcard .phead .code { font-size: 1rem; font-weight: 650; text-decoration: none; }
.pcard .phead .rev { font-size: .8rem; }
.pcard .meta { margin: .45rem 0 0; font-size: .82rem; color: var(--muted); line-height: 1.5; }
.pcard .actions { display: flex; flex-wrap: wrap; gap: .4rem; margin-top: .6rem; }
.pcard .actions .action { font-size: .82rem; padding: .35rem .7rem; text-decoration: none; }

/* Progress along the sixteen stages. Deliberately plain: it reads position, not health. */
.bar {
  height: 6px; background: var(--line); border-radius: 999px; overflow: hidden; margin: .55rem 0 0;
}
.bar i { display: block; height: 100%; background: var(--ok); }

/* ---------------------------------------------------------------------- modal */

/* The dialog is bounded by the viewport and only its body scrolls.
   Previously the backdrop was the scroll container and the header was sticky inside it, so long
   content scrolled the whole dialog — border, rounded corners and all — past the top of the screen
   while the header appeared to float. Constraining .modal and giving the scroll to .modal-body
   keeps the frame still and moves only the content inside it. */
.modal-backdrop {
  position: fixed; inset: 0; z-index: 60; background: rgba(15, 23, 32, .55);
  display: flex; align-items: flex-start; justify-content: center; padding: 4vh 1rem;
  overflow: hidden;
}
.modal {
  background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius);
  box-shadow: 0 12px 40px rgba(0, 0, 0, .25); width: min(720px, 100%); max-width: 100%;
  /* 4vh of backdrop padding top and bottom, so the dialog may use the rest. */
  display: flex; flex-direction: column; max-height: 92vh;
}
.modal > header {
  display: flex; align-items: flex-start; justify-content: space-between; gap: 1rem;
  padding: .9rem 1.1rem; border-bottom: 1px solid var(--line); flex: 0 0 auto;
  background: var(--surface); border-radius: var(--radius) var(--radius) 0 0;
}
.modal > header h2 { margin: 0; }
.modal > header p { margin: .15rem 0 0; font-size: .82rem; }
/* min-height:0 is required: a flex child defaults to min-height:auto and would refuse to shrink
   below its content, pushing the overflow back out to the dialog instead of scrolling here. */
.modal-body { padding: 1rem 1.1rem 1.2rem; overflow-y: auto; flex: 1 1 auto; min-height: 0; }
.modal-actions { margin: .9rem 0 0; }

.dl { display: grid; grid-template-columns: repeat(auto-fit, minmax(190px, 1fr)); gap: .55rem 1.1rem; }
.dl dt { font-size: .7rem; text-transform: uppercase; letter-spacing: .06em; color: var(--muted); }
.dl dd { margin: .1rem 0 0; font-size: .9rem; }

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

ol.stages { list-style: none; margin: 0; padding: 0; display: grid; gap: .25rem; }
ol.stages .stage {
  display: flex; align-items: center; gap: .6rem; padding: .4rem .55rem;
  border: 1px solid var(--line); border-left: 3px solid var(--line);
  border-radius: var(--radius); font-size: .86rem;
}
ol.stages .stage.done { border-left-color: var(--ok); }
ol.stages .stage.current { border-color: var(--accent); border-left-color: var(--accent);
  background: var(--accent-soft); font-weight: 600; }
ol.stages .stage .n {
  min-width: 1.4rem; text-align: right; color: var(--muted); font-size: .75rem;
}
ol.stages .stage .label { flex: 1 1 auto; }
ol.stages .stage .stime { font-size: .72rem; white-space: nowrap; }

ul.itp { list-style: none; margin: .5rem 0 0; padding: 0; display: grid; gap: .3rem; }
.itp-row {
  display: flex; align-items: center; gap: .5rem; flex-wrap: wrap;
  padding: .45rem .55rem; border: 1px solid var(--line); border-radius: var(--radius);
  font-size: .85rem;
}
.itp-row.pending { opacity: .6; }
.itp-row .label { flex: 1 1 12rem; }
.itp-row .itp-actions { display: flex; gap: .3rem; }
.itp-row .action { font-size: .78rem; padding: .25rem .6rem; }

@media (max-width: 620px) {
  .modal-backdrop { padding: 0; }
  .modal { border-radius: 0; min-height: 100%; }
  .pcard .actions .action { flex: 1 1 auto; text-align: center; }
}

/* ------------------------------------------------- register card refinements */

/* The panel code is a link to its passport, so it looks like one. */
.pcard .phead .code { text-decoration: underline; text-underline-offset: 2px; }
.pcard .phead .code:hover { text-decoration-thickness: 2px; }

/* Stage sits hard right, where the eye can run down a column of them. */
.pcard .phead-right { margin-left: auto; display: flex; align-items: center; gap: .45rem; }

/* Stage colour follows the prototype: not started, scheduled, in production, past the yard. */
.badge {
  display: inline-flex; align-items: center; padding: .2rem .6rem; border-radius: 999px;
  font-size: .76rem; font-weight: 600; white-space: nowrap;
  background: var(--line); color: var(--ink);
}
.badge.available { background: var(--line); color: var(--muted); }
.badge.scheduled { background: var(--accent-soft); color: var(--accent); }
.badge.active    { background: var(--warn-soft); color: var(--warn); }
.badge.complete  { background: var(--ok-soft); color: var(--ok); }

/* ITP progress, countable without being read. */
.itp-meter { display: inline-flex; align-items: center; gap: .35rem; }
.itp-meter .dots { display: inline-flex; gap: 3px; }
.itp-meter .dots i {
  width: 7px; height: 7px; border-radius: 50%; background: var(--line);
  border: 1px solid var(--muted); box-sizing: border-box;
}
.itp-meter .dots i.on { background: var(--ok); border-color: var(--ok); }
.itp-meter .n { font-size: .72rem; color: var(--muted); font-variant-numeric: tabular-nums; }
.itp-meter.complete .n { color: var(--ok); font-weight: 600; }
.itp-chip { font-size: .76rem; }

/* `danger` already means a solid red destructive button (the planner's remove control). Raising an
   NCR is a record, not a destruction, so it gets an outlined variant of its own rather than
   overriding `danger` and leaving that button with unreadable text. */
button.action.ncr {
  color: var(--stop); border-color: var(--stop); background: transparent;
}
button.action.ncr:hover { background: var(--stop-soft); }

.itp-row .label .ref { font-size: .72rem; }

.qr-block { display: flex; gap: 1rem; align-items: center; flex-wrap: wrap; }
.qr-block .qr { width: 148px; height: 148px; }
.qr-block .qr svg { width: 100%; height: 100%; display: block; }
.qr-block p { flex: 1 1 12rem; margin: 0; }

/* Actions that create things sit above the filters, where they are found first. */
.toolbar {
  display: flex; flex-wrap: wrap; gap: .5rem; margin: 0 0 .9rem;
  padding-bottom: .9rem; border-bottom: 1px solid var(--line);
}
.modal-actions { display: flex; flex-wrap: wrap; gap: .5rem; align-items: center; }
.modal .row { margin-bottom: .6rem; }

/* ------------------------------------------------- stages with attached items */

/* Wider than the other dialogs: the stage list carries its inspection points beside it, and
   squeezing that into a reading-width column is what made it scroll for three screens. */
.modal.wide { width: min(1180px, 100%); }

ol.stage-list { list-style: none; margin: .6rem 0 0; padding: 0; display: grid; gap: .2rem; }
.stage-row {
  display: grid; grid-template-columns: minmax(190px, 240px) 1fr; gap: .5rem 1rem;
  align-items: start; padding: .3rem .5rem;
  border: 1px solid transparent; border-left: 3px solid var(--line); border-radius: var(--radius);
}
.stage-row.done { border-left-color: var(--ok); }
.stage-row.current {
  border-color: var(--accent); border-left-color: var(--accent); background: var(--accent-soft);
}
.stage-main { display: flex; align-items: baseline; gap: .5rem; font-size: .86rem; }
.stage-row.current .stage-main .label { font-weight: 650; }
.stage-main .n { min-width: 1.3rem; text-align: right; color: var(--muted); font-size: .72rem; }
.stage-main .label { flex: 1 1 auto; }
.stage-main .stime { font-size: .7rem; white-space: nowrap; }

.stage-attached { display: grid; gap: .25rem; }
.attached {
  display: flex; align-items: center; gap: .45rem; flex-wrap: wrap;
  padding: .3rem .5rem; border: 1px solid var(--line); border-radius: var(--radius);
  background: var(--surface); font-size: .82rem;
}
.attached .label { flex: 1 1 14rem; }
.attached .ref { font-size: .72rem; }
.attached-actions { display: flex; gap: .3rem; }
.attached .action { font-size: .76rem; padding: .2rem .55rem; }

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

/* ---------------------------------------------------------------- planner */

/* Panels on the left, beds on the right — the prototype's arrangement, and the one that matches
   the gesture: pick up on the left, drop on the right. */
.planner-split { display: grid; grid-template-columns: minmax(280px, 340px) 1fr; gap: 1.1rem;
  align-items: start; }
/* Grid items default to min-width:auto, so a bed drawn wider than the viewport — which is exactly
   what zoom does — stretched the 1fr track and put a scrollbar on the whole page instead of on the
   beds. min-width:0 lets the track shrink so .beds scrolls inside its own card. */
.planner-split > * { min-width: 0; }
@media (max-width: 1024px) { .planner-split { grid-template-columns: 1fr; } }

.panel-picker .stack { display: grid; gap: .5rem; }
.panel-picker .stack .row { margin: 0; }

ul.cands { list-style: none; margin: .5rem 0 0; padding: 0; display: grid; gap: .4rem;
  max-height: 62vh; overflow-y: auto; }
.cand {
  border: 1px solid var(--line); border-radius: var(--radius); padding: .5rem .6rem;
  background: var(--bg); font-size: .84rem;
}
.cand[draggable="true"] { cursor: grab; }
.cand.dragging { opacity: .5; }
.cand.chosen { border-color: var(--accent); background: var(--accent-soft); }
.cand.placed { opacity: .6; }
.cand-head { display: flex; align-items: center; gap: .4rem; flex-wrap: wrap; }
.cand-head .code { font-weight: 650; }
.cand-meta { font-size: .76rem; margin: .25rem 0 .35rem; line-height: 1.45; }
.cand .action { font-size: .78rem; padding: .22rem .6rem; }

.beds-config { display: flex; flex-wrap: wrap; gap: .75rem; align-items: center; margin-top: .6rem;
  font-size: .85rem; }
.bed-toggle { display: inline-flex; align-items: center; gap: .35rem; }

/* The drop target has to be obvious while a panel is in the air. */
.bed-figure.drop-target { outline: 2px dashed var(--accent); outline-offset: 3px; }

/* --------------------------------------------------------------- calendar */

.calendar { margin-top: .9rem; }
.cal-months { display: grid; grid-template-columns: repeat(auto-fit, minmax(215px, 1fr)); gap: 1rem; }
.cal-month h4 { margin: 0 0 .35rem; font-size: .8rem; text-transform: none; letter-spacing: 0;
  color: var(--ink); }
.cal-grid { display: grid; grid-template-columns: repeat(7, 1fr); gap: 2px; }
.cal-head { font-size: .66rem; color: var(--muted); text-align: center; padding: .15rem 0; }
.cal-cell { min-height: 26px; }
.cal-cell.empty { visibility: hidden; }

.cal-cell.day {
  border: 1px solid var(--line); border-radius: 4px; background: var(--surface); color: var(--ink);
  font: inherit; font-size: .74rem; cursor: pointer; padding: .2rem 0; text-align: center;
}
.cal-cell.day:hover { border-color: var(--accent); }
/* Green when every bed carries something; amber when one is standing empty. */
.cal-cell.day.full { background: var(--ok-soft); border-color: var(--ok); color: var(--ok);
  font-weight: 650; }
.cal-cell.day.partial { background: var(--warn-soft); border-color: var(--warn); color: var(--warn);
  font-weight: 650; }
.cal-cell.day.non-working { background: var(--bg); color: var(--muted); opacity: .55; }
.cal-cell.day.today { box-shadow: inset 0 -2px 0 var(--accent); }
.cal-cell.day.selected { outline: 2px solid var(--accent); outline-offset: 1px; }

.cal-legend { display: flex; flex-wrap: wrap; align-items: center; gap: .35rem; margin-top: .7rem;
  font-size: .76rem; }
.cal-legend .key { width: 11px; height: 11px; border-radius: 3px; border: 1px solid var(--line);
  display: inline-block; margin-left: .5rem; }
.cal-legend .key.full { background: var(--ok-soft); border-color: var(--ok); }
.cal-legend .key.partial { background: var(--warn-soft); border-color: var(--warn); }
.cal-legend .key.none { background: var(--surface); }
.cal-legend .key.non-working { background: var(--bg); opacity: .55; }

/* Panels on a bed are selectable on the production floor. */
.bed-panel { cursor: pointer; }

/* A file input dressed as a button: choosing the file is the act. */
label.action.file { cursor: pointer; display: inline-flex; align-items: center; }

.project-head { display: flex; flex-wrap: wrap; gap: .75rem; align-items: flex-start;
  justify-content: space-between; margin-bottom: .8rem; }
.project-head h2 { margin: 0; }
.project-head p { margin: .15rem 0 0; font-size: .82rem; }
.project-head .marks { display: flex; gap: .5rem; align-items: center; flex-wrap: wrap; }

/* --------------------------------------------------- delivery programme */
/* Figures are read down a column and compared, so they are right-aligned and tabular — proportional
   digits make 8 and 11 hard to compare at a glance in a rate column. */
.num { text-align: right; font-variant-numeric: tabular-nums; white-space: nowrap; }
th.num { text-align: right; }
.small { font-size: .78rem; }
tr.row-stop td { background: var(--stop-soft); }
