/* Dabt — one stylesheet.

   Two zones. Everything above the footer is 1-bit black and white; colour appears only where
   human consequence does. The verdict accents and the acid label colour are the sole
   exceptions above the fold, and both are carrying information rather than decorating.

   Logical properties throughout, so the Arabic build mirrors without a second stylesheet. */

:root {
  --ink: #000;
  --paper: #fff;
  --line: #1a1a1a;
  --line-2: #262626;
  --dim: #565656;
  --mid: #9a9a9a;
  --soft: #7c7c7c;

  --acid: #e4e44a;
  --allow: #00e36a;
  --allow_with_redaction: #ffb000;
  --review: #4fc3ff;
  --deny: #ff2d2d;

  --court-ground: #a9a186;
  --court-cream: #f0ede0;
  --court-dim: #b8b29a;
  --split-a: #ff2d4a;
  --split-b: #12d8e8;

  --mono: "IBM Plex Mono", ui-monospace, SFMono-Regular, Menlo, monospace;
  --sans: "IBM Plex Sans Arabic", system-ui, -apple-system, sans-serif;

  --gutter: clamp(20px, 4vw, 56px);
  --max: 1180px;
}

*, *::before, *::after { box-sizing: border-box; }

html { -webkit-text-size-adjust: 100%; }

body {
  margin: 0;
  background: var(--ink);
  color: var(--paper);
  font-family: var(--sans);
  font-size: 16px;
  line-height: 1.7;
  -webkit-font-smoothing: antialiased;
}

img { max-width: 100%; }

a { color: inherit; }

