/* plain html vibe: black text, white page, browser-default type. The one
   designed thing is the mosaic. Entries sit on the bare page, no cards
   around images or text. */

/* The system UI sans. No font files to host, no licence to carry, no
   attribution, and nothing extra to download. Each platform supplies its
   own: SF on Apple, Segoe on Windows, Roboto on Android, and the free
   Liberation or DejaVu faces on Linux. */
body {
  margin: 0;
  background: #fff;
  color: #000;
  font-size: 1rem;
  font-family: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
}

/* A tap is not a selection. Chrome paints a grey box over whatever was
   tapped, and a drag across the page sweeps text into a selection; both
   read as the page glitching. Text still selects from a deliberate press
   and drag, which the rules below keep for the parts worth copying. */
* {
  -webkit-tap-highlight-color: transparent;
}

/* :visited is listed so a link never changes colour after it is opened. */
a,
a:visited,
a:hover,
a:active {
  color: #0000ff;
}

/* Dark mode is the same page with the two colours swapped. Links keep the
   browser's own colours on purpose, so they read the same either way. */
@media (prefers-color-scheme: dark) {
  body {
    background: #000;
    color: #fff;
  }

  /* Pure blue is unreadable on black, so dark mode lightens it. */
  a,
  a:visited,
  a:hover,
  a:active {
    color: #4ea3ff;
  }
}

.brand {
  font-weight: bold;
}

/* ---- login ---- */

.login-box {
  max-width: 18rem;
  margin: 28vh auto 0;
  padding: 0 1rem;
  text-align: center;
}

.login-box h1 {
  margin: 0 0 1.25rem;
}

.login-box form {
  display: grid;
  gap: 0.375rem;
  text-align: left;
}

.login-box label {
  margin-top: 0.375rem;
}

.login-box button {
  margin-top: 0.75rem;
  justify-self: start;
}

#login-error {
  margin: 0.5rem 0 0;
}

/* ---- account page ---- */

/* One row: the order toggles on the left, the composer centred, the column
   buttons and log out on the right. The outer columns are equal fractions,
   so the composer sits on the middle of the page rather than on the middle
   of what is left over. Every control is bare text: dim when off, full and
   underlined when on. */
.top {
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  align-items: baseline;
  gap: 0.75rem 1rem;
  padding: 0.75rem 1rem 0;
}

.composer {
  display: flex;
  align-items: baseline;
  gap: 0.75rem;
}

.top-right {
  justify-self: end;
  display: flex;
  align-items: baseline;
  gap: 1rem;
}

#composer-input {
  font: inherit;
  color: inherit;
  background: none;
  border: 0;
  border-bottom: 1px solid currentColor;
  border-radius: 0;
  padding: 0.25rem 0;
  width: min(24rem, 45vw);
}

/* The thicker line replaces the focus ring; the negative margin eats the
   extra pixel so nothing below shifts. */
#composer-input:focus {
  outline: none;
  border-bottom-width: 2px;
  margin-bottom: -1px;
}

.top button {
  font: inherit;
  color: inherit;
  background: none;
  border: 0;
  padding: 0;
  cursor: pointer;
  opacity: 0.45;
  user-select: none;
  -webkit-user-select: none;
}

.top button:hover,
.top button.on {
  opacity: 1;
}

.top button.on {
  text-decoration: underline;
  text-underline-offset: 0.2em;
}

.mode-toggle,
.order-toggle,
.col-controls {
  display: flex;
  align-items: baseline;
  gap: 0.75rem;
}

.col-controls button {
  font-size: 1.125rem;
  line-height: 1;
}

/* At the first or last column count the button keeps its space, so the
   other one does not slide along the line. */
.col-controls button.off {
  visibility: hidden;
}

/* A phone has no room for three groups on one line. The composer takes the
   top line to itself, and the two button groups share the line below it. */
@media (max-width: 560px) {
  .top {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: 0.75rem;
  }

  .composer {
    order: -1;
    width: 100%;
    justify-content: center;
  }

  #composer-input {
    width: min(16rem, 55vw);
  }

  .top-right {
    margin-left: auto;
  }
}

