/* Components shared by every view. Two stroke weights (1 px hairline, 2 px emphasis), one corner
   (a 45 degree cut, top right). A view's own styles go in views/<view>.css. */

/* ---------------------------------------------------------------- plane */
.plane {
  --edge: var(--line);
  --edge-cut: var(--line-2);
  position: relative;
  min-width: 0;
  padding: var(--s4);
  background: var(--plane-a);
  border: var(--hair) solid var(--edge);
  clip-path: polygon(0 0, calc(100% - var(--cut)) 0, 100% var(--cut), 100% 100%, 0 100%);
}
.plane::after,
.btn::after,
.seg-ctl::after,
.field::after,
.lockchip::after {
  content: "";
  position: absolute;
  top: -1px;
  right: -1px;
  width: var(--c, var(--cut));
  height: var(--c, var(--cut));
  background: linear-gradient(to top right, transparent calc(50% - 0.75px), var(--edge-cut, var(--edge)) calc(50% - 0.75px), var(--edge-cut, var(--edge)) calc(50% + 0.75px), transparent calc(50% + 0.75px));
  pointer-events: none;
}
.plane.is-near {
  --edge: var(--line-2);
  --edge-cut: var(--line-3);
  background: var(--near-a);
}
/* A lifted plane leaves an outline where it would rest; the offset is the height it floats at.
   The plane and its text never move. The outline drifts a few pixels against it as the pointer
   moves, which is what reads as depth. */
.lifted {
  --echo: var(--line);
  position: relative;
  display: flex;
  flex-direction: column;
  min-width: 0;
  min-height: 0;
  /* The outline sits on the grid; the plane is drawn up and to the left of it. */
  margin: 0 var(--float) var(--float) 0;
}
.lifted-base {
  position: absolute;
  inset: 0;
  transform: translate3d(var(--float), var(--float), 0);
  pointer-events: none;
}
.lifted-echo {
  position: absolute;
  inset: 0;
  border: var(--hair) solid var(--echo);
  background: linear-gradient(to top right, transparent calc(50% - 0.75px), var(--echo) calc(50% - 0.75px), var(--echo) calc(50% + 0.75px), transparent calc(50% + 0.75px)) top right / var(--cut) var(--cut) no-repeat border-box;
  clip-path: polygon(0 0, calc(100% - var(--cut)) 0, 100% var(--cut), 100% 100%, 0 100%);
}
.lifted > .plane {
  flex: 1;
  min-height: 0;
}
.plane-head {
  display: flex;
  align-items: center;
  gap: var(--s2);
  min-height: 18px;
  margin-bottom: var(--s3);
  padding-right: var(--s2);
}
.plane-label {
  display: inline-flex;
  align-items: center;
  gap: var(--s2);
  color: var(--ink-2);
  white-space: nowrap;
}
.plane-label::before {
  content: "";
  width: var(--bold);
  height: 10px;
  background: var(--cyan);
}
.plane-rule {
  flex: 1;
  min-width: var(--s3);
  height: 1px;
  background: var(--line);
}
.plane-note {
  font-size: var(--t-mini);
  color: var(--ink-3);
}
.plane-readout {
  display: inline-flex;
  align-items: center;
  gap: var(--s2);
  font: 400 var(--t-mini)/1 var(--f-mono);
  color: var(--ink-3);
  white-space: nowrap;
}
.plane-readout.link {
  min-height: 40px;
  min-width: 40px;
  justify-content: flex-end;
  margin: calc(var(--s3) * -1) 0;
  font: 500 var(--t-mini)/1 var(--f-display);
  color: var(--cyan);
}
.plane-foot {
  margin-top: var(--s3);
  font-size: var(--t-small);
  color: var(--ink-3);
}

