/* painterly demo page.
   A fixed rail holds everything you touch, the picture takes the rest of the
   window, and the transport never leaves the first screen whatever shape the
   photo is. Set as a printed sheet: a display serif, ruled bands, uppercase
   margin labels, hairlines instead of boxes. Tokens first, then one block per
   piece of the page. */

:root {
  color-scheme: light dark;
  --paper: #f4f3f0;
  --ink: #1c1b19;
  --muted: #6b6965;
  --line: #d8d6d1;

  /* Two surfaces from the same two colours: the rail you work on, and the
     quieter ground the picture hangs on. */
  --rail: color-mix(in oklab, var(--paper) 97%, var(--ink));
  --ground: color-mix(in oklab, var(--paper) 91%, var(--ink));

  --display: "Instrument Serif", Georgia, serif;
  --sans: "Instrument Sans", system-ui, sans-serif;
  --ease: cubic-bezier(0.23, 1, 0.32, 1);
  --rail-width: 20rem;
}

@media (prefers-color-scheme: dark) {
  :root {
    --paper: #151514;
    --ink: #ebe9e4;
    --muted: #9a9893;
    --line: #2b2a28;
    --rail: color-mix(in oklab, var(--paper) 94%, var(--ink));
    --ground: var(--paper);
  }
}

* {
  box-sizing: border-box;
}

body {
  margin: 0;
  background: var(--ground);
  color: var(--ink);
  font: 400 1rem / 1.55 var(--sans);
  scrollbar-color: var(--line) transparent;
}

::selection {
  background: var(--ink);
  color: var(--paper);
}

:focus-visible {
  outline: 2px solid var(--ink);
  outline-offset: 3px;
}

a {
  color: inherit;
  text-underline-offset: 0.2em;
  text-decoration-color: var(--line);
  transition: text-decoration-color 150ms var(--ease);
}

.app {
  height: 100svh;
  display: grid;
  grid-template-columns: var(--rail-width) minmax(0, 1fr);
}

/* The rail. Bands separated by rules, each named in the margin's voice. */

.rail {
  background: var(--rail);
  border-right: 1px solid var(--line);
  padding: 2rem 1.75rem;
  overflow-y: auto;
  display: grid;
  grid-template-rows: auto auto auto 1fr;
  gap: 1.75rem;
  align-content: start;
}

.brand h1 {
  font: 400 2.25rem / 1 var(--display);
  letter-spacing: -0.02em;
  margin: 0 0 0.75rem;
}

.brand p {
  margin: 0;
  font-size: 0.8125rem;
  line-height: 1.6;
  color: var(--muted);
}

.band {
  border-top: 1px solid var(--line);
  padding-top: 1.5rem;
}

legend,
.band h2 {
  font: 500 0.75rem / 1 var(--sans);
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--muted);
  padding: 0;
  margin: 0 0 1rem;
}

/* Subjects: a list of rows, the way a tool lists what it is pointed at. */

.photos {
  border-inline: 0;
  border-bottom: 0;
  margin: 0;
  padding-inline: 0;
  padding-bottom: 0;
  display: grid;
  gap: 0.25rem;
}

.photo {
  position: relative;
  display: grid;
  grid-template-columns: 2.75rem 1fr;
  align-items: center;
  gap: 0.875rem;
  padding: 0.25rem 0;
  cursor: pointer;
  font-size: 0.8125rem;
  line-height: 1.35;
  color: var(--muted);
  transition: color 150ms var(--ease);
}

.photo input {
  position: absolute;
  inset: 0;
  opacity: 0;
  margin: 0;
  cursor: pointer;
}

.photo .thumb {
  width: 2.75rem;
  height: 2.75rem;
  object-fit: cover;
  filter: saturate(0.85);
  outline: 1px solid transparent;
  transition:
    filter 200ms var(--ease),
    outline-color 150ms var(--ease);
}