/* The mosaic. Entries land in source order, left to right, row by row.
   Columns come from the screen width (auto-fill). Each row is as tall as
   its tallest entry, and align-items centres every other entry on that
   row's middle. Columns and rows are the same 2rem apart, and the first
   row sits 2rem below the header.

   Row gap is the distance between one row band and the next, so the tallest
   entries in neighbouring rows are exactly 2rem apart. A shorter entry sits
   centred in a band taller than itself, so the white space under it is its
   share of the band plus the gap. That extra space is the centring, not the
   gap; only masonry removes it, and masonry has no rows to centre in. */
.mosaic {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(340px, 1fr));
  gap: 2rem;
  align-items: center;
  justify-items: center;
  padding: 2rem 1rem 4rem;
}

/* A phone keeps two columns, so the gap comes down with the cards. Both
   axes still share one number, which is the rule that matters. */
@media (max-width: 560px) {
  .mosaic {
    grid-template-columns: repeat(auto-fill, minmax(160px, 1fr));
    gap: 1rem;
  }
}

/* Every entry hugs its content. Text entries add 1rem of padding on all
   four sides; attachments sit bare, so the file is the only thing there.

   Columns get narrow, so every word wraps inside its card rather than
   spilling past it. `anywhere` rather than `break-word`, because it also
   lets the card shrink to the column: break-word still measures the card
   by its longest unbroken word. */
.entry {
  max-width: 100%;
  box-sizing: border-box;
  text-align: left;
  min-width: 0;
  overflow-wrap: anywhere;
}

.entry.note,
.entry.card {
  padding: 1rem;
}

/* images and attachments: the file itself, nothing around it */
.entry figure {
  margin: 0;
}

.entry img {
  display: block;
  max-width: 100%;
  height: auto;
  opacity: 0;
  transition: opacity 0.2s ease;
}

.entry img.loaded {
  opacity: 1;
}

/* under a link's picture: the link, then the user's note. Left aligned to
   the picture's own left edge, so no horizontal padding here. */
.entry-meta {
  margin-top: 0.5rem;
  text-align: left;
  max-width: 100%;
}

/* A long URL has no spaces to break at, so it wraps mid-word like
   everything else in a card. */
.entry-link {
  display: block;
}

/* pre-wrap keeps the line breaks between one save and the next. */
.entry-note {
  margin-top: 0.25rem;
  white-space: pre-wrap;
}

/* A carousel. The frame is the window and the track slides behind it, one
   picture wide. app.js moves the track; there is no scrolling here, so a
   fast flick cannot carry past a slide. touch-action lets a vertical drag
   scroll the page while a horizontal one is ours. */
.carousel-frame {
  position: relative;
  overflow: hidden;
}

.entry .slides {
  display: flex;
  transition: transform 0.25s ease;
  touch-action: pan-y;
  cursor: grab;
}

/* While a finger is down the track tracks the finger, with no easing to
   lag behind it. */
.entry .slides.dragging {
  transition: none;
  cursor: grabbing;
}

.entry .slides img {
  flex: 0 0 100%;
  object-fit: contain;
  user-select: none;
  -webkit-user-drag: none;
}

.carousel-arrow {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  width: 1.75rem;
  height: 1.75rem;
  padding: 0;
  border: 0;
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.85);
  color: #000;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
}

.carousel-arrow svg {
  width: 1rem;
  height: 1rem;
  display: block;
}

.carousel-arrow[hidden] {
  display: none;
}

/* The arrows are for a pointer, so they only appear where there is one to
   hover with. A touch screen swipes instead and keeps the picture clear. */
@media (hover: hover) {
  .carousel-arrow {
    opacity: 0;
    transition: opacity 0.15s ease;
  }

  .carousel-frame:hover .carousel-arrow,
  .carousel-arrow:focus-visible {
    opacity: 1;
  }
}

.carousel-arrow.back {
  left: 0.5rem;
}

.carousel-arrow.forward {
  right: 0.5rem;
}

.entry .dots {
  display: flex;
  justify-content: center;
  gap: 0.375rem;
  margin-top: 0.5rem;
}

.entry .dot {
  width: 0.375rem;
  height: 0.375rem;
  border-radius: 50%;
  background: currentColor;
  opacity: 0.25;
}

.entry .dot.on {
  opacity: 1;
}

/* An x post, drawn from its own parts: avatar, name, handle, text,
   pictures, date, every piece linking back to the post. */
.entry.tweet {
  padding: 1rem;
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
  cursor: pointer;
}

