/* A dim room at night with one lamp on.
   Warm near-black ground, warm off-white ink, one muted purple used almost
   never. The photographs are the only bright thing on the screen; everything
   else recedes into the dark around them.
   Hairlines, engraved labels, generous margins, mostly empty space. */

@font-face {
  font-family: 'EB Garamond';
  src: url('fonts/ebgaramond-latin-roman.woff2') format('woff2-variations');
  font-weight: 400 700;
  font-style: normal;
  font-display: swap;
}

@font-face {
  font-family: 'EB Garamond';
  src: url('fonts/ebgaramond-latin-italic.woff2') format('woff2-variations');
  font-weight: 400 700;
  font-style: italic;
  font-display: swap;
}

:root {
  /* Warm near-black: brown in it rather than blue, so it reads as a room at
     night and not as a screen turned down. */
  --ground: #12171a;
  /* Never pure white. Lamplight on paper. */
  --ink: #e6eced;
  --ink-soft: #8a9698;
  /* The single accent. Muted purple, and used almost nowhere. */
  --accent: #5f9ea0;
  /* Hairlines are barely there — enough to draw a line, not enough to glow. */
  --rule: #273336;

  /* The rooms you are only in for a moment — the encounter and the beat before
     it — sit on a cooler, deeper ground than the app around them. Stepping into
     one should feel like stepping out of the lamplight, not like another page.
     VARIANT A. Two other pairs are proposed in the notes; each is a two-line
     change here and nothing else. */
  --ground-deep: #0d1719;
  --ink-cool: #e4eeee;
  --ink-soft-cool: #7f9295;
  --rule-cool: #1f2e30;

  --margin: clamp(1.25rem, 6vw, 3rem);
  --measure: 34rem;

  font-family: 'EB Garamond', 'Iowan Old Style', Palatino, Georgia, serif;
  /* Old-style figures everywhere, so 21:00 sits in the line rather than on it.
     Declared once here and never overridden. */
  font-variant-numeric: oldstyle-nums;
  font-feature-settings: 'onum' 1;
  color-scheme: only dark;
}

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

html,
body {
  margin: 0;
  background: var(--ground);
  color: var(--ink);
}

body {
  font-size: 1.125rem;
  line-height: 1.5;
  -webkit-text-size-adjust: 100%;
  padding:
    env(safe-area-inset-top) env(safe-area-inset-right)
    env(safe-area-inset-bottom) env(safe-area-inset-left);
}

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

button {
  font: inherit;
  font-variant-numeric: inherit;
  color: inherit;
  background: none;
  border: 0;
  padding: 0;
  cursor: pointer;
}

/* ------------------------------------------------------------- the plate */

.plate {
  max-width: var(--measure);
  margin: 0 auto;
  padding: calc(var(--margin) * 1.5) var(--margin) calc(var(--margin) * 2);
}

.plate-head,
.plate-foot {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 1rem;
}

.plate-foot {
  margin-top: 2.5rem;
}

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

.stack {
  display: flex;
  flex-direction: column;
  gap: 1.5rem;
  align-items: flex-start;
}

/* Engraved labels: letterspaced, small, quiet. */
.text-action,
.day-label,
.mark,
.pseudonym,
.field > span {
  font-size: 0.8125rem;
  letter-spacing: 0.14em;
  text-transform: lowercase;
  color: var(--ink-soft);
}

.text-action {
  color: var(--ink);
  border-bottom: 1px solid var(--rule);
  padding-bottom: 0.15rem;
}

.text-action:disabled {
  color: var(--ink-soft);
  cursor: default;
}

.link {
  font-size: 1rem;
  color: var(--ink);
  border-bottom: 1px solid var(--rule);
}

.link-quiet {
  color: var(--ink-soft);
}

.empty,
.aside,
.notice {
  color: var(--ink-soft);
  font-style: italic;
  max-width: 30rem;
}

.notice-wrong {
  color: var(--accent);
  font-style: normal;
}

/* ----------------------------------------------------------------- forms */

.field {
  display: flex;
  flex-direction: column;
  gap: 0.35rem;
  width: 100%;
  max-width: 20rem;
}

input[type='text'],
input[type='password'],
.writing {
  font: inherit;
  font-variant-numeric: inherit;
  color: var(--ink);
  background: transparent;
  border: 0;
  border-bottom: 1px solid var(--rule);
  padding: 0.35rem 0;
  width: 100%;
}

.writing {
  resize: vertical;
  min-height: 4.5rem;
}

input:focus-visible,
.writing:focus-visible,
button:focus-visible {
  outline: 0;
  border-bottom-color: var(--accent);
}

.writing::placeholder {
  color: var(--ink-soft);
  font-style: italic;
}

/* Both choices weigh the same. Neither is marked as the one to pick. */
.choice {
  border: 0;
  padding: 0;
  margin: 0;
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
}