.photo > span:last-child {
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

.own .thumb {
  display: grid;
  place-items: center;
  border: 1px dashed var(--line);
}

.own small {
  display: block;
  font-size: 0.6875rem;
}

/* Chosen: the picture comes back to full colour and the line goes to ink. */
.photo:has(input:checked) {
  color: var(--ink);
}

.photo input:checked + .thumb {
  filter: none;
  outline-color: var(--ink);
}

.photo input:focus-visible + .thumb {
  outline: 2px solid var(--ink);
  outline-offset: 2px;
}

@media (hover: hover) and (pointer: fine) {
  a:hover {
    text-decoration-color: var(--ink);
  }
  .photo:hover {
    color: var(--ink);
  }
  .photo:hover .thumb {
    filter: none;
  }
  .own:hover .thumb {
    border-color: var(--ink);
  }
}

/* Settings: ruled entries, not boxes. */

.fields {
  display: grid;
  gap: 1.25rem;
}

.field > label {
  display: block;
  font-size: 0.75rem;
  color: var(--muted);
  margin-bottom: 0.25rem;
}

/* A drawn caret instead of the platform's, so the control belongs to the page. */
.select {
  display: grid;
  align-items: center;
}

.select select,
.select::after {
  grid-area: 1 / 1;
}

.select::after {
  content: "";
  justify-self: end;
  width: 0;
  height: 0;
  border-left: 4px solid transparent;
  border-right: 4px solid transparent;
  border-top: 5px solid var(--muted);
  pointer-events: none;
}

.field select {
  appearance: none;
  display: block;
  width: 100%;
  font: 400 0.9375rem / 1.4 var(--sans);
  color: inherit;
  background: none;
  border: 0;
  border-bottom: 1px solid var(--line);
  padding: 0.3rem 1.5rem 0.3rem 0;
  cursor: pointer;
  transition: border-color 150ms var(--ease);
}

.field select:hover {
  border-bottom-color: var(--ink);
}

.colophon {
  align-self: end;
  margin: 0;
  padding-top: 1.5rem;
  border-top: 1px solid var(--line);
  font-size: 0.75rem;
  line-height: 1.6;
  color: var(--muted);
}

/* The viewer: a running head, the plate, and a transport that stays put. */

.viewer {
  display: grid;
  grid-template-rows: auto minmax(0, 1fr) auto auto;
  min-height: 0;
  padding: 0 2rem;
}

.runner {
  display: flex;
  padding: 1.5rem 0;
  border-bottom: 1px solid var(--line);
  font-size: 0.75rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--muted);
}

.readout {
  font-variant-numeric: tabular-nums;
  text-transform: none;
  letter-spacing: 0;
  font-size: 0.8125rem;
  white-space: nowrap;
}

.frame {
  margin: 0;
  min-height: 0;
  display: flex;
  align-items: center;
  /* Room for the framing line, which stands off the canvas and is drawn outside
     it, so the line lands on the column's edge rather than a pixel past it. */
  padding: 1.75rem calc(0.75rem + 1px);
}

.frame canvas {
  display: block;
  max-width: 100%;
  max-height: 100%;
  width: auto;
  height: auto;
  outline: 1px solid var(--line);
  outline-offset: 0.75rem;
}

/* A photo held over the page: the framing line comes forward to take it. */
.viewer[data-dropping] .frame canvas {
  outline-color: var(--ink);
  outline-width: 2px;
}

.caption {
  margin: 0;
  font-size: 0.8125rem;
  color: var(--muted);
  min-height: 1.2em;
}

.transport {
  border: 0;
  border-top: 1px solid var(--line);
  margin: 1rem 0 0;
  padding: 0.875rem 0 1.5rem;
  display: flex;
  align-items: center;
  gap: 1.5rem;
}

.transport button {
  font: 500 0.875rem / 1 var(--sans);
  color: var(--ink);
  background: none;
  border: 0;
  padding: 0.45rem 0;
  cursor: pointer;
  white-space: nowrap;
  transition:
    transform 160ms var(--ease),
    opacity 150ms var(--ease);
}

.transport button:disabled {
  opacity: 0.35;
  cursor: default;
}

.transport button:active:not(:disabled) {
  transform: scale(0.97);
}

