/**
 * Vouchsafe design system: "the redacted reference".
 *
 * A letting reference is a document where everything about you is legible to a
 * stranger. This product is that same document with everything blacked out
 * except the answer. So the redaction bar is the signature device, and the
 * surface is paper rather than a dashboard.
 *
 * Duplicated byte-for-byte in host/ui/base.css. The two origins cannot share a
 * stylesheet without one fetching from the other, and a cross-origin request
 * for CSS would undercut the claim that the vault talks to nobody.
 *
 * Type: Fraunces for anything that carries voice, Public Sans for anything that
 * carries instruction. Both self-hosted, so neither origin makes a third-party
 * request at runtime. System monospace is reserved for machine names, because a
 * tool name is code and should not be dressed as prose.
 */

@font-face {
  font-family: 'Fraunces';
  src: url('./fraunces.woff2') format('woff2-variations');
  font-weight: 300 900;
  font-display: swap;
}

@font-face {
  font-family: 'Public Sans';
  src: url('./publicsans.woff2') format('woff2-variations');
  font-weight: 300 800;
  font-display: swap;
}

:root {
  color-scheme: light;

  /* Paper is the page. An earlier version floated a capped sheet on a darker
     desk, which framed every screen in a grey band and read as a photograph of
     a document rather than as a product. */
  --sheet: #f6f7f3;
  --sheet-sunk: #eceee7;

  /* Ink carries a faint green cast, picked up from the paper. Pure black on
     near-white is harsh at body sizes and reads as a screenshot, not a page. */
  --ink: #1a1c19;
  --ink-mid: #5a5f57;
  --ink-faint: #8b9088;

  --rule: #c9cdc3;
  --rule-soft: #dfe2da;

  /* Stamp inks. Violet is the affirmative because red and green as a pair
     collapse for the eight percent of men with a red-green deficiency, and a
     consent screen is the wrong place to encode meaning in that pair alone. */
  --stamp: #4c2a85;
  --stamp-wash: #ece6f6;
  --refuse: #9e1b32;
  --refuse-wash: #f7e4e7;
  --caution: #8a5a00;
  --caution-wash: #f8eeda;

  /* A modular scale at a perfect fourth from a 17px body. */
  --t-xs: 0.75rem;
  --t-sm: 0.8125rem;
  --t-base: 1.0625rem;
  --t-md: 1.375rem;
  --t-lg: 1.875rem;
  --t-xl: 2.5rem;
  --t-2xl: 3.3125rem;

  --serif: 'Fraunces', Georgia, 'Times New Roman', serif;
  --sans: 'Public Sans', ui-sans-serif, system-ui, -apple-system, sans-serif;
  --mono: ui-monospace, 'SF Mono', Menlo, Consolas, monospace;

  --gap: 1.5rem;
  --sheet-pad: clamp(1.25rem, 3vw, 2.75rem);
}
/* The `hidden` attribute must actually hide.
   The user agent hides it with `display: none`, which any author rule setting a
   display wins against. `.announcer` is `display: grid`, so a banner marked
   hidden painted anyway: every page load opened with an empty bordered box and
   a close button sitting over the masthead. This is stated once, for every
   element that carries the attribute, rather than remembered per component. */
[hidden] { display: none !important; }


* { box-sizing: border-box; }

html { -webkit-text-size-adjust: 100%; }

