/* Dark like the Firefox new tab page, so a new tab never flashes white. */

:root {
  --page: #1c1b22;
  --panel: #2b2a33;
  --panel-hover: #35343f;
  --title: #fbfbfe;
  --description: #cfcfd8;
  --source: #8f8f9d;
  --thumbnail-bar: #22212a;
  --star: #45444f;
  --star-on: #ffc83d;
  --star-preview: #8a7538;

  /*
    A prediction is never gold, so it is never mistaken for a rating. One made for the
    panel itself has a colour of its own, and one that is only the average of its
    source is fainter, since it says nothing about this panel in particular.
  */
  --prediction-line: #8f7bd8;
  --prediction-line-source: #575070;

  /*
    The room down each side of a carousel for its buttons. Everything else on the page
    is inset by the same amount, so the logo, the day headings, the first panel of a
    row and the weather all line up rather than lining up with the buttons.
  */
  --carousel-button: 56px;
  --carousel-gutter: calc(var(--carousel-button) + 8px);
  --accent: #a68cff;
  --error: #ff6b6b;
}

* {
  box-sizing: border-box;
}

body {
  margin: 0;
  padding: 24px 32px 64px 32px;
  background: var(--page);
  color: var(--title);
  font-family: system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
}

/*
  A page that is a list rather than a river still starts where the logo and the first
  panel of a row do, instead of hard against the left edge of the window.
*/
.page-content {
  padding: 0 var(--carousel-gutter);
}

/* The page's own title, above the headings for each run of panels under it. */
.gaps-title {
  margin: 8px 0 16px 0;
  font-size: 26px;
  font-weight: 700;
  color: var(--title);
}

/* A line under a heading saying what the panels below it were picked for. */
.gaps-note {
  display: block;
  margin-top: 4px;
  color: var(--source);
  font-size: 14px;
  font-weight: 400;
}

.day {
  margin: 32px 0 16px 0;
  font-size: 20px;
  font-weight: 700;
  color: var(--title);
}

/* A page of chosen sources has no carousel, so its heading has nothing to line up with. */
main:not([data-flat]) .river-day {
  padding-left: var(--carousel-gutter);
}

.panels {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
  gap: 16px;
}

/*
  One carousel per day, spanning the whole grid, holding that day's panels best first.

  The panel width here is only a starting point. riverbed.js works out how many panels
  of about this size fit the row and then stretches them to fill it exactly, because a
  panel left half off the edge is one a click scrolls straight past unread.
*/
.carousel {
  --carousel-panel: 300px;

  /* Roughly the source line plus the stars row, which the buttons stay above. */
  --carousel-panel-footer: 58px;

  grid-column: 1 / -1;
  position: relative;
  min-width: 0;

  /* The gutters the buttons sit in, so they never cover a panel. */
  padding: 0 var(--carousel-gutter);
}

.carousel-track {
  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: var(--carousel-panel);
  column-gap: 12px;
  height: 100%;
  overflow-x: auto;
  scroll-behavior: smooth;
  scroll-snap-type: x mandatory;
  scrollbar-width: none;
}

.carousel-track::-webkit-scrollbar {
  display: none;
}

.carousel-track .panel {
  scroll-snap-align: start;
}

/*
  The buttons live in the gutters, centred on the thumbnail and title together. They
  can reach that far down without covering anything, now that they are outside the
  panels rather than on top of them.
*/
.carousel-controls {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  bottom: var(--carousel-panel-footer);
  pointer-events: none;
}

.carousel-button {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  z-index: 2;
  display: flex;
  align-items: center;
  justify-content: center;
  width: var(--carousel-button);
  height: var(--carousel-button);
  pointer-events: auto;
  padding: 0;
  border: 2px solid rgba(255, 255, 255, 0.9);
  border-radius: 50%;
  background: rgba(28, 27, 34, 0.55);
  color: #ffffff;
  cursor: pointer;
}

.carousel-chevron {
  width: 28px;
  height: 28px;
}

.carousel-button:hover {
  background: rgba(28, 27, 34, 0.85);
  border-color: #ffffff;
}

.carousel-button[disabled] {
  opacity: 0;
  pointer-events: none;
}

.carousel-button.previous {
  left: 0;
}