/* The three buttons a DVD player has, drawn here rather than imported: sharp
   triangles and hairline bars, the same geometry as the carets on the selects
   and the ticks on the stages. Play and pause are one button; the pressed
   state swaps which glyph shows. */

.group {
  display: flex;
  align-items: center;
  gap: 0.25rem;
}

.transport .icon-button {
  display: grid;
  place-items: center;
  width: 2.25rem;
  height: 2rem;
  padding: 0;
}

.icon-button svg {
  display: block;
  width: 20px;
  height: 20px;
  fill: currentColor;
}

.transport .icon-button.primary {
  width: 2.75rem;
  background: var(--ink);
  color: var(--paper);
}

/* One glyph at a time. These have to out-rank `.icon-button svg` above, which
   is why they name the button as well as the glyph. The button's *name* swaps
   between Play and Pause, which is what a screen reader reads; `data-playing`
   is only how the drawing keeps up. */
.icon-button .glyph-pause,
#play[data-playing] .glyph-play {
  display: none;
}

#play[data-playing] .glyph-pause {
  display: block;
}

/* The stages: the rule beside the buttons, cut where each brush hands over to
   the next and as wide as the time that brush takes. It carries two things at
   once, in two channels that never fight: the rule's *colour* says which
   brushes are planned, and the ink line growing along it says how much is
   painted. So the end of planning changes nothing that has to be unwound —
   the grey rule stays, and the ink fills over it. */

.stages {
  flex: 1;
  display: flex;
  min-width: 4rem;
}

/* No height of its own, so the rule lands on the buttons' centre line and the
   ticks rise from it. */
.stage {
  flex: var(--share, 1) 1 0;
  position: relative;
  height: 0;
  border-bottom: 1px solid var(--line);
  transition: border-color 300ms var(--ease);
}

/* Planned: the paper's line becomes the quieter ink, and stays. */
.stage[data-planned] {
  border-bottom-color: var(--muted);
}

/* The brush being planned breathes, so a slow plan is seen to be working. */
.stage[data-planning] {
  animation: planning 1400ms var(--ease) infinite;
}

@keyframes planning {
  50% {
    border-bottom-color: var(--muted);
  }
}

/* A tick where one brush hands over to the next. */
.stage + .stage::before {
  content: "";
  position: absolute;
  left: 0;
  bottom: 0;
  width: 1px;
  height: 0.375rem;
  background: var(--line);
}

.stage::after {
  content: "";
  position: absolute;
  inset: auto 0 -1px 0;
  height: 1px;
  background: var(--ink);
  transform: scaleX(var(--fill, 0));
  transform-origin: left;
}

@media (hover: hover) and (pointer: fine) {
  .transport button:not(.icon-button):not(:disabled):hover {
    text-decoration: underline;
    text-underline-offset: 0.3em;
  }
  .icon-button:not(.primary):not(:disabled):hover {
    background: color-mix(in oklab, var(--ink) 6%, transparent);
  }
  .icon-button.primary:not(:disabled):hover {
    background: color-mix(in oklab, var(--ink) 84%, var(--paper));
  }
}

/* Narrow: the bench unfolds. Picture and transport first, rail beneath. */

@media (max-width: 55rem) {
  .app {
    height: auto;
    grid-template-columns: 1fr;
  }
  .viewer {
    order: -1;
    padding: 0 1.25rem;
  }
  /* Tighter, because the readout now stays: the transport still clears the fold. */
  .runner {
    padding: 1rem 0;
  }
  .frame {
    padding: 1.5rem 0;
  }
  .frame canvas {
    max-height: 56svh;
  }
  .transport {
    flex-wrap: wrap;
    gap: 0.75rem 1.5rem;
  }
  .stages {
    flex-basis: 100%;
    order: -1;
  }
  .rail {
    border-right: 0;
    border-top: 1px solid var(--line);
    overflow: visible;
    grid-template-rows: none;
  }
}

@media (prefers-reduced-motion: reduce) {
  .transport button:active:not(:disabled) {
    transform: none;
  }
  .stage[data-planning] {
    animation: none;
    border-bottom-color: color-mix(in oklab, var(--muted) 50%, var(--line));
  }
}

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