/* ============================================================
   User Stories: card wall
   Scoped entirely under .stories-* so it never leaks into
   other docs pages. Tuned for the ayu (dark) theme but degrades
   gracefully on light themes via var() fallbacks.

   TYPE IS IN em, NOT rem. mdbook sets :root { font-size: 62.5% }
   so 1rem is 10px in this book, not the browser's 16px. Sizing
   here in rem renders every label at 60% of its intended size,
   which is exactly what happened: a 1.34rem title came out at
   13px next to 16px body text.

   The card is not its own type system either. It borrows the book's,
   and there are exactly three stops:
     1em      body prose (1.6rem, 16px) on every other page
     0.875em  the sidebar, inline code and menu size (14px)
     1.4em    a card heading, between mdbook's h3 (1.17em) and h2 (1.5em)
   Excerpts are 1em because they are prose. Every label and byline
   is 0.875em because that is what the sidebar and the menus use, so a
   card sits on the page instead of looking like a widget with its own
   fonts. Anything new here picks one of those three, never a number
   that only looks right in isolation.
   ============================================================ */

.stories-wrap {
  --mono: var(--mono, "Source Code Pro", ui-monospace, SFMono-Regular, Menlo, monospace);
  margin: 1.5rem 0 3rem;
}

/* ---- the wall gets its own measure ---------------------------------- */
/* mdbook caps every page at --content-max-width (750px). That is a good
   reading measure for prose and a terrible one for a card grid: at 750px a
   330px-minimum grid can only ever be two lanes, which is why the wall read
   like a column of long posts. The wall is the only page that needs more, so
   widen just this page and leave the story pages at the 750px measure, which
   is exactly where long text belongs.
   :has() is used rather than a class on <main> because mdbook owns the page
   template; no build step can add an attribute to it. Browsers without :has()
   simply keep the narrow wall, which is the old behaviour, not a broken one. */
.content main:has(> .stories-wrap) {
  max-width: 1180px;
}

/* ---- story pages ---------------------------------------------------- */
/* A story page is ordinary prose at the normal measure. The only chrome it
   needs is the meta line above the title and the author line under the lede,
   both of which are the same markdown blocks the card uses, so they are
   translated once and rendered in both places. */