.carousel-button.next {
  right: 0;
}

/*
  On a phone the page padding and the two gutters together ate most of the width, so
  both shrink to almost nothing. The buttons then hang over the edges of a panel rather
  than sitting clear of it - worth it to get a readable panel, and they are bigger here
  so they are still easy to hit with a thumb.
*/
@media (max-width: 600px) {
  :root {
    --carousel-button: 64px;
    --carousel-gutter: 3px;
  }

  body {
    padding: 16px 6px 64px 6px;
  }

  /* A list page has no carousel buttons to leave room for, so it fills the screen. */
  .page-content {
    padding: 0;
  }

  .carousel-chevron {
    width: 32px;
    height: 32px;
  }

  /* One panel is the whole screen here, so a fourth line of it is worth the height. */
  .description {
    -webkit-line-clamp: 4;
  }
}

.panel {
  display: flex;
  flex-direction: column;
  background: var(--panel);
  border-radius: 8px;
  overflow: hidden;
}

.panel:hover {
  background: var(--panel-hover);
}

.panel-link {
  display: flex;
  flex-direction: column;
  flex-grow: 1;
  text-decoration: none;
  color: inherit;
}

.thumbnail-wrap {
  position: relative;
}

.thumbnail {
  display: block;
  width: 100%;
  aspect-ratio: 16 / 9;
  object-fit: cover;
  background: var(--thumbnail-bar);
}

/* Faded white marks over the picture: a video, audio, or something paid for. */
.overlays {
  position: absolute;
  top: 0;
  right: 0;
  bottom: 0;
  left: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  pointer-events: none;
}

.overlay-icon {
  width: 56px;
  height: 56px;
  color: rgba(255, 255, 255, 0.75);
  fill: currentColor;
  filter: drop-shadow(0 2px 6px rgba(0, 0, 0, 0.55));
}

.overlay-disc {
  fill: rgba(0, 0, 0, 0.35);
}

.overlay-text {
  font-size: 34px;
  font-weight: 700;
  fill: currentColor;
}

.panel:hover .overlay-icon {
  color: rgba(255, 255, 255, 0.95);
}

/* Added by riverbed.js when the image is not the shape of the container. */
.thumbnail.contain {
  object-fit: contain;
}

.panel-text {
  display: flex;
  flex-direction: column;
  flex-grow: 1;
  padding: 12px 14px 8px 14px;
}

.title {
  margin: 0 0 8px 0;
  font-size: 15px;
  font-weight: 700;
  line-height: 1.3;
  color: var(--title);
}

/*
  The description is a second link to the same place as the panel, so that an
  aggregator's comments link can sit between it and the title as its own link.
*/
.panel-description {
  padding: 0 14px;
}

/* For an item with a discussion of its own, elsewhere, to link to as well. */
.panel-comments {
  align-self: flex-start;
  margin: 0 14px 6px 14px;
  font-size: 12px;
  font-weight: 700;
  color: var(--accent);
  text-decoration: none;
}

.panel-comments:hover {
  text-decoration: underline;
}

