/* ===========================================================================
   atlas.css - the Atlas and the Library.
   Exactly the colours of the Starmap tab, all of them from theme.css.
   Nothing new is invented here - only sizes and placement.
   =========================================================================== */

html, body {
  background:
    radial-gradient(1200px 620px at 50% -140px, var(--panel-soft), transparent 70%),
    var(--page);
}
body { padding-bottom: 60px; }

.wrap { max-width: 1180px; margin: 0 auto;
  padding: calc(var(--bar-h) + 20px) 22px 0; }

/* ------------------------------------------------------------ sort strip - */
.sub {
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  padding: 10px 0 12px; border-bottom: 1px solid var(--hair-soft);
}
.sub .lab { font-size: 11.5px; letter-spacing: .12em; text-transform: uppercase;
  color: var(--ink-faint); }
.sub button {
  height: 28px; padding: 0 12px; border-radius: 14px; cursor: pointer;
  border: 1px solid var(--hair); background: transparent; color: var(--ink-soft);
  font: 12px "Segoe UI", system-ui, sans-serif;
}
.sub button:hover { color: var(--ink); border-color: var(--accent); }
.sub button.on {
  color: #fff; border-color: transparent;
  background: var(--accent-soft);
  box-shadow: 0 0 12px var(--glow);
}
.sub .spacer { flex: 1 1 auto; }

/* --------------------------------------------------------------- chips --- */
.chips { display: flex; flex-wrap: wrap; gap: 8px; padding: 14px 0 4px; }
.chips button {
  height: 26px; padding: 0 11px; border-radius: 13px; cursor: pointer;
  border: 1px solid var(--hair-soft); background: transparent; color: var(--ink-soft);
  font: 11.5px "Segoe UI", system-ui, sans-serif;
  display: inline-flex; align-items: center; gap: 7px;
}
.chips button i { width: 7px; height: 7px; border-radius: 50%;
  background: currentColor; box-shadow: 0 0 8px currentColor; }
.chips button:hover { color: var(--ink); }
.chips button.on { color: #fff; border-color: var(--accent);
  background: var(--accent-soft); }

/* ---------------------------------------------------------------- grid --- */
#grid {
  display: grid; gap: var(--grid-gap); padding: 18px 0 6px;
  grid-template-columns: repeat(auto-fill, minmax(var(--card-min), 1fr));
}

/* A world card is a sigil laid out flat: its four corners are cut off the way
   a button's are, and the cut edge wears the same lit rim - in the world's own
   genre colour, because a card is about one world. The rim is drawn as a ring
   (a padded box masked down to its own edge) so that it follows the cut
   instead of stopping at a corner that is no longer there.
   --hx-card-cut is how deep the cut goes. The VRChat mark in the picture's
   corner is the one thing that keeps its own shape, and the cut is kept small
   enough that it never reaches it: the mark sits 8px in from the top right,
   the cut is 12px wide along the edge, so the mark's own corner is clear. */
.item {
  --hx-card-cut: 12px;
  position: relative; z-index: 0;
  background: var(--card); border: 0; border-radius: 0; overflow: hidden;
  display: flex; flex-direction: column;
  clip-path: polygon(
    var(--hx-card-cut) 0, calc(100% - var(--hx-card-cut)) 0,
    100% var(--hx-card-cut), 100% calc(100% - var(--hx-card-cut)),
    calc(100% - var(--hx-card-cut)) 100%, var(--hx-card-cut) 100%,
    0 calc(100% - var(--hx-card-cut)), 0 var(--hx-card-cut));
  transition: transform .15s, filter .15s;
}
/* the lit rim: one hair of the sigil edge, following the cut all the way round */
.item::after {
  content: ""; position: absolute; inset: 0; z-index: -1; pointer-events: none;
  padding: 1.3px;
  background: var(--hx-edge);
  clip-path: polygon(
    var(--hx-card-cut) 0, calc(100% - var(--hx-card-cut)) 0,
    100% var(--hx-card-cut), 100% calc(100% - var(--hx-card-cut)),
    calc(100% - var(--hx-card-cut)) 100%, var(--hx-card-cut) 100%,
    0 calc(100% - var(--hx-card-cut)), 0 var(--hx-card-cut));
  -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  -webkit-mask-composite: xor;
  mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  mask-composite: exclude;
}
.item:hover { transform: translateY(-2px); }

.shot {
  position: relative; display: block; aspect-ratio: 16 / 10;
  background: #0a0c1c center/cover no-repeat;
  border-bottom: 1px solid var(--hair-soft);
}
.shot .badge {
  position: absolute; left: 8px; top: 8px;
  font-size: 10px; letter-spacing: .08em; text-transform: uppercase;
  padding: 2px 7px; border-radius: 10px;
  color: #fff; background: var(--accent-soft);
  border: 1px solid var(--glow);
}
.shot .noimg {
  position: absolute; inset: 0; display: flex; align-items: center;
  justify-content: center; color: var(--ink-faint); font-size: 11.5px;
}

.item .body { padding: 10px 12px 11px; display: flex; flex-direction: column;
  gap: 6px; flex: 1; }

.item .title { font-size: 13.5px; font-weight: 600; color: var(--ink);
  line-height: 1.35; }
