/* ==========================================================================
   Node Studio, in the SITE's clothes.

   The brief was explicit: do not dress the website like the app. So the
   surfaces, the type and the rules here are the site's — bone, coal, the
   orange accent, hard edges, radius 0. What IS borrowed from the app is the
   part that carries meaning rather than style: the four signal hues, the
   212px node, the paired port rows. A wire on this page is the colour it
   would be in the product, and nothing else is.
   ========================================================================== */


/* ---------- signal hues, per ground ----------
   The app's four hues are tuned for its own dark canvas. Dropped straight onto
   this site's bone they stop being legible: --program measures 1.64:1 against
   bone and --info 2.19:1, so a "live" wire would have been a suggestion rather
   than a line. Each hue therefore has a bone-ground twin, darkened until it
   clears 3:1 as a graphical object, and the status WORDS clear 4.5:1 as text.
   On coal the app's own values are already strong and are used unchanged.

     bone ground            measured vs #F0EDE6
     --w-program #8A6A00    4.34:1
     --w-stream  #6D28D9    6.08:1
     --w-control #8A8279    3.24:1
     --w-failover #B91C1C   5.53:1
     --w-live    #0F766E    4.68:1                                          */
.stu, .stu-host, .stg__canvas{
  --w-program:#8A6A00;
  --w-stream:#6D28D9;
  --w-control:#8A8279;
  --w-failover:#B91C1C;
  --w-live:#0F766E;
}
.plate--coal .stu, .plate--coal .stu-host, .plate--coal .stg__canvas{
  --w-program:var(--program);
  --w-stream:var(--stream);
  --w-control:var(--control);
  --w-failover:var(--failover);
  --w-live:var(--info);
}

.stu {
  /* A fixed pixel layout scaled as a whole rather than reflowed — a node graph
     that reflows is a different graph. The scale factor is set by studio.js,
     which can measure the container; CSS cannot resolve a percentage inside
     min() for the `scale` property, and trying rendered every graph tiny. */
  width: 100%;
  /* No sideways scroll. The canvas is scaled by studio.js to whatever width it
     is given, so nothing ever extends past this box. */
  overflow: hidden;
  /* No panel and no grid: the graph sits on the plate it is in. A framed,
     tinted canvas made every diagram read as a screenshot pasted into the
     page; without it the wiring is just part of the section. */
  background: transparent;
  border: 0;
}

.stu__scale {
  position: relative;
  transform-origin: 0 0;
  /* `scale` and the wrapper's height are both written by studio.js on mount
     and on resize. See fit() there. */
}

.stu__wires { position: absolute; inset: 0; pointer-events: none; }
.stu__wire { fill: none; stroke-width: 1.5; }
.stu__wire--host { stroke-dasharray: 4 4; opacity: .5; }
.stu__wire.is-faint { opacity: .34; }
.stu__wire.is-setup { stroke-dasharray: 5 4; }

.stu__nodes { position: absolute; inset: 0; }