/* ---------------------------------------------------------------- buttons */
.btn {
  --edge: var(--line-2);
  --c: var(--cut-s);
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--s2);
  min-height: 40px;
  padding: 0 var(--s4);
  border: var(--hair) solid var(--edge);
  background: linear-gradient(rgba(98, 221, 242, 0.06), rgba(98, 221, 242, 0.06)), var(--far);
  color: var(--ink);
  font: 500 var(--t-ui) / 1 var(--f-display);
  letter-spacing: 0.02em;
  white-space: nowrap;
  clip-path: polygon(0 0, calc(100% - var(--cut-s)) 0, 100% var(--cut-s), 100% 100%, 0 100%);
  transition: transform var(--fast) var(--ease-out);
}
/* A link that is drawn as a button (it goes to another address) is not underlined. */
a.btn {
  text-decoration: none;
}
.btn::before {
  content: "";
  position: absolute;
  inset: 0;
  background: currentColor;
  opacity: 0;
  transition: opacity var(--fast);
}
.btn:hover:not([disabled]) {
  --edge: var(--line-3);
}
.btn:hover:not([disabled])::before {
  opacity: 0.1;
}
.btn:active:not([disabled]) {
  transform: scale(0.975);
}
.btn:focus-visible,
.seg-opt:focus-visible,
.lockchip:focus-visible {
  outline: none;
  box-shadow: inset 0 0 0 var(--bold) var(--ink);
}
.btn.is-primary {
  --edge: var(--cyan);
  background: linear-gradient(var(--cyan-fill-2), var(--cyan-fill-2)), var(--far);
}
.btn.is-warm {
  --edge: var(--gold-line);
  background: linear-gradient(var(--gold-fill), var(--gold-fill)), var(--far);
  color: var(--gold);
}
.btn.is-warm:hover:not([disabled]) {
  --edge: var(--gold);
}
.btn.is-danger {
  --edge: var(--red-line);
  background: linear-gradient(var(--red-fill), var(--red-fill)), var(--far);
  color: var(--red);
}
.btn.is-danger:hover:not([disabled]) {
  --edge: var(--red);
}
.btn.is-quiet {
  --edge: transparent;
  background: none;
  color: var(--ink-2);
  padding: 0 var(--s3);
  clip-path: none;
}
.btn.is-quiet::after {
  display: none;
}
.btn.is-quiet:hover {
  color: var(--ink);
}
.btn.is-small {
  min-height: 32px;
  padding: 0 var(--s3);
  font-size: var(--t-small);
}
.btn[disabled] {
  opacity: 0.45;
  cursor: default;
}
.btn.is-square {
  width: 40px;
  padding: 0;
}
.btn .icon,
.btn-label {
  position: relative;
}
@media (pointer: coarse) {
  .btn.is-small {
    min-height: 40px;
  }
}

/* ---------------------------------------------------------------- segmented choice */
.seg-ctl {
  --edge: var(--line-2);
  --c: var(--cut-s);
  position: relative;
  display: inline-flex;
  border: var(--hair) solid var(--edge);
  background: var(--far);
  clip-path: polygon(0 0, calc(100% - var(--cut-s)) 0, 100% var(--cut-s), 100% 100%, 0 100%);
}
.seg-opt {
  position: relative;
  min-width: 42px;
  min-height: 38px;
  padding: 0 var(--s3);
  border-left: var(--hair) solid var(--line);
  color: var(--ink-2);
  font: 500 var(--t-small)/1 var(--f-display);
  letter-spacing: 0.02em;
  white-space: nowrap;
}
.seg-opt:first-child {
  border-left: 0;
}
.seg-opt:hover {
  color: var(--ink);
  background: var(--cyan-fill);
}
.seg-opt[aria-pressed="true"] {
  color: var(--ink);
  background: var(--cyan-fill-2);
  box-shadow: inset 0 calc(var(--bold) * -1) 0 var(--cyan);
}
.seg-ctl.is-small .seg-opt {
  min-height: 30px;
  min-width: 36px;
  padding: 0 var(--s2);
  font-size: var(--t-mini);
}
/* Touch: every control is at least 40 px tall. */
@media (pointer: coarse) {
  .seg-opt,
  .seg-ctl.is-small .seg-opt,
  .hud-src,
  .insp-close {
    min-width: 40px;
    min-height: 40px;
  }
  /* The piece numbers become labels; "On the map" in the reader's header does the same job. */
  .piece-mark {
    pointer-events: none;
  }
}

