/* The collection stylesheet. Paper and ink, Young Serif titles, Figtree body,
   wall labels under the works.

   Literal colors appear once, in the token block below, and nowhere else in
   this file or in any template. Everything downstream reads a var(), so a
   change to the palette is a change to one block. */

:root {
  color-scheme: light;

  --paper:      #f7f7f5;   /* page ground */
  --paper-2:    #eeeeeb;   /* image placeholder while loading, table zebra */
  --ink:        #141414;   /* text, rules, filled buttons, dark bands */
  --ink-2:      #333333;   /* lede and body on paper */
  --ink-3:      #6b6b6b;   /* wall labels, meta, secondary nav */
  --rule:       #141414;   /* 1px rules; never lighter than ink on paper */
  --accent:     #4b3fd6;   /* links, the one eyebrow line per page. Nothing else. */
  --on-dark:    #f7f7f5;   /* text on dark bands */
  --on-dark-2:  #c9c9c4;
  --on-dark-3:  #9a9a95;
  --verdict-nailed: #2f7d4f;  --verdict-close: #8a8a86;  --verdict-what: #b3402e; /* prompt vs reality only */

  /* The dialog scrim: ink at 60%. Written as rgb() rather than a hex pair so
     the one place colors live stays the one place they live. */
  --scrim: rgb(20 20 20 / 0.6);

  --serif: "Young Serif", Georgia, "Times New Roman", serif;
  --sans: "Figtree", "Helvetica Neue", Arial, sans-serif;

  /* Body measure, in the middle of the 58-to-66 character range. */
  --measure: 62ch;

  --gutter: 64px;
  --section-gap: 72px;
}

/* --- Reset ---------------------------------------------------------------- */

*, *::before, *::after { box-sizing: border-box; }

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

body {
  margin: 0;
  background: var(--paper);
  color: var(--ink);
  font-family: var(--sans);
  font-size: 17px;
  line-height: 1.5;
  font-weight: 400;
  display: flex;
  flex-direction: column;
  min-height: 100vh;
  /* The one thing standing between a long prompt and a horizontal scrollbar. */
  overflow-wrap: break-word;
}

img, svg { display: block; max-width: 100%; }

h1, h2, h3, h4, p, figure, blockquote, ul, ol, dl, dd { margin: 0; }

ul, ol { padding: 0; list-style: none; }

button, input, textarea, select { font: inherit; color: inherit; }

/* One rule, applied everywhere, so nothing is reachable without being visible. */
:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

/* --- Type ----------------------------------------------------------------- */

.display-xl, .display-l, .display-m, .display-s {
  font-family: var(--serif);
  font-weight: 400;
  text-wrap: balance;
}

.display-xl { font-size: 72px; line-height: 1.02; letter-spacing: -0.015em; }
.display-l  { font-size: 44px; line-height: 1.05; letter-spacing: -0.015em; }
.display-m  { font-size: 30px; line-height: 1.15; }
.display-s  { font-size: 22px; line-height: 1.2; }

.lede  { font-size: 20px; line-height: 1.45; color: var(--ink-2); }

.body  { font-size: 17px; line-height: 1.5; color: var(--ink-2); }
.small { font-size: 15px; line-height: 1.45; }
.label { font-size: 13px; line-height: 1.4; }

/* The reset zeroes every margin, so the system says once where vertical space
   goes: under a title, and between paragraphs that follow one another. A page
   that wants something else says so; a page that says nothing still breathes. */
.display-xl, .display-l, .display-m, .display-s { margin-block-end: 24px; }
p + p { margin-block-start: 20px; }


/* Prose sets its own measure so a page never has to remember to. */
.measure { max-width: var(--measure); }

a { color: var(--accent); text-decoration: none; text-underline-offset: 0.18em; }
a:hover, a:focus-visible { text-decoration: underline; }

.link-plain { color: inherit; }

/* A button that reads as a link: More, Edit, Up, Down, Hide. Without this the
   browser draws its own grey box around the word. */
button.link-plain {
  padding: 0;
  border: 0;
  background: none;
  font: inherit;
  color: var(--ink-3);
  text-underline-offset: 0.18em;
  cursor: pointer;
}
button.link-plain:hover, button.link-plain:focus-visible { color: var(--ink); text-decoration: underline; }

strong, b { font-weight: 600; }

/* --- Layout --------------------------------------------------------------- */

.container {
  width: 100%;
  max-width: 1440px;
  margin-inline: auto;
  padding-inline: var(--gutter);
}

.grid-12 {
  display: grid;
  grid-template-columns: repeat(12, minmax(0, 1fr));
  gap: 32px;
}

.span-7 { grid-column: span 7; }
.span-5 { grid-column: span 5; }
.span-6 { grid-column: span 6; }
.span-12 { grid-column: span 12; }