.item .title:hover { color: #fff; text-decoration: none; }
.item .by { font-size: 11.5px; color: var(--ink-faint); }
.item .by b { color: var(--ink-soft); font-weight: 500; }

/* What the community made of the world, under the byline: one phrase and
   the number of reviews it rests on. It only exists once five reviews do,
   so it never has to look provisional. It reads as one sentence and wraps
   like one, whatever width the card ends up being. */
.item .rvline { font-size: 11.5px; line-height: 1.4; }
.item .rvline::before {
  content: ""; display: inline-block; width: 6px; height: 6px;
  margin-right: 6px; border-radius: 50%; background: var(--accent);
  box-shadow: 0 0 7px var(--glow); vertical-align: 1.5px; }
.item .rvword { color: var(--ink-soft); }
.item .rvn { color: var(--ink-faint); font-variant-numeric: tabular-nums; }
.item .rvn::before { content: "·"; margin: 0 5px; }

.item .foot { display: flex; align-items: center; gap: 10px;
  margin-top: auto; padding-top: 6px; }
.item .stats { font-size: 11px; color: var(--ink-faint);
  font-variant-numeric: tabular-nums; line-height: 1.45; }
.item .stats b { color: var(--ink-soft); font-weight: 500; }

/* the ratio disc - how many visitors kept the world */
.disc { position: relative; width: 40px; height: 40px; flex: none; margin-left: auto; }
.disc svg { width: 40px; height: 40px; display: block; }
.disc circle { fill: none; stroke-width: 4; }
.disc .trk { stroke: var(--track); }
.disc .val { stroke: var(--track-fill); stroke-linecap: round; }
.disc .pct {
  position: absolute; inset: 0; display: flex; align-items: center;
  justify-content: center; font-size: 10.5px; font-weight: 600;
  color: var(--ink); font-variant-numeric: tabular-nums;
}
.disc .pct i { font-style: normal; font-size: 7.5px; opacity: .65; margin-left: 1px; }

/* the little save star */
.star-btn {
  width: 28px; height: 28px; flex: none; cursor: pointer; border-radius: 50%;
  border: 1px solid var(--hair); background: transparent; color: var(--ink-faint);
  font-size: 13px; line-height: 1; padding: 0;
}
.star-btn:hover { color: var(--ink); border-color: var(--accent); }
.star-btn.on { color: #ffd76a; border-color: var(--glow); background: var(--accent-soft); }

/* the little clock that puts a world on the look-at-it-later list */
.later-btn {
  width: 28px; height: 28px; flex: none; cursor: pointer; border-radius: 50%;
  border: 1px solid var(--hair); background: transparent; color: var(--ink-faint);
  font-size: 12.5px; line-height: 1; padding: 0;
}
.later-btn:hover { color: var(--ink); border-color: var(--accent); }
.later-btn.on { color: #9fd9ff; border-color: #5cb8ff; background: rgba(16, 42, 66, .7); }

/* the "+" that files a world into one of my playlists - the same 28px sigil
   as the clock and the star beside it, so all three on a card read as one set
   (hex.css cuts them all to the same hexagon) */
.pl-btn {
  width: 28px; height: 28px; flex: none; cursor: pointer; border-radius: 50%;
  border: 1px solid var(--hair); background: transparent; color: var(--ink-faint);
  font-size: 12.5px; line-height: 1; padding: 0;
}
.pl-btn:hover { color: var(--ink); border-color: var(--accent); }

/* the row above the wall where the later list is switched in and out */
.filters { display: flex; align-items: center; gap: 8px; padding: 4px 0 12px; }
.filters .lab { font-size: 11.5px; letter-spacing: .12em; text-transform: uppercase;
  color: var(--ink-faint); cursor: help; }
.filters select {
  height: 28px; max-width: 260px; padding: 0 8px; cursor: pointer;
  border: 1px solid var(--hair); border-radius: 6px; background: transparent;
  color: var(--ink-soft); font: 12px "Segoe UI", system-ui, sans-serif;
}
.filters select:hover { color: var(--ink); border-color: var(--accent); }
.filters .later-n { font-size: 11.5px; color: var(--ink-faint); }
.filters .spacer { flex: 1 1 auto; }

/* the platform switch: three words, the live one glowing like the sort row.
   It reads as one small instrument, so it sits in the same row as the later
   list instead of crowding the left column. */
.psw { display: inline-flex; align-items: center; gap: 2px; padding: 2px;
  border: 1px solid var(--hair); border-radius: 8px; }
.psw button {
  font: 600 11.5px "Segoe UI", system-ui, sans-serif; letter-spacing: .04em;
  padding: 4px 10px; border: 0; border-radius: 6px; cursor: pointer;
  background: transparent; color: var(--ink-faint); transition: .15s;
}
.psw button:hover { color: var(--ink); background: rgba(255, 255, 255, .06); }
.psw button.on {
  color: #0a0c1a; background: var(--accent);
  box-shadow: 0 0 12px var(--glow);
}
.psw button.p-both.on, .psw button[data-p="both"].on { background: #6ce9a6; }

/* --------------------------------------------------------------- pager --- */
#pages { display: flex; justify-content: center; gap: 6px; padding: 22px 0 10px;
  flex-wrap: wrap; }
#pages button {
  min-width: 30px; height: 30px; padding: 0 9px; border-radius: 6px; cursor: pointer;
  border: 1px solid var(--hair); background: transparent; color: var(--ink-soft);
  font: 12px "Segoe UI", system-ui, sans-serif;
}
#pages button:hover { color: var(--ink); border-color: var(--accent); }
#pages button.cur { color: #fff; border-color: transparent;
  background: var(--accent-soft); }

.empty { padding: 40px 0 60px; color: var(--ink-faint); font-size: 13px; }
.empty h2 { color: var(--ink); font-size: 15px; font-weight: 600; margin: 0 0 6px; }

/* ------------------------------------------------------------- library --- */
.note { font-size: 12px; color: var(--ink-faint); line-height: 1.6;
  margin: 12px 0 0; max-width: 720px; }

.lib { display: flex; flex-direction: column; gap: 12px; padding: 16px 0 20px; }
.lib-row {
  display: flex; align-items: center; gap: 14px; padding: 12px 14px;
  background: var(--card); border: 1px solid var(--hair-soft);
  border-radius: var(--radius);
}
.lib-row:hover { border-color: var(--hair); }
.lib-row img {
  width: 74px; height: 46px; object-fit: cover; border-radius: 6px; flex: none;
  border: 1px solid var(--hair-soft);
}
.lib-row .ph { width: 74px; height: 46px; border-radius: 6px; flex: none;
  border: 1px solid var(--hair-soft); display: flex; align-items: center;
  justify-content: center; color: var(--ink-faint); font-size: 10px; }
.lib-row .info { flex: 1; min-width: 0; }
.lib-row .nm { font-size: 13.5px; font-weight: 600; color: var(--ink); }
.lib-row .mt { font-size: 11.5px; color: var(--ink-faint); }
.lib-row .tagbox { display: flex; align-items: center; gap: 6px; flex-wrap: wrap;
  margin-top: 7px; }
.tag {
  font-size: 11px; padding: 2px 8px; border-radius: 10px;
  background: var(--accent-soft); color: var(--ink);
  border: 1px solid var(--hair-soft);
  display: inline-flex; align-items: center; gap: 6px;
}
.tag b { cursor: pointer; font-weight: 400; opacity: .6; }
.tag b:hover { opacity: 1; color: #fff; }
.lib-row input.tag-in {
  height: 24px; width: 130px; padding: 0 8px; border-radius: 10px;
  border: 1px dashed var(--hair); background: transparent; color: var(--ink);
  font: 11px "Segoe UI", system-ui, sans-serif; outline: none;
}
.lib-row input.tag-in:focus { border-style: solid; border-color: var(--accent); }
.lib-row .drop {
  flex: none; width: 28px; height: 28px; border-radius: 50%; cursor: pointer;
  border: 1px solid var(--hair); background: transparent; color: var(--ink-faint);
  font-size: 13px; padding: 0;
}
.lib-row .drop:hover { color: #ff8f9c; border-color: #ff8f9c; }

/* ======================================================== atlas: the rail ===
   Genres and creators live on the left, the wall of worlds scrolls beside
   them. Atlas and Library share this two-column desktop layout. */
body.tab-atlas { --rail-w: 262px; }

body.tab-atlas .wrap {
  max-width: 1420px;
  display: grid;
  grid-template-columns: var(--rail-w) minmax(0, 1fr);
  gap: 30px;
  align-items: start;
}
body.tab-atlas .col { min-width: 0; }

.rail {
  position: sticky; top: calc(var(--bar-h) + 18px);
  display: flex; flex-direction: column; gap: 16px;
  padding: 16px 0 24px;
  max-height: calc(100vh - var(--bar-h) - 40px); overflow-y: auto;
}
.rail-card {
  /* the rail wears its own sheet: a violet wash (--rail-card) rather than the
     plain grey film (--card) the world cards beside it use, and an edge tinted
     the same way. hex.css draws the rim with --hx-edge and takes the border
     away, so re-pointing --hx-edge here is what actually colours it; the border
     below still holds for any page that loads atlas.css without hex.css. */
  --hx-edge: var(--rail-card-line);
  background: var(--rail-card); border: 1px solid var(--rail-card-line);
  border-radius: var(--radius); padding: 12px 12px 14px;
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, .06);
}
/* a panel that has nothing to say stays away entirely - said out loud, so a
   later rule that sets display cannot quietly bring it back */
.rail-card[hidden] { display: none; }
.rail-h {
  margin: 2px 0 10px; font-size: 11px; font-weight: 600; letter-spacing: .14em;
  text-transform: uppercase; color: var(--ink-faint);
}
.rail-toggle {
  display: block; position: relative; width: 100%; padding: 0;
  border: 0; background: transparent; color: inherit; cursor: pointer;
  font: inherit; letter-spacing: inherit; text-align: center; text-transform: inherit;
}
.rail-toggle:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }
.rail-toggle .rail-chevron {
  position: absolute; top: 50%; right: 5px;
  width: 7px; height: 7px; flex: none; margin: 0;
  border-right: 1.5px solid var(--ink-faint); border-bottom: 1.5px solid var(--ink-faint);
  transform: translateY(-50%) rotate(45deg); transition: transform .15s ease;
}
.rail-toggle[aria-expanded="false"] .rail-chevron { transform: translateY(-50%) rotate(-45deg); }
.rail-body[hidden] { display: none; }
.rail-h .rail-c { color: var(--accent); letter-spacing: 0; }
/* The rail's one plain button - "hide worlds I've reviewed". It is laid out
   like the rows of the lists above it (30px, 9px of air on the left, the whole
   width of the card) and it is painted by hex.css like every other button in
   the house: the cut corners, and the lit edge while it is on (which is why
   .rail-btn.on is named in hex.css - that is where "this one is chosen" lives).
   Greyed out until the site has said which worlds are yours. */
.rail-btn {
  display: flex; align-items: center; gap: 9px; width: 100%; height: 30px;
  padding: 0 9px; margin: 2px 0 0; border-radius: 8px; cursor: pointer;
  border: 1px solid transparent; background: transparent; color: var(--ink);
  font: 12.5px "Segoe UI", system-ui, sans-serif; text-align: left;
}
.rail-btn:hover { color: var(--ink); }
.rail-btn:disabled { opacity: .32; cursor: default; }
.rail-btn:disabled:hover { color: var(--ink); }

.rail-note {
  margin: 6px 2px 0; font-size: 11.5px; line-height: 1.55; color: var(--ink-faint);
}

/* genre list ------------------------------------------------------------- */
.glist { display: flex; flex-direction: column; gap: 2px; }
.glist button {
  display: flex; align-items: center; gap: 9px; width: 100%;
  height: 30px; padding: 0 9px; border-radius: 8px; cursor: pointer;
  border: 1px solid transparent; background: transparent; color: var(--ink-soft);
  font: 12.5px "Segoe UI", system-ui, sans-serif; text-align: left;
}
.glist button i {
  width: 8px; height: 8px; border-radius: 50%; flex: none;
  background: var(--gcl, currentColor); box-shadow: 0 0 9px var(--gcl, currentColor);
}
.glist button .n { margin-left: auto; font-size: 11px; color: var(--ink-faint); }
.glist button:hover { color: var(--ink); border-color: var(--hair); }
.glist button.on { color: #fff; border-color: transparent; background: var(--accent-soft); }
.glist button.on .n { color: #fff; opacity: .75; }
.glist button:disabled { opacity: .32; cursor: default; }
.glist button:disabled:hover { border-color: transparent; color: var(--ink-soft); }

/* creators you follow ---------------------------------------------------- */
.flist { display: flex; flex-direction: column; gap: 3px; }
.flist .frow { display: flex; align-items: center; gap: 6px; }
.flist .fn {
  flex: 1; min-width: 0; height: 30px; padding: 0 9px; border-radius: 8px;
  cursor: pointer; border: 1px solid transparent; background: transparent;
  color: var(--ink); font: 12.5px "Segoe UI", system-ui, sans-serif; text-align: left;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.flist .fn i { font-style: normal; font-size: 11px; color: var(--ink-faint); margin-left: 7px; }
.flist .fn:hover { border-color: var(--hair); }
.flist .fn.on { background: var(--accent-soft); color: #fff; border-color: transparent; }
.flist .fn.on i { color: #fff; opacity: .75; }
.flist .fx {
  flex: none; width: 24px; height: 24px; border-radius: 50%; cursor: pointer;
  border: 1px solid var(--hair); background: transparent; color: var(--ink-faint);
  font: 10px/1 "Segoe UI", system-ui, sans-serif; padding: 0;
}
.flist .fx:hover { color: #ff8f9c; border-color: #ff8f9c; }

/* "only worlds by X" bar ------------------------------------------------- */
.who {
  display: flex; align-items: center; gap: 10px; margin: 14px 0 0;
  padding: 9px 12px; border-radius: 10px; font-size: 12.5px;
  border: 1px solid var(--hair-soft); background: var(--card); color: var(--ink-soft);
}
.who b { color: var(--ink); }

/* follow button on the card ---------------------------------------------- */
.by .cr {
  border: 0; background: transparent; padding: 0; cursor: pointer;
  color: var(--ink); font: inherit; font-weight: 600;
  /* a creator with a VRChat account behind the name is an <a> to their page,
     and a link that reads as a word in a sentence carries no underline until
     it is pointed at */
  text-decoration: none;
}
.by .cr:hover { text-decoration: underline; }
.by .cr.on { color: var(--accent-lit, var(--accent)); }
.by .cr.on::after { content: " \2715"; font-size: 10px; vertical-align: 1px; }
.by .fw {
  margin-left: 7px; height: 20px; padding: 0 8px; border-radius: 10px; cursor: pointer;
  border: 1px solid var(--hair); background: transparent; color: var(--ink-faint);
  font: 10.5px "Segoe UI", system-ui, sans-serif; vertical-align: 1px;
}
.by .fw:hover { color: var(--ink); border-color: var(--accent); }
.by .fw.on { color: #fff; border-color: transparent; background: var(--accent-soft); }

/* endless scroll ---------------------------------------------------------- */
.more {
  display: flex; align-items: center; justify-content: center; gap: 12px;
  flex-wrap: wrap; padding: 24px 0 44px; min-height: 60px;
}
.more button {
  height: 34px; padding: 0 18px; border-radius: 17px; cursor: pointer;
  border: 1px solid var(--hair); background: transparent; color: var(--ink-soft);
  font: 12.5px "Segoe UI", system-ui, sans-serif;
}
.more button:hover { color: var(--ink); border-color: var(--accent);
  box-shadow: 0 0 12px var(--glow); }
.more-info { font-size: 12px; color: var(--ink-faint); letter-spacing: .06em; }
.more.done button { display: none; }

@media (max-width: 900px) {
  /* A fixed top bar is not part of document flow. Reserve its height for
     focus and scroll-into-view targets so a browser cannot tuck the filter
     header and Reach handle underneath it. */
  html { scroll-padding-top: calc(var(--bar-h) + 18px); }
  .rail-card { scroll-margin-top: calc(var(--bar-h) + 18px); }
  .adv-slide, .adv-slide input[type="range"] {
    scroll-margin-top: calc(var(--bar-h) + 18px);
  }
  body.tab-atlas .wrap { grid-template-columns: minmax(0, 1fr); gap: 0; }
  body.tab-library .wrap { grid-template-columns: minmax(0, 1fr); gap: 0; }
  .rail { position: static; max-height: none; overflow: visible;
    flex-direction: row; flex-wrap: wrap; }
  .rail-card { flex: 1 1 260px; }
}

/* an empty box must stay invisible: the class sets display, so [hidden]
   on its own would lose that fight */
.who[hidden], .rail-note[hidden] { display: none; }

/* the rail scrolls on its own when the genre list is long - the bar itself is
   not written here: theme.css keeps one slider for every surface on the site */


/* ================================================ cards wear a genre colour ==
   GENRE_LOOK in serve.py gives every genre a colour. atlas.js hands each card
   two values:  --gcl  the colour itself,  --gcla  the same colour, very faint.
   Everything below only borrows them - no new colours are invented here. */

.item { background: linear-gradient(180deg, var(--gcla, transparent) 0%,
                                       transparent 58%), var(--card); }
/* the genre line along the top of the card - it is cut with the card's own
   corners now, so it starts a hair in and runs to the cut on the right */
.item::before {
  content: ""; display: block; height: 2px; flex: none;
  background: linear-gradient(90deg, var(--gcl, var(--accent)) 0%, transparent 88%);
  opacity: .85;
}
/* the glow under the hand has to be a drop-shadow: a box-shadow would be cut
   away with the corners the card no longer has - and the rim comes alive */
.item:hover { filter: drop-shadow(0 8px 22px -10px var(--gcl, var(--accent))); }
.item:hover::after { background: var(--hx-edge-lit); }
.item .title:hover { color: var(--gcl, #fff); }

/* the genre line right under the creator */
.item .gline { display: flex; }
.gchip {
  font-size: 10.5px; padding: 2px 9px; border-radius: 10px;
  color: var(--gcl, var(--ink-soft)); border: 1px solid var(--gcla, var(--hair-soft));
  background: linear-gradient(90deg, var(--gcla, transparent), transparent);
}

/* the two dates at the bottom of the card */
.item .dates {
  font-size: 10.5px; color: var(--ink-faint); letter-spacing: .01em;
  border-top: 1px solid var(--hair-soft); padding-top: 6px;
}
.item .dates:hover { color: var(--ink-soft); }

/* the picture lives in its own box now, because the arrows sit on top of it */
.thumb { position: relative; aspect-ratio: 16 / 10; flex: none;
  background: #0a0c1c; border-bottom: 1px solid var(--hair-soft); }
/* the picture is a button: pressing it invites you into the world */
.thumb .shot { position: absolute; inset: 0; display: block; width: 100%;
  aspect-ratio: auto; padding: 0; cursor: pointer; text-align: center;
  border: 0; border-bottom: 0; color: inherit;
  background: #0a0c1c center / cover no-repeat;
  transition: filter .14s; }
.thumb .shot:hover { filter: brightness(1.12) saturate(1.06); }
.thumb .shot.busy { cursor: progress; filter: brightness(.86) saturate(.7); }
/* nothing appears over the picture on hover: the picture itself is the
   doorknob, and what it does is told by the hexagon you draw on it and by the
   strip that says what the invitation is doing. */
/* the VRChat mark in the picture's upper-right corner - the door to the same
   world on vrchat.com. It stands on its own: no pill, no frame behind it,
   just the mark with a soft shadow so it stays readable over a bright
   picture. On hover it brightens and lifts a hair, which is the only sign
   it is something you can press. */
.thumb .ext {
  position: absolute; right: 8px; top: 8px; height: 26px;
  display: grid; place-items: center; text-decoration: none;
  background: none; border: 0; padding: 0;
  filter: drop-shadow(0 1px 3px rgba(4, 6, 16, .9));
  transition: transform .12s;
}
.thumb .ext img { display: block; width: 46px; height: 21px; opacity: .82;
  transition: opacity .12s; }
.thumb .ext:hover { transform: translateY(-1px) scale(1.06); }
.thumb .ext:hover img { opacity: 1; }
/* the line that says what the invite is doing */
.thumb .invite-note {
  position: absolute; left: 0; right: 0; bottom: 0; margin: 0;
  padding: 6px 10px; font-size: 11px; line-height: 1.3; text-align: center;
  color: var(--ink-soft); background: rgba(6, 8, 20, .86);
  border-top: 1px solid var(--hair);
  -webkit-backdrop-filter: blur(4px); backdrop-filter: blur(4px);
}
.thumb .invite-note.good { color: #bff5d3; border-top-color: #37d67a; }
.thumb .invite-note.bad { color: #ffd3db; border-top-color: #ff4d6d; }
/* the signs in the picture's upper-left corner: which machines can enter the
   world (PC only / PC + Quest) and, beside it, whether it is one of the top
   rated ones. They share the one corner instead of fighting over it. */
.thumb .badges {
  position: absolute; left: 8px; top: 8px; z-index: 2;
  display: flex; align-items: center; gap: 5px; max-width: calc(100% - 16px);
}
.thumb .badge {
  font-size: 10px; letter-spacing: .08em; text-transform: uppercase;
  padding: 2px 7px; border-radius: 10px; white-space: nowrap;
  color: #fff; background: var(--accent-soft); border: 1px solid var(--glow);
}
/* the platform sign: quiet and readable over any picture, and coloured by
   what it says - Quest-capable worlds get the brighter one, because "you can
   bring your headset" is the more useful thing to know at a glance */
.thumb .plat {
  font-size: 10px; letter-spacing: .06em; text-transform: uppercase;
  padding: 2px 7px; border-radius: 10px; white-space: nowrap;
  color: #d7dcff; background: rgba(6, 8, 20, .66);
  border: 1px solid rgba(215, 220, 255, .26);
  text-shadow: 0 1px 3px rgba(0, 0, 0, .7);
}
.thumb .plat.p-both {
  color: #cdfbe4; border-color: rgba(55, 214, 122, .55);
  background: rgba(8, 34, 22, .62);
}
.thumb .plat.p-quest {
  color: #cdfbe4; border-color: rgba(55, 214, 122, .55);
  background: rgba(8, 34, 22, .62);
}
.thumb .noimg {
  position: absolute; inset: 0; display: flex; align-items: center;
  justify-content: center; color: var(--ink-faint); font-size: 11.5px;
}

/* like / meh / dislike: three squares in the bottom right corner of the
   picture, standing on its bottom edge and against its right edge - the arrow
   sits over its number, so each button is a square block of its own. Neighbours
   share one hairline instead of stacking two.
   The whole square was made a quarter smaller - 34px became 25.5px, and the
   arrow circle, the number and the glyph came down with it by the same three
   quarters, so it is the same button, only quieter. These are the only numbers
   to touch: everything here is a plain multiple of the 34px square. */
.votes { position: absolute; right: 0; bottom: 0; display: flex; }
.vt {
  width: 25.5px; height: 25.5px; padding: 0; cursor: pointer;
  display: flex; flex-direction: column; align-items: center;
  justify-content: center; gap: 0;
  border: 1px solid var(--hair); border-radius: 0;
  background: rgba(8, 10, 24, .74);
  color: var(--ink-soft); font: 8.6px "Segoe UI", system-ui, sans-serif;
  -webkit-backdrop-filter: blur(6px); backdrop-filter: blur(6px);
  transition: color .12s, border-color .12s, background .12s;
}
.vt + .vt { margin-left: -1px; }
.vt i {
  width: 10.5px; height: 10.5px; border-radius: 50%; flex: none;
  display: grid; place-items: center; font-style: normal; font-size: 5.5px;
  background: rgba(255, 255, 255, .09); color: var(--ink-soft);
}
.vt b { font-weight: 600; font-size: 7.5px; line-height: 1.25;
  font-variant-numeric: tabular-nums; }
.vt.up:hover { color: #fff; border-color: #37d67a; }
.vt.up:hover i { background: #37d67a; color: #06170e; }
.vt.up.on { color: #bff5d3; border-color: #37d67a; background: rgba(20, 60, 38, .8); }
.vt.up.on i { background: #37d67a; color: #06170e; }
.vt.dn:hover { color: #fff; border-color: #ff4d6d; }
.vt.dn:hover i { background: #ff4d6d; color: #fff; }
.vt.dn.on { color: #ffd3db; border-color: #ff4d6d; background: rgba(66, 18, 30, .8); }
.vt.dn.on i { background: #ff4d6d; color: #fff; }
.vt.md:hover { color: #fff; border-color: #b9a2ff; }
.vt.md.on { color: #e3dbff; border-color: #b9a2ff; background: rgba(38, 26, 66, .82); }
.vt.md.on i { background: #b9a2ff; color: #120a24; }
/* while the strip that says what the invitation is doing is showing, the
   squares step up to stand on top of it - the exact height of the strip is
   set in atlas.js, since the sentence takes one line or three depending on the
   card's width */

/* the sort button that is on says which way round it is:  down = most first */
.sub [data-s].on::after { content: " \25BC"; font-size: 8px; opacity: .9; }
.sub [data-s].on.up::after { content: " \25B2"; }

/* ================================================== library: the three rails ==
   Genres, your tags and the creators, all pickable, all on the left. */
body.tab-library { --rail-w: 262px; }
body.tab-library .wrap {
  max-width: 1420px; display: grid;
  grid-template-columns: var(--rail-w) minmax(0, 1fr);
  gap: 30px; align-items: start;
}
body.tab-library .col { min-width: 0; }
@media (max-width: 900px) {
  /* This rule follows the desktop grid declaration so the one-column layout
     wins in the cascade on phones and short tablets. */
  body.tab-library .wrap { grid-template-columns: minmax(0, 1fr); gap: 0; }
}

.rail-hint { margin: 6px 2px 0; font-size: 11px; line-height: 1.5;
  color: var(--ink-faint); }
.facets { display: flex; flex-direction: column; gap: 2px; }
.facet {
  display: flex; align-items: center; gap: 9px; width: 100%; height: 29px;
  padding: 0 9px; border-radius: 8px; cursor: pointer;
  border: 1px solid transparent; background: transparent; color: var(--ink-soft);
  font: 12.5px "Segoe UI", system-ui, sans-serif; text-align: left;
}
.facet .n { margin-left: auto; font-size: 11px; color: var(--ink-faint); }
.facet:hover { color: var(--ink); border-color: var(--hair); }
.facet.on { color: #fff; background: var(--accent-soft); border-color: transparent; }
.facet.on .n { color: #fff; opacity: .75; }
.facet:disabled { opacity: .3; cursor: default; }
.facet:disabled:hover { border-color: transparent; color: var(--ink-soft); }
.facet .sw { width: 8px; height: 8px; border-radius: 50%; flex: none;
  background: var(--gcl, currentColor); box-shadow: 0 0 9px var(--gcl, currentColor); }
.facet .tick { width: 14px; height: 14px; border-radius: 4px; flex: none;
  border: 1px solid var(--hair); display: grid; place-items: center;
  font-size: 9px; line-height: 1; color: transparent; }
.facet.on .tick { background: var(--accent); border-color: var(--accent); color: #0b0b18; }
.facet .mine { font-size: 9.5px; letter-spacing: .06em; text-transform: uppercase;
  color: var(--accent); opacity: .8; }
.facet.followed { color: #fff; }
.facet.followed .fv { color: #ffd76a; font-size: 11px; }
.facet .grab { margin-left: auto; font-size: 10.5px; color: var(--ink-faint); }
.facet .n + .grab { margin-left: 8px; }

/* the tag tree: a tag may be a little path - games/shooter, vibe/cozy - so the
   folder is written above what lives inside it, a touch indented and joined by
   a thin line the way a file tree is drawn. Your own tags stand on top. */
.tag-hd { margin: 9px 0 2px; padding: 0 9px; font-size: 10px; letter-spacing: .08em;
  text-transform: uppercase; color: var(--ink-faint); }
.facets .tag-hd:first-child { margin-top: 2px; }
/* The three questions - "what you'd do", "how it should feel", "what you'd see"
   - head a line of words and are the point of the card, so they carry more than
   the small print does: the brighter ink, a touch more weight, and the accent
   rule down their left. The tag tree's dividers borrow .tag-hd but not this
   class, so they stay quiet - they only sort one list, they do not lead it. */
.q-hd { margin: 12px 0 5px; padding: 0 9px 0 8px;
  font-size: 10.5px; font-weight: 600; letter-spacing: .12em;
  color: var(--ink); border-left: 2px solid var(--accent); }
.facets .q-hd:first-child { margin-top: 2px; }
.facet.tag .tt { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.facet.tag .tw { width: 12px; height: 12px; flex: none; display: grid;
  place-items: center; font-style: normal; font-size: 9px; line-height: 1;
  color: var(--ink-faint); border-radius: 3px; }
.facet.tag .tw[data-twk] { cursor: pointer; }
.facet.tag .tw[data-twk]:hover { color: var(--ink); background: var(--hair); }
.facet.tag .tick { margin-left: -3px; }
.facet.tag.on .tw, .facet.tag.kin .tw { color: #fff; }
.facet.tag.kin { color: var(--ink); }
.facet.tag.kin .n { color: var(--ink-soft); }
.facet.tag-kid { padding-left: 27px; position: relative; }
.facet.tag-kid::before { content: ''; position: absolute; left: 14px; top: -9px;
  bottom: 50%; width: 1px; background: var(--hair); }
.facet.tag-kid::after { content: ''; position: absolute; left: 14px; top: 50%;
  width: 7px; height: 1px; background: var(--hair); }

.picks { margin-top: 10px; padding-top: 10px;
  border-top: 1px solid var(--hair-soft); display: flex;
  align-items: center; gap: 8px; flex-wrap: wrap; }
.picks button {
  height: 24px; padding: 0 10px; border-radius: 12px; cursor: pointer;
  border: 1px solid var(--hair); background: transparent; color: var(--ink-faint);
  font: 11.5px "Segoe UI", system-ui, sans-serif;
}
.picks button:hover { color: #ff8f9c; border-color: #ff8f9c; }
.picks .sum { font-size: 11px; color: var(--ink-faint); margin-left: auto; }

/* the rows on the right of the library */
.lib-row { position: relative; border-left: 3px solid var(--gcl, var(--hair-soft)); }
.lib-row .nm a { color: inherit; text-decoration: none; }
.lib-row .nm a:hover { color: var(--gcl, #fff); }
.lib-row .gi { font-size: 10.5px; padding: 1px 8px; border-radius: 9px;
  color: var(--gcl, var(--ink-soft)); border: 1px solid var(--gcla, var(--hair-soft));
  margin-left: 8px; }
.lib-row .dt { font-size: 10.5px; color: var(--ink-faint); margin-top: 5px; }
.lib-row .more { margin-left: 8px; font-size: 10.5px; color: var(--ink-faint); }
.tag.rd { background: transparent; border-style: dashed; color: var(--ink-faint); }

/* when nothing matches the picks on the left */
.lib .none { padding: 30px 2px; color: var(--ink-faint); font-size: 13px;
  line-height: 1.6; }
.lib .none b { color: var(--ink); }

/* ============================================ the look-at-it-later shelf ===
   Sits above the library rows, quiet until you actually put something on the
   list. It behaves like a video site's watch-later queue: newest first, one
   click to drop a world, one to empty the whole thing. */
.later-box { margin: 4px 0 14px; }
.lat-head {
  display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap;
  margin-bottom: 9px;
}
.lat-ttl {
  font-size: 12.5px; font-weight: 600; letter-spacing: .06em; color: #9fd9ff;
}
.lat-n { font-size: 11.5px; color: var(--ink-faint); }
.lat-strip {
  display: flex; gap: 10px; overflow-x: auto; padding: 2px 2px 8px;
  /* the playlist strip scrolls sideways past a few cards - its bar is the one
     in theme.css, the same 8px rail every other scroller on the site wears */
}
.lat-row {
  flex: none; width: 108px; text-decoration: none; color: var(--ink);
  display: flex; flex-direction: column; gap: 5px;
}
.lat-row img, .lat-row .ph {
  --ch: 6px; width: 108px; height: 62px; border-radius: 0; object-fit: cover;
  border: 1px solid var(--hair-soft); display: block;
  clip-path: polygon(
    var(--ch) 0, calc(100% - var(--ch)) 0, 100% 50%,
    calc(100% - var(--ch)) 100%, var(--ch) 100%, 0 50%);
}
.lat-row .ph { display: flex; align-items: center; justify-content: center;
  color: var(--ink-faint); font-size: 11px; }
.lat-row:hover img, .lat-row:hover .ph { border-color: #5cb8ff; }
.lat-nm {
  font-size: 11.5px; font-weight: 600; line-height: 1.3;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.lat-row:hover .lat-nm { color: #9fd9ff; }
.lat-by {
  display: block; width: 108px; margin-top: 5px;
  font-size: 10.5px; color: var(--ink-faint); text-decoration: none;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
a.lat-by:hover { color: #9fd9ff; text-decoration: underline; }
.lat-foot { margin-top: 4px; }
.lat-foot button {
  height: 26px; padding: 0 12px; border-radius: 13px; cursor: pointer;
  font-size: 11px; background: transparent; color: var(--ink-faint);
  border: 1px solid var(--hair);
}
.lat-foot button:hover { color: #ff8f9c; border-color: #ff8f9c; }

/* the clock inside a library row, and its little "later" chip */
.lib-row .later-btn { flex: none; }
.lib-row .gi.lat { color: #9fd9ff; border-color: rgba(92, 184, 255, .5); }

/* ================================================== library: playlists ========
   Several lists of your own, side by side with the clocks. The one built in
   ("Look at it later") is just the first of them. */
.facet.pl-facet .pl-ic {
  width: 14px; flex: none; text-align: center;
  font-size: 11px; color: var(--ink-faint); opacity: .9;
}
.facet.pl-facet.on .pl-ic { color: #cfe6ff; opacity: 1; }
.facet.pl-facet .n { margin-left: auto; }
.facet.pl-facet:not(.on) .tick { color: transparent; }

.pl-newbtn {
  width: 100%; margin: 6px 0 0; height: 27px; padding: 0 9px;
  border-radius: 8px; cursor: pointer; text-align: left;
  border: 1px dashed var(--hair); background: transparent;
  color: var(--ink-faint); font: 12px "Segoe UI", system-ui, sans-serif;
}
.pl-newbtn:hover { color: #9fd9ff; border-color: rgba(92, 184, 255, .55); }
.pl-newin {
  width: 100%; margin: 6px 0 0; height: 27px; padding: 0 9px;
  border-radius: 8px; background: var(--card, #14141f); color: var(--ink);
  border: 1px solid rgba(92, 184, 255, .55); outline: none;
  font: 12px "Segoe UI", system-ui, sans-serif;
}

/* the shelves stack: one block per playlist, each with its own row of worlds */
.shelves { display: flex; flex-direction: column; gap: 2px; }

/* the little pen and bin in a playlist's own header */
.lat-ico {
  flex: none; width: 22px; height: 22px; padding: 0; border-radius: 6px;
  cursor: pointer; background: transparent; border: 1px solid transparent;
  color: var(--ink-faint); font-size: 11px; line-height: 1;
}
.lat-ico:hover { color: #9fd9ff; border-color: rgba(92, 184, 255, .45); }
.lat-ico.del:hover { color: #ff8f9c; border-color: rgba(255, 143, 156, .45); }
.lat-share {
  --share-cut: 7px;
  flex: none; min-width: 50px; height: 24px; padding: 0 11px;
  cursor: pointer; border: 1px solid var(--hair); border-radius: 0;
  background: rgba(5, 5, 14, .22); color: #9fd9ff;
  font: 600 10.5px "Segoe UI", system-ui, sans-serif; letter-spacing: .03em;
  clip-path: polygon(var(--share-cut) 0, calc(100% - var(--share-cut)) 0, 100% 50%,
    calc(100% - var(--share-cut)) 100%, var(--share-cut) 100%, 0 50%);
}
.lat-share:hover { color: #e6f6ff; border-color: #5cb8ff; background: rgba(30, 94, 140, .22); }

/* Private lives on the left, public on the right. The shared gradient rim and
   inset surface make one elongated hexagon; the two states only light their own half. */
.pl-visibility {
  position: relative; isolation: isolate;
  display: inline-flex; align-items: stretch; flex: none;
  width: 117.33px; height: 24px; padding: 1px;
  background: linear-gradient(110deg, #d89cff, #a878ff 52%, #e58ad6);
  clip-path: polygon(12px 0, calc(100% - 12px) 0, 100% 50%, calc(100% - 12px) 100%, 12px 100%, 0 50%);
}
.pl-visibility::before {
  content: ""; position: absolute; z-index: 0; inset: 1px; pointer-events: none;
  background: linear-gradient(180deg, var(--popup), var(--page));
  clip-path: polygon(11px 0, calc(100% - 11px) 0, 100% 50%, calc(100% - 11px) 100%, 11px 100%, 0 50%);
}
.pl-visibility::after {
  content: ""; position: absolute; z-index: 2; top: 6px; bottom: 6px; left: 50%;
  width: 1px; transform: translateX(-50%); pointer-events: none;
  background: var(--hair-soft);
}
.pl-visibility button {
  position: relative; z-index: 1; flex: 1 1 0; min-width: 0; min-height: 22px; padding: 0 8px;
  cursor: pointer; border: 0; border-radius: 0; background: transparent !important; color: var(--ink-faint);
  filter: none !important;
  /* hex.css gives every button its own clipped, layered sigil; these two halves
     must remain plain surfaces inside the single silhouette above. */
  clip-path: none !important;
  font: 600 10.5px "Segoe UI", system-ui, sans-serif; letter-spacing: .025em;
}
.pl-visibility button::before,
.pl-visibility button::after { content: none !important; }
.pl-visibility button:hover { color: var(--ink); }
.pl-visibility button[data-pub="false"][aria-pressed="true"],
.pl-visibility button[data-pub="true"][aria-pressed="true"] {
  color: var(--ink);
  background: linear-gradient(135deg, rgba(205, 125, 220, .14), rgba(143, 123, 255, .14)) !important;
  box-shadow: inset 0 0 8px rgba(176, 115, 214, .07);
}
.pl-visibility button:focus-visible {
  position: relative; z-index: 3; outline: 2px solid var(--accent); outline-offset: -3px;
}
.lat-edit {
  flex: none; width: 190px; height: 24px; padding: 0 8px; border-radius: 7px;
  background: var(--card, #14141f); color: var(--ink);
  border: 1px solid rgba(92, 184, 255, .55); outline: none;
  font: 12.5px "Segoe UI", system-ui, sans-serif; font-weight: 600;
}

/* the + on a library row, next to its clock - the same 28px square as the
   clock and the bin, so the row's three sigils are one size */
.lib-row .pl-btn {
  flex: none; width: 28px; height: 28px; padding: 0; border-radius: 50%;
  cursor: pointer; background: transparent; color: var(--ink-faint);
  border: 1px solid var(--hair); font-size: 12.5px; line-height: 1;
}
.lib-row .pl-btn:hover { color: #9fd9ff; border-color: rgba(92, 184, 255, .55); }
.lib-row .gi.pl { color: #cbb6ff; border-color: rgba(185, 162, 255, .45); }

/* ============================================ the shared playlist picker ======
   One panel for the Library and the world page: a tick per playlist, and a box
   to name a new one on the spot. */
.pl-pop {
  position: fixed; z-index: 60; width: 244px; max-height: 60vh; overflow: auto;
  padding: 10px;
  /* a solid sheet, not a pane of glass: the page keeps moving behind it while
     you tick playlists, and any transparency lets that motion show through the
     words. Same --popup the other lists wear. */
  background: var(--popup, #0e0a20); border: 1px solid var(--hair);
  box-shadow: 0 18px 44px rgba(0, 0, 0, .55);
  font: 12.5px "Segoe UI", system-ui, sans-serif; color: var(--ink);
}
.pl-pop[hidden] { display: none; }
.pl-pop-h {
  font-size: 11px; letter-spacing: .06em; text-transform: uppercase;
  color: var(--ink-faint); margin: 0 2px 8px;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.pl-pop-l { display: flex; flex-direction: column; gap: 2px; }
.pl-pop-r {
  display: flex; align-items: center; gap: 8px; width: 100%; height: 30px;
  padding: 0 8px; border-radius: 8px; cursor: pointer; text-align: left;
  background: transparent; border: 1px solid transparent;
  color: var(--ink-soft); font: 12.5px "Segoe UI", system-ui, sans-serif;
}
.pl-pop-r:hover { color: var(--ink); border-color: var(--hair); }
.pl-pop-r.on { color: #fff; background: rgba(185, 162, 255, .16); }
.pl-pop-r .bx {
  width: 14px; height: 14px; flex: none; border-radius: 4px; font-size: 9px;
  border: 1px solid var(--hair); display: grid; place-items: center;
  color: transparent; line-height: 1;
}
.pl-pop-r.on .bx { background: var(--accent, #7c6cff); border-color: var(--accent, #7c6cff); color: #0b0b18; }
.pl-pop-r .pl-ic { flex: none; font-size: 11px; color: var(--ink-faint); }
.pl-pop-r .pl-pop-nm {
  flex: 1 1 auto; min-width: 0; overflow: hidden;
  text-overflow: ellipsis; white-space: nowrap;
}
.pl-pop-r .pl-pop-c { flex: none; font-size: 11px; color: var(--ink-faint); }
.pl-pop-r.on .pl-pop-c { color: #fff; opacity: .75; }
.pl-pop-new { margin-top: 8px; padding-top: 8px; border-top: 1px solid var(--hair-soft); }
.pl-pop-new input {
  width: 100%; height: 28px; padding: 0 9px; border-radius: 8px;
  background: transparent; color: var(--ink); outline: none;
  border: 1px solid var(--hair); font: 12.5px "Segoe UI", system-ui, sans-serif;
}
.pl-pop-new input:focus { border-color: rgba(92, 184, 255, .55); }
.pl-pop-f {
  display: flex; align-items: center; justify-content: space-between;
  gap: 8px; margin-top: 8px;
}
.pl-pop-f a { font-size: 11px; color: #9fd9ff; text-decoration: none; }
.pl-pop-f a:hover { text-decoration: underline; }
.pl-pop-f button {
  height: 24px; padding: 0 10px; border-radius: 12px; cursor: pointer;
  background: transparent; color: var(--ink-faint); border: 1px solid var(--hair);
  font: 11.5px "Segoe UI", system-ui, sans-serif;
}
.pl-pop-f button:hover { color: #ff8f9c; border-color: #ff8f9c; }

/* one world inside a playlist's strip: the picture, and an x that takes it
   out of that one playlist only */
.lat-cell { position: relative; flex: none; }
.lat-cell .lat-x {
  position: absolute; top: -6px; right: -6px; width: 20px; height: 20px;
  padding: 0; border-radius: 50%; cursor: pointer; line-height: 1;
  font-size: 10px; color: var(--ink-faint);
  background: var(--card, #14141f); border: 1px solid var(--hair);
  opacity: 0; transition: opacity .12s;
}
.lat-cell:hover .lat-x, .lat-cell .lat-x:focus { opacity: 1; }
.lat-cell .lat-x:hover { color: #ff8f9c; border-color: #ff8f9c; }

/* the header of a shelf now carries a pen and a bin, so line it up on the
   middle rather than on the text baseline */
.lat-head { align-items: center; gap: 8px; }
.lat-n { margin-left: auto; }

/* ============================================ hold the picture to invite ===
   A short click on a world's picture opens that world on vrchat.com. Hold it
   down instead and this purple hexagon draws itself over the picture, corner
   after corner: the bar is one dash walking around the outline, the six sparks
   sit on the corners and light up as the bar reaches them, and the
   six-pointed star inside it turns slowly the whole time. When the ring closes
   the invitation is already on its way and the hexagon blooms away. Let go
   early and it just melts, without doing anything.

   atlas.js builds the picture and this hexagon alike; everything here is only
   about how it looks while the press is running. */

.thumb .shot { user-select: none; -webkit-user-select: none; }
.thumb .shot, .thumb .hold-hex { touch-action: manipulation;
  -webkit-touch-callout: none; }

/* the picture goes dark while the press is running, with the world's own
   colour glowing at the middle of it, so the hexagon is the only thing left
   talking and it is talking in this world's colour */
.thumb .shot::after {
  content: ""; position: absolute; inset: 0; pointer-events: none; opacity: 0;
  background: radial-gradient(circle at 50% 50%,
    var(--hx-glow, rgba(150, 60, 255, .34)) 0%,
    rgba(6, 2, 16, .46) 52%, rgba(4, 1, 12, .62) 100%);
  transition: opacity .22s ease;
}
.thumb.holding .shot { filter: brightness(.5) saturate(.6); }
.thumb.holding .shot::after { opacity: 1; }

/* the hexagon's box, sitting right in the middle of the picture */
.thumb .hold-hex {
  position: absolute; left: 50%; top: 50%; width: 62%; max-width: 168px;
  aspect-ratio: 1 / 1; z-index: 4; pointer-events: none;
  opacity: 0; transform: translate(-50%, -50%) scale(.86);
  transition: opacity .16s ease, transform .22s cubic-bezier(.2, .9, .3, 1.3);
  filter: drop-shadow(0 0 16px var(--hx-halo, rgba(150, 60, 255, .5)));
}
.thumb .hold-hex.on { opacity: 1; transform: translate(-50%, -50%) scale(1); }
.thumb .hold-hex.gone { opacity: 0; transform: translate(-50%, -50%) scale(.9);
  transition-duration: .24s; }

/* ================================================== how things are laid out ===
   The little switch in the toolbar writes body[data-view] and this stylesheet
   does the rest, so nothing on the page has to be rebuilt when you change it.
   Three ways to look at the same worlds:
     cards   - the wall the Atlas has always been: a picture, and the text
               under it. The Library's cards give every row a wide picture.
     list    - one world to a row, with a small picture beside it. This is
               what the Library has always been.
     compact - bare rows and no pictures at all, so a great many worlds fit
               on the screen at once. The numbers, the colour and the buttons
               all stay; only the pictures go.
   A tab that carries no switch is untouched: this only ever fires when the
   page says which view it is in. */

/* the switch itself, sitting in the toolbar like the sort buttons do */
.vsw {
  display: inline-flex; align-items: center; border: 1px solid var(--hair-soft);
  border-radius: 7px; overflow: hidden;
}
.vsw button {
  height: 25px; padding: 0 10px; border: 0; border-radius: 0; cursor: pointer;
  background: transparent; color: var(--ink-faint);
  font: 11.5px "Segoe UI", system-ui, sans-serif;
}
.vsw button + button { border-left: 1px solid var(--hair-soft); }
.vsw button:hover { color: var(--ink); background: var(--accent-soft); }
.vsw button.on { color: #fff; background: var(--accent-soft); }

/* ------------------------------------------------------------ the Atlas ---- */
body[data-view="list"] #grid { grid-template-columns: minmax(0, 1fr); gap: 9px; }
/* The card is a grid here, but the little genre line that every card carries
   is drawn by ::before - and being first in the box it would take the picture's
   column. So every part is placed by hand and the line is told to span them
   all. */
body[data-view="list"] #grid .item {
  display: grid; grid-template-columns: 178px minmax(0, 1fr); align-items: stretch;
}
body[data-view="list"] #grid .item::before { grid-column: 1 / -1; grid-row: 1; }
body[data-view="list"] #grid .item .thumb {
  grid-column: 1; grid-row: 2; aspect-ratio: auto; min-height: 104px;
  border-bottom: 0; border-right: 1px solid var(--hair-soft);
}
body[data-view="list"] #grid .item .body { grid-column: 2; grid-row: 2; }
body[data-view="list"] #grid .item .body { padding: 12px 16px 12px 18px; }
body[data-view="list"] #grid .item .title { font-size: 15px; }

body[data-view="compact"] #grid { grid-template-columns: minmax(0, 1fr); gap: 4px; }
body[data-view="compact"] #grid .item { display: block; }
body[data-view="compact"] #grid .item .thumb { display: none; }
body[data-view="compact"] #grid .item .body {
  display: flex; flex-direction: row; align-items: center; gap: 14px;
  padding: 7px 14px;
}
body[data-view="compact"] #grid .item .title {
  flex: 1 1 auto; min-width: 0; order: 0; font-size: 13.5px;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
body[data-view="compact"] #grid .item .by { order: 1; white-space: nowrap; }
body[data-view="compact"] #grid .item .gline { order: 2; }
body[data-view="compact"] #grid .item .foot { order: 3; margin: 0; padding: 0; }
body[data-view="compact"] #grid .item .disc,
body[data-view="compact"] #grid .item .dates { display: none; }
body[data-view="compact"] #grid .item .stats { text-align: right; }

/* --------------------------------------------------------- the Library ---- */
body[data-view="cards"] .lib { flex-direction: row; flex-wrap: wrap; }
body[data-view="cards"] .lib .lib-row {
  flex: 1 1 268px; flex-wrap: wrap; align-content: flex-start;
  align-items: flex-start; gap: 10px;
}
body[data-view="cards"] .lib .lib-row > img,
body[data-view="cards"] .lib .lib-row > .ph {
  flex: 1 1 100%; width: 100%; height: auto; aspect-ratio: 16 / 9; order: 0;
}
body[data-view="cards"] .lib .lib-row > .info { flex: 1 1 100%; order: 1; }
body[data-view="cards"] .lib .lib-row > .pl-btn { order: 2; }
body[data-view="cards"] .lib .lib-row > .later-btn { order: 3; }
body[data-view="cards"] .lib .lib-row > .drop { order: 4; }
body[data-view="cards"] .lib .none { flex: 1 1 100%; }

body[data-view="compact"] .lib { gap: 5px; padding: 12px 0 16px; }
body[data-view="compact"] .lib .lib-row { padding: 7px 12px; gap: 10px; }
body[data-view="compact"] .lib .lib-row > img,
body[data-view="compact"] .lib .lib-row > .ph { display: none; }
body[data-view="compact"] .lib .lib-row .dt { display: none; }

/* ------------------------------- the same sigil, laid flat: Library cards --- */

/* The Library's card view holds the very same thing the Atlas's wall holds - a
   world - so its cards are cut and rimmed the very same way, in the same genre
   colour. The row's own frame goes: the cut takes its place. List and compact
   rows keep their plain frame, because there a row is a line of a list rather
   than a card. */
body[data-view="cards"] .lib .lib-row {
  --hx-card-cut: 12px;
  position: relative; z-index: 0;
  border: 0; border-radius: 0;
  background: linear-gradient(180deg, var(--gcla, transparent) 0%,
                                       transparent 58%), var(--card);
  clip-path: polygon(
    var(--hx-card-cut) 0, calc(100% - var(--hx-card-cut)) 0,
    100% var(--hx-card-cut), 100% calc(100% - var(--hx-card-cut)),
    calc(100% - var(--hx-card-cut)) 100%, var(--hx-card-cut) 100%,
    0 calc(100% - var(--hx-card-cut)), 0 var(--hx-card-cut));
  transition: filter .15s;
}
/* the rim: a full-size sheet of the genre edge, punched hollow in the middle,
   so what is left is an outline that turns the card's own corners */
body[data-view="cards"] .lib .lib-row::after {
  content: ""; position: absolute; inset: 0; z-index: -1; pointer-events: none;
  padding: 1.3px;
  background: var(--hx-edge);
  clip-path: polygon(
    var(--hx-card-cut) 0, calc(100% - var(--hx-card-cut)) 0,
    100% var(--hx-card-cut), 100% calc(100% - var(--hx-card-cut)),
    calc(100% - var(--hx-card-cut)) 100%, var(--hx-card-cut) 100%,
    0 calc(100% - var(--hx-card-cut)), 0 var(--hx-card-cut));
  -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  -webkit-mask-composite: xor;
  mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  mask-composite: exclude;
}
body[data-view="cards"] .lib .lib-row:hover {
  filter: drop-shadow(0 8px 22px -10px var(--gcl, var(--accent)));
}
body[data-view="cards"] .lib .lib-row:hover::after { background: var(--hx-edge-lit); }

/* a compact row is only a few pixels tall, so the same cut would swallow it -
   the cut is made smaller there instead of being taken away */
body[data-view="compact"] #grid .item { --hx-card-cut: 7px; }

/* the folded-away tail of a line of words */
.facet.more { border-style: dashed; justify-content: center; font-size: 11px;
  color: var(--ink-faint); }
.facet.more:hover { color: var(--ink); }

/* ------------------------------------------------ the order a spell arrives in
   "Best match" is semantic.js's ranking, and it means nothing without a phrase
   to rank against - so the button stays out of the sort row until there is
   one. The Atlas puts .has-query and .sort-match on <body> (see atlas.js); the
   second of the two keeps the button visible once it IS the order in use, so
   an open page never shows no order at all. */
body:not(.has-query):not(.sort-match) .sub [data-s="match"] { display: none; }
