/* ==========================================================================
   Lingobit diagrams: visual language (I1 pilot, round 2). Linked after ds.css.

   Rules, types, and glyphs: doc/website-relaunch/illustration-system.md.
   Owner, 2026-10-12: a diagram speaks the site's language — chips for strings, kicker labels,
   mono file names, one accent, a quiet stage; one idea and one main object; few words;
   no logo and no operating-system chrome inside diagrams.

   Colors come only from ds.css roles and brand tokens. A diagram is a fragment from
   site-src/lingobit-relaunch/assets/art/<name>.art.html, inserted inline by build_relaunch.py ({{ART:name}}):
   one .art-switch with a wide and a narrow composition; a container query picks the composition
   by the width the diagram actually gets.
   ========================================================================== */

/* 1. Palette roles ------------------------------------------------------------ */

.art-stage,
.art-switch,
.art {
  --art-ink: var(--ds-ink);
  --art-ink-2: var(--ds-ink-2);
  --art-ink-3: var(--ds-ink-3);
  --art-line: var(--ds-line-strong);       /* outlines of files and chips */
  --art-hair: var(--ds-line);              /* folds */
  --art-paper: var(--ds-bg);               /* files, source chips */
  --art-accent: var(--ds-link);            /* translations, the Lingobit action, change labels */
  --art-tint: var(--ds-tint);              /* stage, translated chips */
  --art-brand: var(--brand-mark);          /* outline of a translated chip */
  --art-surface: var(--ds-surface);        /* title strip and fields of a schematic form */

  /* Status colors of Lingobit Localizer itself (CLingoColors in src/DBLayer/LingoColors.h):
     a diagram that shows statuses uses the product's colors, always next to the status word
     or as a dot (3:1 for graphics); status words themselves stay in ink for AA contrast. */
  --st-not-translated: #DC2626;
  --st-complete: #16A34A;
  --st-for-review: #CA8A04;
  --st-in-process: #C2410C;
  --st-auto: #0D9488;
  --st-new: #2563EB;
  --st-changed: #C2410C;
  --st-locked: #64748B;
}

/* 2. Stage: the quiet panel a diagram stands on --------------------------------- */

.art-stage {
  position: relative;
  border-radius: var(--ds-radius-xl);
  background: var(--art-tint);
  padding: clamp(12px, 4%, 36px);
}

/* Hero: the stage runs past the right edge like the hero screenshot (design-system-spec §3.2) */
.art-stage--bleed {
  margin-right: calc(-1 * min(var(--ds-bleed), 160px));
  border-radius: var(--ds-radius-xl) 0 0 var(--ds-radius-xl);
  padding-right: calc(clamp(12px, 4%, 36px) + min(var(--ds-bleed), 160px) * 0.25);
}

@media (min-width: 1600px) {
  .art-stage--bleed {
    margin-right: 0;
    border-radius: var(--ds-radius-xl);
    padding-right: clamp(12px, 4%, 36px);
  }
}

@media (max-width: 959px) {
  .art-stage--bleed {
    margin-right: 0;
    border-radius: var(--ds-radius-xl);
    padding-right: clamp(12px, 4%, 36px);
  }
}

/* The diagram hero needs about 560 px for its wide drawing (13 px text shown at no less than 0.93). Between 960
   and 1299 px the two-column hero gives it only 400-580 px, so there the hero stacks: the copy above, the diagram
   full width below it (I2). From 1300 px the media column is wide enough again (tags at 12 px). */
@media (min-width: 960px) and (max-width: 1299px) {
  .hero__grid:has(.art-stage--bleed) {
    grid-template-columns: minmax(0, 1fr);
  }

  .art-stage--bleed {
    margin-right: 0;
    border-radius: var(--ds-radius-xl);
    padding-right: clamp(12px, 4%, 36px);
  }
}

/* On a phone the hero stage runs edge to edge: the narrow drawing gets the whole screen width */
@media (max-width: 559px) {
  .art-stage--bleed {
    margin-inline: calc(-1 * var(--ds-gutter));
    border-radius: 0;
    padding: 20px 12px;
  }
}