/* Works. Five across on a desktop, three on a tablet, two on a phone. */
.work-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  column-gap: 20px;
  row-gap: 28px;
}

/* Vertical rhythm inside a block: forms, note stacks, anything that is a list
   of things one under another. */
.stack > * + * { margin-block-start: 20px; }

/* Sections are told apart by space, never by a card or a rule. */
.section { margin-block-start: var(--section-gap); }
.section:first-child { margin-block-start: 0; }

/* The single rule this system allows: under a section title row that carries
   filters or a count. */
.section-head {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: space-between;
  gap: 16px 32px;
  padding-block-end: 16px;
  border-block-end: 1px solid var(--rule);
  margin-block-end: 32px;
}

/* The row aligns on the baseline, so nothing inside it carries the title's
   usual space underneath. */
.section-head > * { margin-block: 0; }

.band {
  background: var(--ink);
  color: var(--on-dark);
  padding-block: var(--section-gap);
}

.band .lede, .band .body { color: var(--on-dark-2); }
.band .wall-label, .band .meta { color: var(--on-dark-3); }
/* The accent is a paper colour. On ink it reads at 2.6:1, so the eyebrow gives
   it up rather than the band giving up the contrast. */
.band .eyebrow { color: var(--on-dark-2); }
.band a { color: var(--on-dark); text-decoration: underline; }

.skip-link {
  position: absolute;
  left: -9999px;
  top: 0;
  background: var(--ink);
  color: var(--paper);
  padding: 12px 20px;
  z-index: 10;
}
.skip-link:focus { left: 0; }

/* The tag manager's noscript fallback. Google ships this hidden by a style
   attribute, which the content security policy drops, so it hides here. */
.tag-manager-fallback { display: none; visibility: hidden; }

/* --- Nav and footer ------------------------------------------------------- */

/* The wordmark and its tagline are one block, and the navigation sits beside
   them. They used to be a nav row with the tagline pulled up under it, which
   put the tagline under the links as soon as the links wrapped. */
.site-header {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 16px 24px;
  padding-block: 25px 0;
}

.nav { padding-block-start: 3px; }

.brand {
  font-family: var(--serif);
  font-weight: 400;
  font-size: 22px;
  line-height: 1.2;
  color: var(--ink);
}
.brand:hover, .brand:focus-visible { text-decoration: none; }

.nav-links {
  display: flex;
  flex-wrap: wrap;
  gap: 28px;
  font-size: 15px;
  font-weight: 500;
}

.nav-links a { color: var(--ink-3); }
.nav-links a:hover, .nav-links a:focus-visible { text-decoration: none; color: var(--ink); }
.nav-links a[aria-current="page"] { color: var(--ink); }

/* The masthead's line, under the wordmark and on every page. The display face,
   because it belongs to the name above it, and quiet, because the name is what
   should be read first. */
.tagline {
  font-family: var(--serif);
  font-size: 15px;
  line-height: 1.3;
  color: var(--ink-3);
  margin-block: 6px 0;
}

/* The curator's own row of links, under the site's, on every page behind the
   door. A rule under it says the pages below it are not the public site. */
.curate-nav {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: space-between;
  gap: 8px 24px;
  margin-block-start: 20px;
  padding-block: 12px;
  border-block-end: 1px solid var(--rule);
}

.page-main { flex: 1; padding-block: 32px var(--section-gap); }

.colophon {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: space-between;
  gap: 8px 32px;
  padding-block: 32px 40px;
  font-size: 14px;
  line-height: 1.45;
  color: var(--ink-3);
}

/* --- Component: work-figure ----------------------------------------------- */

.work-figure { display: block; }

.work-figure img {
  width: 100%;
  height: auto;
  background: var(--paper-2);
}

/* Grids crop to a common 2:3; a work page shows the whole picture. */
.work-grid .work-figure img {
  aspect-ratio: 2 / 3;
  object-fit: cover;
}

/* The held space for a work whose picture is not there: the loading ground at
   the ratio the picture would have had. */
.work-plate {
  aspect-ratio: 2 / 3;
  width: 100%;
  background: var(--paper-2);
}

.wall-label {
  margin-block-start: 12px;
  font-size: 13px;
  line-height: 1.4;
  color: var(--ink-3);
}

.wall-label .prompt {
  font-style: italic;
  display: block;
}

.wall-label .meta { display: block; }

.wall-label a { color: var(--ink-3); }
.wall-label a:hover, .wall-label a:focus-visible { color: var(--accent); }

/* --- Component: button ---------------------------------------------------- */

.button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  height: 50px;
  padding-inline: 24px;
  border: 1px solid var(--ink);
  border-radius: 0;
  background: var(--ink);
  color: var(--paper);
  font-family: var(--sans);
  font-size: 16px;
  font-weight: 600;
  line-height: 1;
  text-decoration: none;
  cursor: pointer;
}
.button:hover, .button:focus-visible { text-decoration: none; color: var(--paper); }