.sr-only {
  position: absolute;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

.skip {
  position: absolute;
  inset-block-start: -100px;
  inset-inline-start: var(--gutter);
  z-index: 20;
  background: var(--acid);
  color: var(--ink);
  font-family: var(--mono);
  font-size: 13px;
  padding: 10px 16px;
  text-decoration: none;
}
.skip:focus { inset-block-start: 12px; }

:focus-visible {
  outline: 2px solid var(--acid);
  outline-offset: 3px;
}

/* ---------- shell ---------- */

.nav {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 20px;
  flex-wrap: wrap;
  padding: 16px var(--gutter);
  max-width: var(--max);
  margin-inline: auto;
}

.nav__brand {
  display: inline-flex;
  align-items: baseline;
  gap: 8px;
  text-decoration: none;
  font-family: var(--mono);
  font-size: 16px;
  font-weight: 600;
  letter-spacing: 1px;
}
.nav__sep { color: var(--line-2); }
.nav__arabic { font-family: var(--sans); font-size: 18px; letter-spacing: 0; }

.nav__links {
  display: flex;
  gap: clamp(12px, 2vw, 26px);
  flex-wrap: wrap;
  font-family: var(--mono);
  font-size: 12px;
  color: var(--dim);
}
.script-arabic .nav__links { font-family: var(--sans); font-size: 14px; }
.nav__links a { text-decoration: none; }
.nav__links a:hover { color: var(--paper); }
.nav__lang { color: var(--acid); }

.section {
  max-width: var(--max);
  margin-inline: auto;
  padding: clamp(38px, 6vw, 76px) var(--gutter);
  border-block-start: 1px solid var(--line);
}

.eyebrow {
  margin: 0 0 22px;
  display: flex;
  align-items: center;
  gap: 14px;
  font-family: var(--mono);
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 2.2px;
  color: var(--acid);
}
.script-arabic .eyebrow { font-family: var(--sans); font-size: 13px; letter-spacing: 0; }

h1, h2 { font-weight: 500; margin: 0; }

.note {
  margin: 22px 0 0;
  font-family: var(--mono);
  font-size: 12px;
  line-height: 1.75;
  color: var(--dim);
}
.script-arabic .note { font-family: var(--sans); font-size: 13.5px; }
.note--lead { margin: -8px 0 24px; }

/* ---------- hero ---------- */

/* The hero is a tall scroll track with a pinned viewport inside it. Scrolling the track is
   what closes the gap, so the approach is something the visitor performs rather than watches.

   Text and art live in separate grid rows and never share space — which is a structural
   guarantee of legibility, not a tuned one. */
.hero { position: relative; height: 260vh; }

.hero__sticky {
  position: sticky;
  inset-block-start: 0;
  height: 100vh;
  height: 100svh;
  max-width: var(--max);
  margin-inline: auto;
  padding: clamp(12px, 2.4vh, 34px) var(--gutter) clamp(16px, 3vh, 40px);
  display: grid;
  grid-template-rows: auto minmax(0, 1fr) auto;
  align-items: center;
  gap: clamp(10px, 2vh, 26px);
}

.hero__stage { position: relative; display: flex; align-items: center; min-block-size: 0; }

.hero__art {
  position: relative;
  inline-size: 100%;
  aspect-ratio: 1800 / 520;
  max-block-size: 100%;
  margin-inline: auto;
}

.hand, .gap-art {
  position: absolute;
  inset: 0;
  inline-size: 100%;
  block-size: 100%;
  image-rendering: pixelated;
  will-change: transform;
}
.gap-art { opacity: 0; }

.hero__headline {
  font-family: var(--mono);
  font-size: clamp(19px, 2.9vw, 42px);
  line-height: 1.16;
  letter-spacing: 0.02em;
  margin-block-start: 6px;
}
.script-arabic .hero__headline { font-family: var(--sans); line-height: 1.35; }
.hero__headline span { display: block; }

.hero__headline-alt {
  margin: 8px 0 0;
  color: var(--soft);
  font-size: clamp(11px, 1.7vw, 20px);
}

.hero__lede {
  margin: 0;
  max-width: 46ch;
  color: var(--mid);
  font-size: clamp(11px, 1.25vw, 15px);
  line-height: 1.7;
}

.hero__actions {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
  margin-block-start: clamp(10px, 1.6vw, 18px);
}

.btn {
  font-family: var(--mono);
  font-size: clamp(9px, 1.05vw, 12px);
  letter-spacing: 1px;
  padding: 9px 15px;
  text-decoration: none;
  white-space: nowrap;
}
.script-arabic .btn { font-family: var(--sans); letter-spacing: 0; font-size: clamp(11px, 1.2vw, 14px); }
.btn--solid { background: var(--paper); color: var(--ink); }
.btn--ghost { border: 1px solid #3a3a3a; color: var(--paper); }
.btn--ghost:hover { border-color: var(--paper); }

.hero__frameworks {
  margin: 0;
  font-family: var(--mono);
  font-size: clamp(8px, 0.9vw, 11px);
  letter-spacing: 1px;
  color: #454545;
}

/* The gate window is centred on the gap, which is the exact horizontal centre of the art. It
   is allowed to stand taller than the art strip, so it stays readable however short that
   strip gets — physical centring, because the layers are already mirrored for RTL. */
.gate {
  position: absolute;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
  width: clamp(186px, 23%, 300px);
  height: clamp(150px, 32vh, 290px);
  border: 2px solid var(--deny);
  display: flex;
  flex-direction: column;
  line-height: normal;
  opacity: 0;
  transition: border-color 140ms linear;
}
.gate__bar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 6px;
  border-block-end: 2px solid var(--deny);
  padding: 3px 6px;
  transition: border-color 140ms linear;
}
.gate__title {
  font-family: var(--mono);
  font-size: 11px;
  color: var(--deny);
  transition: color 140ms linear;
}
.gate__boxes { display: flex; gap: 3px; }
.gate__boxes i {
  display: block;
  width: 10px; height: 8px;
  border: 1px solid var(--deny);
  transition: border-color 140ms linear;
}
.gate__body { margin-block-start: auto; padding: 6px; }
.gate__body p { margin: 0; }
.gate__rule {
  font-family: var(--mono);
  font-size: 9.5px;
  color: var(--paper);
  word-break: break-all;
}
.gate__token {
  font-family: var(--mono);
  font-size: 13px;
  color: var(--deny);
  margin-block-start: 3px !important;
  word-break: break-all;
  transition: color 140ms linear;
}
.gate__phrase {
  font-size: 11px;
  color: var(--paper);
  margin-block-start: 2px !important;
}

.gate[data-verdict="allow"] { border-color: var(--allow); }
.gate[data-verdict="allow"] .gate__bar { border-block-end-color: var(--allow); }
.gate[data-verdict="allow"] .gate__title,
.gate[data-verdict="allow"] .gate__token { color: var(--allow); }
.gate[data-verdict="allow"] .gate__boxes i { border-color: var(--allow); }

.gate[data-verdict="allow_with_redaction"] { border-color: var(--allow_with_redaction); }
.gate[data-verdict="allow_with_redaction"] .gate__bar { border-block-end-color: var(--allow_with_redaction); }
.gate[data-verdict="allow_with_redaction"] .gate__title,
.gate[data-verdict="allow_with_redaction"] .gate__token { color: var(--allow_with_redaction); }
.gate[data-verdict="allow_with_redaction"] .gate__boxes i { border-color: var(--allow_with_redaction); }

.gate[data-verdict="review"] { border-color: var(--review); }
.gate[data-verdict="review"] .gate__bar { border-block-end-color: var(--review); }
.gate[data-verdict="review"] .gate__title,
.gate[data-verdict="review"] .gate__token { color: var(--review); }
.gate[data-verdict="review"] .gate__boxes i { border-color: var(--review); }

.verdicts {
  display: flex;
  gap: 7px;
  flex-wrap: wrap;
  margin-block-start: clamp(10px, 1.8vh, 18px);
}
.verdict {
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 0.5px;
  padding: 8px 12px;
  background: transparent;
  color: var(--soft);
  border: 1px solid var(--line-2);
  cursor: pointer;
}
.verdict:hover { color: var(--paper); border-color: #3a3a3a; }
.verdict[aria-pressed="true"] { color: var(--ink); background: var(--paper); border-color: var(--paper); }

/* ---------- the dot ---------- */

.dot__row {
  display: flex;
  gap: clamp(18px, 3vw, 34px);
  align-items: center;
  flex-wrap: wrap;
}
.dot__glyphs { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }
.dot__base { font-size: clamp(38px, 5vw, 54px); color: #333; line-height: 1; }
.dot__dotted { font-size: clamp(38px, 5vw, 54px); color: var(--paper); line-height: 1; }
.dot__add { font-family: var(--mono); font-size: 10px; color: var(--dim); letter-spacing: 1px; }
.script-arabic .dot__add { font-family: var(--sans); font-size: 12px; letter-spacing: 0; }
.dot__mark {
  font-size: clamp(38px, 5vw, 54px);
  color: var(--acid);
  line-height: 1;
  padding-inline-start: 14px;
  border-inline-start: 1px solid var(--line-2);
}
.dot__body { margin: 0; max-width: 48ch; color: var(--mid); font-size: 14.5px; }

/* ---------- cards ---------- */

.cards, .grid-4 { list-style: none; margin: 0; padding: 0; display: grid; gap: 12px; }
.cards { grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); }
.grid-4 { grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 18px; }

.card { border: 1px solid var(--line-2); padding-block-end: 13px; }
.card__art { display: block; width: 100%; height: auto; image-rendering: pixelated; }
.card p { margin: 0; padding-inline: 12px; }
.card__token {
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 0.5px;
  padding-block-start: 11px !important;
  word-break: break-all;
}
.card__rule { font-family: var(--mono); font-size: 9.5px; color: var(--dim); padding-block-start: 6px !important; word-break: break-all; }
.card__body { font-size: 12.5px; color: var(--mid); padding-block-start: 4px !important; line-height: 1.6; }

.card--allow .card__token { color: var(--allow); }
.card--allow_with_redaction .card__token { color: var(--allow_with_redaction); font-size: 9px; }
.card--review .card__token { color: var(--review); }
.card--deny .card__token { color: var(--deny); }

.tile h2 { font-family: var(--mono); font-size: 12px; letter-spacing: 1.4px; margin-block-end: 8px; }
.script-arabic .tile h2 { font-family: var(--sans); font-size: 15px; letter-spacing: 0; }
.tile p { margin: 0; font-size: 13.5px; color: var(--soft); line-height: 1.7; }
.tile--rule { border-block-start: 2px solid var(--paper); padding-block-start: 11px; }

/* ---------- panes, terminal, code ---------- */

.split-2 { display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: 20px; }
.pane h2 { font-size: 17px; margin-block-end: 8px; }
.pane p { margin: 0; color: var(--soft); font-size: 14px; }

.terminal {
  background: #080808;
  border: 1px solid var(--line-2);
  padding: 16px 18px;
  overflow-x: auto;
  font-family: var(--mono);
  font-size: 12.5px;
  line-height: 1.95;
  color: var(--soft);
}
.terminal p { margin: 0; white-space: pre; }
.terminal__dim { color: #4a4a4a; }
.terminal__deny { color: var(--deny); margin-block-start: 9px !important; }
.terminal__key { color: #4a4a4a; display: inline-block; min-width: 8ch; }
.terminal__alt {
  color: var(--paper);
  font-family: var(--sans);
  margin-block-start: 9px !important;
  white-space: normal !important;
}

.code {
  margin: 0;
  background: #080808;
  border: 1px solid var(--line-2);
  padding: 14px 16px;
  overflow-x: auto;
  font-family: var(--mono);
  font-size: 12px;
  line-height: 1.8;
  color: var(--mid);
}

.licence { margin-block-start: 26px; }
.licence h2 { font-size: 17px; margin-block-end: 8px; }
.licence p { margin: 0; color: var(--soft); font-size: 14px; max-width: 62ch; }

/* ---------- rules table ---------- */

.table-wrap { overflow-x: auto; border: 1px solid var(--line-2); }
.rules { border-collapse: collapse; width: 100%; min-width: 660px; }
.rules th, .rules td {
  text-align: start;
  padding: 10px 14px;
  border-block-end: 1px solid var(--line);
  font-size: 12px;
  vertical-align: top;
}
.rules th {
  font-family: var(--mono);
  font-weight: 500;
  font-size: 10.5px;
  letter-spacing: 1.4px;
  color: var(--acid);
}
.script-arabic .rules th { font-family: var(--sans); font-size: 12.5px; letter-spacing: 0; }
.rules tbody tr:last-child td { border-block-end: 0; }
.rules__id { font-family: var(--mono); font-size: 11px; color: var(--paper); }
.rules__outcome { font-family: var(--mono); font-size: 11px; white-space: nowrap; }
.rules__outcome--allow { color: var(--allow); }
.rules__outcome--allow_with_redaction { color: var(--allow_with_redaction); }
.rules__outcome--review { color: var(--review); }
.rules__outcome--deny { color: var(--deny); }
.rules__conf { color: var(--dim); }

/* ---------- court ---------- */

.court {
  position: relative;
  background: linear-gradient(90deg, #c6ac5c, #b48a82 38%, #8a7aac 72%, #6484be);
  overflow: hidden;
  margin-block-start: clamp(38px, 6vw, 76px);
}
/* The press runs its inks vividly underneath; this is what keeps the type on top of it
   readable. Sits above the image and below the text, so neither is compromised. */
.court::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 1;
  background: linear-gradient(
    to bottom,
    rgb(0, 0, 0) 0%,
    rgba(8, 6, 12, 0.97) 7%,
    rgba(8, 6, 12, 0.40) 20%,
    rgba(8, 6, 12, 0.08) 31%,
    rgba(8, 6, 12, 0.44) 42%,
    rgba(8, 6, 12, 0.86) 60%,
    rgba(8, 6, 12, 0.96) 100%
  );
  pointer-events: none;
}
.court__art {
  position: absolute;
  inset-inline: 0;
  inset-block-start: -7%;
  z-index: 0;
  width: 100%;
  height: 114%;
  object-fit: cover;
  object-position: center center;
  will-change: transform;
}
.court__inner {
  position: relative;
  z-index: 2;
  max-width: var(--max);
  margin-inline: auto;
  padding: clamp(30px, 5vw, 62px) var(--gutter) clamp(22px, 3vw, 36px);
  min-height: clamp(400px, 42vw, 600px);
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
}

.court__label {
  position: relative;
  z-index: 2;
  max-width: var(--max);
  margin-inline: auto;
  padding: 26px var(--gutter) 0;
  font-family: var(--mono);
  font-size: 12px;
  font-weight: 500;
  letter-spacing: 2.4px;
  color: var(--acid);
}
.script-arabic .court__label { font-family: var(--sans); font-size: 14px; letter-spacing: 0; }

.court__line {
  margin: 0;
  font-family: var(--mono);
  font-size: clamp(26px, 5.1vw, 62px);
  font-weight: 500;
  line-height: 1.14;
  letter-spacing: 0.03em;
  color: var(--court-cream);
}
.script-arabic .court__line { font-family: var(--sans); line-height: 1.3; letter-spacing: 0; }
.court__line .split {
  display: block;
  /* the split is the printed feeling: two ink copies knocked out of register behind the glyph */
  text-shadow: -4px 0 0 var(--split-a), 4px 0 0 var(--split-b);
}

.court__line-alt {
  margin: 12px 0 0;
  font-family: var(--mono);
  font-size: clamp(11px, 1.5vw, 16px);
  color: rgba(240, 237, 224, 0.72);
}
.script-arabic .court__line-alt { font-family: var(--sans); }

.court__rule { border: 0; border-block-start: 1px solid rgba(240, 237, 224, 0.34); margin: 22px 0; }

.court__caveat { display: flex; align-items: flex-start; gap: 14px; }
.court__shield { inline-size: 26px; block-size: 28px; flex: none; color: var(--acid); margin-block-start: 2px; }
.court__caveat p { margin: 0; font-size: 15px; color: var(--court-cream); max-width: 78ch; }
.court__caveat-alt { margin: 10px 0 0; font-size: 14px; color: rgba(240, 237, 224, 0.82); max-width: 78ch; }

.court__bar {
  display: flex;
  justify-content: space-between;
  gap: 16px;
  flex-wrap: wrap;
  font-family: var(--mono);
  font-size: 12px;
  letter-spacing: 1.4px;
  color: var(--court-dim);
}
.script-arabic .court__bar { font-family: var(--sans); letter-spacing: 0; font-size: 13.5px; }
.court__links { display: flex; gap: 18px; flex-wrap: wrap; }
.court__links a { color: var(--acid); text-decoration: none; }
.court__links a:hover { text-decoration: underline; }

/* ---------- narrow ---------- */

@media (max-width: 880px) {
  .hero { height: 220vh; }
  .hero__headline { font-size: clamp(24px, 6.4vw, 38px); }
  .hero__headline-alt { font-size: 16px; }
  .hero__lede { font-size: 13.5px; }
  .gate { width: clamp(160px, 46%, 220px); height: clamp(140px, 26vh, 200px); }
  .gate__rule { font-size: 8.5px; }
  .gate__token { font-size: 11px; }
}

@media (max-width: 560px) {
  .court__line .split { text-shadow: -2px 0 0 var(--split-a), 2px 0 0 var(--split-b); }
  .court__inner { min-height: 380px; }
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { transition-duration: 1ms !important; animation-duration: 1ms !important; }
}

/* ---------- scroll reveals ---------- */

/* Sections arrive as you come down the page. Deliberately small movement: this is a
   regulatory product, and content that flies around undermines the claim that the thing is
   sober. Anything that moves is also gated on the visitor not having asked it to stop. */
.reveal {
  opacity: 0;
  transform: translateY(18px);
  transition: opacity 620ms ease, transform 620ms cubic-bezier(0.16, 0.84, 0.44, 1);
}
.reveal.is-in { opacity: 1; transform: none; }

.scroll-cue {
  position: absolute;
  inset-block-end: 14px;
  inset-inline-start: 50%;
  transform: translateX(-50%);
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: 2px;
  color: #3d3d3d;
  pointer-events: none;
  transition: opacity 300ms linear;
}

@media (prefers-reduced-motion: reduce) {
  /* No pinning, no travel, no fades. The hero collapses to one screen showing the resolved
     state, and every section is simply present. */
  .hero { height: auto; }
  .hero__sticky { position: static; height: auto; min-block-size: 100svh; }
  .gate, .gap-art { opacity: 1 !important; }
  .hand { transform: none !important; }
  .reveal { opacity: 1; transform: none; transition: none; }
  .scroll-cue { display: none; }
}

/* ---------- touches ---------- */

/* A hairline that draws out from each section label. Cheap, quiet, and it gives every
   section a moment of arrival without anything sliding around. */
.eyebrow::after {
  content: "";
  flex: 1;
  block-size: 1px;
  background: var(--line-2);
  transform-origin: left center;
  transition: transform 900ms cubic-bezier(0.16, 0.84, 0.44, 1) 140ms;
}
[dir="rtl"] .eyebrow::after { transform-origin: right center; }
.armed:not(.is-in) .eyebrow::after { transform: scaleX(0); }

/* Children arrive in sequence rather than as a block. The parent does not fade in these
   sections, or the two fades would compound into a slow grey wash. */
.armed:not(.is-in) .reveal-item {
  opacity: 0;
  transform: translateY(14px);
  transition: opacity 520ms ease, transform 520ms cubic-bezier(0.16, 0.84, 0.44, 1);
  transition-delay: calc(var(--i, 0) * 70ms);
}
.reveal-item {
  transition: opacity 520ms ease, transform 520ms cubic-bezier(0.16, 0.84, 0.44, 1);
  transition-delay: calc(var(--i, 0) * 70ms);
}

/* The refusal writes itself out a line at a time, so you watch it happen rather than
   arriving at a finished transcript. */
.armed:not(.is-in) .terminal.is-typing .terminal__line,
.armed:not(.is-in) .terminal.is-typing .terminal__alt { opacity: 0; }
.terminal.is-typing .terminal__line,
.terminal.is-typing .terminal__alt {
  transition: opacity 240ms ease;
  transition-delay: calc(var(--i, 0) * 150ms);
}


/* The dot is what changes the letter, so the dotted glyph is what lights up. */
.dot__dotted, .dot__mark { transition: color 700ms ease 260ms; }
.reveal .dot__dotted, .stagger .dot__dotted { color: #333; }
.is-in .dot__dotted { color: var(--paper); }
.reveal .dot__mark, .stagger .dot__mark { color: #333; }
.is-in .dot__mark { color: var(--acid); }

@media (prefers-reduced-motion: reduce) {
  .reveal-item, .terminal.is-typing .terminal__line, .terminal.is-typing .terminal__alt {
    opacity: 1 !important;
    transform: none !important;
    transition: none !important;
  }
  .eyebrow::after { transform: none !important; transition: none !important; }
  .dot__dotted { color: var(--paper) !important; }
  .dot__mark { color: var(--acid) !important; }
  .court__art { transform: none !important; }
}

/* ---------- more touches ---------- */

/* Progress measured in discrete dots rather than a continuous bar. A bar would imply a
   sliding scale, which is the one thing this engine does not have. */
.progress {
  position: fixed;
  inset-block-start: 0;
  inset-inline: 0;
  z-index: 30;
  display: flex;
  gap: 2px;
  padding: 5px var(--gutter) 0;
  pointer-events: none;
}
.progress i {
  flex: 1;
  block-size: 3px;
  background: #1e1e1e;
  transition: background-color 200ms linear;
}
.progress i.on { background: var(--acid); }

.eyebrow__n {
  color: var(--dim);
  font-variant-numeric: tabular-nums;
  letter-spacing: 1px;
}

/* The dot falls onto the letter it changes. Positioned against its own slot rather than
   against font metrics, so it lands the same way in any face. */
.dot__slot { position: relative; display: inline-block; }
.armed:not(.is-in) .dot__drop { opacity: 0; transform: translate(-50%, -34px); }
.dot__drop {
  position: absolute;
  inset-inline-start: 50%;
  inset-block-start: -2px;
  inline-size: 8px;
  block-size: 8px;
  background: var(--acid);
  transition: transform 560ms cubic-bezier(0.2, 0.9, 0.3, 1.35) 220ms, opacity 180ms linear 220ms;
}

/* A call on the request leg: it travels, the gate holds it, then it is released. The pause is
   the whole point, so it is the longest part of the cycle. */
.flow {
  position: relative;
  block-size: 26px;
  margin-block-start: 28px;
}
.flow__rail {
  position: absolute;
  inset-block-start: 12px;
  inset-inline: 0;
  block-size: 1px;
  background: var(--line-2);
}
.flow__gate {
  position: absolute;
  inset-block-start: 4px;
  inset-inline-start: calc(46% - 1px);
  inline-size: 2px;
  block-size: 18px;
  background: var(--dim);
}
.flow__dot {
  position: absolute;
  inset-block-start: 9px;
  inset-inline-start: 0;
  inline-size: 7px;
  block-size: 7px;
  background: var(--paper);
  opacity: 0;
}
.flow.is-running .flow__dot { animation: travel 5s linear infinite; }
.flow.is-running .flow__gate { animation: fire 5s linear infinite; }
@keyframes travel {
  0%   { inset-inline-start: 0;   opacity: 0; }
  6%   { opacity: 1; }
  34%  { inset-inline-start: 45%; opacity: 1; }
  56%  { inset-inline-start: 45%; opacity: 1; }
  92%  { inset-inline-start: 100%; opacity: 1; }
  100% { inset-inline-start: 100%; opacity: 0; }
}
@keyframes fire {
  0%, 33%   { background: var(--dim); block-size: 18px; inset-block-start: 4px; }
  38%, 52%  { background: var(--acid); block-size: 24px; inset-block-start: 1px; }
  60%, 100% { background: var(--dim); block-size: 18px; inset-block-start: 4px; }
}

.terminal__cursor { margin-block-start: 4px !important; }
.terminal__cursor span {
  display: inline-block;
  inline-size: 8px;
  block-size: 14px;
  background: var(--soft);
  vertical-align: -2px;
}
.is-in .terminal__cursor span { animation: blink 1.1s steps(1) infinite; }
@keyframes blink { 0%, 50% { opacity: 1; } 51%, 100% { opacity: 0; } }

/* The press coming into registration. The two ink copies start well out of register and pull
   in as the footer arrives — which is what the chromatic split is imitating in the first place. */
.court__line .split {
  text-shadow: calc(var(--split, 4px) * -1) 0 0 var(--split-a), var(--split, 4px) 0 0 var(--split-b);
}

@media (prefers-reduced-motion: reduce) {
  .dot__drop { opacity: 1 !important; transform: translate(-50%, 0) !important; transition: none !important; }
  .flow.is-running .flow__dot, .flow.is-running .flow__gate,
  .is-in .terminal__cursor span { animation: none !important; }
  .flow__dot { opacity: 1; inset-inline-start: 45%; }
  .progress { display: none; }
}

/* ---------- each card performs its own verdict, on a loop ---------- */

/* Nothing here hides the artwork by default. The card is fully visible with no JS at all;
   these animations only add behaviour on top, and only while the row is on screen. */

/* ALLOW: a call passes. It enters, crosses, and leaves — and another follows. */
.cards.is-running .card--allow .card__art { animation: pass 4.4s cubic-bezier(0.4, 0, 0.5, 1) infinite; }
@keyframes pass {
  0%   { clip-path: inset(0 100% 0 0); }
  38%  { clip-path: inset(0 0 0 0); }
  62%  { clip-path: inset(0 0 0 0); }
  100% { clip-path: inset(0 0 0 100%); }
}

/* ALLOW_WITH_REDACTION: the same passage, but it dims at the moment the regulated values
   are withheld. The bars are already struck through the artwork; this is the withholding. */
.cards.is-running .card--allow_with_redaction .card__art { animation: masked 4.4s cubic-bezier(0.4, 0, 0.5, 1) infinite; }
@keyframes masked {
  0%   { clip-path: inset(0 100% 0 0); opacity: 1; }
  38%  { clip-path: inset(0 0 0 0); opacity: 1; }
  46%  { opacity: 0.35; }
  56%  { opacity: 1; }
  62%  { clip-path: inset(0 0 0 0); }
  100% { clip-path: inset(0 0 0 100%); }
}

/* REVIEW: it never settles. Shifting by exactly one dot inverts the checkerboard phase, so
   the texture keeps flipping between its two equally valid states and resolves to neither.
   The image is carried wider than its card so the shift never exposes an edge. */
.card--review .card__art {
  inline-size: calc(100% + 4px);
  margin-inline-start: -2px;
}
.cards.is-running .card--review .card__art { animation: unsettled 1.7s steps(1) infinite; }
@keyframes unsettled {
  0%, 50% { transform: translateX(0); }
  51%, 100% { transform: translateX(2px); }
}

/* DENY: the wall does not drift into place, it arrives — then holds for most of the cycle,
   because a refusal is a standing state rather than an event. */
.cards.is-running .card--deny .card__art { animation: slam 4.4s cubic-bezier(0.2, 1.3, 0.35, 1) infinite; }
@keyframes slam {
  0%, 6%   { clip-path: inset(47% 0 47% 0); }
  15%, 88% { clip-path: inset(0 0 0 0); }
  97%, 100% { clip-path: inset(47% 0 47% 0); }
}

/* In the rule table the decision lands after the rule it belongs to, never with it. */
.rules__outcome {
  transition: opacity 320ms ease;
  transition-delay: calc(var(--i, 0) * 70ms + 340ms);
}
.armed:not(.is-in) .rules__outcome { opacity: 0; }

@media (prefers-reduced-motion: reduce) {
  .card__art { clip-path: none !important; transition: none !important; animation: none !important; }
  .rules__outcome { opacity: 1 !important; transition: none !important; }
}

/* The legal notice is small text and must never negotiate with the artwork for legibility,
   so it gets a solid panel. The display line above it is large enough to ride the picture
   on the scrim alone, which is what keeps the colour visible at all. */
.court__notice {
  /* No panel. The vignette already carries the bottom of the picture to near-black, so the
     small text clears contrast on its own and a panel would only read as a floating box. */
  margin-block-start: 20px;
}
.court__notice .court__rule { margin: 16px 0; }

/* ---------- figures ---------- */

.fig {
  display: flex;
  align-items: center;
  gap: 8px;
  margin: 12px 0 14px;
  block-size: 22px;
  font-family: var(--mono);
}

/* Install: the bay fills as the two packages land, and the run ends the way the demo ends —
   on a refusal. */
.fig--install .fig__bay { display: flex; gap: 3px; flex: 1; }
.fig--install .fig__bay i {
  flex: 1;
  block-size: 8px;
  background: #232323;
}
.fig--install.is-running .fig__bay i { animation: fill 3.6s linear infinite; }
.fig--install .fig__bay i:nth-child(1) { animation-delay: 0s; }
.fig--install .fig__bay i:nth-child(2) { animation-delay: 0.11s; }
.fig--install .fig__bay i:nth-child(3) { animation-delay: 0.22s; }
.fig--install .fig__bay i:nth-child(4) { animation-delay: 0.33s; }
.fig--install .fig__bay i:nth-child(5) { animation-delay: 0.44s; }
.fig--install .fig__bay i:nth-child(6) { animation-delay: 0.55s; }
.fig--install .fig__bay i:nth-child(7) { animation-delay: 0.66s; }
.fig--install .fig__bay i:nth-child(8) { animation-delay: 0.77s; }
.fig--install .fig__bay i:nth-child(9) { animation-delay: 0.88s; }
.fig--install .fig__bay i:nth-child(10) { animation-delay: 0.99s; }
@keyframes fill {
  0%, 4% { background: #232323; }
  12%, 74% { background: var(--paper); }
  86%, 100% { background: #232323; }
}
.fig--install .fig__stop {
  inline-size: 10px;
  block-size: 10px;
  background: #232323;
  flex: none;
}
.fig--install.is-running .fig__stop { animation: refuse 3.6s linear infinite; }
@keyframes refuse {
  0%, 30% { background: #232323; }
  38%, 74% { background: var(--deny); }
  82%, 100% { background: #232323; }
}

/* Attach: the topology the JSON actually describes. A call leaves the agent, is held at the
   proxy, and only then reaches the server. */
.fig--attach { position: relative; gap: 0; }
.fig--attach .fig__node {
  display: flex;
  align-items: center;
  gap: 5px;
  flex: none;
}
.fig--attach .fig__node::before {
  content: "";
  inline-size: 9px;
  block-size: 9px;
  background: var(--soft);
}
.fig--attach .fig__node--g::before { background: var(--acid); }
.fig--attach .fig__node b {
  font-weight: 400;
  font-size: 9.5px;
  letter-spacing: 0.5px;
  color: var(--dim);
}
.fig--attach .fig__wire { flex: 1; block-size: 1px; background: var(--line-2); margin: 0 8px; }
.fig--attach .fig__packet {
  position: absolute;
  inset-block-start: 7px;
  inset-inline-start: 0;
  inline-size: 6px;
  block-size: 6px;
  background: var(--paper);
  opacity: 0;
}
.fig--attach.is-running .fig__packet { animation: hop 4.4s linear infinite; }
.fig--attach.is-running .fig__node--g::before { animation: check 4.4s linear infinite; }
@keyframes hop {
  0% { inset-inline-start: 4%; opacity: 0; }
  8% { opacity: 1; }
  32% { inset-inline-start: 45%; opacity: 1; }
  54% { inset-inline-start: 45%; opacity: 1; }
  84% { inset-inline-start: 92%; opacity: 1; }
  94%, 100% { inset-inline-start: 92%; opacity: 0; }
}
@keyframes check {
  0%, 30% { background: var(--acid); transform: scale(1); }
  36%, 50% { background: var(--paper); transform: scale(1.5); }
  58%, 100% { background: var(--acid); transform: scale(1); }
}

@media (prefers-reduced-motion: reduce) {
  .fig--install .fig__bay i, .fig--install .fig__stop,
  .fig--attach .fig__packet, .fig--attach .fig__node--g::before { animation: none !important; }
  .fig--install .fig__bay i { background: var(--paper); }
  .fig--install .fig__stop { background: var(--deny); }
  .fig--attach .fig__packet { opacity: 1; inset-inline-start: 45%; }
}
