/* =============================================================================
   ODUEX feature-canvas.css
   SIGNATURE FEATURE 1: the Live Workflow Canvas.

   Every selector is scoped to .wf-canvas and every class is wf-* prefixed, so
   this file cannot reach a single shared component. It declares no token, no
   font-family, no ad-hoc colour and no ad-hoc length: every value below comes
   from core.css.

   THREE DECISIONS WORTH KNOWING BEFORE EDITING

   1. NO GOLD. Not one declaration. The first viewport already spends its three
      gold moments on the nav CTA, the hero CTA and the <em> in the H1. The
      canvas earns its separation from semantic green and from elevation
      instead. Do not add a gold hairline "just to tie it in". That is exactly
      how the old site reached 132 gold references on one page.

   2. THE BASE STATE IS THE FINISHED RUN. With JavaScript off, every node reads
      as completed, with its label, its detail line and its timestamp all
      present in the markup. `.js`, set by boot.js before first paint, is what
      resets the nodes to idle so the run has somewhere to start from. So a
      broken module leaves a readable flow diagram at rest, never a blank box,
      and there is no flash of green resolving to grey.

   3. ONE AMBIENT LOOP. The pulsing .dot-live in the status bar, which comes
      from core.css and stops below 480px and under reduced motion. Nothing else
      in this file animates on a loop. Node state changes are transitions on
      transform, border-colour and background-colour only.
   ============================================================================= */

.wf-canvas{
  width: 100%;
}

/* ── Shell ─────────────────────────────────────────────────────────────────
   The device frame. Descended from the .ipad-shell on the old automations
   page, which is the most on-brand asset ODUEX owns: it shows an automation
   running rather than describing one. */

.wf-canvas .wf-shell{
  background: var(--paper-card);
  border: 1px solid var(--line);
  border-radius: var(--r-xl);
  padding: var(--space-2);
  box-shadow: var(--elev-3);
}

.wf-canvas .wf-bar{
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-2) var(--space-3) var(--space-3);
}
.wf-canvas .wf-bar-dots{
  display: flex;
  gap: var(--space-1);
  flex: 0 0 auto;
}
.wf-canvas .wf-bar-dots span{
  width: var(--space-2);
  height: var(--space-2);
  border-radius: var(--r-pill);
  background: var(--line-strong);
}
.wf-canvas .wf-bar-name{
  margin: 0;
  max-width: none;
  font-size: var(--fs-xs);
  line-height: var(--lh-tight-ui);
  letter-spacing: var(--ls-label);
  text-transform: uppercase;
  color: var(--ink-muted);
}
.wf-canvas .wf-bar-run{
  margin-left: auto;
  flex: 0 0 auto;
  font-size: var(--fs-xs);
  color: var(--ink-faint);
}

.wf-canvas .wf-screen{
  background: var(--paper);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  padding: var(--space-4);
}

/* ── Nodes ─────────────────────────────────────────────────────────────────
   Base = the completed run. See decision 2 in the header. */

.wf-canvas .wf-nodes{
  display: grid;
  gap: var(--space-3);
}

.wf-canvas .wf-node{
  position: relative;
  padding: var(--space-3);
  border: 1px solid var(--ok-fill);
  border-radius: var(--r-md);
  background: color-mix(in srgb, var(--ok-fill) 6%, var(--paper));
  transition: transform var(--d-base) var(--ease-out-expo),
              border-color var(--d-base) var(--ease-out-quart),
              background-color var(--d-base) var(--ease-out-quart);
}

/* The connector between two nodes: a hairline track with a fill that draws
   downward on scaleY as the run reaches the node below it. Positioned from
   the icon column so it lines up with the glyph centre at every width. */
.wf-canvas .wf-node + .wf-node::before,
.wf-canvas .wf-node + .wf-node::after{
  content: '';
  position: absolute;
  left: calc(var(--space-3) + var(--space-6) / 2);
  top: calc(var(--space-3) * -1 - 1px);
  width: 1px;
  height: calc(var(--space-3) + 2px);
}
.wf-canvas .wf-node + .wf-node::before{ background: var(--line-strong); }
.wf-canvas .wf-node + .wf-node::after{
  background: var(--ok-fill);
  transform-origin: top;
  transition: transform var(--d-base) var(--ease-out-quart);
}