/* On a phone a diagram's quiet panel keeps a thin edge, so the narrow drawing gets almost the whole width
   (its text stays at 12 px or more down to 360 px; I1, WPF pilot) */
@media (max-width: 559px) {
  .art-stage:not(.art-stage--bleed) {
    padding: 10px 6px;
  }
}

/* 3. Composition switch ------------------------------------------------------------ */

.art-switch {
  container-type: inline-size;
  width: 100%;
}

/* Each composition is drawn with 13 px text at the least and shown at no less than 0.93 of its
   drawing, so text never renders below 12 px: the switch is at 520 px of available width.
   --art-max on the svg caps how far it grows: 1.3 x its viewBox width for a wide composition,
   1.15 x for a narrow one, so on a phone the diagram text stays near the size of body text. */
.art {
  display: block;
  width: 100%;
  max-width: var(--art-max, none);
  height: auto;
  margin-inline: auto;
  overflow: visible;
  font-family: var(--ds-font-text);
}

.art--narrow {
  display: none;
}

@container (max-width: 519px) {
  .art-switch:has(> .art--narrow) > .art--wide { display: none; }
  .art--narrow { display: block; }
}

/* 4. Objects: file, chip, window shot ------------------------------------------------ */

.art text { font-family: var(--ds-font-text); }

/* File: a card with a folded corner; its name in mono, as file names are in the site's text */
.art .b-file { fill: var(--art-paper); stroke: var(--art-line); stroke-width: 1; stroke-linejoin: round; }
.art .b-fold { fill: var(--art-hair); stroke: var(--art-line); stroke-width: 1; stroke-linejoin: round; }
.art .f-tag { fill: var(--art-tint); }
.art .k-skel { fill: var(--art-line); }
.art .k-skel-2 { fill: var(--ds-line); }
.art .t-name { fill: var(--art-ink-2); font-family: var(--ds-font-mono); font-size: 13px; }
.art .t-name-l { fill: var(--art-ink-2); font-family: var(--ds-font-mono); font-size: 14px; }

/* String: a chip like the site's chips (ds.css .chips: 1 px line, radius 4, semibold).
   Source: white with dark text. Translation: tint with accent text. */
.art .k-chip { fill: var(--art-paper); stroke: var(--art-line); stroke-width: 1; }
.art .k-chip-tr { fill: var(--art-tint); stroke: var(--art-brand); stroke-width: 1; }
.art .k-chip-t-s { fill: var(--art-ink); font-size: 13px; font-weight: 600; }
.art .k-chip-t { fill: var(--art-ink); font-size: 14px; font-weight: 600; }
.art .k-chip-t-l { fill: var(--art-ink); font-size: 16px; font-weight: 600; }
.art .k-chip-tr-t-s { fill: var(--art-accent); font-size: 13px; font-weight: 600; }
.art .k-chip-tr-t { fill: var(--art-accent); font-size: 14px; font-weight: 600; }
.art .k-chip-tr-t-l { fill: var(--art-accent); font-size: 16px; font-weight: 600; }

/* A real window shot as proof (hero variant b): the window shadow, as for screenshots */
.art .b-shot { fill: none; stroke: rgb(20 24 26 / 0.16); stroke-width: 1; }
.art .s-shot { filter: drop-shadow(0 1px 1.5px rgb(20 24 26 / 0.08)) drop-shadow(0 10px 18px rgb(20 24 26 / 0.14)); }

/* Schematic form: an application window drawn in the site's style, not in Windows chrome.
   A card with a quiet title strip, fields, a check box, and buttons; its texts are real. */
