/* Archivo, a white ground, four colours, and a grid.
 *
 * Swiss typographic style, taken literally rather than as a mood: one typeface
 * at a few fixed sizes and exactly two weights, everything flush left and
 * ragged right, hierarchy carried by weight and position instead of ornament,
 * square corners, no shadows, no gradients. Every measurement is a multiple of
 * 4px so panels and rows land on the same lattice.
 *
 * Three rules this app holds to, and they are worth stating because each one
 * is a thing a designer reaches for by reflex:
 *   no rules      nothing is separated by a line. Whitespace does it, and the
 *                 spacing scale below is what makes that legible.
 *   no capitals   no label is set in capitals, ever. Weight and width are the
 *                 emphasis, and neither of them shouts.
 *   two weights   fat and thin, with nothing in between to blur the jump.
 *
 * The colour rule the calendar is built on:
 *   light blue  the subsystem is available, so a run may be placed
 *   blue        the satellite's own comms, and other people's runs
 *   orange      you, your selection, and the deadline
 *   red         a conflict, or a window nobody may task
 * Greys are structure only, so nothing is ever distinguished by grey alone.
 */

/* Archivo, self-hosted rather than fetched from Google's CDN: this runs on an
 * internal host behind a mesh, and a typeface that needs the public internet
 * would be the only part of the app that did. SIL OFL 1.1, fonts/OFL.txt. */
@font-face {
  font-family: "Archivo";
  src: url("/static/fonts/archivo-var.woff2") format("woff2-variations");
  font-weight: 100 900;
  font-stretch: 62% 125%;
  font-display: swap;
}

:root {
  --bg: #ffffff;
  --bg-soft: #f6f8fa;
  --bg-sink: #eceff3;
  --rule: #1b1f24;          /* structural hairlines: near-black, Swiss */
  --rule-soft: #ced4dc;
  --rule-faint: #e6eaef;
  --text: #1b1f24;
  --muted: #59626e;
  --faint: #8b939e;

  --blue: #1257a8;
  --blue-mid: #b3d3f2;
  --blue-wash: #e4f0fc;
  --orange: #e2680a;
  --orange-wash: #fdeddc;
  --red: #c62a1c;
  --red-wash: #fae3e0;

  /* One spacing scale, and nothing off it. Swiss whitespace is not "more
   * padding everywhere" — it is a small number of intervals used
   * consistently, so the eye learns the rhythm and grouping becomes legible
   * without boxes or rules to enforce it. */
  /* Two weights and no others. Hierarchy is carried by the jump between them,
     which is only legible if there is nothing in the middle to soften it: a
     600 next to a 700 reads as an accident, not as a level. */
  --w-fat: 800;
  --w-thin: 300;

  --u: 4px;
  --s0: 2px;    /* inside a label: text to its own edge */
  --s1: 4px;    /* inside a line: label to value */
  --s2: 8px;    /* between lines of one thing */
  --s3: 16px;   /* between things */
  --s4: 24px;   /* between groups of things */
  --s5: 40px;   /* between regions */
  --row-h: 72px;
  --gutter-w: 56px;
}

* { box-sizing: border-box; }

html { -webkit-font-smoothing: antialiased; }

body {
  margin: 0;
  background: var(--bg);
  color: var(--text);
  font-family: "Archivo", system-ui, sans-serif;
  font-size: 13px;
  /* Set explicitly: the browser default of 400 is neither of the two weights
     this app has, and it would appear wherever a rule forgot to say. */
  font-weight: var(--w-thin);
  line-height: 1.4;
  /* Figures line up in columns; a scheduler is mostly numbers. */
  font-variant-numeric: tabular-nums;
  height: 100vh;
  display: grid;
  grid-template-rows: auto 1fr auto;
  overflow: hidden;
}

/* Four sizes, no more. */
h2 { font-size: 20px; font-weight: var(--w-fat); letter-spacing: -0.01em; margin: 0; }
h3 { font-size: 16px; font-weight: var(--w-fat); letter-spacing: -0.01em; margin: 0; }

.eyebrow, .section-title, .panel-tab, .col-head .date, .card-line,
.band-label, .orbit-no, .hour-label, .commit, .kv dt, .kv dd, .mono,
.card-flag, .card-drag-hint, .seam-label, .run .st, .kind, .share-name, .share-figure {
  font-family: "Archivo", system-ui, sans-serif;
}