/* The timestamp column is a FIXED token width, not auto. A stamp appearing or
   clearing must not reflow the label beside it: this element sits in the first
   viewport and the CLS budget for the page is 0.02. */
.wf-canvas .wf-node-row{
  display: grid;
  grid-template-columns: var(--space-6) minmax(0, 1fr) var(--space-16);
  align-items: center;
  gap: var(--space-3);
}

.wf-canvas .wf-node-icon{
  /* wcag: ui, a 16px stroke glyph, not text, so 3:1 applies (this is 4.45:1) */
  display: grid;
  place-items: center;
  width: var(--space-6);
  height: var(--space-6);
  border-radius: var(--r-sm);
  background: color-mix(in srgb, var(--ok-fill) 14%, var(--paper));
  color: var(--ok);
  transition: background-color var(--d-base) var(--ease-out-quart),
              color var(--d-base) var(--ease-out-quart);
}
/* core.css sets `svg { fill: currentColor }`, which outranks a fill="none"
   attribute. These are stroke icons, so the fill is cleared here. */
.wf-canvas .wf-node-icon svg{
  width: var(--space-4);
  height: var(--space-4);
  fill: none;
  stroke: currentColor;
}

.wf-canvas .wf-node-body{
  display: grid;
  gap: var(--space-1);
  min-width: 0;
}
.wf-canvas .wf-node-label{
  font-size: var(--fs-sm);
  font-weight: 600;
  line-height: var(--lh-tight-ui);
  color: var(--ink);
}
/* Single line, clipped. A detail line that wraps would change the node height
   mid-run, and the CLS budget for this page is 0.02. */
