/* Glass-cockpit palette. In a cockpit colour carries meaning rather than
   decorating: magenta is the active leg, cyan is water, amber is caution. The
   panel borrows that vocabulary instead of picking one accent hue. */
:root {
  color-scheme: dark;
  --bezel:    #070B10;
  --panel:    #0B1016;
  --raised:   #131C25;
  --inset:    #05080C;
  --rule:     #1E2A36;
  --rule-lit: #33465A;
  --data:     #E8F0F7;
  --dim:      #7D93A8;
  --magenta:  #FF2D9B;
  --cyan:     #38C6E8;
  --amber:    #FFB020;
  --warn:     #FF4D5E;

  --mono: ui-monospace, "SF Mono", SFMono-Regular, Menlo, Consolas, monospace;
  --sans: system-ui, -apple-system, "Segoe UI", sans-serif;
  /* Barlow Condensed is We Went Flying's display face; it is already being
     fetched for the label typefaces, so the bar costs no extra request. */
  --display: "Barlow Condensed", var(--sans);

  --bar-h: 46px;
}

* { box-sizing: border-box; }

body {
  margin: 0;
  background: var(--bezel);
  color: var(--data);
  font: 13px/1.5 var(--sans);
}

/* A strip of We Went Flying above the tool, so this reads as part of the site
   rather than a page that happens to share a domain. Its own colours, not the
   site's: the tool is a dark instrument panel and a paper-white bar over it
   would fight the render underneath. */
.wwf-bar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  height: var(--bar-h);
  padding: 0 20px;
  box-sizing: border-box;
  background: var(--panel);
  border-bottom: 1px solid var(--rule-lit);
}
.wwf-brand {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  color: var(--data);
  text-decoration: none;
  font: 700 15px/1 var(--display, var(--sans));
  letter-spacing: .14em;
  text-transform: uppercase;
}
.wwf-brand svg { color: var(--magenta); }
.wwf-brand:hover { color: var(--magenta); }
.wwf-bar-links { display: flex; align-items: center; gap: 18px; }
.wwf-bar-links a, .wwf-bar-links span {
  font: 600 11px/1 var(--sans);
  letter-spacing: .12em;
  text-transform: uppercase;
  text-decoration: none;
  color: var(--dim);
}
.wwf-bar-links a:hover { color: var(--data); }
.wwf-bar-links [aria-current] { color: var(--magenta); }

main {
  display: grid;
  grid-template-columns: 356px 1fr;
  height: calc(100vh - var(--bar-h));
}

#panel {
  overflow-y: auto;
  padding-bottom: 28px;
  background: var(--panel);
  border-right: 1px solid var(--rule);
}

h1 {
  margin: 0;
  padding: 16px 20px 14px;
  border-bottom: 1px solid var(--rule);
  font: 600 12px/1 var(--sans);
  letter-spacing: .22em;
  text-transform: uppercase;
}
/* Power-on lamp. The panel is live the moment you land on it. */
h1::before {
  content: "";
  display: inline-block;
  width: 7px; height: 7px;
  margin-right: 10px;
  border-radius: 50%;
  background: var(--magenta);
  box-shadow: 0 0 8px var(--magenta);
  vertical-align: middle;
}

/* Placarded groups, the way an instrument panel is legended. */
section { padding: 18px 20px; border-bottom: 1px solid var(--rule); }

/* Collapsible groups. Native <details>, so they are keyboard-operable and
   need no script. Flight, label and export stay open because you touch them
   every time; the rest are settings you adjust once and leave alone. */
.group { padding: 0 20px; border-bottom: 1px solid var(--rule); }
.group > summary {
  display: flex;
  align-items: center;
  margin: 0 -20px;
  padding: 16px 20px;
  list-style: none;
  cursor: pointer;
}
.group > summary::-webkit-details-marker { display: none; }
.group > summary h2 { margin: 0; transition: color .12s; }
.group > summary::after {
  content: "+";
  margin-left: auto;
  font: 600 14px/1 var(--mono);
  color: var(--dim);
}
.group[open] > summary::after { content: "\2212"; }
.group > summary:hover h2, .group[open] > summary h2 { color: var(--data); }
.group > summary:focus-visible { outline: 2px solid var(--cyan); outline-offset: -2px; }
.group[open] { padding-bottom: 4px; }
.group[open] > summary { margin-bottom: 16px; }

