/* Shared page styles for the feed and the profile — the nav rail, the column
   grid, the composer, the post/carousel/comment shapes, and the friends rail.
   Extracted rather than duplicated: both pages render the same post through the
   same renderer in feed.js, so two copies of these rules is two places for them
   to drift. style.css stays scoped to the plane and avatar view. */

/* The feed. Three columns: the global nav rail, the posts, the friends.
   Inline here rather than in style.css, which is scoped to the plane and
   avatar view — the convention music.html states for its own page rules. */
body.feed-body {
  margin: 0;
  /* Room for the fixed rail, on the BODY rather than as a margin on `.app`.
     That is what lets `.app` centre itself: `margin-left: 56px` pins the left
     edge, so `margin-right: auto` has nothing to balance against and the whole
     app hugs the left of the screen however wide it gets. Reserving the space
     out here leaves `.app` free to sit in the middle of what remains. */
  padding-left: 56px;
  box-sizing: border-box;
  min-height: 100%;
  background: #f2f2f2;
  color: #2b2b2b;
  font-family: var(--entry-font-helvetica);
  font-size: 16px;
}

/* --- the nav rail (nav.js) ------------------------------------------- */
/* Fixed and narrow. On the world page this same rail floats over the
   plane, which is why it stops its own clicks — see nav.js. */
#nav {
  position: fixed;
  top: 0;
  left: 0;
  bottom: 0;
  width: 56px;
  z-index: 300;
  background: #ffffff;
  /* Lighter than a content border on purpose: this line separates the rail from
     the page, and the borders inside the page (a post, a friend row) are part of
     the content. Chrome should be the quieter of the two. */
  border-right: 1px solid #e0e0e0;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 6px;
  padding: 10px 0;
  box-sizing: border-box;
}
/* Bare glyphs. Each was a filled grey box with its own border, which turned the
   rail into a column of eight buttons competing with the page — most visible
   next to the machines, where the covers are the only thing on screen that
   should carry weight. The 36px stays: it is the hit area, and a touch target
   does not shrink just because its border went.
   NO `background` in here, and none in any rule that reaches .nav-avatar: the
   avatar is a background-image, and the shorthand would silently erase it —
   test/avatars.test.js refuses it. */
#nav .nav-item,
#nav .nav-avatar {
  width: 36px;
  height: 36px;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 18px;
  line-height: 1;
  text-decoration: none;
  color: #767676;
  /* Explicitly none, not merely undeclared: log out is a <button>, and a button
     draws the UA's own border when nothing overrides it — deleting the old
     `1px solid` left exactly one boxed icon at the bottom of the rail. The
     avatar takes its edge back below, with longhands, after this. */
  border: none;
  box-sizing: border-box;
  cursor: pointer;
  font-family: inherit;
  padding: 0;
  flex: none;
}
/* Only the icons — the same reason the shared rule above states no background:
   the `background` SHORTHAND resets background-image to none, and at
   `#nav .nav-avatar` (1,1,0) it would beat `.pfp` (0,1,0) regardless of source
   order, erasing the picture. `none` here is not decoration, it is the removal
   of the old grey fill, and it is scoped to `.nav-item` so it can never reach a
   face. */
#nav .nav-item { background: none; }
#nav .nav-item:hover { color: #1a1a1a; }
/* The eight items that are a drawing (public/icons, named in nav.js) —
   everything but the island, which is the rule below this one. 32px for a 16px sprite — exactly 2x, and `pixelated` so the upscale
   is nearest-neighbour: half a pixel of blur is all it takes for pixel art to
   look like a photograph of pixel art.
   Muted at rest and full strength on approach, which is the same thing `color`
   does for the glyphs beside them — without it the rail would be a column of
   full-colour stickers and the page would be the quiet part. */
#nav .nav-icon {
  display: block;
  width: 32px;
  height: 32px;
  image-rendering: pixelated;
  opacity: 0.75;
}
/* The island is not a drawing: it is YOU, the top 16 pixels of the down-facing
   cell of your own sprite sheet — a head-and-shoulders portrait, already on the
   same 16px grid as every icon beside it, and the only item in the rail that is
   different for each person.
   Longhands only, which is the rule test/avatars.test.js exists to enforce: the
   image here IS the avatar, and `background` is a shorthand that would reset it
   to none from a selector the class that paints cannot outrank. */