/* ---------------------------------------------------------------- inputs */
.field {
  --edge: var(--line-2);
  --c: var(--cut-s);
  position: relative;
  display: flex;
  align-items: center;
  gap: var(--s2);
  min-height: 40px;
  padding: 0 var(--s3);
  background: rgba(5, 14, 22, 0.92);
  border: var(--hair) solid var(--edge);
  color: var(--ink-3);
  clip-path: polygon(0 0, calc(100% - var(--cut-s)) 0, 100% var(--cut-s), 100% 100%, 0 100%);
}
.field input,
.field select {
  flex: 1;
  min-width: 0;
  height: 38px;
  padding: 0;
  border: 0;
  background: none;
  color: var(--ink);
  font: 400 var(--t-ui) / 1 var(--f-read);
  outline: none;
  appearance: none;
}
.field input::placeholder {
  color: var(--ink-3);
  opacity: 1;
}
.field:focus-within {
  --edge: var(--ink);
  box-shadow: inset 0 0 0 var(--hair) var(--ink);
  color: var(--ink);
}
/* A phone's browser zooms the page when a field with smaller type takes the focus. */
@media (pointer: coarse) {
  .field input,
  .field select {
    font-size: 16px;
  }
}
.field-label {
  font: 500 var(--t-micro) / 1 var(--f-display);
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--ink-3);
  white-space: nowrap;
}

/* Marking box: warm when ticked, because a marked row is about to be acted on. */
.tick {
  display: grid;
  place-items: center;
  width: 40px;
  height: 40px;
  flex: none;
}
.tick-box {
  display: grid;
  place-items: center;
  width: 18px;
  height: 18px;
  border: var(--hair) solid var(--line-3);
  color: transparent;
}
.tick:hover .tick-box {
  border-color: var(--ink);
}
.tick[aria-checked="true"] .tick-box,
.tick[aria-checked="mixed"] .tick-box {
  border-color: var(--gold);
  background: var(--gold-fill);
  color: var(--gold);
}
.tick:focus-visible {
  outline-offset: -6px;
}

/* ---------------------------------------------------------------- readouts */
.readout {
  display: flex;
  flex-direction: column-reverse;
  gap: var(--s1);
  min-width: 0;
}
.readout-value {
  font-size: var(--t-head);
  font-weight: 400;
  line-height: 1.1;
  letter-spacing: -0.02em;
  color: var(--ink);
}
.kv {
  display: grid;
}
.kv-row {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: var(--s3);
  min-height: 32px;
  padding: var(--s1) 0;
  border-top: var(--hair) solid var(--line);
}
.kv-row dt {
  font: 500 var(--t-micro) / 1.2 var(--f-display);
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--ink-3);
  white-space: nowrap;
}
.kv-row dd {
  display: flex;
  flex-wrap: wrap;
  justify-content: flex-end;
  gap: 0 var(--s2);
  font: 400 var(--t-small) / 1.3 var(--f-mono);
  color: var(--ink);
  text-align: right;
  min-width: 0;
  overflow-wrap: anywhere;
}
.kv-sub {
  color: var(--ink-3);
}

/* A meter is two lines: a hairline track and an emphasis stroke for the value. */
.meter {
  position: relative;
  display: block;
  height: var(--bold);
  background: linear-gradient(var(--line), var(--line)) center / 100% 1px no-repeat;
  overflow: hidden;
}
.meter-fill {
  position: absolute;
  inset: 0;
  background: var(--cyan);
  transform-origin: left center;
  transform: scaleX(var(--v, 0));
}
.meter.is-warm .meter-fill {
  background: var(--gold);
}

/* Sources: told apart by the shape of their mark, not by colour. */
.src {
  display: inline-flex;
  align-items: center;
  gap: var(--s2);
  font: 500 var(--t-small) / 1 var(--f-display);
  letter-spacing: 0.02em;
  color: var(--ink-2);
  white-space: nowrap;
}
.src-mark {
  flex: none;
  width: 7px;
  height: 7px;
}
.src-mark.is-m0 {
  background: var(--cyan);
  border-radius: 50%;
}
.src-mark.is-m1 {
  border: 1.5px solid var(--cyan);
  border-radius: 50%;
}
.src-mark.is-m2 {
  background: var(--cyan);
  transform: rotate(45deg) scale(0.82);
}
.src-mark.is-m3 {
  height: 2px;
  width: 8px;
  background: var(--cyan);
}
/* A fifth, sixth and seventh source: a hollow square, a triangle, a cross. */
.src-mark.is-m4 {
  border: 1.5px solid var(--cyan);
}
.src-mark.is-m5 {
  width: 8px;
  background: var(--cyan);
  clip-path: polygon(50% 0, 100% 100%, 0 100%);
}
.src-mark.is-m6 {
  width: 8px;
  height: 8px;
  background: linear-gradient(var(--cyan), var(--cyan)) center / 100% 1.5px no-repeat, linear-gradient(var(--cyan), var(--cyan)) center / 1.5px 100% no-repeat;
}
/* A source's name can be any length: it is cut, and the whole of it is in the tooltip. */
.src-fit {
  display: flex;
  min-width: 0;
  max-width: 100%;
}
.src-text {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  /* Room for the descenders, which the cut would otherwise clip. */
  line-height: 1.3;
}