/* ---------- the node ---------- */
.stu__node {
  position: absolute;
  /* Unfilled, so the plate shows through. The outline and the hairlines carry
     the box. ONE consequence worth knowing: with no fill, a wire routed across
     a node is visible through it, so the layouts in graphs.js are arranged so
     no wire crosses a node body. Drag one over a wire and you will see it — a
     fill would hide that, and hiding it is what we gave up. */
  background: transparent;
  border: 1px solid var(--coal);
  border-radius: 0;
  cursor: grab;
  touch-action: none;          /* or a drag scrolls the page on a phone */
  user-select: none;
  padding-bottom: 6px;
}
.plate--coal .stu__node { border-color: #5A534B; color: var(--on-coal); }
.stu__node.is-drag { cursor: grabbing; border-color: var(--beam); }
.stu__node:focus-visible { outline: 3px solid var(--beam); outline-offset: 2px; }

.stu__head {
  height: 36px;               /* = HEADH */
  display: flex; align-items: center; gap: 8px;
  padding: 0 9px;
  border-bottom: 1px solid var(--rule-bone);
}
.plate--coal .stu__head { border-bottom-color: #453F39; }

.stu__ttl {
  font-size: 12.5px; font-weight: 700; letter-spacing: -.01em;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}

.stu__stat {
  margin-left: auto; display: flex; align-items: center; gap: 5px;
  font-family: var(--mono); font-size: 9px; letter-spacing: .1em;
  text-transform: uppercase; color: var(--on-bone-3); white-space: nowrap;
}
.plate--coal .stu__stat { color: var(--on-coal-3); }
.stu__dot {
  width: 6px; height: 6px; border-radius: 50%;
  background: var(--on-bone-3); flex: none;
}
.plate--coal .stu__dot { background: var(--on-coal-3); }

/* State is carried by the dot AND the word, never by colour alone — the same
   rule the product's own dashboard follows. */
.is-live  .stu__dot { background: var(--w-live); }
.is-error .stu__dot { background: var(--w-failover); }
.is-rec   .stu__dot { background: var(--w-stream); }
.is-setup .stu__dot { background: var(--beam); }
.is-live  .stu__stat { color: var(--w-live); }
.is-error .stu__stat { color: var(--w-failover); }
.is-rec   .stu__stat { color: var(--w-stream); }

.stu__kind {
  /* Fixed 20px = KINDH in studio.js. If this height changes, that constant
     changes with it or the wires stop meeting the ports. */
  height: 20px; display: flex; align-items: center;
  font-family: var(--mono); font-size: 9px; letter-spacing: .1em;
  text-transform: uppercase; color: var(--on-bone-3);
  padding: 0 9px;
}
.plate--coal .stu__kind { color: var(--on-coal-3); }

/* ---------- port rows ---------- */
.stu__body { padding: 2px 0; }
.stu__row {
  height: 26px;               /* = ROWH */
  display: flex; align-items: center; gap: 6px;
  padding: 0 9px;
  position: relative;
}
.stu__plabel { font-size: 11px; font-weight: 600; white-space: nowrap; }
.stu__ptype {
  font-family: var(--mono); font-size: 8.5px; letter-spacing: .08em;
  text-transform: uppercase; color: var(--on-bone-3);
}
.plate--coal .stu__ptype { color: var(--on-coal-3); }
.stu__right { margin-left: auto; display: flex; align-items: center; gap: 6px; }

.stu__port {
  width: 9px; height: 9px; border-radius: 50%;
  background: var(--pc); flex: none;
  position: absolute;
  /* An absolutely positioned flex child is NOT centred by align-items, so the
     centring is explicit — otherwise every port floats to the row's top edge
     while the wire maths aims for its middle. */
  top: 50%; margin-top: -4.5px;
}
.stu__port--in  { left: -5px; }
.stu__port--out { right: -5px; }
/* Hollow = the port exists and nothing is plugged in. "Ports are anatomy,
   wires are state" is the product's own law, and this ring is where a visitor
   can actually see it. */
.stu__port.is-open,
.stu__port.is-add {
  background: transparent;
  box-shadow: inset 0 0 0 1.5px var(--pc);
}

/* The bottom line of a node. Height must stay in step with METAH in
   studio.js, which the port and canvas maths both depend on. */
.stu__meta {
  height: 22px; display: flex; align-items: center;
  margin: 2px 9px 0;
  font-family: var(--mono); font-size: 8.5px; letter-spacing: .06em;
  color: var(--on-bone-3);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  border-top: 1px solid var(--rule-bone);
}
.plate--coal .stu__meta { color: var(--on-coal-3); border-top-color: #453F39; }

/* ---------- controls + fallback ---------- */
.stu__reset {
  margin-top: 12px;
  font-family: var(--mono); font-size: 10px; letter-spacing: .14em;
  text-transform: uppercase; font-weight: 600;
  background: transparent; color: var(--on-bone-2);
  border: 1px solid var(--on-bone-3); border-radius: 0;
  padding: 8px 13px; cursor: pointer;
}
.stu__reset:hover { color: var(--on-bone); border-color: var(--beam); }
.plate--coal .stu__reset { color: var(--on-coal-2); border-color: var(--on-coal-3); }
.plate--coal .stu__reset:hover { color: var(--on-coal); border-color: var(--beam); }

.stu-host{min-width:0}

.stu__hint {
  font-family: var(--mono); font-size: 10px; letter-spacing: .1em;
  text-transform: uppercase; color: var(--on-bone-3);
  margin-top: 10px;
}
.plate--coal .stu-host{min-width:0}

.stu__hint { color: var(--on-coal-3); }

/* The pipeline as a table, for no-JS, for a failed mount, and for anyone who
   would rather read the wiring than drag it. */
.stu-fb { width: 100%; border-collapse: collapse; margin-top: 4px; }
.stu-fb caption {
  text-align: left; font-family: var(--mono); font-size: 10px;
  letter-spacing: .12em; text-transform: uppercase;
  color: var(--on-bone-3); padding-bottom: 10px;
}
.plate--coal .stu-fb caption { color: var(--on-coal-3); }
.stu-fb th, .stu-fb td {
  text-align: left; font-size: 13px; padding: 9px 12px 9px 0;
  border-bottom: 1px solid var(--rule-bone);
}
.plate--coal .stu-fb th, .plate--coal .stu-fb td { border-bottom-color: var(--rule-coal); }
.stu-fb th {
  font-family: var(--mono); font-size: 9.5px; letter-spacing: .12em;
  text-transform: uppercase; color: var(--on-bone-3); font-weight: 600;
}
.plate--coal .stu-fb th { color: var(--on-coal-3); }

@media (prefers-reduced-motion: reduce) {
  .stu__node { transition: none; }
}


/* ---------- mobile ----------
   Measured, not assumed: the reset button came out 111x34 on a phone, under the
   44px a thumb reliably hits, and the hint is a full sentence rather than a
   label so 10px reads as texture at this size. */
@media (max-width: 640px) {
  .stu__reset { min-height: 44px; padding: 12px 16px; }
  .stu__hint { font-size: 11.5px; line-height: 1.5; }
}

/* ---------- stacked layout ----------
   One column, chosen by studio.js when the wide arrangement would have to scale
   below 0.85. The canvas is then narrow enough to render at nearly 1:1, so the
   type needs no compensation — which is the whole reason to restack rather than
   shrink.

   In a single column every wire leaves a node's RIGHT edge and re-enters a left
   edge below it, so wires necessarily pass across node boxes. Wires are painted
   under the node layer, so the fix is simply for a node to be opaque — and it is
   given its own plate's colour, so it still reads as sitting directly on the
   section exactly as the unfilled version does. Same appearance, minus the
   cables showing through the boxes. */
/* Everything the scale will allow, at small widths. The fixed heights the port
   maths depends on are untouched (HEADH 36, ROWH 26, METAH 22, KINDH 20), and
   each size below still fits its row.

   .stu__ptype goes entirely. It is the smallest text in the node and the most
   redundant — a port's type is already carried by the colour of its dot and of
   the wire leaving it — so dropping it buys the port NAME the room to grow,
   which is the label a reader actually needs. */
@media (max-width: 900px) {
  .stu__ttl    { font-size: 15px; }
  .stu__plabel { font-size: 14px; }
  .stu__kind   { font-size: 10.5px; letter-spacing: .06em; }
  .stu__stat   { font-size: 10.5px; letter-spacing: .06em; }
  .stu__meta   { font-size: 10px; letter-spacing: .02em; }
  .stu__ptype  { display: none; }
  .stu__port   { width: 11px; height: 11px; margin-top: -5.5px; }
  .stu__port--in  { left: -5.5px; }
  .stu__port--out { right: -5.5px; }
  .stu__wire   { stroke-width: 2; }
  .stu__node   { border-width: 1.5px; }
}