#nav .nav-sprite {
  display: block;
  width: 32px;
  height: 32px;
  background-image: var(--sheet, url('/sprites/sprite_sheet.png'));
  background-repeat: no-repeat;
  /* Twice the whole 32x192 sheet, so the 32px window lands exactly on the top
     half of the first cell at 2x — the same upscale every icon gets, and the
     same reason: half a pixel of blur and pixel art looks like a photograph of
     pixel art. */
  background-size: 64px 384px;
  background-position: 0 0;
  image-rendering: pixelated;
  opacity: 0.75;
}
#nav .nav-item:hover .nav-icon,
#nav .nav-item.current .nav-icon,
#nav .nav-item:hover .nav-sprite,
#nav .nav-item.current .nav-sprite { opacity: 1; }
/* Where you are: the glyph in the ink, on a soft grey backing. Tried as an
   accent underline first, which read as an underlined word in a column of
   symbols. The backing has no border and no colour, so it says "here" without
   becoming chrome again, and it works in both orientations — a left-hand bar
   would not survive the rail lying down along the bottom.
   Scoped to `.nav-item`: behind the avatar a fill is invisible anyway, since the
   picture covers it, so the avatar says the same thing with its edge. And
   background-COLOR, never the `background` shorthand — see above. */
#nav .current { color: #1a1a1a; }
#nav .nav-item.current { background-color: #ededed; }
#nav .nav-avatar.current { border-color: #1a1a1a; }

/* Your own picture. Longhands only, for the reason stated above — the tint
   behind a picture is picture.js's business and the image is too, so this file
   must not use a shorthand that clears either.
   Square, like every profile picture: it is a cropped square, and the icons
   below it are 36px too, so the rail reads as one column. */
#nav .nav-avatar {
  width: 36px;
  height: 36px;
  font-size: 18px;
  /* The one box in the rail that keeps an edge: it is a picture, and a pale one
     would otherwise bleed into the white rail. Longhands only — see above. */
  border-width: 1px;
  border-style: solid;
  border-color: #d0d0d0;
}
/* Bottom of the column, and nothing else: it brightens on approach like every
   other icon in the rail. It used to be a glyph, and used to turn oxblood here —
   dropped with the glyph rather than reinvented as a red-tinted sprite, because
   the only permanent colour in the chrome putting emphasis on logging out was
   the problem that rule was already walking back. */
#nav .nav-logout { margin-top: auto; }


/* --- profile pictures -------------------------------------------------
   A person's uploaded square, or their initial when they have none. Every
   social surface uses this: the nav rail, a post head, a friend row, the
   profile card, and the editor's own preview.

   NOT the world character. `.avatar.sprited` in style.css draws that, on the
   plane, from a sprite sheet — see migration 0015 for why the two are separate
   things. The only survivor of the brief period when a sprite stood in for a
   picture is `.portrait` further down, which now has exactly one consumer: the
   world-character picker, where a sprite is the thing being chosen.

   `cover` plus a centred position, so a stored square fills the box at any size
   and a non-square one (an old row, a hand-rolled upload) is cropped rather
   than squashed. background-color comes from picture.js, which derives a tint
   from the username so a column of initials is still scannable.

   NEVER use the `background` SHORTHAND on any of these boxes: it resets
   background-image to none, and at id-scoped specificity it silently beats this
   rule. test/avatars.test.js refuses it. */
.pfp {
  background-repeat: no-repeat;
  background-size: cover;
  background-position: center;
  /* Only meaningful in the `no-picture` state, and harmless otherwise: an
     element showing an image has no text in it. Declared here rather than under
     `.no-picture` so the letter is centred by the same rule that sizes the box.
   */
  display: flex;
  align-items: center;
  justify-content: center;
  color: #3f3f3f;
  font-weight: bold;
  line-height: 1;
  overflow: hidden;
  text-decoration: none;
}
/* The initial. Sized as a fraction of the box so one rule serves a 24px post
   head and a 56px profile card without either one naming a font size. */
.pfp.no-picture { font-size: 0.55em; }

/* Kept for the WORLD-CHARACTER picker, and nothing else — a sprite is what that
   control is choosing, so it is the one place a sheet belongs outside the plane.
   Every sheet is a 2-column x 8-row grid, so `200% 800%` scales it until exactly
   one cell fills the box at whatever size the box is, and `0 0` picks column 0
   (the idle frame) of row 0 (facing down). No cell dimension appears here, which
   is why the rule survives a change of box size.
   Keep a portrait box at 2:3, the cell's own ratio, or the character stretches. */