.choice label {
  display: flex;
  align-items: baseline;
  gap: 0.6rem;
}

.choice input {
  accent-color: var(--accent);
}

.shutter input[type='file'] {
  position: absolute;
  width: 1px;
  height: 1px;
  opacity: 0;
}

.capture-preview,
.full-image {
  width: 100%;
  border: 1px solid var(--rule);
}

/* Whatever shape the photograph is, it has to fit on the screen with the
   writing and the choice still visible underneath it. */
.capture-preview {
  max-height: 45dvh;
  object-fit: contain;
}

.swap {
  margin: 0;
}

/* ------------------------------------------------------------------ home */

/* One thing to do, and a great deal of nothing around it. */
.plate-home {
  min-height: 68dvh;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  justify-content: center;
  gap: 2.25rem;
}

.reflection {
  margin: 0;
  max-width: 20rem;
  font-style: italic;
  font-size: 1.25rem;
  line-height: 1.4;
  color: var(--ink-soft);
}

/* The largest thing on the screen, and the only thing you are asked to do. */
.set-a-plate {
  font-size: 2.125rem;
  line-height: 1.1;
  color: var(--ink);
  border-bottom: 1px solid var(--rule);
  padding-bottom: 0.35rem;
}

.last-plate {
  margin: 0;
  font-size: 0.8125rem;
  letter-spacing: 0.14em;
  color: var(--ink-soft);
}

.plate-home .plate-foot {
  gap: 1.5rem;
  justify-content: flex-start;
}

/* --------------------------------------------------- the beat between */

/* The same room as an encounter, one line in the middle of it. No spinner, no
   bar, nothing counting: two beats of text and then the photograph. */
.plate-meeting {
  position: fixed;
  inset: 0;
  z-index: 4;
  background: var(--ground-deep);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: var(--margin);
  animation: surface 260ms cubic-bezier(0.16, 0.84, 0.24, 1);
}

.meeting-line,
.meeting-name {
  margin: 0;
  text-align: center;
  font-size: 1.25rem;
}

.meeting-line {
  font-style: italic;
  color: var(--ink-soft-cool);
}

.meeting-name {
  color: var(--ink-cool);
  letter-spacing: 0.08em;
}

/* --------------------------------------------------------------- archive */

.day {
  margin-bottom: 2.75rem;
}

.day-label {
  font-weight: 400;
  margin: 0 0 0.75rem;
}

.day-entries {
  list-style: none;
  margin: 0;
  padding: 0;
}

/* One meal, one line, with a hairline under it. A page from a book rather
   than a grid of tiles. */
.entry-open {
  display: flex;
  width: 100%;
  align-items: baseline;
  gap: 1rem;
  padding: 0.7rem 0;
  text-align: left;
  border-bottom: 1px solid var(--rule);
}