/* An outcome that isn't OK, as a small chip with the word in it (locked, disconnected, failed,
   error). Gold, because it wants noticing; never red, because nothing was destroyed. */
.chip {
  display: inline-flex;
  align-items: center;
  min-height: 18px;
  padding: 0 6px;
  border: var(--hair) solid var(--gold-line);
  background: var(--gold-fill);
  color: var(--gold);
  font: 500 var(--t-micro) / 1 var(--f-display);
  letter-spacing: 0.1em;
  text-transform: uppercase;
}

/* ---------------------------------------------------------------- the seal and other gauges */
.seal {
  overflow: visible;
}
/* Unlocked, the everyday state: every segment quietly lit, the ring open at the bottom. */
.seal .seg {
  fill: none;
  stroke: var(--cyan);
  stroke-opacity: 0.6;
  stroke-width: 2;
}
/* Locked for a time: the gold segments are the time left until it unlocks. */
.seal.is-timed .seg {
  stroke: var(--cyan-2);
  stroke-opacity: 0.5;
  stroke-width: 1;
}
.seal.is-timed .seg.is-lit,
.seal.is-sealed .seg {
  stroke: var(--gold);
  stroke-opacity: 1;
  stroke-width: 2;
}
.seal-minor {
  fill: none;
  stroke: var(--line-2);
  stroke-width: 1;
}
.seal-major {
  fill: none;
  stroke: var(--ink-3);
  stroke-width: 1;
}
.seal-label,
.gauge-label {
  font: 400 var(--t-micro) var(--f-mono);
  fill: var(--ink-3);
}
/* Locked with no end: the ring is closed, in gold. */
.seal-closure {
  fill: none;
  stroke: var(--gold);
  stroke-opacity: 0;
  stroke-width: 2;
}
.seal.is-sealed .seal-closure {
  stroke-opacity: 1;
}
.seal:not(.is-timed) .seal-label {
  display: none;
}
.gauge {
  overflow: visible;
}
.gauge-track,
.gauge-minor {
  fill: none;
  stroke: var(--line-2);
  stroke-width: 1;
}
.gauge-major {
  fill: none;
  stroke: var(--ink-3);
  stroke-width: 1;
}
.gauge-value {
  fill: none;
  stroke: var(--cyan);
  stroke-width: 2;
}
.gauge-needle {
  fill: none;
  stroke: var(--ink);
  stroke-width: 2;
}
.gauge.is-warm .gauge-value {
  stroke: var(--gold);
}
.mglyph-ring {
  fill: none;
  stroke: var(--ink-3);
  stroke-width: 1.5;
}
.mglyph.is-ready .mglyph-ring {
  stroke: var(--cyan);
}
.mglyph-core {
  fill: var(--cyan);
}
.mglyph-arc {
  fill: none;
  stroke: var(--gold);
  stroke-width: 1.5;
}
.hold .seg {
  fill: none;
  stroke: var(--red);
  stroke-opacity: 0.32;
  stroke-width: 2;
}
.hold .seg.is-lit {
  stroke-opacity: 1;
}

/* ---------------------------------------------------------------- lock controls */
/* One component in three places (lock.js): under the seal on the overview (is-hero), beside the dial
   on the system view (is-panel), in the strip's popover (is-pop). Unlocked is cool and quiet; locked
   is the state that gets gold. */