.art .u-card { fill: var(--art-paper); stroke: var(--ds-line); stroke-width: 1; }
.art .s-card { filter: drop-shadow(0 1px 2px rgb(20 24 26 / 0.06)) drop-shadow(0 14px 28px rgb(20 24 26 / 0.12)); }
.art .u-bar { fill: var(--art-surface); }
.art .u-hair { fill: none; stroke: var(--ds-line); stroke-width: 1; }
.art .u-input { fill: var(--art-surface); stroke: var(--ds-line); stroke-width: 1; }
.art .u-input-bad { fill: none; stroke: var(--st-not-translated); stroke-width: 1.5; }
.art .u-check { fill: var(--art-accent); }
.art .u-check-v { fill: none; stroke: var(--art-paper); stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; }
.art .u-btn { fill: var(--art-paper); stroke: var(--art-line); stroke-width: 1; }
.art .u-btn-main { fill: var(--ds-action); }
.art .u-chev { fill: none; stroke: var(--art-ink-2); stroke-width: 1.4; stroke-linecap: round; stroke-linejoin: round; }
.art .u-title { fill: var(--art-ink); font-size: 16px; font-weight: 600; }
.art .u-label { fill: var(--art-ink); font-size: 13px; }
.art .u-value { fill: var(--art-ink-2); font-size: 13px; }
.art .u-btn-t { fill: var(--art-ink); font-size: 13px; font-weight: 600; }
.art .u-btn-main-t { fill: var(--ds-on-action); font-size: 13px; font-weight: 600; }
.art .u-name { fill: var(--art-ink-2); font-family: var(--ds-font-mono); font-size: 13px; }
/* An x:Uid key: the same accent in the XAML, the BAML, and the satellite (wpf-inside) */
.art .t-uid { fill: var(--art-accent); }
/* Surfaces (owner, 2026-10-16; illustration-system.md): Lingobit Localizer is a card with a grey header and the panel
   label (headers are marked .lz / .lz-t for a later accent, unstyled now); a preview of the user's form stands flat on
   the dotted canvas (.u-flat: no shadow, no close cross); the running application is a free window with a shadow and a
   close cross. */
.art .u-flat { fill: var(--art-paper); stroke: var(--art-line); stroke-width: 1; }

/* A literal that will not be translated (wpf-code-strings) */
.art .t-bad { fill: var(--st-not-translated); }
.art .u-ja { font-family: "Yu Gothic UI", "Meiryo UI", "Hiragino Sans", "Noto Sans JP", var(--ds-font-text); }