.button-secondary {
  background: transparent;
  color: var(--ink);
}
.button-secondary:hover, .button-secondary:focus-visible { color: var(--ink); }

.button:disabled, .button[aria-disabled="true"] {
  opacity: 0.4;
  cursor: default;
}

/* --- Component: input ----------------------------------------------------- */

.input {
  width: 100%;
  height: 52px;
  padding: 0 18px;
  border: 1px solid var(--ink);
  border-radius: 0;
  background: var(--paper);
  color: var(--ink);
  font-family: var(--sans);
  font-size: 17px;
}

.input:focus { outline: 2px solid var(--ink); outline-offset: 0; }

textarea.input {
  height: auto;
  padding: 14px 18px;
  line-height: 1.5;
  resize: vertical;
}

.field { display: block; max-width: var(--measure); }

.field > .field-label {
  display: block;
  margin-block-end: 8px;
  font-size: 13px;
  line-height: 1.4;
  font-weight: 600;
  color: var(--ink-3);
}

/* --- Component: filter-row ------------------------------------------------ */

.filter-nav { max-width: 100%; }

.filter-row {
  display: flex;
  flex-wrap: wrap;
  gap: 8px 20px;
  font-size: 14px;
  font-weight: 500;
}

.filter-row a { color: var(--ink-3); }
.filter-row a:hover, .filter-row a:focus-visible { color: var(--ink); text-decoration: none; }
.filter-row a[aria-current="true"] { color: var(--ink); }

/* The rest of a long row, folded under it. Opened, it is an index rather than
   a paragraph: columns a visitor can run an eye down, A to Z. */
.filter-more { justify-self: stretch; }
.filter-more summary {
  cursor: pointer;
  font-size: 14px;
  font-weight: 500;
  color: var(--ink-3);
}
.filter-more summary:hover { color: var(--ink); }
.filter-more[open] summary { margin-block-end: 12px; }
.filter-more .filter-row {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(11rem, 1fr));
  gap: 6px 20px;
}

/* --- Component: eyebrow --------------------------------------------------- */

.eyebrow {
  margin-block-end: 12px;
  font-size: 13px;
  line-height: 1.4;
  font-weight: 600;
  color: var(--accent);
}

/* --- Component: dialog ---------------------------------------------------- */

.dialog {
  max-width: 520px;
  width: calc(100% - 48px);
  padding: 32px;
  border: 1px solid var(--ink);
  border-radius: 0;
  background: var(--paper);
  color: var(--ink);
}

.dialog::backdrop { background: var(--scrim); }

.dialog-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 12px;
  margin-block-start: 24px;
}

/* --- Component: flash ----------------------------------------------------- */

/* A line at the top of the content, not a toast: it stays put, it is readable
   by a screen reader in document order, and it survives a page without script. */
.flash {
  margin-block-end: 32px;
  padding: 14px 18px;
  border: 1px solid var(--rule);
  font-size: 15px;
  line-height: 1.45;
  max-width: var(--measure);
}

.flash-error { background: var(--ink); color: var(--on-dark); }

/* --- Motion --------------------------------------------------------------- */

@media (prefers-reduced-motion: no-preference) {
  a { transition: color 120ms linear; }
  .dialog { transition: opacity 160ms linear; }
}

/* --- Narrow widths -------------------------------------------------------- */

@media (max-width: 719px) {
  :root { --gutter: 24px; --section-gap: 64px; }

  /* The scale above is the desktop scale. Seventy-two pixels of Young Serif
     does not fit a 360px viewport, and "no horizontal scroll at 360" is the
     criterion that wins. */
  .display-xl { font-size: 44px; line-height: 1.05; }
  .display-l  { font-size: 34px; line-height: 1.08; }
  .display-m  { font-size: 26px; line-height: 1.18; }

  .grid-12 { grid-template-columns: minmax(0, 1fr); gap: 32px; }
  .span-4, .span-5, .span-6, .span-7, .span-8, .span-12 { grid-column: span 1; }

  .site-header { flex-direction: column; align-items: stretch; padding-block: 20px 0; }
  .nav { padding-block-start: 0; }
  .nav-links { gap: 8px 20px; }
}

@media (min-width: 720px) {
  .work-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}

@media (min-width: 1200px) {
  .work-grid { grid-template-columns: repeat(5, minmax(0, 1fr)); }
}

/* --- The collection pages -------------------------------------------------- */

/* The front page's hero. The picture sits beside the title on a desktop and
   under it everywhere else, which the 12-column grid already does. */
.hero { align-items: start; }

/* The picture is centred in its column. Held to 420px at the column's left
   edge, it left a gutter on one side and none on the other, and the title's
   column read as pushed away from it. */