.portrait {
  background-image: var(--sheet, url('/sprites/sprite_sheet.png'));
  background-repeat: no-repeat;
  background-size: 200% 800%;
  background-position: 0 0;
  image-rendering: pixelated;
}

/* --- the three columns ----------------------------------------------- */
.app {
  /* Centred in the space beside the rail, not pinned to it. `max-width` alone
     does nothing without this — a capped block with no auto margin just stops
     growing and stays where it started, which on a wide screen is the far
     left. */
  margin: 0 auto;
  display: grid;
  /* auto-fit-style breakpoints without a media query would not work here:
     the rails have fixed jobs, so the columns collapse at the content's
     real minimum instead of a guessed device width. */
  grid-template-columns: minmax(0, 1fr) 260px;
  gap: 24px;
  max-width: 1040px;
  padding: 20px 24px 60px;
  box-sizing: border-box;
}
@media (max-width: 860px) {
  .app { grid-template-columns: minmax(0, 1fr); }
  #friends-rail { order: -1; }
}

/* --- the feed column ------------------------------------------------- */
.feed-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px;
  margin-bottom: 14px;
}
.feed-head h1 { margin: 0; font-size: 22px; }
/* Top-right, per the design: you post from the page you read on. */
#new-post-btn {
  font-family: inherit;
  font-size: 13px;
  padding: 5px 12px;
  background: #2b2b2b;
  color: #fff;
  border: 1px solid #1a1a1a;
  cursor: pointer;
  flex: none;
}
#feed-error, #feed-empty, #feed-older {
  font-size: 13px;
  color: #6e6e6e;
  margin: 10px 0;
}
#feed-error { color: #7a1f1f; }
#feed-error[hidden], #feed-empty[hidden], #feed-older[hidden] { display: none; }

/* --- the composer ---------------------------------------------------- */
#composer {
  background: #ffffff;
  border: 1px solid #bdbdbd;
  padding: 12px 14px;
  margin-bottom: 18px;
}
#composer[hidden] { display: none; }
#composer label { display: block; font-size: 11px; color: #6e6e6e; margin-top: 6px; }
#composer input[type="text"],
#composer textarea {
  font-family: inherit;
  font-size: 14px;
  width: 100%;
  padding: 4px 6px;
  border: 1px solid #cccccc;
  background: #ffffff;
  box-sizing: border-box;
}
#composer textarea { min-height: 120px; resize: vertical; }
/* Thumbnails of what you picked, each with its own remove control — that is
   how a rejected photo gets dropped without losing the other five. */
#composer-thumbs { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 8px; }
.thumb { position: relative; width: 72px; height: 72px; flex: none; }
.thumb img {
  width: 100%; height: 100%; object-fit: cover;
  border: 1px solid #bdbdbd; background: #ececec; display: block;
}
.thumb button {
  position: absolute; top: -6px; right: -6px;
  width: 18px; height: 18px; line-height: 1;
  font-size: 11px; padding: 0; cursor: pointer;
  background: #d64545; color: #fff; border: 1px solid #7a1f1f;
}
.composer-actions { display: flex; align-items: center; gap: 10px; margin-top: 10px; }
#composer-submit {
  font-family: inherit; font-size: 13px; padding: 4px 12px;
  background: #2b2b2b; color: #fff; border: 1px solid #1a1a1a; cursor: pointer;
}
#composer-submit[disabled] { opacity: 0.55; cursor: default; }
#composer-status { font-size: 12px; color: #7a1f1f; }
#composer-status[hidden] { display: none; }
#composer-count { font-size: 11px; color: #6e6e6e; margin-left: auto; }