h2 {
  margin: 0 0 14px;
  font: 600 10px/1 var(--sans);
  letter-spacing: .18em;
  text-transform: uppercase;
  color: var(--dim);
}

.drop {
  display: grid;
  place-items: center;
  min-height: 76px;
  padding: 14px;
  text-align: center;
  color: var(--dim);
  background: var(--inset);
  border: 1px dashed var(--rule-lit);
  border-radius: 2px;
  cursor: pointer;
  transition: border-color .12s, color .12s;
}
.drop:hover, .dragging .drop { border-color: var(--magenta); color: var(--data); }
.drop:focus-within { outline: 2px solid var(--magenta); outline-offset: 2px; }

/* Flight data block: readouts set like an avionics data field, values right
   aligned and tabular so digits stay put as they change. */
dl {
  display: grid;
  grid-template-columns: auto 1fr;
  margin: 14px 0 0;
  background: var(--inset);
  border: 1px solid var(--rule);
  border-radius: 2px;
}
dt, dd { padding: 6px 10px; border-bottom: 1px solid var(--rule); }
dt {
  align-self: center;
  font: 500 10px/1.4 var(--sans);
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--dim);
}
dd {
  margin: 0;
  text-align: right;
  font: 500 13px/1.4 var(--mono);
  font-variant-numeric: tabular-nums;
  color: var(--cyan);
}
dl > :nth-last-child(-n+2) { border-bottom: 0; }

label {
  display: block;
  margin-bottom: 15px;
  font: 500 11px/1.4 var(--sans);
  letter-spacing: .04em;
  color: var(--dim);
}

/* A note explains the control above it, so the next control needs air or the
   two read as one. */
.note + label, .note + .presets { margin-top: 18px; }

output {
  float: right;
  font: 500 12px var(--mono);
  font-variant-numeric: tabular-nums;
  color: var(--data);
}

input[type=range] {
  -webkit-appearance: none;
  appearance: none;
  width: 100%;
  height: 18px;
  margin-top: 5px;
  background: transparent;
  cursor: pointer;
}
input[type=range]::-webkit-slider-runnable-track { height: 2px; background: var(--rule-lit); }
input[type=range]::-moz-range-track { height: 2px; background: var(--rule-lit); }
input[type=range]::-webkit-slider-thumb {
  -webkit-appearance: none;
  width: 4px; height: 14px;
  margin-top: -6px;
  border: 0; border-radius: 1px;
  background: var(--magenta);
}
input[type=range]::-moz-range-thumb {
  width: 4px; height: 14px;
  border: 0; border-radius: 1px;
  background: var(--magenta);
}
input[type=range]:focus-visible { outline: 2px solid var(--cyan); outline-offset: 3px; }

input[type=text] {
  width: 100%;
  margin-bottom: 8px;
  padding: 8px 10px;
  background: var(--inset);
  border: 1px solid var(--rule);
  border-radius: 2px;
  color: var(--data);
  font: 12px var(--mono);
}
input[type=text]:focus-visible { outline: 2px solid var(--cyan); outline-offset: -1px; }

select {
  width: 100%;
  margin-top: 5px;
  padding: 8px 10px;
  background: var(--inset);
  border: 1px solid var(--rule);
  border-radius: 2px;
  color: var(--data);
  font: 12px var(--mono);
}
select:focus-visible { outline: 2px solid var(--cyan); outline-offset: -1px; }

.check {
  display: flex;
  align-items: center;
  gap: 9px;
  margin: -4px 0 12px;
  cursor: pointer;
}
.check input { margin: 0; accent-color: var(--magenta); }

/* Filament loadout. Each swatch carries the extruder number the part is
   assigned to in the exported file, because that is what you need when the
   plate opens in the slicer. */