.hero-work { width: 100%; max-width: 420px; justify-self: center; }

/* Two controls side by side, wrapping rather than shrinking. */
.button-row {
  display: flex;
  flex-wrap: wrap;
  gap: 12px;
  margin-block-start: 32px;
}

/* The line under a pair of buttons that says what the second one costs. */
.note { margin-block-start: 16px; color: var(--ink-3); }

/* --- Share ---------------------------------------------------------------
   A disclosure that opens onto the link, painted as the third button in the
   row. The marker is taken off because a summary that looks like a button and
   carries a triangle looks like neither. */
.share { position: relative; }

.share > summary {
  list-style: none;
  cursor: pointer;
  user-select: none;
}

.share > summary::-webkit-details-marker { display: none; }

.share-panel {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  align-items: center;
  margin-block-start: 12px;
  padding: 16px;
  border: 1px solid var(--rule);
  background: var(--paper-2);
}

/* Wide enough to show the whole of a work's address, and allowed to shrink
   onto its own line on a phone rather than pushing the button off the row. */
.share-url {
  flex: 1 1 240px;
  min-width: 0;
  height: 44px;
  font-size: 15px;
}

.share-copy { flex: 0 0 auto; }

.page-main > .band { margin-block-start: var(--section-gap); }

.band-copy { align-self: center; }

/* --- The index ------------------------------------------------------------- */

/* Two filter rows stacked on the right of the title row: subjects, then
   verdicts once there are any. */
.filter-stack { display: grid; gap: 10px; justify-items: start; }

.search-form {
  display: flex;
  flex-wrap: wrap;
  align-items: end;
  gap: 12px;
  margin-block-end: 32px;
}

.search-form .field { flex: 1 1 320px; }

/* The button sits on the input's baseline, which means matching its height
   rather than the field's, since the field carries a label above it. */
.search-form .button { height: 52px; }

/* The title and, when there is a search to undo, the link that undoes it. */
.heading-row {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 8px 16px;
}

.clear { font-weight: 500; }

.empty { margin-block: 8px 0; }

/* Plain text at the foot of a list of works. Not buttons: turning a page is
   going somewhere, and the browser already draws that. */
.pagination {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  gap: 16px 32px;
  margin-block-start: var(--section-gap);
  font-size: 15px;
  font-weight: 500;
}

/* One link on its own goes to the side it belongs on, so "Earlier works" does
   not slide left when there is nothing later. */
.pagination > a:only-child:last-child { margin-inline-start: auto; }

/* --- The work page --------------------------------------------------------- */

.work-page { align-items: start; }

.work-plate-column { display: flex; flex-direction: column; align-items: center; }

/* The whole picture, never cropped. It is bounded by the viewport rather than
   by the column so a 512 × 768 work does not push the wall label off the
   screen on a short window. */
.work-full {
  width: auto;
  max-width: 100%;
  max-height: calc(100vh - 160px);
  height: auto;
  background: var(--paper-2);
}

.work-label .lede { margin-block-end: 32px; }

/* The wall label proper: a term and its value on one row, terms in the quiet
   ink so the values are what the eye lands on. */
.label-list {
  display: grid;
  grid-template-columns: max-content minmax(0, 1fr);
  gap: 8px 24px;
  font-size: 15px;
  line-height: 1.45;
}

.label-list dt { color: var(--ink-3); }
.label-list dd { color: var(--ink-2); margin: 0; }

/* The document pages: About and License.
   Headings sit in a rail to the left of the prose rather than above it, so a
   heading and the paragraph it introduces start on the same line. A page of
   short sections stacked in one column reads as a list of disconnected blocks,
   which is what this is here to stop. Everything a reader actually reads sits
   in one column at one measure — the spec sheet included, because a definition
   list wider than the paragraph above it is the thing the eye notices first. */
.document-head { margin-block-end: 56px; }
.document-head > h1 { margin-block-end: 0; }

/* A document with no section headings — the license — is one column at the
   same measure, aligned with its own title. Running it through the rail layout
   above would indent the whole page past an empty gutter, which reads as a
   mistake rather than as a margin. */
.doc-body { max-width: var(--measure); }
.doc-body > * + * { margin-block-start: 14px; }
.doc-body > .lede + * { margin-block-start: 22px; }

.doc-section {
  display: grid;
  grid-template-columns: 200px minmax(0, var(--measure));
  column-gap: 48px;
  margin-block-start: 44px;
}
.doc-section:first-of-type { margin-block-start: 0; }
.doc-section > * { grid-column: 2; }
.doc-section > * + * { margin-block-start: 14px; }
.doc-section > h2 {
  grid-column: 1;
  grid-row: 1;
  margin-block-end: 0;
  color: var(--ink-3);
}
.doc-section > h2 + * { margin-block-start: 0; }
.doc-section > .button { margin-block-start: 24px; justify-self: start; }
.doc-section > .label-list { margin-block-start: 20px; }