body {
  margin: 0;
  /* The stage bleeds past the sheet's padding by a negative margin. Without
     this the bleed adds to the document width on a narrow screen and the whole
     page scrolls sideways. */
  overflow-x: hidden;
  background: var(--sheet);
  color: var(--ink);
  font-family: var(--sans);
  font-size: var(--t-base);
  line-height: 1.6;
  font-weight: 400;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

/* ---- the sheet -------------------------------------------------------- */

.sheet {
  /* Full bleed. The content is still capped for line length by `.sheet > *`
     below, but nothing draws an edge, so the dark stage can run right across
     the viewport and the page reads as one surface. */
  overflow-x: clip;
  margin: 0;
  background: var(--sheet);
  padding: 0 var(--sheet-pad) clamp(3rem, 6vw, 5rem);
}

/*
 * Measure, not frame. Every direct child of the sheet is centred within a
 * readable width; the stage opts out with its own negative margin so it alone
 * touches both edges.
 */
.sheet > * {
  max-width: 78rem;
  margin-inline: auto;
}

/* ---- masthead --------------------------------------------------------- */

.masthead {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  gap: var(--gap);
  align-items: start;
  /* The sheet no longer pads its own top edge, so the masthead owns the space
     above the wordmark. */
  padding-top: clamp(1.25rem, 3vw, 2.25rem);
  padding-bottom: 1.5rem;
  border-bottom: 1px solid var(--ink);
}

.wordmark {
  font-family: var(--serif);
  font-variation-settings: 'opsz' 96, 'SOFT' 0, 'WONK' 1;
  font-weight: 800;
  font-size: var(--t-lg);
  line-height: 1;
  letter-spacing: -0.02em;
  margin: 0;
}

.wordmark span {
  font-weight: 400;
  color: var(--ink-mid);
  font-style: italic;
}

.standfirst {
  font-family: var(--serif);
  font-variation-settings: 'opsz' 40, 'SOFT' 20;
  font-size: var(--t-md);
  line-height: 1.32;
  font-weight: 400;
  max-width: 34ch;
  margin: 0.7rem 0 0;
  color: var(--ink);
  text-wrap: balance;
}

.meta {
  text-align: right;
  font-size: var(--t-sm);
  color: var(--ink-mid);
}

.meta code {
  font-family: var(--mono);
  font-size: var(--t-xs);
  color: var(--ink-faint);
  display: block;
  margin-top: 0.35rem;
  word-break: break-all;
}

/* ---- the hero: a line of a reference, redacted ------------------------ */

.reference {
  margin: 2.5rem 0 0;
  border-top: 1px solid var(--rule-soft);
}

.reference-row {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  gap: 1rem 1.75rem;
  align-items: center;
  padding: 1rem 0;
  border-bottom: 1px solid var(--rule-soft);
}

.reference-q {
  font-family: var(--serif);
  font-variation-settings: 'opsz' 32;
  font-size: var(--t-md);
  line-height: 1.28;
  margin: 0;
}

/* The device the whole product is named for. */
.redacted {
  display: inline-block;
  position: relative;
  background: var(--ink);
  color: transparent;
  border-radius: 1px;
  padding: 0 0.3em;
  user-select: none;
}

.redacted::selection { background: var(--ink); }

/* Wipes across the value when a check runs. One orchestrated moment, not an
   entrance animation on every element. */
@keyframes wipe {
  from { clip-path: inset(0 100% 0 0); }
  to   { clip-path: inset(0 0 0 0); }
}

.redacted.is-wiping { animation: wipe 420ms cubic-bezier(0.2, 0, 0, 1) both; }

@keyframes stamp-in {
  from { opacity: 0; transform: scale(1.5) rotate(-7deg); }
  60%  { opacity: 1; transform: scale(0.94) rotate(-2deg); }
  to   { opacity: 1; transform: scale(1) rotate(-2.5deg); }
}

.stamp {
  font-family: var(--serif);
  font-variation-settings: 'opsz' 60, 'WONK' 1;
  font-weight: 800;
  font-size: var(--t-md);
  letter-spacing: 0.02em;
  text-transform: lowercase;
  color: var(--stamp);
  border: 2.5px solid currentColor;
  border-radius: 3px;
  padding: 0.05em 0.5em 0.12em;
  transform: rotate(-2.5deg);
  white-space: nowrap;
}

.stamp.is-fresh { animation: stamp-in 380ms cubic-bezier(0.2, 0, 0, 1) both; }
.stamp.no { color: var(--refuse); }
.stamp.pending { color: var(--ink-faint); border-style: dashed; border-width: 2px; }

@media (prefers-reduced-motion: reduce) {
  .redacted.is-wiping, .stamp.is-fresh { animation: none; }
}

/* ---- section headings ------------------------------------------------- */

.section {
  margin-top: 3rem;
}

.section h2 {
  font-family: var(--serif);
  font-variation-settings: 'opsz' 48, 'SOFT' 10;
  font-weight: 600;
  font-size: var(--t-lg);
  line-height: 1.15;
  letter-spacing: -0.015em;
  margin: 0 0 0.35rem;
}

.section .lede {
  color: var(--ink-mid);
  max-width: 62ch;
  margin: 0 0 1.5rem;
}

/* ---- columns ---------------------------------------------------------- */

.split {
  display: grid;
  grid-template-columns: minmax(0, 1.25fr) minmax(0, 1fr);
  gap: 2.5rem;
  align-items: start;
}

@media (max-width: 62rem) {
  .split { grid-template-columns: 1fr; gap: 2rem; }
  .masthead { grid-template-columns: 1fr; }
  .meta { text-align: left; }
}

/* ---- permission rows -------------------------------------------------- */

.perm {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  gap: 0.75rem 1.25rem;
  align-items: center;
  padding: 0.95rem 0;
  border-bottom: 1px solid var(--rule-soft);
}

.perm:last-child { border-bottom: 0; }

.perm-title {
  font-weight: 500;
  font-size: var(--t-base);
  margin: 0;
}

.perm-reveals {
  color: var(--ink-mid);
  font-size: var(--t-sm);
  margin: 0.15rem 0 0;
  max-width: 52ch;
}

.perm-name {
  font-family: var(--mono);
  font-size: var(--t-xs);
  color: var(--ink-faint);
}

.perm.is-live .perm-title::before {
  content: '';
  display: inline-block;
  width: 0.5rem;
  height: 0.5rem;
  border-radius: 50%;
  background: var(--stamp);
  margin-right: 0.55rem;
  vertical-align: 0.08em;
}

/* ---- controls --------------------------------------------------------- */

button {
  font-family: var(--sans);
  font-size: var(--t-sm);
  font-weight: 500;
  color: var(--ink);
  background: transparent;
  border: 1.5px solid var(--ink);
  border-radius: 2px;
  padding: 0.4rem 0.85rem;
  cursor: pointer;
  transition: background 140ms ease, color 140ms ease, border-color 140ms ease;
}

button:hover { background: var(--ink); color: var(--sheet); }
button:focus-visible { outline: 2.5px solid var(--stamp); outline-offset: 2px; }
button[disabled] { opacity: 0.35; cursor: not-allowed; }
button[disabled]:hover { background: transparent; color: var(--ink); }

button.primary { background: var(--ink); color: var(--sheet); }
button.primary:hover { background: var(--stamp); border-color: var(--stamp); }

button.grant { border-color: var(--stamp); color: var(--stamp); }
button.grant:hover { background: var(--stamp); color: #fff; }

button.revoke { border-color: var(--refuse); color: var(--refuse); }
button.revoke:hover { background: var(--refuse); color: #fff; }

button.quiet { border-color: var(--rule); color: var(--ink-mid); }
button.quiet:hover { background: var(--rule-soft); color: var(--ink); }

.controls { display: flex; gap: 0.6rem; flex-wrap: wrap; align-items: center; }

label { display: block; }

.field-label {
  display: block;
  font-size: var(--t-sm);
  font-weight: 500;
  color: var(--ink-mid);
  margin-bottom: 0.25rem;
}

input, select {
  font-family: var(--sans);
  font-size: var(--t-base);
  color: var(--ink);
  background: #fff;
  border: 1px solid var(--rule);
  border-radius: 2px;
  padding: 0.4rem 0.55rem;
  width: 100%;
}

input:focus, select:focus { outline: 2.5px solid var(--stamp); outline-offset: 1px; border-color: var(--stamp); }

/* ---- figures ---------------------------------------------------------- */

.figure {
  font-family: var(--serif);
  font-variation-settings: 'opsz' 144, 'WONK' 1;
  font-weight: 800;
  font-size: var(--t-2xl);
  line-height: 0.92;
  letter-spacing: -0.03em;
  font-variant-numeric: tabular-nums;
  margin: 0;
}

.figure-label {
  font-size: var(--t-sm);
  color: var(--ink-mid);
  margin: 0.3rem 0 0;
  max-width: 26ch;
}

.figure.stamped { color: var(--stamp); }
.figure.refused { color: var(--refuse); }

/* ---- notes, ledger ---------------------------------------------------- */

.note { color: var(--ink-mid); font-size: var(--t-sm); }

.ledger {
  list-style: none;
  margin: 0;
  padding: 0;
  max-height: 19rem;
  overflow: auto;
  border-top: 1px solid var(--rule-soft);
}

.ledger li {
  display: grid;
  grid-template-columns: 4.2rem 1fr;
  gap: 0.85rem;
  padding: 0.5rem 0;
  border-bottom: 1px solid var(--rule-soft);
  font-size: var(--t-sm);
}

.ledger time { font-family: var(--mono); font-size: var(--t-xs); color: var(--ink-faint); }
.ledger .what { color: var(--ink-mid); word-break: break-word; }
.ledger .what b { font-weight: 600; color: var(--ink); }
.ledger .answer { color: var(--stamp); font-weight: 600; }
.ledger .denied { color: var(--refuse); font-weight: 600; }

ul.plain { list-style: none; margin: 0; padding: 0; }

/* ---- notices ---------------------------------------------------------- */

.notice {
  padding: 0.9rem 1.1rem;
  margin: 1.25rem 0;
  border-left: 3px solid var(--caution);
  background: var(--caution-wash);
  font-size: var(--t-sm);
}

.notice.bad { border-left-color: var(--refuse); background: var(--refuse-wash); }
.notice.good { border-left-color: var(--stamp); background: var(--stamp-wash); }
.notice strong { font-weight: 700; }

.tag {
  font-size: var(--t-xs);
  font-weight: 600;
  padding: 0.12rem 0.45rem;
  border-radius: 2px;
  background: var(--sheet-sunk);
  color: var(--ink-mid);
  white-space: nowrap;
}

.tag.live { background: var(--stamp-wash); color: var(--stamp); }
.tag.off { background: var(--refuse-wash); color: var(--refuse); }

.cost {
  font-family: var(--mono);
  font-size: var(--t-xs);
  color: var(--ink-mid);
  white-space: nowrap;
}

.cost.high { color: var(--refuse); font-weight: 700; }

.bar {
  height: 3px;
  background: var(--rule-soft);
  overflow: hidden;
  margin: 0.6rem 0 0;
}

.bar > i {
  display: block;
  height: 100%;
  background: var(--stamp);
  transition: width 320ms cubic-bezier(0.2, 0, 0, 1), background 220ms;
}

.bar > i.over { background: var(--refuse); }

.sunk {
  background: var(--sheet-sunk);
  padding: 1.1rem 1.25rem;
  border-radius: 2px;
}

hr.rule { border: 0; border-top: 1px solid var(--rule-soft); margin: 2rem 0; }

.visually-hidden {
  position: absolute;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}


/* ---- embedded ---------------------------------------------------------- */

/*
 * The vault is opened two ways: as its own page, and inside a letting agent's
 * site in a narrow column. Embedded, the reader has already been told what this
 * is by the page around it, and what they need is the controls, immediately.
 * So the explanatory hero, the file editor and the log are dropped rather than
 * shrunk, and remain one click away in their own tab.
 */

.embedded {
  background: var(--sheet);
}

.embedded .sheet {
  margin: 0;
  padding: 1.1rem 1.15rem 1.5rem;
  box-shadow: none;
  max-width: none;
}

.embedded .standfirst,
.embedded .hero-reference,
.embedded .file-editor,
.embedded .ledger-section {
  display: none;
}

.embedded .masthead {
  padding-bottom: 0.85rem;
  gap: 0.5rem;
}

.embedded .wordmark { font-size: var(--t-md); }

.embedded .section { margin-top: 1.4rem; }

.embedded .split {
  grid-template-columns: 1fr;
  gap: 1.4rem;
}

.embedded .section h2 { font-size: var(--t-md); }

.embedded .figure { font-size: var(--t-xl); }

.embedded .perm { padding: 0.7rem 0; }

.embedded .perm-reveals { font-size: var(--t-xs); }

/* A rule the page around it cannot draw: this panel is a different website. */
.embedded-note {
  display: none;
  font-size: var(--t-xs);
  color: var(--ink-faint);
  border-top: 1px solid var(--rule-soft);
  padding-top: 0.6rem;
  margin-top: 1.4rem;
}

.embedded .embedded-note { display: block; }

/* ==========================================================================
   Stage: the dark band.

   The paper surface is honest for a document but it is one value all the way
   down, and a page with no tonal range has no hierarchy no matter how good the
   type is. So the argument gets a stage: near-black, full bleed, with the claim
   at a size the rest of the page never reaches. Everything below it is quiet by
   comparison, which is what makes it read as the important part.
   ========================================================================== */

.stage {
  background: var(--ink);
  color: var(--sheet);
  /*
   * The band must reach both edges while its text stays on the same measure as
   * the masthead above it. Two earlier attempts got this wrong: a negative
   * margin depended on the sheet's padding never changing, and `100vw` plus a
   * percentage padding resolved against the wrong box and left the headline
   * 51px adrift of the wordmark, measured.
   *
   * So the stage keeps the measure exactly, like every other child, and only
   * its *paint* escapes: a spread shadow floods the colour outward and the
   * clip-path lets it through horizontally while still cropping it vertically.
   * Alignment is then true by construction rather than by arithmetic that has
   * to be kept in step.
   */
  margin-top: 1.5rem;
  margin-bottom: 0;
  padding-block: clamp(2.5rem, 6vw, 4.5rem) clamp(2rem, 4vw, 3rem);
  box-shadow: 0 0 0 100vmax var(--ink);
  clip-path: inset(0 -100vmax);
  position: relative;
  overflow: hidden;
}

/* Faint ruled lines, like the form this replaces. Decoration that is on-brief. */
.stage::before {
  content: '';
  position: absolute;
  inset: 0;
  background-image: repeating-linear-gradient(
    to bottom, transparent 0 2.35rem, rgba(246, 247, 243, 0.055) 2.35rem 2.4rem
  );
  pointer-events: none;
}

.stage > * { position: relative; }

.stage-claim {
  font-family: var(--serif);
  font-variation-settings: 'opsz' 144, 'WONK' 1, 'SOFT' 0;
  font-weight: 800;
  font-size: clamp(2.4rem, 6.2vw, 5rem);
  line-height: 0.98;
  letter-spacing: -0.035em;
  margin: 0;
  max-width: 20ch;
  text-wrap: balance;
}

.stage-claim em {
  font-style: normal;
  display: inline-block;
  background: var(--sheet);
  color: var(--ink);
  padding: 0 0.14em;
  transform: rotate(-0.5deg);
}

.stage-sub {
  font-size: var(--t-md);
  line-height: 1.4;
  color: #b9beb4;
  max-width: 46ch;
  margin: 1.4rem 0 0;
}

.stage-figures {
  display: flex;
  gap: clamp(1.75rem, 5vw, 4rem);
  flex-wrap: wrap;
  margin-top: 2.25rem;
  padding-top: 1.5rem;
  border-top: 1px solid rgba(246, 247, 243, 0.18);
}

.stage-figure {
  font-family: var(--serif);
  font-variation-settings: 'opsz' 144, 'WONK' 1;
  font-weight: 800;
  font-size: clamp(1.9rem, 3.6vw, 2.9rem);
  line-height: 1;
  letter-spacing: -0.02em;
  font-variant-numeric: tabular-nums;
  margin: 0;
}

.stage-figure.good { color: #c4a7f0; }
.stage-figure.bad { color: #e79aa8; }

.stage-figure + .stage-figure-label,
.stage-figure-label {
  font-size: var(--t-sm);
  color: #9aa094;
  margin: 0.3rem 0 0;
  max-width: 18ch;
}

/* The graph sits on the stage, so its lines glow instead of fading. */
.stage .graph-frame {
  margin: 1.5rem calc(var(--sheet-pad) * -0.4) 0;
}

.on-dark { --rule-soft: rgba(246, 247, 243, 0.16); }

.stage button {
  border-color: var(--sheet);
  color: var(--sheet);
}

.stage button:hover { background: var(--sheet); color: var(--ink); }

.stage .tag { background: rgba(246, 247, 243, 0.12); color: #cdd2c7; }
.stage .tag.live { background: rgba(196, 167, 240, 0.18); color: #d6bffa; }
.stage .tag.off { background: rgba(231, 154, 168, 0.16); color: #f0b6c1; }

.stage code { color: #8d9386; }

/* ---- property cards ---------------------------------------------------- */

.listing {
  display: grid;
  grid-template-columns: 5.5rem minmax(0, 1fr) auto;
  gap: 0 1.25rem;
  align-items: center;
  padding: 1.1rem 0;
  border-bottom: 1px solid var(--rule-soft);
}

.listing:last-child { border-bottom: 0; }

/*
 * A plan glyph rather than a photograph. Every letting site is photographs of
 * empty rooms; this one has none to show, and a fabricated stock image would be
 * a lie about a property that does not exist. The glyph encodes the actual
 * facts: one cell per bedroom, filled to the household limit.
 */
.plan {
  display: grid;
  gap: 2px;
  aspect-ratio: 1;
  padding: 4px;
  border: 1.5px solid var(--ink);
  background: var(--sheet);
}

.plan i {
  background: var(--rule-soft);
  border-radius: 1px;
}

.plan i.filled { background: var(--stamp); }

.listing-title {
  font-family: var(--serif);
  font-variation-settings: 'opsz' 32;
  font-size: var(--t-md);
  font-weight: 600;
  line-height: 1.2;
  margin: 0;
}

.listing-meta { color: var(--ink-mid); font-size: var(--t-sm); margin: 0.25rem 0 0; }

.listing-price {
  font-family: var(--serif);
  font-variation-settings: 'opsz' 60;
  font-weight: 700;
  font-size: var(--t-md);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

.listing.is-active { background: var(--stamp-wash); box-shadow: -1rem 0 0 var(--stamp-wash), 1rem 0 0 var(--stamp-wash); }

@media (max-width: 34rem) {
  .listing { grid-template-columns: 3.5rem minmax(0, 1fr); }
  .listing > :last-child { grid-column: 1 / -1; margin-top: 0.6rem; }
}

/* ---- sticky sidebar ---------------------------------------------------- */

@media (min-width: 62rem) {
  .sticky-panel {
    position: sticky;
    top: 1.25rem;
  }
}

/* ---- narrow screens ----------------------------------------------------- */

@media (max-width: 46rem) {
  /*
   * The figures are a row of three claims. Flex-wrapping them produced orphans:
   * a number on one line and its label on the next, with the button wedged
   * between. A two-column grid keeps each number with its own label.
   */
  .stage-figures {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 1.5rem 1.25rem;
  }

  .stage-figures > :last-child {
    grid-column: 1 / -1;
    margin-left: 0 !important;
  }

  .stage-figure-label { max-width: none; }

  /*
   * The graph is 1200 units wide and unreadable squeezed into 390px. Rather
   * than shrink it into illegibility or drop it, it keeps a legible minimum and
   * scrolls inside its own container, so the page itself never scrolls sideways.
   */
  .graph-frame {
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
    scrollbar-width: thin;
  }

  .graph-frame svg {
    min-width: 44rem;
  }

  .graph-frame::after {
    content: 'scroll to see the whole diagram';
    /* Sticky, because this sits inside the element that scrolls. Left at
       `display: block` it scrolled away with the diagram and the reader saw
       "croll to see the whole diagram" the moment they moved it. Pinning it to
       the left edge keeps the instruction legible while the thing it describes
       moves underneath. */
    position: sticky;
    left: 0;
    display: block;
    width: fit-content;
    font-size: var(--t-xs);
    color: #8d9386;
    padding-top: 0.4rem;
  }

  .stage-claim { font-size: clamp(2rem, 9vw, 2.6rem); }

  /* The price belongs beside the property, not stranded under its own card. */
  .listing {
    grid-template-columns: 3.25rem minmax(0, 1fr);
    row-gap: 0.75rem;
  }

  .listing > :last-child {
    grid-column: 1 / -1;
    display: flex;
    align-items: baseline;
    gap: 0.5rem;
    text-align: left;
  }

  .listing > :last-child .listing-meta { margin: 0; }
  .listing > :last-child button { margin-left: auto; }
}

/*
 * Tool names are long identifiers that must not be broken or clipped. Two
 * columns only once there is genuinely room for two full names; below that the
 * names collided with the next column.
 */
.tool-columns {
  columns: 1;
}

@media (min-width: 52rem) {
  .tool-columns {
    columns: 2;
    column-gap: 2.5rem;
  }
}

.tool-columns > li {
  break-inside: avoid;
}

/* ==========================================================================
   Messages and decisions.

   Two different jobs that are often given one shape. A message reports what
   already happened and must never take space from the page or block anything.
   A decision needs an answer, so it opens against the control that raised it,
   where the thing being decided about is still visible.
   ========================================================================== */

.toasts {
  position: fixed;
  /* Bottom right. The page's own content is left-aligned to a measure, so the
     left corner sits under it while the right is clear at every width. */
  right: max(1rem, env(safe-area-inset-right));
  bottom: max(1rem, env(safe-area-inset-bottom));
  z-index: 60;
  display: flex;
  flex-direction: column-reverse;   /* newest nearest the corner */
  gap: 0.55rem;
  width: min(26rem, calc(100vw - 2rem));
  /* The stack itself is not a target; only the messages in it are, so the page
     underneath stays clickable everywhere the stack is merely empty space. */
  pointer-events: none;
}

.toast {
  pointer-events: auto;
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  gap: 0.5rem;
  align-items: start;
  padding: 0.7rem 0.7rem 0.75rem 0.85rem;
  /*
   * Paper, like everything else. An earlier version was a dark card, which read
   * as somebody else's component dropped onto the page: the one dark surface in
   * the design is the stage, and it earns that by carrying the argument. A
   * message does not.
   *
   * The stamp rule on the left is the same device the permission rows use, so a
   * confirmation is visibly the same family as the thing it confirms.
   */
  background: var(--sheet);
  color: var(--ink);
  border: 1px solid var(--rule);
  border-left: 3px solid var(--stamp);
  border-radius: 2px;
  box-shadow: 0 8px 24px -12px rgba(26, 28, 25, 0.45), 0 1px 2px rgba(26, 28, 25, 0.12);
  touch-action: pan-y;             /* the page keeps vertical scroll; we take sideways */
  will-change: transform, opacity;
  animation: toast-in 260ms cubic-bezier(0.2, 0, 0, 1) both;
}

.toast.is-dragging { transition: none; cursor: grabbing; }
.toast:not(.is-dragging) { transition: transform 180ms ease, opacity 180ms ease; }

.toast-bad { border-left-color: var(--refuse); background: var(--refuse-wash); }
.toast-plain { border-left-color: var(--rule); }

.toast-message {
  margin: 0;
  font-size: var(--t-sm);
  line-height: 1.45;
  font-weight: 500;
}

.toast-detail {
  margin: 0.25rem 0 0;
  font-size: var(--t-xs);
  line-height: 1.45;
  color: var(--ink-mid);
}

.toast-close {
  background: transparent;
  border: 0;
  color: var(--ink-faint);
  font-size: 1.15rem;
  line-height: 1;
  padding: 0.1rem 0.25rem;
  cursor: pointer;
  border-radius: 3px;
  /* A 24px target, which is the floor for a pointer target, without making the
     glyph itself large enough to shout. */
  min-width: 1.6rem;
  min-height: 1.6rem;
}

.toast-close:hover { color: var(--ink); background: var(--sheet-sunk); }
.toast-close:focus-visible { outline: 2.5px solid var(--stamp); outline-offset: 1px; }

@keyframes toast-in {
  from { opacity: 0; transform: translateY(0.6rem) scale(0.98); }
  to   { opacity: 1; transform: none; }
}

@keyframes toast-out {
  to { opacity: 0; transform: translateX(1.5rem) scale(0.97); }
}

.toast.is-leaving { animation: toast-out 170ms ease both; }

@media (prefers-reduced-motion: reduce) {
  .toast, .toast.is-leaving { animation: none; }
  .toast:not(.is-dragging) { transition: none; }
}

/* On a phone the corner is where the thumb is, so the stack spans the width and
   sits above the home indicator rather than hugging one edge. */
@media (max-width: 34rem) {
  .toasts {
    left: 0.6rem;
    right: 0.6rem;
    width: auto;
  }
}

/* ---- decisions ---------------------------------------------------------- */

.confirm {
  position: absolute;
  z-index: 50;
  right: 0;
  top: calc(100% + 0.45rem);
  width: min(20rem, calc(100vw - 2rem));
  background: var(--sheet);
  border: 1.5px solid var(--ink);
  border-radius: 3px;
  padding: 0.85rem 0.9rem 0.9rem;
  box-shadow: 0 10px 28px -12px rgba(26, 28, 25, 0.5);
  animation: confirm-in 150ms cubic-bezier(0.2, 0, 0, 1) both;
}

@keyframes confirm-in {
  from { opacity: 0; transform: translateY(-0.3rem); }
  to   { opacity: 1; transform: none; }
}

@media (prefers-reduced-motion: reduce) {
  .confirm { animation: none; }
}

.confirm-question {
  margin: 0;
  font-family: var(--serif);
  font-variation-settings: 'opsz' 24;
  font-size: var(--t-base);
  font-weight: 600;
  line-height: 1.25;
}

.confirm-detail {
  margin: 0.35rem 0 0;
  font-size: var(--t-sm);
  color: var(--ink-mid);
  line-height: 1.45;
}

.confirm-actions {
  display: flex;
  gap: 0.5rem;
  margin-top: 0.8rem;
  justify-content: flex-end;
}

/* Near the right edge of a phone the popover would run off; pin it instead. */
@media (max-width: 34rem) {
  .confirm {
    position: fixed;
    left: 0.6rem;
    right: 0.6rem;
    top: auto;
    bottom: 0.6rem;
    width: auto;
  }
}

/* ==========================================================================
   The announcer: what the walkthrough speaks through.

   A banner at the top of the viewport, not a block in the page. The
   walkthrough talks for about a minute and then stops; anything occupying real
   layout for that minute pushes the content it is describing out from under the
   reader, and leaves its final sentence stranded in the document afterwards.
   ========================================================================== */

.announcer {
  position: fixed;
  /* Below the masthead, not over it. Centred at the top of the viewport the
     banner sat straight across the wordmark, and a walkthrough that covers the
     name of the thing it is explaining is not a good first impression. */
  top: calc(4.75rem + env(safe-area-inset-top));
  left: 50%;
  transform: translateX(-50%);
  z-index: 70;
  width: min(46rem, calc(100vw - 1.5rem));
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  gap: 0.5rem;
  align-items: start;
  padding: 0.85rem 0.8rem 0.9rem 1rem;
  background: var(--sheet);
  border: 1px solid var(--ink);
  border-top: 3px solid var(--stamp);
  border-radius: 2px;
  box-shadow: 0 14px 34px -14px rgba(26, 28, 25, 0.5);
  animation: announcer-in 240ms cubic-bezier(0.2, 0, 0, 1) both;
}

@keyframes announcer-in {
  from { opacity: 0; transform: translate(-50%, -0.6rem); }
  to   { opacity: 1; transform: translate(-50%, 0); }
}

@media (prefers-reduced-motion: reduce) {
  .announcer { animation: none; }
}

/* A stopped walkthrough must not look like a finished one. */
.announcer.is-halted { border-top-color: var(--refuse); background: var(--refuse-wash); }

.announcer-step {
  margin: 0 0 0.2rem;
  font-size: var(--t-xs);
  letter-spacing: 0.02em;
  color: var(--ink-faint);
}

.announcer-line {
  margin: 0;
  font-family: var(--serif);
  font-variation-settings: 'opsz' 36;
  font-size: var(--t-md);
  font-weight: 600;
  line-height: 1.25;
  text-wrap: balance;
}

.announcer-detail {
  margin: 0.25rem 0 0;
  font-size: var(--t-sm);
  color: var(--ink-mid);
  line-height: 1.45;
}

.announcer-output {
  font-family: var(--mono);
  font-size: var(--t-xs);
  color: var(--ink-mid);
  background: #fff;
  border: 1px solid var(--rule-soft);
  padding: 0.6rem 0.7rem;
  margin: 0.7rem 0 0;
  white-space: pre-wrap;
  max-height: 9rem;
  overflow: auto;
}

.announcer-close {
  background: transparent;
  border: 0;
  color: var(--ink-faint);
  font-size: 1.3rem;
  line-height: 1;
  padding: 0.1rem 0.3rem;
  min-width: 1.75rem;
  min-height: 1.75rem;
  cursor: pointer;
  border-radius: 3px;
}

.announcer-close:hover { color: var(--ink); background: var(--sheet-sunk); }
.announcer-close:focus-visible { outline: 2.5px solid var(--stamp); outline-offset: 1px; }

@media (max-width: 34rem) {
  /* The masthead stacks on a phone, so the banner needs more room above it. */
  .announcer { top: calc(6.5rem + env(safe-area-inset-top)); padding: 0.7rem 0.6rem 0.75rem 0.8rem; }
  .announcer-line { font-size: var(--t-base); }
  .announcer-output { max-height: 7rem; }
}

/*
 * A requirement that is standing between someone and an application.
 *
 * Marked only after they try to apply. Flagging every unmet requirement the
 * moment the page loads tells a person they have failed before they have done
 * anything, and by the time it matters they have stopped seeing it.
 */
.reference-row.is-required .reference-q::after {
  content: '\002a';
  color: var(--refuse);
  font-weight: 700;
  margin-left: 0.2em;
}

.reference-row.is-required {
  background: var(--refuse-wash);
  box-shadow: -0.75rem 0 0 var(--refuse-wash), 0.75rem 0 0 var(--refuse-wash);
}