.swatch {
  display: grid;
  grid-template-columns: 30px 1fr 36px;
  align-items: center;
  gap: 12px;
  margin-bottom: 10px;
  padding: 9px 10px;
  background: var(--inset);
  border: 1px solid var(--rule);
  border-radius: 2px;
  font: 500 11px/1 var(--sans);
  letter-spacing: .04em;
  color: var(--data);
  cursor: pointer;
}
.swatch:focus-within { border-color: var(--cyan); }
.swatch .slot {
  font: 600 10px/1 var(--mono);
  letter-spacing: .06em;
  color: var(--dim);
}
.swatch input[type=color] {
  -webkit-appearance: none;
  appearance: none;
  width: 36px; height: 20px;
  padding: 0;
  border: 1px solid var(--rule-lit);
  border-radius: 2px;
  background: none;
  cursor: pointer;
}
.swatch input[type=color]::-webkit-color-swatch-wrapper { padding: 0; }
.swatch input[type=color]::-webkit-color-swatch { border: 0; border-radius: 1px; }
.swatch input[type=color]::-moz-color-swatch { border: 0; border-radius: 1px; }

/* Five colours that actually suit each part, next to the full picker for
   anything else. */
.presets {
  display: flex;
  gap: 6px;
  margin: -4px 0 14px;
}
.chip {
  width: 100%;
  height: 18px;
  padding: 0;
  margin: 0;
  background: var(--chip);
  border: 1px solid var(--rule-lit);
  border-radius: 2px;
  cursor: pointer;
}
.chip:hover { border-color: var(--data); }

select.filament {
  margin: -4px 0 14px;
  padding: 7px 9px;
  font: 11px var(--mono);
}

.size {
  width: 100%;
  margin: 0;
  padding: 8px 0;
  background: var(--inset);
  border: 1px solid var(--rule);
  border-radius: 2px;
  color: var(--dim);
  font: 500 11px/1 var(--mono);
  letter-spacing: .04em;
  text-transform: none;
  cursor: pointer;
}
.size:hover { color: var(--data); border-color: var(--rule-lit); filter: none; }
.size[aria-pressed="true"] { color: var(--magenta); border-color: var(--magenta); }
.chip:focus-visible { outline: 2px solid var(--cyan); outline-offset: 2px; }

/* A quiet way back to the defaults, so experimenting with colour costs nothing. */
.reset {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  width: auto;
  margin: 0 0 14px;
  padding: 6px 10px;
  background: transparent;
  border: 1px solid var(--rule);
  border-radius: 2px;
  color: var(--dim);
  font: 500 10px/1 var(--sans);
  letter-spacing: .1em;
  text-transform: uppercase;
  cursor: pointer;
}
.reset:hover { color: var(--data); border-color: var(--rule-lit); background: transparent; filter: none; }
.reset:focus-visible { outline: 2px solid var(--cyan); outline-offset: 2px; }

button {
  width: 100%;
  margin-bottom: 8px;
  padding: 11px;
  background: var(--magenta);
  color: #12000A;
  border: 0;
  border-radius: 2px;
  font: 600 11px/1 var(--sans);
  letter-spacing: .12em;
  text-transform: uppercase;
  cursor: pointer;
}
button.secondary { background: var(--raised); color: var(--data); border: 1px solid var(--rule-lit); }
button:hover:not(:disabled) { filter: brightness(1.15); }
button:disabled { background: var(--raised); color: var(--dim); border-color: var(--rule); cursor: not-allowed; }
button:focus-visible { outline: 2px solid var(--cyan); outline-offset: 2px; }

.note { margin: 8px 0 0; font: 11px/1.5 var(--sans); color: var(--dim); }
.note.warn { color: var(--amber); }

/* Amber is caution and red is warning, as they are in the cockpit. */
#messages { padding: 0 20px; }
#messages p {
  margin: 12px 0 0;
  padding: 9px 11px;
  border-left: 2px solid var(--amber);
  background: rgba(255, 176, 32, .07);
  color: var(--amber);
  font-size: 11.5px;
}
#messages p.error {
  border-left-color: var(--warn);
  background: rgba(255, 77, 94, .08);
  color: #FF8A95;
}

#viewport { position: relative; background: var(--bezel); overflow: hidden; }

/* A file over the window can land anywhere, so say so on the side the eye is
   on. The outline sits inside the viewport rather than around the whole page:
   the render is the target people aim at. */