@media (max-width: 1023px) {
  /* Under the width the rail needs, the heading goes back above its prose.
     Every child moves to the one remaining column: a child still asking for
     column 2 would make grid create an implicit one and take the width with
     it, leaving the headings stacked one letter wide. */
  .doc-section { grid-template-columns: minmax(0, 1fr); column-gap: 0; }
  .doc-section > * { grid-column: 1; }
  .doc-section > h2 { grid-row: auto; margin-block-end: 12px; }
  .doc-section > h2 + * { margin-block-start: 0; }
}

@media (max-width: 1199px) {
  /* Under the width the two columns need, the picture goes first at full
     width and the label follows it, which is the order the markup is in. */
  .work-page { grid-template-columns: minmax(0, 1fr); }
  .work-page > .span-5, .work-page > .span-7 { grid-column: span 1; }
  .work-full { max-height: none; }
}

@media (max-width: 719px) {
  .hero-work { max-width: none; }
  .search-form .field { flex-basis: 100%; }
}

/* --- The rack ------------------------------------------------------------- */

/* /machines is a diagram of a room: ten servers down the page, four Tesla K80
   halves across each one. It is not a table. A table would promise that the
   columns mean something comparable, and slot 2 of one server has nothing to do
   with slot 2 of another — they are positions in a chassis, not a series. */

/* The page reads top to bottom: the title and its lede, a clear gap, then the
   rack with the hardware beside it, both starting on the same line. */
.machines-head .lede { margin-block-start: 16px; }

.rack-layout {
  row-gap: 56px;
  column-gap: 56px;
  margin-block-start: 48px;
  align-items: start;
}

.rack-aside h2 { margin-block: 0 16px; }
.rack-aside .loop-status { color: var(--ink); }

.rack {
  display: grid;
  row-gap: 6px;
}

.rack-row {
  display: grid;
  grid-template-columns: 72px repeat(4, minmax(0, 1fr));
  align-items: center;
  column-gap: 6px;
}

.rack-node {
  color: var(--ink-3);
  font-variant-numeric: tabular-nums;
}

/* A cell is 40px tall whatever it says, so the rack keeps its shape while the
   fleet changes under it. Text that does not fit is clipped rather than
   wrapping: a cell that grows moves every row below it. */
.slot {
  height: 40px;
  display: flex;
  align-items: center;
  padding-inline: 10px;
  font-size: 12px;
  line-height: 1.2;
  white-space: nowrap;
  overflow: hidden;
  font-variant-numeric: tabular-nums;
}

/* The four states, and the whole legend the lede describes. Dark is a visitor
   waiting, violet is the loop, light is a machine with nothing to do, and an
   outline is a machine that is not answering. */
.slot-commission { background: var(--ink); color: var(--paper); }
.slot-loop { background: var(--accent); color: var(--paper); }
.slot-resting { background: var(--paper-2); color: var(--ink-3); }
.slot-off { background: var(--paper); box-shadow: inset 0 0 0 1px var(--paper-2); }

.rack-counts {
  margin-block-start: 20px;
  color: var(--ink-3);
  font-variant-numeric: tabular-nums;
}

.rack-week { color: var(--ink-3); font-variant-numeric: tabular-nums; }

/* Under 1200px the aside goes below the rack rather than beside it, and the
   rack keeps its full width: forty cells at half the width are unreadable long
   before the paragraph beside them is. */
@media (max-width: 1199px) {
  .rack-layout > .span-7,
  .rack-layout > .span-5 { grid-column: span 12; }
}

@media (max-width: 700px) {
  .rack-row { grid-template-columns: 64px repeat(4, minmax(0, 1fr)); column-gap: 4px; }
  .slot { padding-inline: 6px; font-size: 11px; }
}

/* --- The commission ------------------------------------------------------

   The one page where the site is a studio rather than a museum. The frames
   still hang the way works do — paper ground, wall label underneath, nothing
   overlaid on the picture — and the only thing that moves is the blur coming
   off a frame as the machine drawing it makes up its mind. */

/* The form is one column at the body measure, so the box, the shape, the small
   print and the row of controls all share a left and a right edge. Nothing in
   it sets its own width. */
.commission-form { max-width: var(--measure); margin-block-start: 40px; }

.commission-form > * + *,
.sign-in-form > * + * { margin-block-start: 24px; }
.fixed-settings { color: var(--ink-3); max-width: var(--measure); }
.terms { color: var(--ink-3); max-width: var(--measure); }

/* Shape holds three short words. At the full measure it reads as an empty
   bar, so it is held to the width its longest option needs. */
.commission-form .select { max-width: 280px; }