.entry-note {
  flex: 1 1 auto;
  min-width: 0;
  color: var(--ink-soft);
  font-style: italic;
  font-size: 0.9375rem;
  /* One line, however much was written. */
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Your own meal, opened: the whole photograph, bounded by the screen, sitting
   in the dark the way it does in an encounter.
   Nothing here starts invisible. A photograph that the stylesheet hides and
   only a script can reveal is one missed event away from a blank screen — which
   is exactly how the archive's thumbnails failed before they were removed. */
.entry-frame-full {
  position: relative;
  display: block;
  width: 100%;
  max-height: 60dvh;
}

.entry-frame-full .full-image {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: contain;
  border: 0;
}

.entry-open:disabled {
  cursor: default;
}

.entry-time {
  font-size: 0.9375rem;
  color: var(--ink);
  white-space: nowrap;
}

.stamp {
  display: flex;
  justify-content: space-between;
  color: var(--ink-soft);
  font-size: 0.9375rem;
  padding-top: 0.5rem;
}

/* ------------------------------------------------------------- encounter */

/* An encounter is not a page in the app. It is a room of its own: the whole
   screen, edge to edge, with nothing of the archive behind it. One photograph
   in the dark, and the dark goes all the way out. */
.encounter {
  position: fixed;
  inset: 0;
  z-index: 5;
  background: var(--ground-deep);
  color: var(--ink-cool);
  display: flex;
  flex-direction: column;
  padding:
    calc(env(safe-area-inset-top) + var(--margin))
    var(--margin)
    calc(env(safe-area-inset-bottom) + var(--margin));
}

.plate-head-encounter {
  margin-bottom: 1.25rem;
}

.pseudonym {
  font-style: italic;
  text-transform: none;
  letter-spacing: 0.06em;
}

.stage {
  position: relative;
  overflow: hidden;
  /* Takes whatever height is left once the header and the label have had
     theirs. min-height: 0 is what lets it actually shrink to fit. */
  flex: 1 1 auto;
  min-height: 0;
  /* Every gesture in here is ours; the browser must not also scroll or zoom. */
  touch-action: none;
}

.track {
  display: flex;
  height: 100%;
  will-change: transform;
}

/* Narrower than the stage, with a gutter, so the photographs either side are
   visible at the edges while you drag and you can see what you are pulling
   toward. The percentages here are the ones encounter.js does its arithmetic
   with: SLIDE 86, GAP 3. */
/* Full width, so one photograph fills the view and the next is entirely off the
   screen until you pull it in. The gutter is only ever seen mid-drag. These two
   numbers are the ones encounter.js does its arithmetic with — SLIDE and GAP —
   and a test asserts they still agree. */
.slide {
  flex: 0 0 100%;
  margin-right: 4%;
  display: flex;
  align-items: center;
  justify-content: center;
}

/* A frame that already knows the shape of the photograph inside it. */
/* Bounded by the glass in both directions. Without the height cap a portrait
   photograph is drawn at the full width of the screen and runs off the bottom,
   which is what made them read as impossibly tall and narrow.
   No hairline here: in the viewer the photograph sits in the dark on its own. */
.plate-frame {
  position: relative;
  width: 100%;
  max-height: 100%;
  margin: 0 auto;
  transform-origin: center center;
  will-change: transform;
}

.plate-thumb,
.plate-full {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
}

/* The whole photograph, never cropped and never stretched. When the frame is
   capped by the height of the screen the picture simply centres inside it, with
   the dark either side. */
.plate-full {
  object-fit: contain;
}

/* A tile in the set is a tile: it may crop to keep the grid tidy. */
.plate-thumb {
  object-fit: cover;
}

/* An empty frame is the ground showing through, nothing more. No stand-in, no
   shimmer, no spinner: either there is a photograph or there is the dark. */
.plate-thumb,
.plate-full {
  z-index: 1;
}

.stranger-line {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 1.5rem;
  color: var(--ink-soft-cool);
  font-size: 0.9375rem;
  margin: 0.75rem 0 0;
}

.stranger-caption {
  font-style: italic;
  color: var(--ink-cool);
}

/* Their date and their time, exactly as they wrote them. */
.stranger-stamp {
  white-space: nowrap;
}

/* Plates laid out on paper: two columns, wide gutters, each photograph at its
   own proportions with a hairline round it and its time engraved beneath. */
.sheet {
  margin-top: 1.5rem;
  /* Takes the room's height when the stage steps aside, and scrolls within it. */
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
}

.sheet-list {
  list-style: none;
  margin: 0;
  padding: 0;
  /* Two columns, written as the shorthand: the longhand contains a word the
     invariant scan looks for, and the scan is worth more than the longhand. */
  columns: 2;
  column-gap: var(--margin);
}

.sheet-cell {
  break-inside: avoid;
  margin: 0 0 calc(var(--margin) * 1.2);
  cursor: pointer;
}

.sheet-plate {
  position: relative;
  width: 100%;
  border: 1px solid var(--rule);
  overflow: hidden;
}

.sheet-rule {
  display: block;
  border-top: 1px solid var(--rule-cool);
  margin: 0.55rem 0 0.3rem;
}

.sheet-time {
  display: block;
  font-size: 0.75rem;
  letter-spacing: 0.14em;
  color: var(--ink-soft-cool);
}

/* The travelling copy, while a photograph moves between the stage and the set.
   Its position is set once; only its transform is ever animated. */
.flyer {
  position: fixed;
  z-index: 10;
  overflow: hidden;
  border: 1px solid var(--rule);
  transform-origin: center center;
  pointer-events: none;
  will-change: transform;
}

.flyer-image {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.hidden {
  display: none;
}

/* One screen giving way to another: 180ms, ease-out, transform and opacity
   only. Never a hard cut, never longer. */
.plate {
  animation: arrive 180ms cubic-bezier(0.22, 0.61, 0.36, 1);
}

@keyframes arrive {
  from { opacity: 0; transform: translate3d(0, 4px, 0); }
  to { opacity: 1; transform: translate3d(0, 0, 0); }
}

/* Arriving in an encounter is not a page turn — the app gives way to a room
   with one photograph in it. A plain fade up out of the dark, a little slower
   than a screen change, and nothing moves while it happens. */
.encounter {
  animation: surface 260ms cubic-bezier(0.16, 0.84, 0.24, 1);
}

@keyframes surface {
  from { opacity: 0; }
  to { opacity: 1; }
}

/* Every tap says so immediately, before any request has been made. */
.text-action:active,
.link:active,
.entry-open:active,
.sheet-cell:active {
  opacity: 0.55;
}

@media (prefers-reduced-motion: reduce) {
  .plate,
  .encounter,
  .plate-meeting {
    animation: none;
  }

  /* Movement off, but nothing hidden: anything that fades in is simply
     already there. */
  .entry-image,
  .plate-thumb,
  .plate-full {
    opacity: 1 !important;
    transition: none !important;
  }

  .track,
  .flyer,
  .sheet {
    transition: none !important;
  }
}
