/* CordisCanvas host (#50): the layout of the stage, the world and the item wrappers, and the live layer's settle
   crossfade. Global, not scoped: the markup is the contract the engines in cordis-ui.js read, and a consumer's own CSS
   (Studio's diagram sheets) reaches the same class names. Link it after app.css.

   D20, the rule this sheet is written around: the engine owns `transform`, `transition`, `width` and `height` on the
   mover body (`.cordis-canvas__body[data-cordis-mover]`), so NO rule here sets any of them on it. An item is placed by
   the custom properties the host renders on the wrapper (--cordis-item-x / -y / -w / -h / -z, in px), and the body
   fills the wrapper by its insets, not by a size. */

.cordis-canvas {
  position: relative;
  overflow: hidden;
  min-width: 0;
  min-height: 0;
  background-color: var(--cordis-bg);
  /* Its own stacking context: an item's z (the host's order, unbounded) and the gesture chrome above it are ranked
     inside the canvas and never against the page's popups. */
  isolation: isolate;
  /* The engines own every pointer gesture, touch included: the browser must not scroll or zoom the page under one. */
  touch-action: none;
}

.cordis-canvas:focus,
.cordis-canvas:focus-visible {
  outline: none;   /* the canvas draws its own selection */
}

/* The screen-space lattice: a <canvas> repainted on the pan-zoom frames, never part of the transformed plane. */
.cordis-canvas__lattice {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  pointer-events: none;
}

/* The pan-zoom content: the engine writes its transform. The world is the origin of every item coordinate. */
.cordis-canvas__viewport {
  position: absolute;
  inset: 0;
  transform-origin: 0 0;
}

/* A Free world is a point (items overflow it), a Fitted one is the design box the host sized inline. */
.cordis-canvas__world {
  position: absolute;
  left: 0;
  top: 0;
  width: 0;
  height: 0;
  transform-origin: 0 0;
}

.cordis-canvas__underlay,
.cordis-canvas__items,
.cordis-canvas__overlay {
  position: absolute;
  left: 0;
  top: 0;
  width: 100%;
  height: 100%;
}

/* Under and over the items, nothing takes a press unless it asks to: a press on the empty plane must reach the root. */
.cordis-canvas__underlay,
.cordis-canvas__overlay,
.cordis-canvas__screen {
  pointer-events: none;
}

.cordis-canvas__hit {
  pointer-events: auto;
}

.cordis-canvas__screen {
  position: absolute;
  inset: 0;
}

/* One item: placed by custom properties, never by a transform (the engine's glide uses the body's). */
.cordis-canvas__item {
  position: absolute;
  left: var(--cordis-item-x);
  top: var(--cordis-item-y);
  width: var(--cordis-item-w);
  height: var(--cordis-item-h);
  z-index: var(--cordis-item-z, 0);
}

.cordis-canvas__item:focus,
.cordis-canvas__item:focus-visible {
  outline: none;
}

.cordis-canvas__item:focus-visible > .cordis-canvas__body {
  box-shadow: var(--cordis-focus-ring);
}

/* The body fills the wrapper by its insets: width and height are the engine's to write while a gesture is drawn. */
.cordis-canvas__body {
  position: absolute;
  inset: 0;
}

/* An item in the hand stays above the others, and keeps that through the glide onto where the host rendered it. */
.cordis-canvas__item:has(> .cordis-canvas__body[data-cordis-gesture]),
.cordis-canvas__item:has(> .cordis-canvas__body[data-cordis-settling]) {
  z-index: 99999;
}

.cordis-canvas.is-editable .cordis-canvas__item:not([data-cordis-inert]) {
  cursor: grab;
  touch-action: none;
}

.cordis-canvas.is-editable .cordis-canvas__body[data-cordis-gesture="move"] {
  cursor: grabbing;
  opacity: 0.85;
}

/* An inert item (D19) is never pressed; only a .cordis-canvas__hit inside it may take one. */
.cordis-canvas__item--inert {
  pointer-events: none;
}

/* The selection ring: the canvas's one emphasis. :where() keeps it at zero specificity, so a host whose items draw
   their own (Studio's boxes) overrides it with a plain rule. */
:where(.cordis-canvas__item.is-selected) > .cordis-canvas__body {
  box-shadow: var(--cdg-emph-box);
}

/* An item a collaborator holds wears their colour (the colour is --cdg-remote, rendered on the wrapper). */
:where(.cordis-canvas__item.cdg-remote) > .cordis-canvas__body {
  box-shadow: 0 0 0 2px var(--cdg-remote, var(--cordis-primary));
}

/* The resize zones exist only on a selected, editable item, so they are simply shown; app.css draws their size. */
.cordis-canvas__body > .cdg-resize {
  display: block;
}

/* A marquee outlines the items it would select, when the host asks for it. Nothing of it is drawn on a create band. */
.cordis-canvas[data-cordis-marquee-outline] .cordis-canvas__item[data-cordis-marquee-hit] > .cordis-canvas__body {
  box-shadow: var(--cdg-emph-box);
}

.cordis-canvas[data-cordis-create="true"] .cordis-canvas__item[data-cordis-marquee-hit] > .cordis-canvas__body {
  box-shadow: none;
}

/* The band is appended to the world beside the items, and an item's --cordis-item-z is the host's own order with no
   ceiling (Studio's is its node order), so with no z-index of its own every item above 0 painted over it. It takes the
   top of the range: an item can only tie it, and the band, appended last, wins the tie. The root
   isolates, so this never lifts the band over anything outside the canvas. */
.cordis-canvas .cdg-marquee {
  z-index: 2147483647;
}

/* A create band is the marquee relabelled, so it is drawn as the create tool's ghost. Demoted (the tool ended
   mid-gesture) the page renders data-cordis-create="false" and the band takes the plain marquee look again. */
.cordis-canvas[data-cordis-create="true"] .cdg-marquee {
  background: color-mix(in srgb, var(--cordis-primary) 22%, transparent);
}

/* A preview reply's mark on an item (the nest target): the host's own CSS names what each mark looks like. */
.cordis-canvas__item[data-cordis-mark] > .cordis-canvas__body {
  box-shadow: var(--cdg-emph-box);
}

/* Read aloud, never seen: the keyboard help the list is described by, and the selection announcement. */
.cordis-canvas__sr {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/* The live layer: what follows the items during a gesture is drawn by the consumer into a [data-cordis-live] element it
   rendered empty. The cordisUi.canvas module sets [data-cordis-live-fading] on every live layer a consumer drew into when a
   gesture settles, and empties the layer when this transition ends (at once when the duration computes to 0s).
   A host that links this sheet gets the fade; one that does not gets an instant swap, never a stuck layer. */
[data-cordis-live] {
  opacity: 1;
  transition: opacity var(--cordis-canvas-settle) var(--cordis-ease);
}

[data-cordis-live][data-cordis-live-fading] {
  opacity: 0;
  pointer-events: none;
}

/* A committed original the layer was standing in for stays out of sight until the module removes the marker. */
[data-cordis-live-hidden] {
  visibility: hidden;
}

@media (prefers-reduced-motion: reduce) {
  [data-cordis-live] {
    transition-duration: 0s;
  }
}