.story-meta {
  font-family: var(--mono);
  font-size: 0.875em;
  letter-spacing: 0.09em;
  text-transform: uppercase;
}
.story-meta p {
  margin: 0;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.6rem;
}
.story-meta em {
  display: inline-flex;
  align-items: center;
  gap: 0.45rem;
  font-style: normal;
  color: var(--fg);
  opacity: 0.9;
}
.story-meta em::before {
  content: "";
  width: 7px;
  height: 7px;
  border-radius: 2px;
  background: var(--links, #0096cf);
  box-shadow: 0 0 8px var(--links, #0096cf);
  flex: none;
}
.story-meta strong {
  color: var(--links, #0096cf);
  border: 1px solid var(--links, #0096cf);
  border-radius: 999px;
  padding: 0.14rem 0.6rem;
  white-space: nowrap;
  font-weight: 600;
}
.story-author {
  margin: 1.1rem 0 1.8rem;
  padding-bottom: 0.9rem;
  border-bottom: 1px solid var(--table-border-color, rgba(255, 255, 255, 0.1));
  font-family: var(--mono);
  font-size: 0.875em;
  font-weight: 600;
  color: var(--links, #0096cf);
}
.story-author p { margin: 0; }

/* ---- intro strip ------------------------------------------------ */
/* The lede is markdown now (xgettext cannot see text inside an HTML block),
   so it has no class of its own: it is the first paragraph in the wrapper. */
.stories-wrap > p:first-of-type {
  max-width: 62ch;
  font-size: 1.06em;
  line-height: 1.7;
  color: var(--fg);
}

/* ---- filter pills (Hermes-style) --------------------------------- */
.stories-filters {
  display: flex;
  flex-wrap: wrap;
  gap: 0.6rem;
  margin: 1.6rem 0 0.2rem;
}
.filter-pill {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.42rem 1.05rem;
  border: 1px solid var(--table-border-color, rgba(255, 255, 255, 0.16));
  border-radius: 999px;
  background: transparent;
  color: var(--fg);
  font-family: var(--mono);
  font-size: 0.875em;
  font-weight: 600;
  letter-spacing: 0.04em;
  cursor: pointer;
  transition: background 0.18s ease, color 0.18s ease, border-color 0.18s ease, transform 0.18s ease;
}
/* The pill label is markdown too, so it ships wrapped in a <p>. The filter
   script flattens it on load; this keeps the first paint tidy. */
.filter-pill p { margin: 0; }
.filter-pill b {
  font-size: 0.75em;
  font-weight: 700;
  opacity: 0.6;
}
.filter-pill:hover {
  border-color: var(--links, #0096cf);
  color: var(--links, #0096cf);
  transform: translateY(-1px);
}
.filter-pill.active {
  background: var(--links, #0096cf);
  border-color: var(--links, #0096cf);
  color: var(--bg, #0f1419);
}
.filter-pill.active b { opacity: 0.85; }
.story-card.is-hidden { display: none; }

/* ---- the wall --------------------------------------------------- */
.stories-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(330px, 1fr));
  gap: 1.5rem;
  margin-top: 2rem;
  align-items: stretch;
}

/* Each card is an <a> that opens the story's own page. It used to be a
   <details> that expanded in place, which forced the whole story into a
   narrow column: the card is a highlight now, and the story is read at a
   proper measure on its own page. */
/* One neutral card. There used to be six accent hues here, each colouring a
   top bar, a category pill, a glowing dot, the title on hover and the hover
   ring: 23 cards, six colours, and a reader had to work out whether the colour
   meant anything. It did not, beyond a category the label already spells out,
   so colour is gone and the title carries the card. */
.story-card {
  position: relative;
  display: flex;
  flex-direction: column;
  background: var(--theme-popup-bg, #14191f);
  border: 1px solid var(--table-border-color, rgba(255, 255, 255, 0.09));
  border-radius: 10px;
  overflow: hidden;
  transition: transform 0.18s ease, border-color 0.18s ease;
  animation: story-rise 0.5s ease both;
}
.story-card:nth-child(1) { animation-delay: 0.02s; }
.story-card:nth-child(2) { animation-delay: 0.08s; }
.story-card:nth-child(3) { animation-delay: 0.14s; }
.story-card:nth-child(4) { animation-delay: 0.20s; }
.story-card:nth-child(5) { animation-delay: 0.26s; }
.story-card:nth-child(6) { animation-delay: 0.32s; }
@keyframes story-rise {
  from { opacity: 0; transform: translateY(14px); }
  to   { opacity: 1; transform: translateY(0); }
}

.story-card:hover {
  transform: translateY(-2px);
  border-color: rgba(255, 255, 255, 0.24);
}

/* ---- card face --------------------------------------------------- */
/* The anchor is the whole face, so the entire card is one click target and
   one link for a crawler to follow. mdbook's prose link styles are undone
   here because this is a card, not a sentence. */
.story-card .story-link {
  display: flex;
  flex-direction: column;
  gap: 0.7rem;
  padding: 1.45rem 1.5rem 1.35rem;
  color: inherit;
  text-decoration: none;
  flex: 1 1 auto;
}
.story-card .story-link:hover { text-decoration: none; }

/* Meta line: source on the left, category pill on the right. The blocks are
   plain paragraphs (a heading would bring mdbook's permalink anchor with it,
   and a nested <a> closes the card link), so they are styled by position.
   Scoped to .story-card because the same blocks render on a story page, where
   they sit left-aligned above the title instead of pushed to both edges. */
/* The category is a label, not a badge. No pill, no outline, no glowing dot:
   it is one quiet line of metadata above the title. The source platform went
   with it, since it is provenance and the story page carries it beside the
   link to the original post. */
.story-card .story-meta {
  margin: 0;
  font-family: var(--mono);
  font-size: 0.875em;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--fg);
  opacity: 0.55;
}

.story-card .story-title {
  font-size: 1.4em;
  font-weight: 800;
  line-height: 1.28;
  letter-spacing: -0.01em;
  color: var(--fg);
}
.story-card .story-title p { margin: 0; }

/* The excerpt is clamped, which is the point of a highlight card: the card
   shows what the story is about, the page carries the story. -webkit-line-clamp
   is the only cross-browser clamp that works on wrapped text. */
.story-card .story-excerpt {
  font-size: 1em;
  line-height: 1.55;   /* a little more air than .content p: cards are scanned */
  color: var(--fg);
  opacity: 0.85;
}
.story-card .story-excerpt p {
  margin: 0;
  display: -webkit-box;
  -webkit-line-clamp: 3;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

/* Author line sits at the bottom of every card, so cards of unequal text
   still line up along a row. */
.story-card .story-author {
  margin-top: auto;
  padding-top: 0.55rem;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.6rem;
  font-family: var(--mono);
  font-size: 0.875em;
  font-weight: 600;
  color: var(--fg);
  opacity: 0.6;
}
.story-card .story-author p { margin: 0; }
/* The read affordance is drawn by CSS: an arrow is chrome, and a word here
   would mean translating "read" into six languages to say nothing. */
.story-card .story-author::after {
  content: "\2192";
  color: var(--fg);
  opacity: 0.6;
  transition: transform 0.22s ease, opacity 0.18s ease, color 0.18s ease;
}
.story-card:hover .story-author::after {
  opacity: 1;
  transform: translateX(4px);
}

/* ---- share CTA --------------------------------------------------- */
.stories-share {
  margin-top: 2.6rem;
  padding: 1.6rem 1.5rem;
  border: 1px dashed var(--table-border-color, rgba(255, 255, 255, 0.18));
  border-radius: 12px;
  background: var(--theme-popup-bg, rgba(255, 255, 255, 0.02));
  text-align: center;
}
.stories-share h2 {
  margin: 0 0 0.5rem;
  font-size: 1.35em;
  border: none;
}
.stories-share p { margin: 0.3rem auto 1rem; max-width: 52ch; }
.stories-share .share-row {
  display: flex;
  flex-wrap: wrap;
  gap: 0.7rem;
  justify-content: center;
}
.stories-share .share-row > p { margin: 0; }
.stories-share .share-row a {
  padding: 0.5rem 1.1rem;
  border: 1px solid var(--links, #0096cf);
  border-radius: 8px;
  font-family: var(--mono);
  font-size: 0.875em;
  font-weight: 600;
  text-decoration: none;
  transition: background 0.18s ease, color 0.18s ease, transform 0.18s ease;
}
.stories-share .share-row a:hover {
  background: var(--links, #0096cf);
  color: var(--bg, #0f1419);
  transform: translateY(-2px);
  text-decoration: none;
}

/* ---- menu bar ---------------------------------------------------- */
/* custom.js appends the language select to .menu-bar itself, not to
   .right-buttons, so it inherits none of the icon row's spacing. As a
   native select it also drew a bordered pill, the only boxed control in
   a row of bare icons, and it sat flush against the screen edge: the
   menu bar runs edge to edge (negative page padding) and .right-buttons
   carries the 15px inset that the select, being its sibling, never got.
   Styled here as a text control instead: the icon colour, the 0.875em
   menu size, no box, and the same inset as the icons beside it.
   The chevron is an inline SVG because a select cannot carry a ::after.
   Its grey is the dark themes' --icons (#737480) and still reads on the
   light ones, where the icons are dark. */
.menu-bar #lang-switcher {
  align-self: center;
  height: auto;
  margin: 0 12px 0 2px;
  padding: 0 13px 0 2px;
  font-family: var(--mono);
  font-size: 0.875em;
  font-weight: 600;
  letter-spacing: 0.06em;
  line-height: 1;
  color: var(--icons);
  background-color: transparent;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='8' height='5' viewBox='0 0 8 5'%3E%3Cpath d='M0 0l4 5 4-5z' fill='%23737480'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right center;
  background-size: 8px 5px;
  border: 0;
  border-radius: 0;
  opacity: 1;
  cursor: pointer;
  -webkit-appearance: none;
  appearance: none;
}
.menu-bar #lang-switcher:hover { color: var(--icons-hover); }

/* Seven things share a 50px row: menu, theme, search, title, print,
   GitHub and the language switcher. mdbook's own responsive rule stops
   at 420px and only trims icon padding, so from 421 to 620px the row was
   cramped and the two right-hand icons sat 6px apart. Tighten the
   gutters here, and stop the bar wrapping: with flex-wrap on, the last
   child (the switcher) dropped onto a second row on a narrow phone and
   the header doubled in height. Below 400px the title takes a step down
   so it truncates with an ellipsis instead of squeezing the icons. */
@media (max-width: 620px) {
  #mdbook-menu-bar { flex-wrap: nowrap; }
  #mdbook-menu-bar .fa-svg,
  #mdbook-menu-bar .icon-button { padding: 0 7px; }
  #mdbook-menu-bar .right-buttons { margin: 0 8px; }
  .menu-bar #lang-switcher { margin-right: 10px; }
}
@media (max-width: 400px) {
  #mdbook-menu-bar .menu-title { font-size: 1.9rem; }
}

/* ---- share row --------------------------------------------------- */
/* Built by theme/custom.js on a story page only, right under the author line.
   The wall used to repeat this row on all 23 cards: five links of small text
   each, 115 controls competing with the titles, all pointing at the same five
   places. The story is what gets shared, so the row lives where the story is
   read, and the wall stays a wall. */
.story-share {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.4rem;
  font-family: var(--mono);
  font-size: 0.875em;
  letter-spacing: 0.06em;
  text-transform: uppercase;
}
.story-share a,
.story-share button {
  margin: 0;
  padding: 0.2rem 0.5rem;
  border: 1px solid var(--table-border-color, rgba(255, 255, 255, 0.14));
  border-radius: 999px;
  background: none;
  color: var(--fg);
  font: inherit;
  letter-spacing: inherit;
  text-transform: inherit;
  text-decoration: none;
  opacity: 0.66;
  cursor: pointer;
  transition: opacity 0.18s ease, border-color 0.18s ease, color 0.18s ease;
}
.story-share a:hover,
.story-share button:hover {
  opacity: 1;
  border-color: var(--links, #0096cf);
  color: var(--links, #0096cf);
  text-decoration: none;
}
.story-share button:disabled { cursor: default; }

/* Story page: the author line already ends the chrome with a rule and 1.8rem
   of air below it. Pull the row up into that gap so the share pills read as
   the byline's last line rather than as a stranded bar. */
.story-share-page {
  margin: -1.1rem 0 1.9rem;
}