.dragging #viewport::after {
  content: '';
  position: absolute;
  inset: 10px;
  border: 2px dashed var(--magenta);
  border-radius: 3px;
  pointer-events: none;
}
canvas { display: block; width: 100%; height: 100%; }

/* An empty viewport should say what to do, not sit blank. */
.stage {
  position: absolute;
  inset: 0;
  display: grid;
  place-items: center;
  pointer-events: none;
}
.stage[hidden] { display: none; }
.stage { gap: 16px; grid-auto-flow: row; align-content: center; }
.stage-cta {
  pointer-events: auto;
  margin: 0;
  padding: 14px 22px;
  min-height: 44px;
  display: inline-flex;
  align-items: center;
  background: var(--magenta);
  color: #12000A;
  border-radius: 2px;
  font: 600 11px/1 var(--sans);
  letter-spacing: .12em;
  text-transform: uppercase;
  cursor: pointer;
}
.stage-cta:hover { filter: brightness(1.15); }
.stage-hint {
  margin: 0;
  font: 500 10px/1 var(--mono);
  letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--rule-lit);
}

/* Fetching tiles and meshing take about ten seconds between them. The wait
   belongs where the model will appear, not in the sidebar where it reads as a
   status for something you have not started yet. */
.progress {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 18px;
  background: rgba(7, 11, 16, .72);
  font: 500 11px/1 var(--mono);
  letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--data);
}
.progress[hidden] { display: none; }
/* A heading indicator: the ring is the compass card, the magenta arc the bug. */
.spinner {
  width: 46px; height: 46px;
  border: 2px solid var(--rule-lit);
  border-top-color: var(--magenta);
  border-radius: 50%;
  animation: spin .9s linear infinite;
}
@keyframes spin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) {
  .spinner { animation-duration: 3s; }
}

.sheet-handle { display: none; }

.visually-hidden {
  position: absolute;
  width: 1px; height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

@media (prefers-reduced-motion: reduce) {
  * { transition: none !important; animation: none !important; }
}

/* Phone: the render gets the whole screen and the controls become a sheet that
   sits out of the way until you reach for it. Loading a flight happens in the
   viewport itself, so the sheet never has to be opened just to start. */
@media (max-width: 860px) {
  main {
    grid-template-columns: 1fr;
    grid-template-rows: 1fr;
    height: calc(100dvh - var(--bar-h));
  }

  #viewport { grid-area: 1 / 1; align-self: start; width: 100%; }

  #panel {
    grid-area: 1 / 1;
    align-self: end;
    z-index: 2;
    width: 100%;
    max-height: 78dvh;
    border-right: 0;
    border-top: 1px solid var(--rule-lit);
    border-radius: 12px 12px 0 0;
    box-shadow: 0 -18px 40px rgba(0, 0, 0, .45);
    transform: translateY(calc(100% - var(--sheet-peek, 60px)));
    transition: transform .26s ease;
    overscroll-behavior: contain;
  }
  #panel.open { transform: translateY(0); }

  .sheet-handle {
    position: sticky;
    top: 0;
    z-index: 1;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 8px;
    width: 100%;
    min-height: 60px;
    margin: 0;
    padding: 10px 20px 14px;
    background: var(--panel);
    border: 0;
    border-radius: 12px 12px 0 0;
    color: var(--dim);
    font: 600 10px/1 var(--sans);
    letter-spacing: .18em;
    text-transform: uppercase;
    cursor: pointer;
  }
  .sheet-handle .grip {
    width: 38px; height: 4px;
    border-radius: 2px;
    background: var(--rule-lit);
  }
  .sheet-handle:focus-visible { outline: 2px solid var(--cyan); outline-offset: -3px; }
  #panel.open .sheet-handle { border-bottom: 1px solid var(--rule); }

  /* The title is the desktop chrome; on a phone the handle already names it. */
  h1 { display: none; }

  /* Touch targets. A 4 mm-wide slider thumb is a mouse affordance. */
  input[type=range] { height: 34px; }
  input[type=range]::-webkit-slider-thumb { width: 10px; height: 26px; margin-top: -12px; }
  input[type=range]::-moz-range-thumb { width: 10px; height: 26px; }
  .group > summary { min-height: 52px; }
  button { padding: 14px; }
  .chip { height: 30px; }
  .swatch input[type=color] { width: 46px; height: 28px; }

  .progress { font-size: 10px; }
}