.wf-canvas .wf-node-sub,
.wf-canvas .wf-node-value{
  font-size: var(--fs-xs);
  line-height: var(--lh-tight-ui);
  color: var(--ink-muted);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.wf-canvas .wf-node-sub:empty{ display: none; }

/* The classification line is emptied at idle and refilled token by token. The
   zero-width space keeps a line box in existence while it is empty, so the node
   holds its height through the whole run instead of collapsing and re-growing. */
.wf-canvas .wf-node-value::after{ content: '\200B'; }

.wf-canvas .wf-node-stream{
  font-size: var(--fs-xs);
  color: var(--ink);
}
/* The caret exists only while tokens are arriving. It does not blink: the
   status dot is the one ambient loop this viewport is allowed. */
.wf-canvas .wf-caret{
  display: none;
  width: 1px;
  height: 1em;
  margin-left: var(--space-1);
  background: var(--ink-muted);
  vertical-align: text-bottom;
}
.wf-canvas .wf-node.is-streaming .wf-caret{ display: inline-block; }

.wf-canvas .wf-node-time{
  font-size: var(--fs-xs);
  color: var(--ok);
  text-align: right;
  white-space: nowrap;
}

/* ── Idle state, only ever reached when JavaScript is available ─────────── */

.js .wf-canvas .wf-node:not(.is-done){
  border: 1px solid var(--line);
  background: var(--paper);
}
.js .wf-canvas .wf-node:not(.is-done) .wf-node-icon{
  background: var(--paper-sunk);
  color: var(--ink-muted);
}
.js .wf-canvas .wf-node:not(.is-done) .wf-node-label{ color: var(--ink-muted); }
.js .wf-canvas .wf-node:not(.is-done) .wf-node-sub,
.js .wf-canvas .wf-node:not(.is-done) .wf-node-value,
.js .wf-canvas .wf-node:not(.is-done) .wf-node-stream,
.js .wf-canvas .wf-node:not(.is-done) .wf-node-time{ color: var(--ink-faint); }
.js .wf-canvas .wf-node + .wf-node:not(.is-done)::after{ transform: scaleY(0); }

/* ── Running: the classification node while it waits for an answer ──────── */

.js .wf-canvas .wf-node.is-running{
  border: 1px solid var(--control-line);
  background: var(--paper);
}
.js .wf-canvas .wf-node.is-running .wf-node-label{ color: var(--ink); }
.js .wf-canvas .wf-node.is-running .wf-node-icon{
  background: var(--paper-sunk);
  color: var(--ink);
}

/* ── The settle. The node lifts 2px as it fires and drops back. ─────────── */

.wf-canvas .wf-node.is-firing{ transform: translateY(-2px); }

/* ── The trigger node carries the input: the flow starts where you type ─── */

.wf-canvas .wf-form{
  display: grid;
  gap: var(--space-2);
  margin-top: var(--space-3);
  padding-top: var(--space-3);
  border-top: 1px solid var(--line);
}
.wf-canvas .wf-form-label{
  font-size: var(--fs-xs);
  font-weight: 600;
  line-height: var(--lh-tight-ui);
  color: var(--ink);
}
.wf-canvas .wf-form-row{
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
}
.wf-canvas .wf-input{
  flex: 1 1 60%;
  min-width: 0;
  min-height: 44px;
  padding: var(--space-2) var(--space-3);
  background: var(--field-bg);
  color: var(--ink);
  border: 1px solid var(--control-line);
  border-radius: var(--r-sm);
  font-size: var(--fs-sm);
  line-height: var(--lh-tight-ui);
  transition: border-color var(--d-fast) var(--ease-out-quart);
}
.wf-canvas .wf-input::placeholder{ color: var(--ink-faint); opacity: 1; }
.wf-canvas .wf-input:hover{ border-color: var(--ink-faint); }
.wf-canvas .wf-input:focus{ border-color: var(--ink); }
.wf-canvas .wf-run{ flex: 0 0 auto; }
.wf-canvas .wf-hint{
  max-width: none;
  font-size: var(--fs-xs);
  line-height: var(--lh-tight-ui);
  color: var(--ink-muted);
}

/* ── Status bar ─────────────────────────────────────────────────────────── */

.wf-canvas .wf-status{
  display: flex;
  align-items: center;
  gap: var(--space-2);
  max-width: none;              /* it is a <p>; core caps every p at 68ch */
  margin-top: var(--space-4);
  padding-top: var(--space-3);
  border-top: 1px solid var(--line);
}
.wf-canvas .wf-status .mono{
  font-size: var(--fs-xs);
  color: var(--ink-muted);
}

/* ── Under 768px: the compact four-node stack ──────────────────────────────
   The two nodes that come off are the ones whose story is already told by the
   nodes above them. They are display:none rather than hidden by opacity, so
   the module reads them out of the run and the action count stays truthful. */

@media (max-width: 767px){
  .wf-canvas .wf-bar{ display: none; }
  .wf-canvas .wf-shell{ border-radius: var(--r-lg); }
  .wf-canvas .wf-screen{ padding: var(--space-3); }
  .wf-canvas .wf-nodes{ gap: var(--space-2); }
  .wf-canvas .wf-node{ padding: var(--space-2) var(--space-3); }
  .wf-canvas .wf-node + .wf-node::before,
  .wf-canvas .wf-node + .wf-node::after{
    top: calc(var(--space-2) * -1 - 1px);
    height: calc(var(--space-2) + 2px);
  }
  .wf-canvas [data-wf-node="calendar"],
  .wf-canvas [data-wf-node="notify"]{ display: none; }
  .wf-canvas .wf-status{ margin-top: var(--space-3); }
}

/* Core's rule for the whole site: no animation below 480px except reveals and
   the drawer. State still changes colour, it just stops moving. */
@media (max-width: 479px){
  .wf-canvas .wf-node{
    transition: border-color var(--d-base) var(--ease-out-quart),
                background-color var(--d-base) var(--ease-out-quart);
  }
  .wf-canvas .wf-node.is-firing{ transform: none; }
  .wf-canvas .wf-node + .wf-node::after{ transition: none; }
}

/* ── Reduced motion ────────────────────────────────────────────────────────
   core.css already neutralises every duration and the status dot. The module
   mirrors it in JavaScript by painting the finished state at mount with no
   stepping at all, so these are belt and braces rather than the mechanism. */

@media (prefers-reduced-motion: reduce){
  .wf-canvas .wf-node.is-firing{ transform: none !important; }
  .wf-canvas .wf-node + .wf-node::after{ transform: none !important; }
  .wf-canvas .wf-caret{ display: none !important; }
}

/* ── Print ─────────────────────────────────────────────────────────────────
   On paper the canvas is a diagram, not a control. */

@media print{
  .wf-canvas .wf-form{ display: none; }
  .wf-canvas .wf-shell{ box-shadow: none; }
}