.select {
  appearance: none;
  background-image: linear-gradient(45deg, transparent 50%, var(--ink-3) 50%),
                    linear-gradient(135deg, var(--ink-3) 50%, transparent 50%);
  background-position: calc(100% - 20px) calc(50% + 2px), calc(100% - 14px) calc(50% + 2px);
  background-size: 6px 6px, 6px 6px;
  background-repeat: no-repeat;
  padding-inline-end: 40px;
}

.commission-prompt {
  font-size: 20px;
  line-height: 1.4;
  font-style: italic;
  color: var(--ink-2);
}

/* The page is two equal columns — what was asked for beside what the machines
   made of it — so the frames are two across and two down rather than four.

   Four across put each frame at about a fifth of the width it has now: a row of
   tall thin slots that read as a fence rather than as four pictures, under a
   heading that spanned the whole page and lined up with neither. A commission
   is four renderings of one prompt, and two by two is the shape that says so. */
.frame-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 28px 20px;
}

/* The column the prompt, the receipt and the controls share. The gaps are
   larger than .stack's because this column carries four different kinds of
   thing and the space is what separates them; nothing here draws a rule. */
.commission-column > * + * { margin-block-start: 32px; }
.commission-aside { margin-block-start: 48px; }

/* The held space a frame occupies before anything has been drawn into it. It
   is the same ground a missing work uses, because that is what it is: a place
   on the wall with nothing on it yet. */
.frame-plate {
  background: var(--paper-2);
  display: block;
  width: 100%;
  overflow: hidden;
}

/* The three shapes a commission can ask for. The plate holds the one the
   machines are drawing, so the page does not reflow when the frames land. */
.frame-portrait  { aspect-ratio: 512 / 768; }
.frame-landscape { aspect-ratio: 768 / 512; }
.frame-square    { aspect-ratio: 1 / 1; }

.frame-plate img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* A preview is a half-drawn picture decoded cheaply, and it looks like one.
   The blur is set per frame from the step the machine is on, so it is an
   inline style rather than a class; what is here is the transition it eases
   along and the opacity it comes in at. */
.frame-drawing .frame-plate img {
  opacity: 0.6;
  filter: blur(12px);
  transition: filter 400ms linear, opacity 400ms linear;
}

.frame-done .frame-plate img {
  opacity: 1;
  filter: none;
}

/* A frame nobody has finished breathes: a slow swell in brightness, held to a
   few percent so it reads as a machine working rather than as something
   flashing for attention. It stops the moment the frame lands.

   The animation is on the plate rather than on the picture inside it because
   the picture already carries a filter — the blur, written per step from the
   stream — and a second filter on the same element replaces the first. */
@keyframes frame-pulse {
  0%, 100% { filter: brightness(1); }
  50%      { filter: brightness(0.93); }
}

.frame-waiting .frame-plate,
.frame-drawing .frame-plate {
  animation: frame-pulse 2.2s ease-in-out infinite;
}

.frame-failed .frame-plate {
  border: 1px solid var(--paper-2);
  background: var(--paper);
}

/* A finished frame opens full size. The link is the whole plate. */
.frame-open { display: block; width: 100%; height: 100%; cursor: zoom-in; }

/* The seed, directly under the picture it drew: the number somebody copies to
   ask for the same frame again, set in figures that line up down the row. */
.frame-seed {
  display: block;
  font-variant-numeric: tabular-nums;
  color: var(--ink-2);
}

/* What a machine read in the frame, once one has. */
.frame-description {
  display: block;
  margin-block-start: 6px;
  color: var(--ink-2);
}

/* The zip is there for whoever wants the prompt and settings with the
   picture, which is not most people, so it is a line of small print rather
   than a button. */
.frame-download {
  display: block;
  margin-block-start: 6px;
  font-size: 12px;
}

/* The lightbox: one frame at the size the machine rendered it, or as much of
   that as the window holds. */
.lightbox {
  max-width: calc(100vw - 32px);
  max-height: calc(100vh - 32px);
  padding: 0;
  border: 0;
  background: transparent;
  overflow: visible;
}
.lightbox::backdrop { background: var(--scrim); }
.lightbox form { display: flex; justify-content: flex-end; margin-block-end: 8px; }
.lightbox-close, .lightbox-close:hover, .lightbox-close:focus-visible {
  background: var(--paper);
  color: var(--ink);
}
.lightbox img {
  display: block;
  max-width: calc(100vw - 32px);
  max-height: calc(100vh - 80px);
  width: auto;
  height: auto;
  cursor: zoom-out;
}

.button-small {
  height: 32px;
  padding-inline: 14px;
  font-size: 13px;
}

.checkbox {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  font-size: 15px;
  line-height: 1.45;
  color: var(--ink-2);
  cursor: pointer;
}

