/*
 * A terminal-style listing. Colors are from the Kanso theme
 * (github.com/webhooked/kanso.nvim): Mist when dark, Pearl when light. Most
 * of the page is plain text, and color is kept for three things: pink for
 * showtimes, as Kanso colors numbers, blue for controls (keys and buttons,
 * as lazygit colors its key hints), and green for what's current, as
 * lazygit colors a focused panel, and for the movies just added. Headings
 * and the names that head a group of rows are a dimmer text, and "you are
 * here" is inverse video. Everything is JetBrains Mono, at one size.
 */

@font-face {
  font-family: "JetBrains Mono";
  font-weight: 400;
  font-display: swap;
  src: url("/static/fonts/JetBrainsMono-Regular.woff2") format("woff2");
}

@font-face {
  font-family: "JetBrains Mono";
  font-weight: 700;
  font-display: swap;
  src: url("/static/fonts/JetBrainsMono-Bold.woff2") format("woff2");
}

/* Each color is light-dark(pearl, mist). The page follows the device's
   setting unless the visitor picked one, which the server sets as
   data-mode. Pearl's own accents are brighter than Mist's, so its green
   and pink here are our own, muted toward Mist's and dark enough to read
   on its light background. */
:root {
  --bg: light-dark(#f2f1ef, #22262d); /* pearlWhite0, mistBg0 */
  --line: light-dark(#dddddb, #393b44); /* pearlWhite2, mistBg2 */
  --lit: light-dark(#e2e1df, #2a2c35); /* pearlWhite1, mistBg1 */
  --text: light-dark(#22262d, #c5c9c7); /* pearlBlack0, fg */
  --heading: light-dark(#545464, #a4a7a4); /* pearlBlack1, gray2: each theme's fg_dim */
  --muted: light-dark(#6d6d69, #909398); /* pearlGray3, gray3 */
  --accent: light-dark(#84627a, #bca6bc); /* pink, lightened from Kanso's #a292a3 to stand clear of --muted */
  --current: light-dark(#5d7342, #8a9a7b); /* pearlGreen, green3 */
  --control: light-dark(#4d699b, #8ba4b0); /* pearlBlue4, blue3 */

  --font: "JetBrains Mono", ui-monospace, "SF Mono", Menlo, Consolas, monospace;

  color-scheme: light dark;
}

:root[data-mode="dark"] {
  color-scheme: dark;
}

:root[data-mode="light"] {
  color-scheme: light;
}

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

/* Spacing is in whole cells, as in a terminal: 1rlh is one line of text
   and 1ch one column. The font is set here, on the root, so that 1rlh is the
   height of a line. */
html {
  font: 400 14px/1.6 var(--font);
  -webkit-text-size-adjust: 100%;
}

body {
  max-width: 80ch;
  margin: 0 auto;
  padding: 2rlh 2ch 4rlh; /* room at the foot for the pinned bar */
  background: var(--bg);
  color: var(--text);
}

::selection {
  background: var(--text);
  color: var(--bg);
}

/* Links are underlined, since on a phone there's no hovering to find
   them. The line is the link's own color, faded so a listing full of
   titles stays calm, and comes up to full strength under the pointer. */
a {
  color: inherit;
  text-decoration: underline 1px color-mix(in srgb, currentColor 40%, transparent);
  text-underline-offset: 1px;
}

a:hover,
a:focus-visible {
  text-decoration-color: currentColor;
}

/* The buttons drawn as controls take the same line, so a bracketed
   control reads the same whether it's a link or a button. */
button.hint,
.panel-close button {
  text-decoration: underline 1px color-mix(in srgb, currentColor 40%, transparent);
  text-underline-offset: 1px;
}

button.hint:hover,
button.hint:focus-visible,
.panel-close button:hover,
.panel-close button:focus-visible {
  text-decoration-color: currentColor;
}

/* Green, as a focused panel's border is. Pink would say "tickets". */
:focus-visible {
  outline: 1px solid var(--current);
  outline-offset: 1px;
}

/* Links and buttons show keyboard focus the way they show the pointer, so
   the cursor (see app.js) looks like hovering. */
a:focus-visible,
button:focus-visible {
  outline: none;
}

h1,
h2,
h3 {
  font-size: inherit;
}

/* Keys are written in brackets everywhere, as in [d]Date. */
kbd {
  font: inherit;
}

kbd::before {
  content: "[";
}

kbd::after {
  content: "]";
}

/* Header */

.top {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0 2ch;
}

.site-title {
  color: var(--heading);
  font-weight: 700;
}

.updated {
  flex-basis: 100%;
  color: var(--muted);
}

/* Several fetches in a row have failed. Full text color, with its mark
   muted like the + before what's new: a warning, not an accent. */
.stale {
  color: var(--text);
}

.stale .mark {
  color: var(--muted);
}

/* What's new, and the jump list */

.added {
  margin-top: 1rlh;
  color: var(--muted);
}

.added a {
  color: var(--current);
}

/* The panel at the top of each view, drawn like an unfocused lazygit
   panel: the views as tabs in its top border, the current one green, and a
   count in its bottom border. */
.jump-panel {
  position: relative;
  margin-top: 1rlh;
  padding: 1rlh 2ch;
  border: 1px solid var(--line);
  border-radius: 6px;
}

/* With the cursor inside, the panel has focus. */
.jump-panel:has(:focus-visible) {
  border-color: var(--current);
}

.jump-tabs,
.jump-count {
  position: absolute;
  display: flex;
  gap: 1ch;
  padding: 0 1ch;
  background: var(--bg);
  line-height: 1;
}

.jump-tabs {
  top: 0;
  left: 2ch;
  transform: translateY(-50%);
}

.jump-count {
  right: 2ch;
  bottom: 0;
  color: var(--muted);
  transform: translateY(50%);
}

.view {
  color: var(--control);
}

.view[aria-current] {
  color: var(--current);
  font-weight: 700;
}

.tab-sep {
  color: var(--line);
}

/* The by-date jump list: a calendar strip, a row per month. */
.cal-month {
  display: grid;
  grid-template-columns: 5ch 1fr;
}

.cal-name {
  color: var(--muted);
}

.cal-days {
  display: flex;
  flex-wrap: wrap;
  gap: 0 1ch;
}

.cal-days a {
  min-width: 2ch;
  text-align: right;
}

.cal-days a.first {
  background: var(--text);
  color: var(--bg);
  text-decoration: none;
}

/* Keep jumped-to headings off the top edge. */
section[id],
.entry {
  scroll-margin-top: 1rlh;
}

/* The by-movie jump list: a line per movie, in two blocks.
   One grid spans both blocks, and every row borrows its columns through
   subgrid, so the dates column is exactly as wide as the longest date in
   either block -- "through tomorrow" today -- and the two blocks stay in
   step. A fixed width either wrapped the long ones or reserved space the
   short ones never used, and the leaders are what fills the gap. */
.jump-movies {
  display: grid;
  grid-template-columns: 1fr auto;
  column-gap: 1ch;
}

.jump-head {
  grid-column: 1 / -1;
  color: var(--muted);
}


.jump-head + .jump-table {
  margin-bottom: 1rlh;
}

.jump-table {
  display: grid;
  grid-column: 1 / -1;
  grid-template-columns: subgrid;
  list-style: none;
}

.jump-table li {
  display: grid;
  grid-column: 1 / -1;
  grid-template-columns: subgrid;
}

/* Like the listing's rows: the dots run on from the name and are clipped
   at the column's edge. A name too long for its column is cut short. */
.jt-title,
.vt-name {
  min-width: 0;
  overflow: hidden;
}

.jt-title a,
.vt-name a {
  display: inline-block;
  max-width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
  vertical-align: bottom;
  white-space: nowrap;
}

/* Never wrapped: the column is sized from these, and a squeezed row takes
   it out of the title's ellipsis instead. */
.jt-dates {
  color: var(--muted);
  white-space: nowrap;
}

/* The by-theater jump list: a small table of venues. */
/* Sized from the counts, like the by-movie jump list, but a level
   shallower: there is only one list, so the grid sits on it directly. */
.venues-table {
  display: grid;
  grid-template-columns: 1fr auto;
  column-gap: 1ch;
  list-style: none;
}

.venues-table li {
  display: grid;
  grid-column: 1 / -1;
  grid-template-columns: subgrid;
}

/* Never wrapped, as .jt-dates. */
.vt-count {
  color: var(--muted);
  white-space: nowrap;
}

.venue-info {
  margin-bottom: 1rlh;
  color: var(--muted);
}

.venue-info span + span::before {
  content: " · ";
}

.venue-info a {
  color: var(--text);
}

/* Section headings drawn as a rule: ── today ──────── */

.rule {
  display: flex;
  align-items: center;
  gap: 1ch;
  color: var(--heading);
  margin-top: 2rlh;
}

.rule::before,
.rule::after {
  content: "";
  border-top: 1px solid var(--line);
}

.rule::before {
  width: 2ch;
}

.rule::after {
  flex: 1;
}

/* Movie entries */

.entry {
  padding-top: 1rlh;
}

.entry-head {
  display: grid;
  grid-template-columns: 1fr auto;
  column-gap: 1ch;
  align-items: last baseline;
}

.entry-title {
  font-weight: 700;
}

/* Labels like "last day" are highlighted cells, the way a terminal colors
   a label's background. Things you press are marked instead, like
   *info and [q]. */
.tag {
  padding: 0 1ch;
  border-radius: 2px;
  background: var(--line);
  color: var(--text);
  font-weight: 400;
  white-space: nowrap;
}

/* Dot leaders run on from the end of a title's last line to the next
   column, clipped at the edge of the title's cell, so they still line up
   when a long title wraps. The leader is a zero-width box at the end of the
   title, so it never takes space from the title or wraps away from it; its
   dots just spill to the right. It starts with a no-break space to keep a
   gap after the last word, and has more dots than any cell is wide, so a
   change to the layout can't leave it short. */
.entry-title {
  min-width: 0;
  overflow: hidden;
}

/* In the by-date and by-theater rows, the dots run on under the times
   column and the times cover them (see .times), so they stop just before
   the first time rather than at the column's edge. */
.c-movie {
  min-width: 0;
}

.showing {
  overflow: hidden;
}

.leader {
  display: inline-block;
  width: 0;
  color: var(--line);
  font-weight: 400;
  white-space: nowrap;
}

.leader::after {
  content: "\a0........................................................................................................................................................................................................";
}

.meta {
  color: var(--muted);
  white-space: nowrap;
}

.meta span + span::before {
  content: " · ";
}

/* Info opens a movie's panel. It's marked like a footnote, *info, since
   it has no key of its own: brackets are kept for keys, like [q]. */
.info-link {
  color: var(--control);
  font-weight: 400;
  white-space: nowrap;
}

.info-link::before {
  content: "*";
}

/* By date and by theater: a label (venue or date), movie, times, in fixed
   columns. */

.rule + .group,
.group + .group {
  margin-top: 1rlh;
}

/* Times get a fixed column (three fit on a line) so they line up from row
   to row. */
.showing {
  display: grid;
  grid-template-columns: 15ch 1fr 20ch;
  column-gap: 1ch;
  align-items: baseline;
}

/* When a title wraps, the times line up with its last line, where the dot
   leader ends, as in a printed program. */
.showing .c-movie,
.showing .c-times {
  align-self: last baseline;
}


/* By movie: venue, date, times, in fixed columns so everything lines up
   down the page. */

.day {
  display: grid;
  grid-template-columns: 15ch 16ch 1fr;
  align-items: baseline;
}

/* The label column is quiet, but what's in it is a link, so it keeps the
   text color: gray is for things you can't click. */
.c-label {
  color: var(--muted);
}

.c-label a {
  color: var(--heading);
}

.c-times {
  display: flex;
  justify-content: flex-end;
}

/* The times sit on the row's own background, hiding the dot leader beneath
   them and the space to their right. */
.times {
  display: inline-flex;
  flex-wrap: wrap;
  justify-content: flex-end;
  gap: 0 1ch;
  margin-right: -1ch;
  padding: 0 1ch;
  background: var(--row-bg);
}

/* Rows reach a little past their text on each side, so the highlight
   under the pointer has some room, like lazygit's selected line. */
.showing,
.day {
  --row-bg: var(--bg);
  margin: 0 -1ch;
  padding: 0 1ch;
  border-radius: 2px;
  background: var(--row-bg);
}

/* Opaque, so the times' background (which hides the dot leader) matches.
   The keyboard cursor's row is lit the same way. */
@media (hover: hover) {
  .showing:hover,
  .day:hover {
    --row-bg: var(--lit);
  }
}

.showing:has(:focus-visible),
.day:has(:focus-visible) {
  --row-bg: var(--lit);
}

/* Room around a row the cursor scrolls to, clear of the key hints. */
.added,
.cal-month,
.jump-table li,
.venues-table li,
.venue-info,
.entry-head,
.showing,
.day {
  scroll-margin: 1rlh 0 2rlh;
}

/* Showtimes run pink while there are seats and are struck through in gray
   when they're sold out. One with tickets is underlined, as links are;
   one without, not yet on sale, isn't. */
.t {
  --time: var(--accent);
  min-width: 5ch;
  color: var(--time);
  text-align: right;
  white-space: nowrap;
}

a.t:hover,
a.t:focus-visible {
  background: var(--time);
  color: var(--bg);
  text-decoration: none;
}

/* --time rather than color, so hover and the cursor fill it gray, not pink:
   a sold-out time must never look like a ticket. */
.t.sold-out,
a.t.sold-out:hover,
a.t.sold-out:focus-visible {
  --time: var(--muted);
  text-decoration: line-through;
}

.oc,
.oc-time {
  text-decoration: none;
}

/* Panels: the info and help dialogs, drawn as a bordered box over the
   dimmed page, with the title set into the top border and the counter and
   close button into the bottom, like lazygit. An open panel has focus, so
   its border is green, as lazygit draws its focused panel. */

dialog.panel {
  width: min(72ch, calc(100vw - 4ch));
  max-width: none;
  max-height: none;
  margin: auto;
  padding: 0;
  overflow: visible;
  border: 1px solid var(--current);
  border-radius: 6px;
  background: var(--bg);
  color: var(--text);
  font: inherit;
}

dialog.panel::backdrop {
  background: rgb(0 0 0 / 0.5);
}

/* Dim less when the page is light. (The backdrop can't use the page's
   color variables, so this checks the mode itself.) */
:root[data-mode="light"] dialog.panel::backdrop {
  background: rgb(0 0 0 / 0.2);
}

@media (prefers-color-scheme: light) {
  :root:not([data-mode="dark"]) dialog.panel::backdrop {
    background: rgb(0 0 0 / 0.2);
  }
}

/* Without JavaScript, a panel opened by the server isn't modal. */
dialog.panel[open]:not(:modal) {
  position: fixed;
  inset: 0;
  z-index: 1;
}

.panel-title,
.panel-nav,
.panel-close {
  position: absolute;
  padding: 0 1ch;
  background: var(--bg);
  line-height: 1;
}

.panel-title {
  top: 0;
  left: 2ch;
  max-width: calc(100% - 4ch);
  overflow: hidden;
  font-weight: 700;
  text-overflow: ellipsis;
  white-space: nowrap;
  transform: translateY(-50%);
}

.panel-title {
  color: var(--current);
}

.panel-nav,
.panel-close {
  bottom: 0;
  transform: translateY(50%);
}

.panel-nav {
  left: 2ch;
  color: var(--muted);
}

.panel-nav {
  display: flex;
  gap: 1ch;
}

.panel-nav a {
  color: var(--control);
}

.panel-close {
  right: 2ch;
}

.panel-close button {
  border: 0;
  background: none;
  color: var(--control);
  font: inherit;
  line-height: 1;
  cursor: pointer;
}


.panel-body {
  max-height: calc(100dvh - 4rlh);
  padding: 1rlh 2ch;
  overflow-y: auto;
  scrollbar-color: var(--line) transparent;
  scrollbar-width: thin;
}

/* The body takes focus when a panel opens, so it starts at the top and
   scrolls with the keyboard. */
.panel-body:focus {
  outline: none;
}

.info-body {
  display: grid;
  grid-template-columns: 192px 1fr;
  gap: 3ch;
  align-items: start;
}

/* Fetched at twice this width, for denser screens. Framed in the gray the
   trailer is, so a picture in a panel always sits in a box. */
.poster {
  display: block;
  width: 192px;
  height: auto;
  border: 1px solid var(--line);
}

.info-body .meta {
  white-space: normal;
}

.synopsis {
  margin-top: 1rlh;
}

/* The label column is sized from the labels: dt and dd are already the
   grid's own children, so auto is enough and no subgrid is needed. A fixed
   width had to guess, and "Rated PG-13" is wider than the 10ch it guessed. */
.credits,
.keys {
  display: grid;
  grid-template-columns: auto 1fr;
  column-gap: 2ch;
}

.credits dt,
.keys dt {
  white-space: nowrap;
}

.credits {
  margin-top: 1rlh;
}

.credits dt,
.keys dd {
  color: var(--muted);
}

.keys dt {
  color: var(--control);
}

.trailer {
  margin-top: 1rlh;
}

.trailer a {
  color: var(--control);
}

/* An info panel shows either the movie's details or its trailer, with
   [esc] Back in the bottom border in place of the other movies. */
.trailer-body,
.trailer-nav,
.info[data-view="trailer"] .info-body,
.info[data-view="trailer"] .panel-nav:not(.trailer-nav) {
  display: none;
}

.info[data-view="trailer"] .trailer-body {
  display: block;
}

.info[data-view="trailer"] .trailer-nav {
  display: flex;
}

.trailer-body video {
  display: block;
  width: 100%;
  height: auto;
  aspect-ratio: 4 / 3; /* the trailers' size, so nothing jumps as one loads */
  border: 1px solid var(--line);
  background: #000;
}

/* The video takes focus when it opens, by key or by click; the border
   stands in for the focus ring so both look the same. */
.trailer-body video:focus-visible {
  outline: none;
}

/* The panel a showtime opens before leaving for its tickets: the showing,
   a word of warning, and the way on. Narrower than a movie's details, as
   there's less in it. */
dialog.tickets {
  width: min(48ch, calc(100vw - 4ch));
}

.tickets-movie {
  font-weight: 700;
}

.tickets-when {
  white-space: normal;
}

.tickets-note,
.tickets-go {
  margin-top: 1rlh;
}

.tickets-go a {
  color: var(--control);
}

/* Footer */

footer {
  margin-top: 3rlh;
  padding-top: 1rlh;
  border-top: 1px solid var(--line);
  color: var(--muted);
}

footer a {
  color: var(--text);
}

.modes {
  margin-top: 1rlh;
}

.modes a {
  color: var(--control);
}

.modes a[aria-current] {
  color: var(--current);
  font-weight: 700;
}

.notfound,
.empty {
  margin-top: 1rlh;
}

.empty {
  color: var(--muted);
}

/* The bar pinned to the bottom of the window, like lazygit's key hints —
   but its items are buttons, drawn as "[q] Close" is. Each takes one of two
   forms: the keyed one, "[gg] Top", where the key works, and the worded
   one, "[Top]", where it doesn't. The two differ only in that, and in the
   help button, which the worded form drops. The keyed form needs both a keyboard,
   which only a pointer device is assumed to have, and the script that makes
   the keys work, which marks the page. */
.hints {
  position: fixed;
  right: 0;
  bottom: 0;
  left: 0;
  display: flex;
  justify-content: center;
  padding: 0 1ch;
  background: var(--bg);
}

.hint {
  color: var(--control);
}


button.hint {
  border: 0;
  padding: 0;
  background: none;
  font: inherit;
  line-height: inherit;
  cursor: pointer;
}

/* The worded form: the key is dropped and the label takes the brackets.
   Items that only make sense with a keyboard sit this form out, so the
   separator has to skip them — hidden elements are still siblings. */
.hint b {
  display: none;
}

.hint span::before {
  content: "[";
}

.hint span::after {
  content: "]";
}

/* Separators are their own elements, as the jump tabs' are: inside a
   button, an underline on hover would run through them. Each carries the
   visibility of the item after it, so neither form opens with one. */
.hint-sep {
  margin: 0 1ch;
  color: var(--line);
}

.hints .wide {
  display: none;
}

@media (hover: hover) and (pointer: fine) and (min-width: 40rem) {
  :root.js .hints .wide {
    display: inline;
  }

  :root.js .hint b {
    display: inline;
    margin-right: 1ch;
    font-weight: 400;
  }

  :root.js .hint span::before,
  :root.js .hint span::after {
    content: none;
  }
}

@media (max-width: 40rem) {
  .jump-panel {
    padding: 1rlh 1ch;
  }

  .jump-tabs {
    left: 1ch;
  }

  .jump-count {
    right: 1ch;
  }

  .cal-month {
    grid-template-columns: 4ch 1fr;
  }

  /* The venue gets a line of its own above its movies or days. The times
     take only the width they need, up to the three a line holds, so a
     title isn't wrapped beside empty space; they're right-aligned, so they
     still line up at the edge. */
  .showing {
    grid-template-columns: 1fr fit-content(20ch);
  }


  .day {
    grid-template-columns: 18ch 1fr;
  }

  .c-label {
    grid-column: 1 / -1;
  }

  /* Rows indent under their venue or day, like a tree, so the label on
     its own line reads as heading them rather than as one more row. */
  .c-movie,
  .c-date {
    padding-left: 2ch;
  }

  .c-label:empty {
    display: none;
  }

  .info-body {
    grid-template-columns: 1fr;
    row-gap: 1rlh;
  }
}