.lockctl {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--s3);
  text-align: center;
}
.lock-read {
  display: grid;
  justify-items: center;
  gap: var(--s1);
}
.lock-big {
  font: 500 var(--t-num) / 1.16 var(--f-display);
  letter-spacing: 0.02em;
  color: var(--ink);
}
.lock-big.is-locked {
  color: var(--gold);
}
/* The countdown of a timed lock: hours, minutes and seconds in the number face. */
.lock-big.is-count {
  font: 300 var(--t-num) / 1.12 var(--f-mono);
  letter-spacing: -0.02em;
  font-variant-numeric: tabular-nums;
}
.lockctl.is-locked .lock-label {
  color: var(--gold);
}
.lock-sub {
  font-size: var(--t-small);
  color: var(--ink-2);
}
.lock-actions {
  display: flex;
  align-items: center;
  justify-content: center;
  flex-wrap: wrap;
  gap: var(--s3);
}
.lock-pick-wrap {
  display: inline-flex;
  align-items: center;
  gap: var(--s2);
}
.lock-pick-label {
  white-space: nowrap;
}
.lockctl[aria-busy="true"] .lock-actions {
  opacity: 0.6;
  pointer-events: none;
}

/* ---------------------------------------------------------------- small things */
.empty {
  display: grid;
  gap: var(--s2);
  padding: var(--s2) 0;
}
.empty-title {
  font: 500 var(--t-ui) / 1.3 var(--f-display);
  letter-spacing: 0.02em;
  color: var(--ink-2);
}
.empty-text {
  max-width: 46ch;
  font-size: var(--t-small);
  color: var(--ink-3);
}
.empty-actions {
  display: flex;
  gap: var(--s2);
  margin-top: var(--s2);
}
/* A view with a bar of its own at the bottom of the page (a bulk bar, a save bar) sets --toast-lift
   on <html> for itself (html[data-view="…"]), so a toast never covers the bar's buttons. */
.toast {
  position: fixed;
  left: 50%;
  bottom: calc(var(--dock-h) + 20px + var(--toast-lift, 0px));
  z-index: 60;
  display: flex;
  align-items: center;
  gap: var(--s4);
  max-width: calc(100% - 32px);
  padding: var(--s3) var(--s4);
  transform: translateX(-50%);
  background: var(--near);
  border: var(--hair) solid var(--line-3);
  font: 500 var(--t-ui) / 1.3 var(--f-display);
  letter-spacing: 0.02em;
  animation: toast-in var(--move) var(--ease-out);
}
.toast.is-warm {
  border-color: var(--gold);
  color: var(--gold);
}
.toast.is-danger {
  border-color: var(--red);
  color: var(--red);
}
/* An action inside the toast ("Undo"): in the signal colour whatever the toast's tone. */
.toast-action {
  flex: none;
  min-height: 32px;
  margin: calc(var(--s2) * -1) calc(var(--s2) * -1) calc(var(--s2) * -1) 0;
  padding: 0 var(--s3);
  border-left: var(--hair) solid var(--line-2);
  color: var(--cyan);
  font: 500 var(--t-ui) / 1 var(--f-display);
  letter-spacing: 0.02em;
  white-space: nowrap;
}
.toast-action:hover {
  color: var(--ink);
}
@media (pointer: coarse) {
  .toast-action {
    min-height: 40px;
  }
}
@keyframes toast-in {
  from {
    opacity: 0;
    transform: translateX(-50%) scale(0.94);
  }
}

/* ---------------------------------------------------------------- list rows */
/* One row of a list of memories: a marking box, two lines of text, then columns. Memories and Trash
   both use it; each sets its own columns in its own stylesheet. */