/* One narrow label style, used for every eyebrow in the app. Archivo's width
   axis does the narrowing, which is what it is for. Nothing in this app is set
   in capitals: weight, width and colour separate a label from its value, and
   they do it without shouting. The tracking is small for the same reason -
   wide tracking is an uppercase idiom and looks like a mistake on lowercase. */
.eyebrow, .section-title, .panel-tab, .rail-head, .col-head .dst {
  font-size: 11px;
  font-weight: var(--w-fat);
  letter-spacing: 0.01em;
  font-stretch: 88%;
}

button, select, textarea, input { font-family: inherit; font-size: inherit; color: inherit; }
button {
  cursor: pointer; background: var(--bg); border: 1px solid var(--rule-soft);
  border-radius: 0; padding: var(--s2) var(--s2);
  color: var(--text); font-weight: var(--w-fat);
}
button:hover:not(:disabled) { background: var(--bg-sink); }
button:disabled { cursor: not-allowed; color: var(--faint); background: var(--bg-soft); border-color: var(--rule-faint); }
button.primary { background: var(--orange); border-color: var(--orange); color: #fff; }
button.primary:hover:not(:disabled) { background: #c85a08; }
.spacer { flex: 1; }
.muted { color: var(--muted); }
a { color: var(--blue); }

/* ---------- header ---------- */
.header {
  display: flex; align-items: baseline; justify-content: space-between;
  gap: var(--s5); padding: var(--s3) var(--s4) var(--s4);
}
.header-left { display: flex; align-items: baseline; gap: var(--s2); }
/* Wider than the scale's "between things", because these three are not a list:
   they are who you are, how long you have, and the thing you press. */
.header-right { display: flex; align-items: baseline; gap: var(--s4); }
.brand { font-weight: var(--w-fat); font-stretch: 88%; }
.satellite { color: var(--muted); font-stretch: 88%; }
.clock-toggle { display: flex; border: 1px solid var(--rule-soft); }
.clock-toggle button { border: 0; border-radius: 0; font-size: 11px; font-stretch: 88%; padding: var(--s1) var(--s2); }
.clock-toggle button.is-active { background: var(--bg-sink); }
/* The one figure in the header. Orange while it is a fact, and only when it is
   about to cost somebody their pass does it become a block of red: an alarm
   that is always on is not an alarm. */
.commit { font-weight: var(--w-fat); color: var(--orange); white-space: nowrap; }
.commit.urgent { color: #fff; background: var(--red); padding: 1px var(--s2); }
/* Identity is a dropdown, and the screen says so. Styled as a control rather
   than as a name, because it is one. */
.who { font-size: 11px; color: var(--faint); font-stretch: 88%; }
.who select { margin-left: var(--s1); background: var(--bg); border: 1px solid var(--rule-soft); border-radius: 0; padding: 1px var(--s2); font-size: 12px; color: var(--text); }

/* ---------- layout: the lattice ---------- */
.main {
  display: grid; grid-template-columns: 248px 1fr 312px; min-height: 0;
  /* The rail, the calendar and the panel are separated by the gap between
     them and by nothing else. At --s5 the eye reads three regions without a
     rule having to say so, which is what the scale above is for. */
  gap: var(--s5); padding: 0 var(--s4) var(--s4);
}
.rail, .panel { min-height: 0; overflow-y: auto; background: var(--bg); }
.panel { display: flex; flex-direction: column; }
.rail-head {
  display: flex; align-items: center; justify-content: space-between; gap: var(--s3);
  padding: 0 0 var(--s3);
  color: var(--faint);
}
.rail-head button { font-size: 11px; font-stretch: 88%; padding: 2px var(--s2); }

/* ---------- rail ---------- */
#cards { display: flex; flex-direction: column; gap: var(--s3); }
.card {
  padding: var(--s3);
  border-left: var(--s1) solid var(--owner);
  cursor: grab; background: var(--bg);
}
.card:hover { background: var(--bg-soft); }
.card.is-selected { background: var(--blue-wash); }
.card.is-unplaceable { cursor: not-allowed; background: var(--bg-sink); }
.card-owner { font-weight: var(--w-fat); }
.card-name { color: var(--muted); }
.card-line { font-size: 11px; color: var(--muted); margin-top: var(--s1); font-stretch: 88%; }
.bar { height: 6px; background: var(--bg-sink); margin-top: var(--s2); }
.bar > i { display: block; height: 100%; background: var(--owner); }
.card-flag { font-size: 11px; margin-top: var(--s2); font-weight: var(--w-fat); font-stretch: 88%; }
.card-flag.bad { color: var(--red); }
.card-flag.ok { color: var(--muted); font-weight: var(--w-thin); }
.card-flag.warn { color: #8a4306; }
.card-drag-hint { font-size: 10px; color: var(--faint); margin-top: var(--s2); font-stretch: 88%; line-height: 1.5; }

/* ---------- calendar ---------- */
.calendar { display: flex; flex-direction: column; min-width: 0; min-height: 0; }
.cal-toolbar {
  display: flex; align-items: center; gap: var(--s5);
  padding: 0 0 var(--s3);
  font-size: 11px; color: var(--muted); flex-wrap: wrap;
}
.zoom select { background: var(--bg); border: 1px solid var(--rule-soft); border-radius: 0; padding: 1px var(--s2); }

.cal-scroll { overflow: auto; min-height: 0; flex: 1; }
.cal-grid { display: grid; column-gap: var(--s3); }
.time-col { width: var(--gutter-w); position: sticky; left: 0; z-index: 3; background: var(--bg); }
/* Width comes from the grid, so seven days share the screen. */
.day-col { position: relative; min-width: 0; }
.col-head {
  position: sticky; top: 0; z-index: 2; background: var(--bg);
  padding: 0 0 var(--s2);
  white-space: nowrap; font-size: 12px; overflow: hidden;
}
.col-head .dow { font-weight: var(--w-fat); }
.col-head .date { color: var(--muted); font-size: 11px; }
.col-head .dst { color: #8a4306; margin-left: var(--s1); }
.col-body { position: relative; }

/* Hour labels sit in their own column, clear of every rule in the grid. */
.hour-label {
  position: absolute; right: var(--s2); font-size: 10px; color: var(--muted);
  transform: translateY(-50%); background: var(--bg); padding: 0 var(--s2);
}
.seam { position: absolute; left: 0; right: 0; border-top: 2px dashed var(--orange); }
.seam-label {
  position: absolute; left: var(--s1); font-size: 10px; color: #8a4306; font-weight: var(--w-fat);
  background: var(--orange-wash); padding: 1px var(--s2); transform: translateY(-120%);
  font-stretch: 88%;
}

.layer { position: absolute; left: 0; right: 0; pointer-events: none; }
.avail { background: var(--blue-wash); }
.contact { background: var(--blue-mid); border-top: 1px solid var(--blue); border-bottom: 1px solid var(--blue); }
.maint { background: var(--red-wash); border-top: 1px solid var(--red); border-bottom: 1px solid var(--red); }

/* No label ever straddles a rule: each carries its band's own fill as an opaque
   background and is inset far enough that the band's border passes above it. */
.band-label {
  position: absolute; left: var(--s2); font-size: 10px; font-weight: var(--w-fat);
  letter-spacing: 0.04em; font-stretch: 75%; white-space: nowrap;
  padding: 0 var(--s2); pointer-events: none; z-index: 1;
}
.band-label.for-contact { color: #0b3c73; background: var(--blue-mid); }
.band-label.for-maint { color: #8f1f13; background: var(--red-wash); }
.band-label.for-nodata { color: var(--muted); background: var(--bg); font-weight: var(--w-fat); }

.orbit-line { border-top: 2px dashed var(--rule-soft); }
/* Offset below its line, with an opaque ground, so the dashes never run
   through the digits. */
.orbit-no {
  position: absolute; right: var(--s1); font-size: 10px; color: var(--muted);
  background: var(--bg); padding: 0 var(--s2); transform: translateY(2px);
}
.beyond-horizon { background: repeating-linear-gradient(45deg, #fff, #fff 4px, var(--bg-sink) 4px, var(--bg-sink) 8px); }
.horizon-edge { border-top: 2px solid var(--rule); }
.now-line { position: absolute; left: 0; right: 0; border-top: 2px solid var(--orange); z-index: 2; pointer-events: none; }
.now-line::after {
  content: "now"; position: absolute; left: var(--s1); top: -7px; font-size: 10px; font-weight: var(--w-fat);
  color: #fff; background: var(--orange); padding: 0 var(--s2);
  font-stretch: 88%;
}

.run {
  position: absolute; left: var(--s2); right: var(--s3); z-index: 1;
  background: var(--owner); border: 1px solid var(--owner); color: #fff;
  overflow: hidden; padding: 0 var(--s1); font-size: 11px; line-height: 1.2;
  font-weight: var(--w-fat); cursor: grab;
  /* Archivo's width axis, not a smaller size: at a week's density the names
     have to narrow rather than shrink, or they stop being letters. */
  font-stretch: 75%;
}
.run.is-selected { outline: 2px solid var(--orange); outline-offset: 2px; }
.run.is-conflicted { background: var(--red); border-color: var(--red); }
/* Frozen: the plan has been committed to it, and it stops being draggable. */
.run.is-frozen { cursor: default; box-shadow: inset 0 0 0 1px #fff; }
.run.is-dragging { cursor: grabbing; opacity: .85; }
.run .st { display: block; font-weight: var(--w-thin); font-size: 9px; opacity: .92; }
.drop-ghost {
  position: absolute; left: var(--s2); right: var(--s3);
  border: 2px dashed var(--orange); background: var(--orange-wash); pointer-events: none; z-index: 4;
}
.drop-ghost::after {
  content: attr(data-label); position: absolute; left: var(--s1); top: 2px;
  font-size: 10px; font-weight: var(--w-fat); color: #8a4306; background: var(--orange-wash);
  padding: 0 var(--s2); font-stretch: 75%;
}

/* ---------- panel ---------- */
.panel-tabs { display: flex; gap: var(--s3); }
.panel-tab {
  flex: 1; background: transparent; border: 0; border-bottom: 2px solid transparent;
  color: var(--muted); padding: var(--s3) var(--s1);
}
.panel-tab.is-active { color: var(--text); border-bottom-color: var(--rule); }
.count { background: var(--bg-sink); padding: 0 var(--s2); font-size: 9px; color: var(--muted); }
.panel-body { padding: var(--s3) 0; overflow-y: auto; flex: 1; }
.kv { display: grid; grid-template-columns: 84px 1fr; gap: var(--s2) var(--s3); margin: var(--s2) 0 0; }
.kv dt { color: var(--faint); font-size: 11px; font-weight: var(--w-fat); font-stretch: 88%; align-self: center; }
.kv dd { margin: 0; font-size: 12px; }
/* The largest interval in the panel, because a section heading is the only
   thing there that starts a new idea. */
.section-title {
  color: var(--text); margin: var(--s5) 0 var(--s2);
  padding-bottom: var(--s1);
}
.panel-body > .section-title:first-child { margin-top: 0; }
.row-actions { display: flex; gap: var(--s2); flex-wrap: wrap; margin-top: var(--s5); }
.conflict {
  border-left: 4px solid var(--rule-soft); padding: var(--s2) var(--s3);
  margin-bottom: var(--s2); background: var(--bg-soft); font-size: 12px; line-height: 1.5;
}
.conflict.blocking { border-left-color: var(--red); background: var(--red-wash); }
.conflict .kind {
  font-size: 11px; font-weight: var(--w-fat);
  color: var(--muted); display: block; font-stretch: 88%;
}
.postmortem { color: var(--red); font-size: 11px; }

/* ---------- toasts ---------- */
.toasts { position: fixed; right: var(--s4); bottom: 80px; display: flex; flex-direction: column; gap: var(--s2); z-index: 40; }
.toast {
  background: var(--bg); border: 1px solid var(--rule); border-left: 4px solid var(--blue);
  padding: var(--s3) var(--s4); max-width: 400px; font-size: 12px; line-height: 1.5;
}
.toast.bad { border-left-color: var(--red); background: var(--red-wash); }

/* Startup failure. Built from DOM rather than innerHTML with a style
   attribute, for the same reason. */
.fatal { padding: var(--s5); color: var(--red); font-size: 13px; }

/* A proposed run: drawn as not yet real, because it is not. */
.run.is-proposed {
  background: var(--bg); color: var(--owner); border: 2px dashed var(--owner);
}

/* The fair split. Minutes, not runs: the bar is the unit being equalised, so
   it is the thing drawn, and the figure beside it carries the run count. */
.share { display: grid; grid-template-columns: 1fr; gap: 2px; margin-bottom: var(--s2); }
.share-name { font-size: 11px; font-weight: var(--w-fat); }
.share-track { display: flex; height: 8px; background: var(--bg-sink); }
.share-track > i.held { background: var(--blue); }
.share-track > i.added { background: var(--orange); }
.share-figure { font-size: 10px; color: var(--muted); font-stretch: 88%; }
.note { font-size: 11px; line-height: 1.5; margin: var(--s2) 0 0; }

/* A run clipped at a day boundary: square off the cut edges so it reads as
   continuing into the neighbouring column rather than as a shorter run. */
.run.is-continued { border-top-width: 0; border-bottom-width: 0; }