.checkbox input {
  margin: 2px 0 0;
  width: 16px;
  height: 16px;
  accent-color: var(--ink);
}

.receipt { color: var(--ink-3); margin-block-start: 16px; }

.commission-actions {
  margin-block-start: 32px;
  display: flex;
  align-items: center;
  gap: 16px;
  flex-wrap: wrap;
}

/* The form boxes nothing and lays nothing out: its children join the row above,
   so the two buttons sit together whatever is in front of them. One of them
   posts this form and the other is a link, and a flex row cannot align them
   while a form element stands between the two.

   The Turnstile widget is 300px wide and takes its own line, which is what
   leaves the buttons a row of their own. Before this it shared the row and
   pushed the second button onto a line by itself, half a column to the left of
   the first. */
.commission-actions form { display: contents; }
.commission-actions .cf-turnstile { flex-basis: 100%; }

/* A phone has no room for a row with two ends. The controls stack, full width,
   in the order they are read: change what is in the box, then send it. */
@media (max-width: 519px) {
  .commission-controls, .control-group { display: grid; gap: 12px; }
  .commission-controls > .control-group + .button { margin-inline-start: 0; }
  .commission-controls .button { width: 100%; }
}

@media (prefers-reduced-motion: reduce) {
  .frame-drawing .frame-plate img { transition: none; }
  .frame-waiting .frame-plate, .frame-drawing .frame-plate { animation: none; }
}

/* --- Prompt vs reality ------------------------------------------------------ */

/* The round is the page: the band fills the viewport and the work hangs on it,
   so a picture that is hard to read sits where a difficult painting would. */
.versus-band {
  padding-block: calc(var(--section-gap) + 16px);
  margin-block-end: var(--section-gap);
}

.round-ask { align-self: center; }

/* Somebody else's words, quoted rather than styled as a heading: this is the
   one line on the site that was typed by a visitor. */
.round-prompt {
  margin: 8px 0 32px;
  padding: 0;
  border: 0;
  max-width: 30ch;
  font-size: 24px;
  line-height: 1.4;
  color: var(--on-dark);
}

.round-asked, .round-instead { color: var(--on-dark-3); }

.round-question {
  margin-block-end: 16px;
  font-size: 17px;
  color: var(--on-dark-2);
}

/* On ink the secondary button is a hairline of paper rather than of ink. */
.band .button-secondary {
  border-color: var(--on-dark);
  color: var(--on-dark);
  background: transparent;
}

.band .button-secondary:hover, .band .button-secondary:focus-visible {
  background: var(--on-dark);
  color: var(--ink);
}

/* The one primary button that appears on a band: paper, with the ink's own
   text, so the way on is the brightest thing in the band. */
.button-paper {
  background: var(--paper);
  border-color: var(--paper);
  color: var(--ink);
}

.button-paper:hover, .button-paper:focus-visible {
  background: var(--on-dark-2);
  border-color: var(--on-dark-2);
}

/* Three rectangles, one bar. It is 10 units tall in its own coordinates and
   10 pixels tall on the page, stretched to whatever width it is given. */
.verdict-bar {
  display: block;
  width: 100%;
  height: 10px;
  margin-block-end: 12px;
  background: var(--on-dark-3);
}

.verdict-bar .verdict-nailed { fill: var(--verdict-nailed); }
.verdict-bar .verdict-close  { fill: var(--verdict-close); }
.verdict-bar .verdict-what   { fill: var(--verdict-what); }

.verdict-legend {
  display: flex;
  flex-wrap: wrap;
  gap: 4px 20px;
  margin: 0;
  font-size: 12px;
  color: var(--on-dark-3);
  font-variant-numeric: tabular-nums;
}

/* The visitor's own answer, so somebody can find their vote in the result. */
.verdict-mine { color: var(--on-dark); font-weight: 600; }

.round-votes {
  margin-block: 16px 0;
  font-size: 13px;
  color: var(--on-dark-2);
  font-variant-numeric: tabular-nums;
}

/* The work, uncropped, on the ink. It is bounded by the height of the window
   as well as by its column, so the round is one screen: a band taller than the
   viewport hides the question under the picture. */
.round-work {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
}

.round-work img {
  max-width: 100%;
  max-height: 62vh;
  width: auto;
  height: auto;
  background: var(--ink-2);
}

.round-work .body { color: var(--on-dark-2); max-width: var(--measure); }
.round-instead { margin-block: 16px 4px; }

/* Six across under the band, which is the whole of a session's history. */
.earlier-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }

/* On paper the verdict colours label a work rather than fill a bar, so they
   are the same three names used as ink. */
.wall-label .verdict-nailed { background: none; color: var(--verdict-nailed); }
.wall-label .verdict-close  { background: none; color: var(--verdict-close); }
.wall-label .verdict-what   { background: none; color: var(--verdict-what); }