.description {
  margin: 0 0 8px 0;
  font-size: 13px;
  line-height: 1.4;
  color: var(--description);

  display: -webkit-box;
  -webkit-line-clamp: 3;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

.source {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 0 14px 6px 14px;
  font-size: 12px;
  color: var(--source);
}

/* The stars and Hide share the bottom row, so they sit on one line at each end of it. */
.panel-footer {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 0 12px 10px 12px;
}

.hide-button {
  padding: 4px 11px;
  border: 1px solid var(--star);
  border-radius: 6px;
  background: none;
  color: var(--source);
  font-size: 13px;
  font-family: inherit;
  cursor: pointer;
}

.hide-button:hover {
  border-color: var(--source);
  color: var(--title);
}

/*
  Both a panel and an activity row fade out over a second before being taken off the
  page, so a click that removes something is visibly a removal and not a glitch.
*/
.panel,
.activity-row {
  transition: opacity 1s ease;
}

.panel.fading,
.activity-row.fading {
  opacity: 0;
  pointer-events: none;
}

.stars {
  display: flex;
  gap: 2px;

  /* Room under the stars for the prediction line, which is placed against this box. */
  position: relative;
  padding-bottom: 5px;
}

.star {
  padding: 0 3px;
  border: none;
  background: none;
  color: var(--star);
  font-size: 19px;
  line-height: 1;
  cursor: pointer;
}

.star.on {
  color: var(--star-on);
}

/*
  While the mouse is running along the stars they are only a preview of a rating not
  given yet, so they are dimmer than a rating that was actually clicked.
*/
.stars.previewing .star.on {
  color: var(--star-preview);
}

/*
  A line under the stars, as long a part of them as the panel was predicted to be
  worth, so 3.4 of 5 ends part way along the fourth star. A prediction is a fraction
  and a rating is a count, so it is drawn as a length rather than as more stars.

  The template works out its width, since nothing about it changes after the page is
  sent, not even when the panel is rated.
*/
.stars::after {
  content: "";
  position: absolute;
  left: 0;
  bottom: 0;
  height: 2px;
  width: var(--prediction, 0);
  background: var(--prediction-line);
}

/*
  Dashed, and not only fainter, when the number is the average of the source rather
  than a prediction of this panel. A broken line reads as a stand-in at a glance, and
  a dim solid one only reads as a prediction someone thought little of.
*/
.stars[data-prediction-type="source"]::after {
  background: repeating-linear-gradient(90deg,
                                        var(--prediction-line-source) 0 3px,
                                        transparent 3px 6px);
}

.source-icon {
  width: 16px;
  height: 16px;
  border-radius: 3px;
}

.source-label {
  color: var(--source);
  text-decoration: none;
}

.source-label:hover {
  color: var(--accent);
  text-decoration: underline;
}

.recency {
  margin-left: auto;
}

.top-bar {
  display: flex;
  align-items: center;
  gap: 24px;
  flex-wrap: wrap;
  padding: 0 var(--carousel-gutter) 12px var(--carousel-gutter);
  border-bottom: 1px solid var(--panel);
}

.logo {
  display: flex;
  align-items: center;
  gap: 14px;
  color: var(--title);
  font-size: 34px;
  font-weight: 700;
  text-decoration: none;
}

.logo img {
  width: 72px;
  height: 72px;
}

/*
  The weather and the gear, held together so the gear stays hard right whether or not
  the weather ever turns up.
*/
.top-bar-right {
  display: flex;
  align-items: center;
  gap: 24px;
  margin-left: auto;
  min-width: 0;
}

.settings {
  position: relative;
}

.settings-button {
  padding: 0;
  border: none;
  background: none;
  color: var(--source);
  font-size: 34px;
  line-height: 1;
  cursor: pointer;
}

.settings-button:hover,
.settings.open .settings-button {
  color: var(--title);
}

/*
  Hangs under the gear on a desktop. On a phone there is no room beside anything, so
  it drops to almost the full width of the screen instead, see the media query below.
*/
.settings-menu {
  display: none;
  position: absolute;
  top: calc(100% + 8px);
  right: 0;
  z-index: 10;
  flex-direction: column;
  gap: 14px;
  width: 240px;
  padding: 16px;
  border: 1px solid var(--star);
  border-radius: 8px;
  background: var(--panel);
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.4);
}

.settings.open .settings-menu {
  display: flex;
}

.settings-link {
  color: var(--description);
  font-size: 14px;
  text-decoration: none;
}

.settings-link:hover {
  color: var(--accent);
}

.settings-link.active {
  color: var(--title);
  font-weight: 700;
}

@media (max-width: 600px) {
  .settings-menu {
    position: fixed;
    top: 12px;
    right: 12px;
    left: 12px;
    width: auto;
  }
}

.toggle {
  display: flex;
  align-items: center;
  gap: 8px;
  cursor: pointer;
  user-select: none;
}

.toggle-input {
  position: absolute;
  opacity: 0;
  width: 0;
  height: 0;
}

.toggle-track {
  position: relative;
  width: 34px;
  height: 20px;
  flex-shrink: 0;
  border-radius: 999px;
  background: var(--star);
  transition: background 0.15s ease;
}

/* The knob, drawn on the track so there is one less element in the markup. */
.toggle-track::after {
  content: "";
  position: absolute;
  top: 3px;
  left: 3px;
  width: 14px;
  height: 14px;
  border-radius: 50%;
  background: var(--description);
  transition: transform 0.15s ease, background 0.15s ease;
}