@media (prefers-reduced-motion: reduce) {
  #panel { transition: none; }
}

/* Which spool goes in which slot. The file assigns parts to slots but cannot
   name the colours, so this is where you read them off. */
.slots { display: grid; gap: 4px; margin: 10px 0 0; }
.slot { display: flex; align-items: center; gap: 8px; font: 400 11px/1.4 var(--mono); color: var(--dim); }
.slot .chip {
  width: 15px; height: 15px; flex: 0 0 auto;
  border: 1px solid var(--rule-lit); border-radius: 2px;
}
.slot .n { color: var(--data); }

/* Departure and arrival carry the same controls, so they need telling apart at
   a glance: a rule and a heading, indented enough to read as one thing. */
.field {
  margin-top: 16px;
  padding: 12px 0 2px;
  border-top: 1px solid var(--rule);
}
.field h3 {
  margin: 0 0 10px;
  font: 600 11px/1 var(--sans);
  letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--cyan);
}

/* The welcome, which shows a finished print. It opens once, because a modal
   that greets you every visit stops being a welcome and becomes a toll. */
.welcome {
  padding: 0;
  border: 1px solid var(--rule-lit);
  border-radius: 4px;
  background: var(--panel);
  color: var(--data);
  width: min(920px, calc(100vw - 32px));
  max-height: calc(100dvh - 32px);
  overflow: auto;
}
.welcome::backdrop { background: rgba(3, 6, 10, .78); }
.welcome figure { margin: 0; display: grid; }
.welcome img {
  display: block;
  width: 100%;
  height: auto;
  border-bottom: 1px solid var(--rule);
}
.welcome figcaption { padding: 20px 24px 24px; }
.welcome h2 {
  margin: 0 0 8px;
  font: 700 15px/1.2 var(--display, var(--sans));
  letter-spacing: .12em;
  text-transform: uppercase;
}
.welcome p { margin: 0 0 18px; color: var(--dim); max-width: 62ch; }
.welcome-go {
  font: 600 11px/1 var(--sans);
  letter-spacing: .12em;
  text-transform: uppercase;
  color: #12000A;
  background: var(--magenta);
  border: 0;
  border-radius: 2px;
  padding: 12px 20px;
  min-height: 40px;
  cursor: pointer;
}
.welcome-go:hover { filter: brightness(1.15); }

/* The way back to it, sitting quietly above the drop zone. */
.link-quiet {
  display: block;
  margin: 0 0 8px;
  padding: 0;
  background: none;
  border: 0;
  color: var(--dim);
  font: 400 11px/1.4 var(--sans);
  text-decoration: underline;
  text-underline-offset: 3px;
  cursor: pointer;
}
.link-quiet:hover { color: var(--magenta); }

/* Two columns once there is room for the photo to keep its shape. */
@media (min-width: 760px) {
  /* The photograph is the point, so it keeps its own shape rather than being
     cropped to fit a column. */
  .welcome figure { grid-template-columns: 1.2fr 1fr; align-items: center; }
  .welcome img { border-bottom: 0; border-right: 1px solid var(--rule); }
}

/* Runway rows carry a pair of arrows: parallel runways are drawn either side
   of the centre in list order, so the order is what decides which sits above
   the other, and only the eye can settle that. */
.rwy { display: flex; align-items: end; gap: 8px; }
.rwy label { flex: 1; min-width: 0; }
.rwy-order { display: flex; flex-direction: column; gap: 2px; padding-bottom: 2px; }
.rwy-move {
  width: 22px;
  height: 15px;
  padding: 0;
  display: grid;
  place-items: center;
  font: 11px/1 var(--sans);
  color: var(--dim);
  background: var(--raised);
  border: 1px solid var(--rule);
  border-radius: 2px;
  cursor: pointer;
}
.rwy-move:hover:not(:disabled) { color: var(--data); border-color: var(--rule-lit); }
.rwy-move:disabled { opacity: .3; cursor: default; }