/* --- a post ---------------------------------------------------------- */
.post {
  background: #ffffff;
  border: 1px solid #bdbdbd;
  padding: 12px 14px;
  margin-bottom: 16px;
  cursor: pointer;
}
.post-head { display: flex; align-items: center; gap: 8px; font-size: 13px; }
.post-avatar {
  width: 32px; height: 32px; flex: none;
  border: 1px solid #bdbdbd; box-sizing: border-box;
}
.post-author { font-weight: bold; color: #2b2b2b; text-decoration: none; }
.post-when { color: #6e6e6e; margin-left: auto; font-size: 12px; }
.post-title { font-size: 18px; font-weight: bold; margin-top: 8px; }
/* pre-wrap so a written paragraph keeps its shape; overflow-wrap so a long
   unbroken string cannot push the column sideways. */
.post-body { margin-top: 6px; white-space: pre-wrap; overflow-wrap: anywhere; line-height: 1.45; }
.post-more { color: #6e6e6e; font-size: 12px; margin-top: 4px; }
.post-foot { margin-top: 10px; font-size: 12px; color: #6e6e6e; display: flex; gap: 12px; }
.post-foot button {
  font-family: inherit; font-size: 12px; padding: 0;
  background: none; border: none; color: #7a1f1f; cursor: pointer;
}

/* --- the photo carousel ---------------------------------------------- */
/* One photo at a time with arrows. The arrows and dots stop their own
   clicks — they sit inside a clickable post, and without that, browsing
   photos would open the detail view every time. */
/* The frame hugs the photo.
   `width: fit-content` on the box and `auto` dimensions on the image: the photo
   scales to fit inside both caps at its own aspect ratio, and the box then
   shrink-wraps to whatever that came to. So the border traces the picture rather
   than the column, and there is no band beside a tall one to colour in — which
   is why `transparent` alone was not the whole fix: `max-height` still held the
   BOX at full width, so `contain` went on letterboxing inside it, in white.
   `max-width: 100%` keeps a big photo inside the column. A photo smaller than
   the column now draws at its own size instead of being upscaled to fit.
   `object-fit` stays as a belt: with auto dimensions the box already matches the
   photo, so it has nothing to do unless something else constrains both sides.
   And NO border. The box was drawing a 1px grey frame of its own around every
   photo, which is what the shrink-wrap left hugging the picture; the post is
   already a bordered card, and a frame inside a frame is one too many. The trade
   is that a photo which is white at its edges now has nothing marking where it
   stops. */
.gallery {
  position: relative;
  /* Centred in the column: the box is only as wide as the photo now, so a tall
     or small one sat against the left edge with all the slack on one side.
     Written as ONE shorthand rather than `margin-top` plus two auto longhands,
     so there is no longhand above a shorthand for the shorthand to discard. */
  margin: 10px auto 0;
  width: fit-content;
  max-width: 100%;
  background: transparent;
  border: none;
}
.gallery img {
  display: block;
  width: auto;
  height: auto;
  max-width: 100%;
  max-height: 520px;
  object-fit: contain;
}
.gallery-arrow {
  position: absolute; top: 50%; transform: translateY(-50%);
  width: 30px; height: 44px; font-size: 16px; line-height: 1;
  background: rgba(255, 255, 255, 0.94); color: #2b2b2b;
  border: 1px solid #bdbdbd; cursor: pointer; font-family: inherit; padding: 0;
}
.gallery-arrow.prev { left: 6px; }
.gallery-arrow.next { right: 6px; }
.gallery-arrow[hidden] { display: none; }
/* The dots sit under the photo on the post's own white, with nothing ruled off:
   the strip had a top border and a white fill of its own, which read as the
   bottom edge of the frame that is now gone. */
.gallery-dots {
  display: flex; justify-content: center; gap: 5px;
  padding: 6px 0; background: transparent; border-top: none;
}
.gallery-dots span {
  width: 6px; height: 6px; border-radius: 50%;
  background: #cccccc; display: block;
}
.gallery-dots span.on { background: #d64545; }

/* --- comments (in an expanded post) --------------------------------- */
.comments { margin-top: 12px; padding-top: 10px; border-top: 1px dashed #cccccc; }
.comments ul { list-style: none; margin: 0 0 10px; padding: 0; display: flex; flex-direction: column; gap: 8px; }
.comments li { display: flex; gap: 8px; align-items: flex-start; font-size: 14px; }
.comment-main { min-width: 0; flex: 1; }
.comment-author { font-weight: bold; }
.comment-body { white-space: pre-wrap; overflow-wrap: anywhere; line-height: 1.35; }
.comment-form { display: flex; flex-direction: column; gap: 6px; }
.comment-form textarea {
  font-family: inherit; font-size: 13px; padding: 4px 6px; min-height: 48px;
  border: 1px solid #cccccc; background: #ffffff; resize: vertical;
  box-sizing: border-box; width: 100%;
}
.comment-form button {
  align-self: flex-start; font-family: inherit; font-size: 12px; padding: 3px 10px;
  background: #2b2b2b; color: #fff; border: 1px solid #1a1a1a; cursor: pointer;
}

/* --- the friends rail (friends.js) ---------------------------------- */
#friends-rail h2 { font-size: 14px; margin: 0 0 8px; }
#friends-rail section { margin-bottom: 20px; }
#friend-list, #request-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 10px; }
/* A friend row: avatar, username beside it, their thought beneath. */
.friend-row { display: flex; gap: 8px; align-items: flex-start; font-size: 13px; }
.friend-row .friend-picture {
  width: 32px; height: 32px; flex: none;
  border: 1px solid #bdbdbd; box-sizing: border-box;
}
.friend-main { min-width: 0; flex: 1; }
.friend-name { display: block; color: #2b2b2b; font-weight: bold; text-decoration: none; }
/* Clamped to two lines: the 60-character server cap can still spill to a
   third at rail width. */
.friend-thought {
  color: #6e6e6e; font-size: 12px; line-height: 1.25; font-style: italic;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
  overflow: hidden;
}
.friend-row .drop {
  font-family: inherit; font-size: 12px; padding: 0; flex: none;
  background: none; border: none; color: #7a1f1f; cursor: pointer;
}
#add-friend { display: flex; gap: 6px; margin-bottom: 6px; }
#add-friend input {
  font-family: inherit; font-size: 13px; padding: 3px 6px; min-width: 0; flex: 1;
  border: 1px solid #cccccc; background: #ffffff;
}
#add-friend button {
  font-family: inherit; font-size: 12px; padding: 3px 8px; flex: none;
  background: #ececec; color: #2b2b2b; border: 1px solid #bdbdbd; cursor: pointer;
}
#friends-status { font-size: 12px; margin: 0; }
#friends-status[hidden] { display: none; }
/* already_friends / already_pending are NOT errors — see friends.js. */
#friends-status.error { color: #7a1f1f; }
#friends-status.info { color: #2b2b2b; }
.rail-empty { font-size: 12px; color: #6e6e6e; margin: 0; }
.rail-empty[hidden] { display: none; }
.request-row { display: flex; gap: 6px; align-items: baseline; font-size: 13px; flex-wrap: wrap; }
.request-row button {
  font-family: inherit; font-size: 11px; padding: 2px 7px;
  background: #ececec; color: #2b2b2b; border: 1px solid #bdbdbd; cursor: pointer;
}
.request-row .accept { background: #2b2b2b; color: #fff; border-color: #1a1a1a; }

/* --- no island on a phone -------------------------------------------- */
/* The island is walked with the arrow keys and drawn on a plane the size of the
   viewport, so there is no honest phone version of it yet — and its button goes
   rather than being left to disappoint. boot() in app.js turns away anyone who
   arrives anyway, from a bookmark or a shared visit link.
   A block of its own, because this is a DEVICE question and the bar below is a
   window one: a phone in landscape is 844px wide and would sail past a width
   rule, while a touchscreen laptop reports a fine pointer and real hover and
   keeps the island. Stated once here and once as NO_WORLD_QUERY in nav.js;
   test/mobile-nav.test.js keeps the two strings equal. */
@media (max-width: 700px), (pointer: coarse) and (any-hover: none) {
  #nav .nav-world { display: none; }
}

/* --- mobile: the rail lies down along the bottom --------------------- */
/* Phone-shaped screens. A 56px column down the side is a sixth of a 320px
   screen, and a thumb reaches the bottom of a phone rather than its edge — so
   the rail becomes a bar and every page swaps its reserved space from the left
   to below.
   Layout only — which item the bar carries is the block above's business. The
   width is stated twice, once here and once as MOBILE_QUERY in nav.js (the
   world page reads it to decide whether to run its render loops), and
   test/mobile-nav.test.js keeps the two equal. */
@media (max-width: 700px) {
  #nav {
    top: auto;
    right: 0;
    width: auto;
    height: 52px;
    flex-direction: row;
    justify-content: space-around;
    gap: 0;
    padding: 0 8px;
    border-right: none;
    border-top: 1px solid #bdbdbd;
  }
  /* `margin-top: auto` pushed log out to the bottom of a column; in a row it
     would push it off the top. The shorthand clears it — deliberately, and it
     is written as a shorthand ALONE so there is no longhand above it to lose. */
  #nav .nav-logout { margin: 0; }
  /* `html body.x` (0,1,2) rather than `body.x` (0,1,1): settings.html links
     this file ABOVE its own <style>, so an equal-specificity rule here would
     lose the tie on source order and leave 72px of dead space down the left of
     that one page. The extra element wins outright everywhere, whatever order a
     page links things in. */
  html body.feed-body {
    padding-left: 0;
    padding-bottom: 68px;
  }
  html body.machine-body,
  html body.globe-body,
  html body.trip-body,
  html body.settings-body {
    padding: 20px 12px 68px;
  }
}