.toggle-input:checked + .toggle-track {
  background: var(--accent);
}

.toggle-input:checked + .toggle-track::after {
  transform: translateX(14px);
  background: #ffffff;
}

.toggle-input:focus-visible + .toggle-track {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

.toggle-label {
  color: var(--source);
  font-size: 13px;
  white-space: nowrap;
}

.toggle:hover .toggle-label {
  color: var(--title);
}

body.hide-media .panel[data-media="video"],
body.hide-media .panel[data-media="audio"],
body.hide-text .panel[data-media="text"],
body.hide-paywalled .panel[data-paywalled="1"],
body.hide-rated .panel[data-rated="1"],
body.hide-ai .panel[data-ai="1"] {
  display: none;
}

/* Everything past the few panels riverbed.js keeps of a day older than yesterday. */
.panel.past-day-limit {
  display: none;
}

/* A day left with nothing to show loses its heading too, rather than sitting empty. */
.day.empty-day,
.panels.empty-day {
  display: none;
}

.nothing-shown {
  display: none;
  margin: 96px 0;
  color: var(--source);
  font-size: 16px;
  text-align: center;
}

/*
  With both media and text off there is no panel left to draw anywhere, so a page of
  panels drops its headings and notes as well and says what to turn back on. Only a
  page holding the message empties itself, since the lists and the settings pages have
  nothing to do with either toggle.
*/
body.hide-media.hide-text main:has(> .nothing-shown) > *:not(.nothing-shown) {
  display: none;
}

body.hide-media.hide-text .nothing-shown {
  display: block;
}

/* Hidden until riverbed.js has real weather to show. */
.weather {
  display: none;
  align-items: center;
  gap: 8px;
  min-width: 0;
  color: var(--description);
  font-size: 14px;
  text-decoration: none;
}

.weather.visible {
  display: flex;
}

.weather:hover {
  color: var(--title);
}

.weather-emoji {
  font-size: 24px;
}

.weather-now {
  color: var(--title);
  font-size: 21px;
  font-weight: 700;
}

/* One line each: the place, then today's numbers, then how it feels. */
.weather-details {
  display: flex;
  flex-direction: column;
  min-width: 0;
  color: var(--source);
  font-size: 13px;
  line-height: 1.35;
}

.weather-line {
  display: flex;
  gap: 10px;
  white-space: nowrap;
}

.weather-place {
  color: var(--description);
  font-weight: 700;
}

/* Columns close together, rather than spread across the whole page. */
.source-list {
  max-width: 860px;
}

.source-row {
  display: flex;
  align-items: center;
  gap: 14px;
  padding: 7px 0;
  border-bottom: 1px solid var(--panel);
  font-size: 14px;
}

.source-head {
  color: var(--source);
  font-weight: 700;
  border-bottom-color: var(--source);
}

.source-head span {
  color: var(--source);
  font-size: 12px;
}

.source-row-label {
  width: 220px;
  color: var(--title);
  text-decoration: none;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.source-row-label:hover {
  color: var(--accent);
}

.source-row-type {
  width: 70px;
  color: var(--source);
  font-size: 12px;
}

.source-row-state {
  width: 80px;
  color: var(--description);
  font-size: 12px;
}

/* Only trouble is worth a colour. A working source should not shout about it. */
.source-row-state[data-state="error"] {
  color: var(--error);
}

.source-row-state[data-state="unset"],
.source-row-state[data-state="no results"] {
  color: var(--star-on);
}

.source-row-latest {
  width: 210px;
  color: var(--description);
  font-size: 12px;
}

.source-row-results {
  width: 50px;
  color: var(--source);
  font-size: 12px;
  text-align: right;
}

.source-row-stars {
  width: 100px;
  color: var(--star-on);
  font-size: 12px;
  text-align: right;
}

.source-row-rated {
  width: 90px;
  color: var(--source);
  font-size: 12px;
  text-align: right;
}

.source-head [data-sort] {
  cursor: pointer;
  user-select: none;
}

.source-head [data-sort]:hover {
  color: var(--accent);
}

/* The column the table is sorted by says so, and which way. */
.source-head [data-sort].sorted::after {
  content: " ▼";
}

.source-head [data-sort].sorted.ascending::after {
  content: " ▲";
}

/* Laid out like the sources table, so both list pages read the same way. */
.activity-list {
  max-width: 1000px;
}

.activity-row {
  display: flex;
  align-items: center;
  gap: 14px;
  padding: 7px 0;
  border-bottom: 1px solid var(--panel);
  font-size: 14px;
}

.activity-head {
  color: var(--source);
  font-weight: 700;
  border-bottom-color: var(--source);
}

.activity-head span {
  color: var(--source);
  font-size: 12px;
}

.activity-action {
  width: 110px;
  color: var(--star-on);
  white-space: nowrap;
}

/* Says what the row records, where a rating row shows its stars. */
.hidden-mark {
  color: var(--source);
  font-size: 12px;
  text-transform: uppercase;
  letter-spacing: 0.5px;
}

.activity-title {
  flex: 1;
  min-width: 0;
  color: var(--title);
  text-decoration: none;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.activity-title:hover {
  color: var(--accent);
}

.activity-source {
  width: 160px;
  color: var(--source);
  font-size: 12px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.activity-when {
  width: 110px;
  color: var(--source);
  font-size: 12px;
  white-space: nowrap;
}

/* Kept even on rating rows, which have nothing to undo, so the columns line up. */
.activity-undo {
  width: 70px;
}

.activity-search input {
  width: 100%;
  max-width: 420px;
  box-sizing: border-box;
  padding: 8px 12px;
  border: 1px solid var(--star);
  border-radius: 999px;
  background: var(--panel);
  color: var(--title);
  font-size: 14px;
}

.activity-search input:focus {
  outline: none;
  border-color: var(--accent);
}

.filter-buttons {
  margin-top: 12px;
  max-width: 1000px;
}

/* Floated rather than a flex row, so the buttons simply wrap onto a new line. */
.filter-button {
  float: left;
  margin: 0 8px 8px 0;
  padding: 5px 12px;
  border: 1px solid var(--star);
  border-radius: 999px;
  color: var(--source);
  font-size: 13px;
  text-decoration: none;
  user-select: none;
}

.filter-button:hover {
  color: var(--title);
  border-color: var(--source);
}

.filter-button.active {
  background: var(--accent);
  border-color: var(--accent);
  color: #ffffff;
}

.filter-buttons::after {
  content: "";
  display: block;
  clear: both;
}

.pages {
  display: flex;
  gap: 16px;
  align-items: center;
  margin-top: 20px;
  font-size: 14px;
  color: var(--source);
}

.pages a {
  color: var(--accent);
}

.empty {
  color: var(--source);
  font-size: 14px;
}

.failure {
  margin-bottom: 20px;
  font-size: 13px;
}

.failure-source {
  font-weight: 700;
  color: var(--error);
}

.failure-when {
  color: var(--source);
  font-weight: 400;
}

.failure-error {
  margin-top: 6px;
  color: var(--description);
  font-family: monospace;
  word-break: break-word;
}

/* The logo and one narrow column in the middle of the page, since it is all it holds. */
.login {
  display: flex;
  flex-direction: column;
  align-items: center;
  margin-top: 80px;
}

.login-logo {
  margin-bottom: 24px;
}

.login-form {
  display: flex;
  flex-direction: column;
  gap: 8px;
  width: 280px;
}

.login-label {
  color: var(--source);
  font-size: 12px;
}

.login-input {
  padding: 8px 10px;
  border: 1px solid var(--star);
  border-radius: 6px;
  background: var(--panel);
  color: var(--title);
  font-family: inherit;
  font-size: 14px;
}

.login-input:focus {
  outline: none;
  border-color: var(--accent);
}

.login-submit {
  margin-top: 12px;
  padding: 8px 10px;
  border: 1px solid var(--accent);
  border-radius: 6px;
  background: none;
  color: var(--title);
  font-family: inherit;
  font-size: 14px;
  cursor: pointer;
}

.login-submit:hover {
  background: var(--panel-hover);
}

.login-back {
  margin-top: 4px;
  color: var(--source);
  font-size: 13px;
  text-align: center;
}

.login-error {
  margin: 4px 0 0 0;
  color: var(--error);
  font-size: 13px;
}