.rows-scroll {
  flex: 1;
  min-height: 0;
  overflow: auto;
  margin: 0 calc(var(--s4) * -1);
  padding: 0 var(--s4);
}
.row {
  display: grid;
  grid-template-columns: 40px minmax(0, 1fr);
  align-items: center;
  column-gap: var(--s3);
  position: relative;
  min-height: 62px;
  margin-left: calc(var(--s3) * -1);
  border-bottom: var(--hair) solid var(--line);
  transition: transform 160ms var(--ease-out);
}
.row-main {
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-self: stretch;
  min-width: 0;
  padding: var(--s2) 0;
  text-decoration: none;
}
.row-main:focus-visible {
  outline-offset: -2px;
}
.row-text {
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
  overflow: hidden;
  /* A title that is one long word (an address, a command) breaks where it has to. */
  overflow-wrap: anywhere;
  font: 400 var(--t-ui) / 1.42 var(--f-read);
}
.row-title {
  color: var(--ink);
}
.row-rest {
  color: var(--ink-3);
}
.row-meta {
  display: none;
}
.row-meta.is-shown {
  display: flex;
  align-items: center;
  gap: var(--s3);
  margin-top: var(--s1);
  font: 400 var(--t-micro) / 1 var(--f-mono);
  color: var(--ink-3);
}
.row-meta span {
  white-space: nowrap;
}
.row-meta .src {
  font-size: var(--t-mini);
  color: var(--ink-3);
}
/* A search's words, where they were found. */
mark {
  background: rgba(98, 221, 242, 0.24);
  color: var(--ink);
}
.col-src .src {
  font-size: var(--t-mini);
}
.col-when,
.col-num,
.col-ver {
  font: 400 var(--t-mini) / 1 var(--f-mono);
  color: var(--ink-2);
  text-align: right;
  white-space: nowrap;
}
.col-ver {
  color: var(--ink-3);
}
.row:hover {
  background: rgba(98, 221, 242, 0.05);
}
/* Empty slots stay ruled: the list is waiting, not missing. */
.rows-empty {
  margin-left: calc(var(--s3) * -1);
  padding: var(--s5) var(--s3) var(--s5) calc(var(--s3) * 2);
  border-bottom: var(--hair) solid var(--line);
}
.row.is-blank {
  pointer-events: none;
}

/* The count at the top of a summary of marked memories: a hero number and what it counts. */
.marked-main {
  display: flex;
  align-items: baseline;
  gap: var(--s3);
  margin: var(--s2) 0 var(--s4);
}
.marked-grid {
  display: grid;
  grid-template-columns: repeat(3, auto);
  justify-content: space-between;
  gap: var(--s3);
  padding: var(--s4) 0;
  border-top: var(--hair) solid var(--line);
}
.store-unit {
  font: 500 var(--t-ui) / 1 var(--f-display);
  letter-spacing: 0.02em;
  color: var(--ink-2);
}
.num-hero.is-warm {
  color: var(--gold);
}
.num-hero.is-danger {
  color: var(--red);
}
.lifted.is-warm {
  --echo: var(--gold-2);
}
.lifted.is-danger {
  --echo: var(--red-line);
}
/* Where a mark means destruction (the trash), it is red. */
.tick.is-danger[aria-checked="true"] .tick-box {
  border-color: var(--red);
  background: var(--red-fill);
  color: var(--red);
}

/* Up to 1040 px a row is a marking box and text; its columns become a meta line under the text. */
@media (max-width: 1040px) {
  .rows-scroll {
    overflow: visible;
    margin: 0 calc(var(--s3) * -1);
    padding: 0 var(--s3);
  }
  .row {
    column-gap: var(--s1);
    margin-left: calc(var(--s2) * -1);
    min-height: 76px;
    align-items: start;
  }
  .row > .col-src,
  .row > .col-when,
  .row > .col-num,
  .row > .col-ver {
    display: none;
  }
  .row .tick {
    margin-top: var(--s1);
  }
  .row-main {
    padding: var(--s3) 0;
  }
  .row-meta {
    display: flex;
    align-items: center;
    gap: var(--s3);
    margin-top: var(--s2);
    font: 400 var(--t-micro) / 1 var(--f-mono);
    color: var(--ink-3);
  }
}

/* ---------------------------------------------------------------- hold to confirm */
/* Hold to delete: the ring fills while the button is held. */
.holdbtn {
  position: relative;
  display: flex;
  align-items: center;
  gap: var(--s3);
  width: 100%;
  min-height: 64px;
  padding: 0 var(--s4);
  border: var(--hair) solid var(--red-line);
  background: var(--red-fill);
  color: var(--red);
  text-align: left;
  user-select: none;
  -webkit-user-select: none;
  touch-action: none;
  transition: transform var(--fast) var(--ease-out);
}
.holdbtn[disabled] {
  border-color: var(--line);
  background: none;
  color: var(--ink-3);
  cursor: default;
}
.holdbtn[disabled] .hold .seg {
  stroke: var(--ink-4);
}
.holdbtn.is-holding {
  border-color: var(--red);
  transform: scale(0.985);
}
.holdbtn:focus-visible {
  outline-color: var(--red);
}
.hold-label {
  display: grid;
  gap: 4px;
}
.hold-title {
  font: 500 var(--t-ui) / 1 var(--f-display);
  letter-spacing: 0.02em;
}
.hold-hint {
  font: 500 var(--t-micro) / 1 var(--f-display);
  letter-spacing: 0.1em;
  text-transform: uppercase;
}