@media (min-width: 720px) {
  .earlier-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}

@media (min-width: 1200px) {
  .earlier-grid { grid-template-columns: repeat(6, minmax(0, 1fr)); }
}

/* --- The wall --------------------------------------------------------------- */

/* Kiosk mode: paper edge to edge, one work hanging on it. Every size here is
   in viewport units, because the screen this runs on is a television across a
   room and not a window somebody is reading at arm's length. */
.wall-body {
  height: 100vh;
  overflow: hidden;
  background: var(--paper);
  cursor: default;
}

/* The cursor goes after three seconds of nobody moving it. The class is put on
   by the script, so a page with no JavaScript keeps its cursor. */
.wall-body.wall-idle { cursor: none; }

.wall {
  height: 100vh;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 6vh 6vw;
}

/* The picture and the label, one column, both starting at the same left edge.
   The figure shrinks to whichever is wider and the whole thing is centred on
   the screen, which is how a label hangs beside a painting. */
.wall-frame {
  margin: 0;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 3vh;
  /* The figure is the picture's width, so the picture is what gets centred on
     the screen. The label hangs off it to the right, starting at its left edge
     the way a museum's does — it is wider than the picture and it is not what
     the room is looking at. */
  width: min-content;
  max-width: 100%;
}

/* The whole picture, never cropped and never boxed.
   78% of the height is the wall's proportion and the calc is the floor under
   it: on a 1080p screen the label and the margins need the rest, and a label
   clipped off the bottom of a television is worse than a picture an inch
   smaller. On anything taller — the 4K panel in a lobby — the 78% wins. */
.wall-work {
  max-height: min(78vh, calc(88vh - 150px));
  max-width: 100%;
  width: auto;
  height: auto;
  object-fit: contain;
}

/* The label reads from three metres: the sizes scale with the height of the
   screen rather than being fixed at a desk's reading distance. */
.wall-plate {
  width: max-content;
  max-width: min(62ch, 86vw);
  text-align: left;
}

.wall-accession {
  margin: 0;
  font-size: clamp(20px, 2.6vh, 34px);
}

.wall-prompt {
  margin: 0.6vh 0 0;
  font-style: italic;
  color: var(--ink-2);
  font-size: clamp(17px, 2.1vh, 28px);
  line-height: 1.35;
}

.wall-detail {
  margin: 0.6vh 0 0;
  color: var(--ink-3);
  font-size: clamp(13px, 1.5vh, 20px);
}

/* The two corners. Both are the quiet ink, because neither is the work. */
.wall-address, .wall-paused {
  position: fixed;
  bottom: 2.4vh;
  margin: 0;
  color: var(--ink-3);
  font-size: clamp(12px, 1.3vh, 18px);
}

.wall-address { right: 2.4vw; }

.wall-paused {
  left: 2.4vw;
  display: flex;
  align-items: center;
  gap: 8px;
}

/* display: flex outruns the user agent's rule for the hidden attribute, so the
   word has to hide itself. Without this the wall says it is paused from the
   moment it loads, which is the one thing this word exists to tell the truth
   about. */
.wall-paused[hidden] { display: none; }

.wall-paused svg { fill: var(--ink-3); }

/* The crossfade between two works: the picture and its label go together, so
   the label never belongs to the picture before it. */
.wall-frame, .wall-address {
  transition: opacity 800ms ease;
}

.wall-fading .wall-frame,
.wall-fading + .wall-address { opacity: 0; }

@media (prefers-reduced-motion: reduce) {
  .wall-frame, .wall-address { transition: none; }
}

/* The sentence under a work, set like a caption. */
.work-description { margin-block: 16px 0; color: var(--ink-2); text-align: center; }
.work-description-by { color: var(--ink-3); white-space: nowrap; }

/* The form's one row of controls. "Surprise me" and "Make it better" change
   what is in the box and sit at its left edge; the control that sends it is
   pushed to the right edge of the form, so the row reads as a choice between
   editing and submitting rather than as three buttons in a heap. */
.commission-controls {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 12px;
  margin-block-start: 32px;
}

.control-group { display: flex; flex-wrap: wrap; gap: 12px; }

.commission-controls > .control-group + .button { margin-inline-start: auto; }

/* What "Make it better" came back with: each rewrite as it would be rendered,
   and the link that puts it in the box. As wide as the box it fills. */
.suggestions { max-width: var(--measure); }
.suggestion-list { list-style: none; margin: 12px 0 0; padding: 0; }
.suggestion {
  display: flex; flex-wrap: wrap; align-items: baseline; justify-content: space-between;
  gap: 12px 24px; padding-block: 14px; border-block-start: 1px solid var(--rule);
}
.suggestion:last-child { border-block-end: 1px solid var(--rule); }
.suggestion-prompt { margin: 0; flex: 1 1 28ch; }