/* The whole head and the date link back to the post, but they are part of
   the card rather than links to read, so they take the page's own colour
   and never restyle on hover. The :hover selectors are here only to
   outrank the site-wide link colour, which is more specific than a bare
   class would be. */
.tweet-head,
.tweet-head:visited,
.tweet-head:hover,
.tweet-head:active,
.tweet-date,
.tweet-date:visited,
.tweet-date:hover,
.tweet-date:active {
  color: inherit;
  text-decoration: none;
  cursor: pointer;
}

.tweet-head {
  display: flex;
  align-items: center;
  gap: 0.5rem;
}

/* Scoped under .entry so the general picture rule, which sets height to
   auto, does not collapse this one to nothing. */
.entry .tweet-avatar {
  width: 2.5rem;
  height: 2.5rem;
  border-radius: 50%;
  flex: none;
  object-fit: cover;
}

.tweet-names {
  display: flex;
  flex-direction: column;
  min-width: 0;
  line-height: 1.2;
}

.tweet-name {
  font-weight: bold;
}

.tweet-handle {
  opacity: 0.6;
}

/* The head is a row: avatar, then the names. min-width lets the names
   column shrink below its content so long ones wrap instead of pushing the
   card wider than its column. */
.tweet-head {
  min-width: 0;
}

.tweet-names {
  flex: 1;
  min-width: 0;
}


.tweet-body {
  white-space: pre-wrap;
}

.tweet-media img {
  width: 100%;
  height: auto;
}

.tweet-date {
  opacity: 0.6;
}

/* notes: just the text, all of it. A note used to stop at twelve lines,
   which hid most of a long one with no way to read the rest. A long note
   now runs its full length and makes its row band as tall as it is. */
.entry.note {
  max-width: 32ch;
  white-space: pre-wrap;
}

/* links and social saves: a minimal embed card. No border, no box,
   just the title link. */
.entry.card .card-title {
  display: block;
}

/* Tight layout, for cards too narrow to read otherwise. app.js measures
   the column and turns it on. The head stacks, so the name gets the card's
   full width instead of what is left beside the avatar. */
.mosaic.dense .entry.tweet,
.mosaic.dense .entry.note,
.mosaic.dense .entry.card {
  padding: 0.5rem;
}

.mosaic.dense .tweet-head {
  flex-direction: column;
  align-items: flex-start;
  gap: 0.25rem;
}

.mosaic.dense .entry .tweet-avatar {
  width: 1.75rem;
  height: 1.75rem;
}

/* Free order: the whole entry is a drag handle, and the one being carried
   goes faint until it lands. Selection is off here, because a drag is a
   move; the other orders keep the text selectable. */
.mosaic.free .entry {
  cursor: grab;
  user-select: none;
  -webkit-user-select: none;
}

.entry.drag-ghost {
  opacity: 0.4;
}

/* The right-click menu on a card. The one boxed thing on the site: a
   floating menu needs an edge to exist against the page. */
.context-menu {
  position: fixed;
  z-index: 10;
  min-width: 8rem;
  background: #fff;
  border: 1px solid #000;
}

.context-menu button {
  display: block;
  width: 100%;
  text-align: left;
  font: inherit;
  color: inherit;
  background: none;
  border: 0;
  padding: 0.375rem 1rem;
  cursor: pointer;
  user-select: none;
  -webkit-user-select: none;
}

.context-menu button:hover {
  background: #000;
  color: #fff;
}

/* display:block above outranks the browser's [hidden] rule, so hidden
   needs saying again: a card without editable words has no edit row. */
.context-menu button[hidden] {
  display: none;
}

/* Delete hovers red, in both themes, and the armed second press stays it. */
.context-menu button:last-child:hover,
.context-menu button.armed {
  background: #ff0000;
  color: #fff;
}

@media (prefers-color-scheme: dark) {
  .context-menu {
    background: #000;
    border-color: #fff;
  }

  .context-menu button:hover {
    background: #fff;
    color: #000;
  }
}

/* Editing in place: the text swaps for this box, same type, thin line. */
.edit-box {
  font: inherit;
  color: inherit;
  background: none;
  border: 1px solid currentColor;
  padding: 0.5rem;
  box-sizing: border-box;
  width: 100%;
  min-width: 16rem;
  resize: vertical;
  display: block;
}

.status {
  text-align: center;
  margin: 2rem 1rem 0;
}