/* ---------------------------------------------------------------- loading and failed (states.js) */
/* Loading: a small ring whose lit arc steps round. Opacity and transform only; still with reduced motion. */
.wait {
  display: flex;
  align-items: center;
  gap: var(--s3);
  padding: var(--s2) 0;
}
.wait-ring {
  flex: none;
  overflow: visible;
}
.wait-ring path {
  fill: none;
  stroke-width: 2;
}
.wait-dim path {
  stroke: var(--cyan-2);
  stroke-opacity: 0.5;
}
.wait-lit path {
  stroke: var(--cyan);
}
.wait-lit {
  transform-box: view-box;
  transform-origin: 50% 50%;
  animation: wait-turn 1.2s steps(12) infinite;
}
@keyframes wait-turn {
  to {
    transform: rotate(360deg);
  }
}
.wait-label {
  color: var(--ink-2);
}
/* Failed: gold, because it wants attention; never red, because nothing was destroyed. */
.fault {
  display: grid;
  gap: var(--s2);
  padding: var(--s2) 0 var(--s2) var(--s3);
  border-left: var(--bold) solid var(--gold);
}
.fault-title {
  font: 500 var(--t-ui) / 1.3 var(--f-display);
  letter-spacing: 0.02em;
  color: var(--gold);
}
.fault-text {
  max-width: 52ch;
  font-size: var(--t-small);
  color: var(--ink-2);
}
.fault-code {
  font: 400 var(--t-mini) / 1 var(--f-mono);
  color: var(--ink-3);
}

/* ---------------------------------------------------------------- dialog (dialog.js) */
.dialog {
  width: min(480px, calc(100vw - 32px));
  max-width: none;
  max-height: calc(100vh - 32px);
  max-height: calc(100dvh - 32px);
  margin: auto;
  padding: 0;
  border: 0;
  background: none;
  color: var(--ink);
  overflow: visible;
}
.dialog.is-wide {
  width: min(920px, calc(100vw - 32px));
}
.dialog::backdrop {
  background: rgba(2, 6, 10, 0.8);
}
.dialog[open] {
  animation: dialog-in 180ms var(--ease-out);
}
@keyframes dialog-in {
  from {
    opacity: 0;
    transform: scale(0.96);
  }
}
.dialog-plane {
  --edge: var(--line-3);
  position: relative;
  display: flex;
  flex-direction: column;
  max-height: calc(100vh - 32px);
  max-height: calc(100dvh - 32px);
  padding: var(--s4);
  background: linear-gradient(to top right, transparent calc(50% - 0.75px), var(--edge) calc(50% - 0.75px), var(--edge) calc(50% + 0.75px), transparent calc(50% + 0.75px)) top right / var(--cut) var(--cut) no-repeat border-box, var(--near);
  border: var(--hair) solid var(--edge);
  clip-path: polygon(0 0, calc(100% - var(--cut)) 0, 100% var(--cut), 100% 100%, 0 100%);
}
.dialog.is-warm .dialog-plane {
  --edge: var(--gold);
}
.dialog.is-warm .plane-label::before {
  background: var(--gold);
}
.dialog.is-danger .dialog-plane {
  --edge: var(--red);
}
.dialog.is-danger .plane-label::before {
  background: var(--red);
}
.dialog-body {
  display: grid;
  gap: var(--s3);
  min-height: 0;
  overflow: auto;
}
.dialog-text {
  max-width: 56ch;
  color: var(--ink-2);
}
.dialog-actions {
  display: flex;
  flex-wrap: wrap;
  justify-content: flex-end;
  gap: var(--s2);
  margin-top: var(--s5);
}