/* Status: a dot in the product's color plus the word in ink */
.art .st-dot-red { fill: var(--st-not-translated); }
.art .st-dot-green { fill: var(--st-complete); }
.art .st-dot-yellow { fill: var(--st-for-review); }
.art .st-dot-orange { fill: var(--st-in-process); }
.art .st-dot-teal { fill: var(--st-auto); }
.art .st-dot-blue { fill: var(--st-new); }
.art .st-dot-gray { fill: var(--st-locked); }
.art .st-t { fill: var(--art-ink-2); font-size: 13px; }
.art .st-t-b { fill: var(--art-ink); font-size: 13px; font-weight: 600; }
.art .st-ring-red { fill: none; stroke: var(--st-not-translated); stroke-width: 1.5; }
.art .st-ring-green { fill: none; stroke: var(--st-complete); stroke-width: 1.5; }
.art .st-fill-red { fill: var(--st-not-translated); }
.art .st-fill-green { fill: var(--st-complete); }
.art .st-tag-red,
.art .st-cell-red { fill: #FEF2F2; stroke: var(--st-not-translated); stroke-width: 1; }
.art .st-tag-red-t { fill: #B91C1C; font-size: 12.5px; font-weight: 600; }
.art .st-tag-orange { fill: #FFF7ED; stroke: var(--st-changed); stroke-width: 1; }
.art .st-tag-orange-t { fill: #C2410C; font-size: 12.5px; font-weight: 600; }
.art .st-tag-blue { fill: #EFF6FF; stroke: var(--st-new); stroke-width: 1; }
.art .st-tag-blue-t { fill: #1D4ED8; font-size: 12.5px; font-weight: 600; }
.art .st-tag-gray { fill: #F8FAFC; stroke: var(--st-locked); stroke-width: 1; }
.art .st-tag-gray-t { fill: #475569; font-size: 12.5px; font-weight: 600; }
.art .st-chip-red { fill: #FEF2F2; stroke: var(--st-not-translated); stroke-width: 1.5; }
.art .st-chip-red-t { fill: #B91C1C; font-size: 13px; font-weight: 600; }
.art .st-kick-red { fill: #B91C1C; font-size: 13px; font-weight: 600; letter-spacing: 0.06em; text-transform: uppercase; }
.art .k-ring { fill: none; stroke: var(--art-accent); stroke-width: 2; }
.art .u-person { fill: var(--art-accent); }
.art .st-tag-green,
.art .st-cell-green { fill: #F0FDF4; stroke: var(--st-complete); stroke-width: 1; }
.art .st-tag-green-t { fill: #15803D; font-size: 12.5px; font-weight: 600; }
.art .st-skel-red { fill: #F87171; }
.art .st-line-red { fill: none; stroke: var(--st-not-translated); stroke-width: 1.5; stroke-linecap: round; stroke-linejoin: round; }

/* Narrowing (Crash Finder): a translation as a chip with a text line, the places still in question
   as quiet slots, and the part a run tests as a bar over them */
.art .k-skel-tr { fill: var(--art-brand); }
.art .u-slots { fill: var(--art-surface); stroke: var(--ds-line); stroke-width: 1; }
.art .k-cell { fill: var(--art-tint); stroke: var(--art-brand); stroke-width: 1; }

/* Steps of a process (hero): the tour's verbs; a step is checked green when it is done */
.art .g-step { fill: var(--art-paper); stroke: var(--art-line); stroke-width: 1; }
.art .g-step-n { fill: var(--art-ink-2); font-size: 13px; font-weight: 600; }
.art .g-step-done { fill: var(--st-complete); }
.art .g-step-line { fill: none; stroke: var(--art-line); stroke-width: 1.5; stroke-linecap: round; }
.art .g-step-line-done { fill: none; stroke: var(--st-complete); stroke-width: 1.5; stroke-linecap: round; }

/* A change made in the application (the new build): a dashed accent outline; what was removed stays as a ghost */
.art .k-diff { fill: none; stroke: var(--art-accent); stroke-width: 1.25; stroke-dasharray: 3 2.5; }
.art .r-ghost { opacity: 0.35; }
.art .r-dim { opacity: 0.4; }
.art .r-strike-x { fill: none; stroke: var(--art-ink-2); stroke-width: 1.5; stroke-linecap: round; }

/* An action in the scene: the pointer, and a button while it is pressed */
.art .u-cursor { fill: var(--art-paper); stroke: var(--art-ink); stroke-width: 1.1; stroke-linejoin: round; }
.art .u-btn-press { fill: var(--ds-link); }

/* Preview of a dialog in the project (owner, 2026-10-12: the dialog must not read as Lingobit's own window).
   The dialog is a small window with its own title bar on a dotted canvas; a control being edited has a
   dashed selection frame with handles. The resource tree sits on the left of the project. */
.art .u-win { fill: var(--art-paper); stroke: var(--art-line); stroke-width: 1; }
.art .u-canvas { fill: var(--art-surface); }
.art .u-dot { fill: var(--art-line); }
.art .u-check-off { fill: var(--art-paper); stroke: var(--art-line); stroke-width: 1; }
.art .u-sel { fill: var(--art-tint); stroke: var(--art-brand); stroke-width: 1; }
.art .u-tree { fill: var(--art-ink); font-size: 13px; }
.art .k-sel { fill: none; stroke: var(--art-accent); stroke-width: 1.25; stroke-dasharray: 3 2.5; vector-effect: non-scaling-stroke; }
.art .k-handle { fill: var(--art-paper); stroke: var(--art-accent); stroke-width: 1.25; }
.art .k-found { fill: none; stroke: var(--art-accent); stroke-width: 1; stroke-dasharray: 2.5 2; }

/* Round 3f: the tour frames (one picture for each fact) */
.art .st-tag-teal { fill: #F0FDFA; stroke: var(--st-auto); stroke-width: 1; }
.art .st-tag-teal-t { fill: #0F766E; font-size: 12.5px; font-weight: 600; }
.art .st-lead { fill: none; stroke: var(--st-not-translated); stroke-width: 1; stroke-dasharray: 2 2; }
.art .u-lock { fill: none; stroke: var(--st-locked); stroke-width: 1.5; stroke-linecap: round; }
.art .u-lock-b { fill: var(--st-locked); }
.art .u-zh { font-family: "Microsoft YaHei UI", "Microsoft YaHei", "PingFang SC", "Noto Sans SC", var(--ds-font-text); }
.art .u-term { fill: #FCFCFD; stroke: var(--art-line); stroke-width: 1; }
.art .u-term-bar { fill: #F1F3F5; }
.art .u-term-t { fill: var(--art-ink); font-family: var(--ds-font-mono); font-size: 13px; }
.art .u-term-d { fill: var(--art-ink-2); font-family: var(--ds-font-mono); font-size: 13px; }
.art .u-term-cur { fill: var(--art-ink); }
.art .u-cover { fill: #FCFCFD; }
/* Typing into a form: the cover that slides off the text is the paper of the window (F1: vis-hero) */
.art .u-cover-paper { fill: var(--art-paper); }
.art .t-op { fill: var(--art-accent); font-size: 30px; font-weight: 600; }
.art .k-mark { fill: #FEF3C7; }
.art .k-term { fill: none; stroke: var(--art-accent); stroke-width: 1.5; stroke-linecap: round; }
.art .k-ticks { fill: none; stroke: var(--art-brand); stroke-width: 1; vector-effect: non-scaling-stroke; }
.art .k-bar { fill: var(--art-tint); stroke: var(--art-brand); stroke-width: 1; }
.art .st-zone-green { fill: var(--st-complete); opacity: 0.2; }
.art .st-zone-gray { fill: var(--art-ink-3); opacity: 0.18; }
.art .st-tick-red { fill: none; stroke: var(--st-not-translated); stroke-width: 3; vector-effect: non-scaling-stroke; }

/* Round 3g */
.art .k-bound { fill: none; stroke: var(--art-accent); stroke-width: 1; stroke-dasharray: 2 2; opacity: 0.55; }
.art .st-tag-yellow { fill: #FEFCE8; stroke: var(--st-for-review); stroke-width: 1; }
.art .st-tag-yellow-t { fill: #A16207; font-size: 12.5px; font-weight: 600; }
.art .k-mark-red { fill: #FEE2E2; }
.art .st-zone-red { fill: var(--st-not-translated); opacity: 0.16; }
.art .u-menu { fill: var(--art-paper); stroke: var(--art-line); stroke-width: 1; }
.art .u-menu-hi { fill: var(--art-tint); }

.art .zone-red { fill: #FECACA; }
.art .zone-green { fill: #BBF7D0; }
.art .zone-gray { fill: #E2E5E9; }

/* I1, WPF pilot: the part of a process that is Lingobit's (a band on the tinted stage: paper, dashed brand outline) */
.art .k-band { fill: var(--art-paper); stroke: var(--art-brand); stroke-width: 1; stroke-dasharray: 4 3; }

/* Round 3h: a placeholder or a changed word, marked on its own letters (a stroke painted under them), so the mark
   fits the text in any font instead of a box measured for one */
.art .k-ph { fill: #15803D; font-weight: 700; stroke: #DCFCE7; stroke-width: 5px; stroke-linejoin: round; paint-order: stroke; }
.art .k-src { fill: var(--art-ink); }   /* translatable text inside mono markup (html-segments) */
.art .k-ph-bad { fill: #B91C1C; font-weight: 700; stroke: #FEE2E2; stroke-width: 5px; stroke-linejoin: round; paint-order: stroke; }
.art .k-ins { fill: #C2410C; font-weight: 700; stroke: #FFEDD5; stroke-width: 5px; stroke-linejoin: round; paint-order: stroke; }

/* 5. Labels: the site's kicker. Groups are grey; language and change are accent ------- */

.art .t-kick { fill: var(--art-accent); font-size: 13px; font-weight: 600; letter-spacing: 0.06em; text-transform: uppercase; }
.art .t-kick-mute { fill: var(--art-ink-2); font-size: 13px; font-weight: 600; letter-spacing: 0.06em; text-transform: uppercase; }
.art .t-kick-l { fill: var(--art-accent); font-size: 14px; font-weight: 600; letter-spacing: 0.06em; text-transform: uppercase; }
.art .t-kick-mute-l { fill: var(--art-ink-2); font-size: 14px; font-weight: 600; letter-spacing: 0.06em; text-transform: uppercase; }
.art .t-scan { fill: var(--art-accent); font-size: 13px; font-weight: 600; }

/* 6. Lines: the Lingobit action is a thin accent arrow ------------------------------- */

.art .c-flow { fill: none; stroke: var(--art-accent); stroke-width: 1.75; stroke-linecap: round; stroke-linejoin: round; }
.art .c-thin { fill: none; stroke: var(--art-accent); stroke-width: 1.5; stroke-linecap: round; stroke-linejoin: round; }
.art .r-strike { fill: none; stroke: var(--art-ink-3); stroke-width: 1.5; stroke-linecap: round; }
.art .r-off { opacity: 0.45; }

/* Visible only while animated: the scan line and the light on a source translation */
.art .c-scan { fill: none; stroke: var(--art-accent); stroke-width: 2; stroke-linecap: round; opacity: 0; }
.art .k-flash { fill: none; stroke: var(--art-accent); stroke-width: 2; opacity: 0; }
.art .c-glow { opacity: 0; }
.art .c-glow-0 { stop-color: var(--art-accent); stop-opacity: 0; }
.art .c-glow-1 { stop-color: var(--art-accent); stop-opacity: 0.16; }

/* 7. Figure ------------------------------------------------------------------------ */

.art-fig {
  margin: 0;
  min-width: 0;
}

.art-fig__caption {
  margin-top: 12px;
  max-width: 60ch;
  font-size: var(--ds-fs-small);
  line-height: var(--ds-lh-small);
  color: var(--ds-ink-2);
}

/* A diagram frame in the tour scene: the tinted stage instead of a screenshot frame */
.scene__stage--art {
  cursor: auto;
  background: var(--ds-tint);
  box-shadow: 0 0 0 1px rgb(20 24 26 / 0.06);
}

.scene__stage--art .scene__frame {
  display: grid;
  place-items: center;
}

/* Frames lie on top of each other: a frame that is not shown must not take clicks or focus
   (the Replay button of an animated frame sat under the transparent frames after it) */
.scene__stage--art .scene__frame:not(.is-current) {
  visibility: hidden;
  pointer-events: none;
  transition: opacity 0.5s ease, visibility 0s linear 0.5s;
}

.scene__stage--art .scene__frame.is-current {
  visibility: visible;
  transition: opacity 0.5s ease, visibility 0s;
}

.scene__stage--art .art-fig {
  width: 100%;
}

/* Where the tour is an accordion, a diagram frame takes the height of its drawing: the narrow
   composition is taller than 9:5. Frames stack in one grid cell, so the cross-fade still works. */
@media (max-width: 959px) {
  .scene__stage--art {
    display: grid;
    aspect-ratio: auto;
  }

  .scene__stage--art .scene__frame {
    position: relative;
    grid-area: 1 / 1;
    align-self: center;
  }
}

/* On a phone the diagram fills the scene: the stage steps back and the files stand on the section */
@media (max-width: 559px) {
  .scene__stage--art {
    background: transparent;
    box-shadow: none;
  }
}

/* The tour on a narrow screen (I2, owner 2026-10-14): no frame switching inside a step. Under the open step each
   fact is a pair, its title and text, then its own picture; the pairs follow one another down the page, and each
   animation plays when it scrolls into view. The shared stage, the tour Pause button, and the step links belong to
   the wide tour only (on a phone a link after the facts read like a section heading). */
.tour__fact-art {
  display: none;
}

@media (max-width: 959px) {
  .tour__panel .scene,
  .tour__panel .links,
  .tour-has-auto .tour__toggle {
    display: none;
  }

  .tour__facts {
    border-top: 0;
  }

  .tour__facts > li + li {
    margin-top: 32px;
  }

  .tour__fact {
    padding: 0;
    border-bottom: 0;
    color: var(--ds-ink-2);
    cursor: auto;
    pointer-events: none;
  }

  .tour__fact strong,
  .tour__fact[aria-current="true"] strong {
    color: var(--ds-ink);
  }

  .tour__fact[aria-current="true"] {
    color: var(--ds-ink-2);
    box-shadow: none;
  }

  .tour__fact-art {
    display: block;
    margin-top: 14px;
  }
}

/* Homepage placements outside the tour (pilot; they move to ds.css with the page).
   The migration band keeps the column of its old schematic; the final call to action gets an animation beside the copy. */
.band__art--anim {
  width: 100%;
  max-width: 560px;
  justify-self: center;
}

.final-cta__inner {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: var(--ds-space-m) var(--ds-gap);
  align-items: center;
}

@media (min-width: 960px) {
  .final-cta__inner {
    grid-template-columns: minmax(0, 6fr) minmax(0, 5fr);
    column-gap: var(--ds-hero-gap);
  }
}

.final-cta__art {
  width: 100%;
  max-width: 560px;
  justify-self: center;
}

/* A diagram in a lane of the tour (Translate step): the tinted stage instead of a screenshot frame;
   not a zoom button, because an animated diagram has its own Pause button */
.lane__shot--art {
  position: relative;
  padding: clamp(10px, 3%, 20px);
  background: var(--ds-tint);
  box-shadow: 0 0 0 1px rgb(20 24 26 / 0.06);
  cursor: auto;
}

@media (max-width: 559px) {
  .lane__shot--art {
    padding: 8px 0;
    background: transparent;
    box-shadow: none;
  }
}

.js .scene__stage--art .art-anim.is-armed .art-controls,
.js .lane__shot--art .art-anim.is-armed .art-controls {
  position: absolute;
  right: 10px;
  bottom: 10px;
  margin: 0;
}

/* 8. Motion -------------------------------------------------------------------------
   One idea per animation, 3-8 s, then it stops on the final frame. The markup is the final
   frame: without JS, with prefers-reduced-motion, and after the end the diagram shows it.
   art.js arms the animation (every moving part waits in its first keyframe), plays it once
   the figure is half visible, pauses it off screen, and gives a Pause / Play / Replay button
   (WCAG 2.2.2: it runs longer than 5 s). */

.art-anim .art-controls {
  display: none;
}

.js .art-anim.is-armed .art-controls {
  display: flex;
  justify-content: flex-end;
  margin-top: 8px;
}

.art-toggle {
  display: inline-grid;
  place-items: center;
  width: 32px;
  min-height: 32px;
  padding: 0;
  border: 1px solid var(--ds-line-strong);
  border-radius: var(--ds-radius-m);
  background: var(--ds-bg);
  color: var(--ds-ink-2);
  font: inherit;
  font-size: 13px;
  line-height: 1.4;
  cursor: pointer;
}

.art-toggle:hover {
  color: var(--ds-ink);
  border-color: var(--ds-control);
}

.art-toggle:focus-visible {
  outline: 2px solid var(--ds-focus);
  outline-offset: 2px;
}

/* Every animated part carries .m; its .m-* class names the movement. Fragments are timed in --t / --d at
   pace 1; --art-pace stretches every animation at once (owner, 2026-10-12: twice as slow). */
.art-anim {
  --art-pace: 2;
}

.art-anim.is-armed .m {
  animation-duration: calc(var(--d, 0.5s) * var(--art-pace, 1));
  animation-delay: calc(var(--t, 0s) * var(--art-pace, 1));
  animation-timing-function: cubic-bezier(0.2, 0, 0, 1);
  animation-fill-mode: both;
  animation-play-state: paused;
}

.art-anim.is-armed.is-playing .m {
  animation-play-state: running;
}

/* Final-frame visibility: what is gone at the end has opacity 0 in the markup */
.art .f-hide { opacity: 0; }

.art-anim.is-armed .m-in { animation-name: art-in; }
.art-anim.is-armed .m-out { animation-name: art-out; }
.art-anim.is-armed .m-pulse { animation-name: art-pulse; }
.art-anim.is-armed .m-up { animation-name: art-up; }
.art-anim.is-armed .m-fly { animation-name: art-fly; }
.art-anim.is-armed .m-draw { animation-name: art-draw; stroke-dasharray: var(--len) var(--len); }
.art-anim.is-armed .m-off { animation-name: art-off; }
.art-anim.is-armed .m-scan { animation-name: art-scan; animation-timing-function: linear; }
.art-anim.is-armed .m-flash { animation-name: art-flash; }
.art-anim.is-armed .m-cursor { animation-name: art-cursor; transform-box: fill-box; transform-origin: 0 0; }
.art-anim.is-armed .m-grow { animation-name: art-grow; transform-box: fill-box; transform-origin: 0 50%; }
.art-anim.is-armed .m-grow-c { animation-name: art-grow; transform-box: fill-box; transform-origin: 50% 50%; }
.art-anim.is-armed .m-slide { animation-name: art-slide; }
.art-anim.is-armed .m-zoom { animation-name: art-zoom; transform-box: fill-box; transform-origin: 0 0; }
.art-anim.is-armed .m-wipe { animation-name: art-wipe; animation-timing-function: steps(28, end); }
.art-anim.is-armed .m-grow-y { animation-name: art-grow-y; transform-box: fill-box; transform-origin: 0 0; }

@keyframes art-out {
  from { opacity: 1; }
  to { opacity: 0; }
}

/* Shown only for a while (a warning before it is fixed): hidden in the final frame */
@keyframes art-pulse {
  0% { opacity: 0; }
  12%, 88% { opacity: 1; }
  100% { opacity: 0; }
}

@keyframes art-in {
  from { opacity: 0; }
}

@keyframes art-up {
  from { opacity: 0; transform: translateY(6px); }
}

/* Something arrives: from --dx / --dy back to its place */
@keyframes art-fly {
  from { opacity: 0; transform: translate(var(--dx, 0), var(--dy, 0)); }
  25% { opacity: 1; }
}

@keyframes art-draw {
  from { stroke-dashoffset: var(--len); }
  to { stroke-dashoffset: 0; }
}

/* The project translation the scan takes lights up for a moment */
@keyframes art-flash {
  from { opacity: 0; }
  30% { opacity: 1; }
  to { opacity: 0; }
}

/* Something passes by --dx / --dy and disappears: the scan line over a new build, a task on its way */
@keyframes art-scan {
  from { opacity: 0; transform: translate(0, 0); }
  8% { opacity: 1; }
  92% { opacity: 1; }
  to { opacity: 0; transform: translate(var(--dx, 0), var(--dy, 0)); }
}

/* The pointer comes from --dx / --dy, clicks, and goes: it is not in the final frame */
@keyframes art-cursor {
  from { opacity: 0; transform: translate(var(--dx, 0), var(--dy, 0)); }
  15% { opacity: 1; }
  55% { transform: translate(0, 0) scale(1); }
  62% { transform: translate(0, 0) scale(0.86); }
  70% { transform: translate(0, 0) scale(1); }
  88% { opacity: 1; }
  to { opacity: 0; transform: translate(0, 0) scale(1); }
}

/* A selection frame widens to its final size from --sx of it (non-scaling stroke keeps the dashes) */
@keyframes art-grow {
  from { transform: scaleX(var(--sx, 1)); }
}

/* A camera move on a narrow drawing: something grows from a small place (--dx, --dy, scale --s) to its own:
   the narrow hero opens the dialog out of its row in the tree */
@keyframes art-zoom {
  from { opacity: 0; transform: translate(var(--dx, 0), var(--dy, 0)) scale(var(--s, 0.5)); }
  30% { opacity: 1; }
}

/* Something moves to its place from --dx / --dy without fading: the handles of a frame that widens */
@keyframes art-slide {
  from { transform: translate(var(--dx, 0), var(--dy, 0)); }
}

/* Typing on a terminal: a cover slides off the text to the right in steps, then goes */
@keyframes art-wipe {
  from { opacity: 1; transform: translate(0, 0); }
  99% { opacity: 1; }
  to { opacity: 0; transform: translate(var(--dx, 0), 0); }
}

/* A card grows down to its final height from --sy of it, as rows arrive (wpf-hero: what you ship) */
@keyframes art-grow-y {
  from { transform: scaleY(var(--sy, 1)); }
}

/* Something that stops being current fades to its muted final state */
@keyframes art-off {
  from { opacity: 1; }
}

@media (prefers-reduced-motion: reduce) {
  .art-anim *,
  .art-anim *::before,
  .art-anim *::after {
    animation: none !important;
  }

  .js .art-anim.is-armed .art-controls {
    display: none;
  }
}
