/* ============================================================================
   CrystalShards - Mineral design system

   Art direction is taken from the subject: shards are broken crystal, so the
   surfaces are cut rather than rounded, edges are hairlines, and the accent is
   the beryl green of the specimen photography rather than an arbitrary brand
   colour. Type is the IBM Plex superfamily: Condensed carries the vertical,
   cut feel in headings, Sans does the reading, Mono carries versions and
   counts because they are data, not prose.

   Light is the default because the sibling documentation site is a long-form
   reading surface. Dark is the same system with the polarity inverted, and is
   honoured automatically via prefers-color-scheme.

   Accessibility decisions that are load bearing, do not undo them casually:
   - --focus is 2px at >= 3:1 against both grounds. Never set outline:none
     without an equivalent replacement.
   - --edge-strong is the border for anything whose boundary is the only
     indicator of a control. Plain --edge is decorative and deliberately faint.
   - Interactive targets get --tap (24px) minimum height.
   - Links inside prose are underlined; the accent alone does not carry meaning.
   ========================================================================= */

:root {
  /* ---- ground and ink -------------------------------------------------- */
  --paper:        #f4f6f6;
  --surface:      #ffffff;
  --surface-sunk: #eef1f1;
  --ink:          #0e1416;   /* 17.13:1 on --paper */
  --ink-muted:    #55666b;   /* 6.05:1 on --surface */
  --ink-faint:    #5f7075;   /* 5.17:1 on --surface, 4.77:1 on --paper */

  /* ---- edges ----------------------------------------------------------- */
  --edge:         #d8dfe0;   /* decorative hairline */
  --edge-strong:  #7b8e92;   /* 3.43:1 on --surface, 3.02:1 on --surface-sunk */

  /* ---- accent, sampled from the specimen ------------------------------- */
  --beryl:        #0d6f68;   /* 6.02:1 on --surface */
  --beryl-deep:   #08423e;
  --beryl-wash:   #e3efee;
  --on-beryl:     #ffffff;   /* 6.02:1 on --beryl */

  /* ---- status ---------------------------------------------------------- */
  --ok:           #1e7e34;   /* 5.14:1 with white */
  --warn:         #a15c00;   /* 4.62:1 on --surface */
  --danger:       #b3261e;   /* 5.94:1 with white */
  --ore:          #8a5a2b;   /* metadata icon tint, 5.28:1 on --surface */

  /* ---- focus ----------------------------------------------------------- */
  --focus:        #0d6f68;
  --focus-ring:   0 0 0 2px var(--surface), 0 0 0 4px var(--focus);

  /* ---- type ------------------------------------------------------------ */
  --sans: "IBM Plex Sans", system-ui, -apple-system, "Segoe UI", sans-serif;
  --cond: "IBM Plex Sans Condensed", var(--sans);
  --mono: "IBM Plex Mono", ui-monospace, "SF Mono", Menlo, monospace;

  --step--1: 0.8125rem;
  --step-0:  1rem;
  --step-1:  1.125rem;
  --step-2:  clamp(1.25rem, 1.1rem + 0.6vw, 1.5rem);
  --step-3:  clamp(1.5rem, 1.3rem + 1vw, 1.9rem);
  --step-4:  clamp(2.1rem, 1.6rem + 2.4vw, 3.1rem);
  --step-5:  clamp(2.6rem, 1.8rem + 3.6vw, 3.9rem);

  /* ---- space and geometry ---------------------------------------------- */
  --gap:   1.125rem;
  --pad:   1.375rem;
  --bay:   4rem;
  --shell: 74rem;
  --facet: 14px;   /* the cut corner */
  --tap:   24px;   /* WCAG 2.2 target minimum */
}

@media (prefers-color-scheme: dark) {
  :root {
    --paper:        #070a0b;
    --surface:      #101718;
    --surface-sunk: #0b1112;
    --ink:          #e9f1f0;   /* 17.31:1 on --paper */
    --ink-muted:    #9fb2b3;   /* 8.24:1 on --surface */
    --ink-faint:    #83989a;   /* 5.94:1 on --surface */

    --edge:         #222e30;
    --edge-strong:  #5b6e70;   /* 3.38:1 on --surface, 3.10:1 on --surface-sunk */

    --beryl:        #4fd6c4;   /* 10.15:1 on --surface */
    --beryl-deep:   #1d6f66;
    --beryl-wash:   #14302e;
    --on-beryl:     #04221f;   /* 9.37:1 on --beryl */

    --ok:           #4ade80;
    --warn:         #f0b357;
    --danger:       #ff7b72;
    --ore:          #d9a066;

    --focus:        #4fd6c4;
    --focus-ring:   0 0 0 2px var(--paper), 0 0 0 4px var(--focus);
  }
}

/* ============================================================================
   Base
   ========================================================================= */

*, *::before, *::after { box-sizing: border-box; }

html { -webkit-text-size-adjust: 100%; }

/* A column so the footer can be pushed to the bottom of a short page. Without
   it a page with little content leaves the footer floating mid-viewport with
   dead background beneath it, which reads as a broken page rather than a short
   one. `main` takes the slack, so the footer sits at the bottom of the viewport
   when content is short and directly after content when it is not.

   100dvh rather than 100vh: on mobile browsers vh is the tallest viewport, so
   vh leaves the footer below the fold behind the address bar on first paint. */
body {
  margin: 0;
  min-height: 100dvh;
  display: flex;
  flex-direction: column;
  background: var(--paper);
  color: var(--ink);
  font: 400 var(--step-0)/1.6 var(--sans);
  -webkit-font-smoothing: antialiased;
}

body > main { flex: 1 0 auto; }
body > .site-footer { flex-shrink: 0; }

h1, h2, h3, h4 {
  font-family: var(--cond);
  font-weight: 700;
  line-height: 1.08;
  letter-spacing: -0.015em;
  margin: 0 0 0.5em;
}
h1 { font-size: var(--step-4); }
h2 { font-size: var(--step-3); }
h3 { font-size: var(--step-2); }
h4 { font-size: var(--step-1); }

p { margin: 0 0 1em; }

a { color: var(--beryl); text-underline-offset: 0.18em; }

/* Prose links carry an underline: the accent alone is not a sufficient cue. */
p a, li a, .shard-description a, .readme-content a { text-decoration: underline; }

/* Navigational and component links opt out, they have their own affordances. */
.nav-link, .logo, .shard-name a, .shard-title a, .view-all-link,
.pagination-link, .dependency-link, .footer-links a, .link-list a,
.button, .search-button { text-decoration: none; }

img { max-width: 100%; height: auto; display: block; }

code, kbd, samp { font-family: var(--mono); font-size: 0.92em; }

/* One focus treatment, everywhere. */
:where(a, button, input, select, textarea, summary, [tabindex]):focus-visible {
  outline: 2px solid var(--focus);
  outline-offset: 2px;
  border-radius: 1px;
}

::selection { background: var(--beryl); color: var(--on-beryl); }

/* ============================================================================
   Primitives
   ========================================================================= */

.container { max-width: var(--shell); margin: 0 auto; padding: 0 1.5rem; }

/* The cut corner. One clipped bevel, never a uniform radius. */
.facet {
  clip-path: polygon(0 0, calc(100% - var(--facet)) 0, 100% var(--facet), 100% 100%, 0 100%);
}

.skip-link {
  position: absolute;
  left: 0.5rem;
  top: -3rem;
  z-index: 100;
  padding: 0.7rem 1.1rem;
  background: var(--beryl);
  color: var(--on-beryl);
  font-weight: 600;
  transition: top 0.12s ease-out;
}
.skip-link:focus { top: 0.5rem; }

.visually-hidden {
  position: absolute !important;
  width: 1px; height: 1px;
  margin: -1px; padding: 0; border: 0;
  clip-path: inset(50%);
  overflow: hidden; white-space: nowrap;
}

.text-muted { color: var(--ink-muted); }

/* ============================================================================
   Header
   ========================================================================= */

.site-header { background: var(--surface); border-bottom: 1px solid var(--edge); }

/* Three columns rather than a flex row, so the search field is centred on the
   bar rather than sitting wherever the brand and menu widths happen to leave
   it.

   The outer tracks are 1fr and 1fr, NOT auto and auto. With auto columns the
   middle track is centred in whatever space the brand and menu leave over, and
   since the wordmark and the nav links are different widths that is not the
   centre of anything. Equal outer tracks put the field on the real centre line
   and keep it there as the menu changes length. */
.navbar {
  display: grid;
  /* The centre track carries the width. A percentage width on the field inside
     an `auto` track is circular and collapses to content, which rendered the
     bar at 238px instead of the intended 512. */
  grid-template-columns: 1fr minmax(0, 32rem) 1fr;
  align-items: center;
  gap: 2rem;
  max-width: var(--shell); margin: 0 auto; padding: 0 1.5rem;
  min-height: 4.125rem;
}

.navbar-brand { display: flex; align-items: center; justify-self: start; }

.logo {
  display: inline-flex; align-items: center; gap: 0.65rem;
  font: 700 1.3rem/1 var(--cond); letter-spacing: 0.005em;
  color: var(--ink);
}
/* The mark is a drawn hexagonal section, not an emoji. */
.logo::before {
  content: "";
  width: 1.32rem; height: 1.55rem;
  background: var(--beryl);
  clip-path: polygon(50% 0, 100% 27%, 100% 73%, 50% 100%, 0 73%, 0 27%);
}

.navbar-menu { display: flex; flex-wrap: wrap; gap: 1.6rem; justify-self: end; }

.nav-link {
  display: inline-flex; align-items: center;
  min-height: var(--tap);
  color: var(--ink-muted);
  font-size: 0.925rem; font-weight: 500;
  border-bottom: 2px solid transparent;
}
.nav-link:hover { color: var(--ink); border-bottom-color: var(--beryl); }

/* ============================================================================
   Intro band

   Not a hero. A registry landing page earns its keep by getting you to a
   package, so this stays one screen-inch tall and the package lists start
   near the top. Search is in the masthead, reachable from every page.
   ========================================================================= */

.intro {
  display: flex; flex-wrap: wrap; align-items: end; gap: 1.5rem 3rem;
  padding: clamp(1.75rem, 3.5vw, 2.75rem) 0 clamp(1.25rem, 2.5vw, 1.75rem);
  border-bottom: 1px solid var(--edge);
}
.intro-copy { flex: 1 1 28rem; min-width: 0; }

.intro-title {
  font-size: var(--step-4); margin: 0 0 0.35em;
}
.intro-title .accent { color: var(--beryl); }

.intro-lede {
  font-size: var(--step-1); color: var(--ink-muted);
  max-width: 54ch; margin: 0;
}

.intro-stats { display: flex; gap: 2.5rem; margin: 0; }
.intro-stats div { display: flex; flex-direction: column-reverse; }
.intro-stats dt {
  font: 400 0.7rem/1.4 var(--mono);
  letter-spacing: 0.13em; text-transform: uppercase; color: var(--ink-muted);
}
.intro-stats dd {
  margin: 0; font: 700 var(--step-3)/1 var(--cond); color: var(--ink);
}

/* ---- masthead search --------------------------------------------------- */

.search-form { display: flex; max-width: 34rem; border: 1px solid var(--edge-strong); background: var(--surface); }
.search-form:focus-within { outline: 2px solid var(--focus); outline-offset: 2px; }
.search-input {
  flex: 1; min-width: 0;
  border: 0; background: none; color: var(--ink);
  padding: 0.6rem 0.85rem; font: 400 0.95rem var(--sans);
}
.search-input::placeholder { color: var(--ink-faint); }
.search-input:focus { outline: none; }
.search-button {
  border: 0; cursor: pointer;
  background: var(--ink); color: var(--paper);
  font: 600 0.88rem var(--sans); padding: 0 1.1rem;
  min-height: var(--tap);
}
.search-button:hover { background: var(--beryl); color: var(--on-beryl); }

/* In the masthead the button is icon-only and the field takes a definite width.
   `flex` would do nothing here: the bar is a grid, and a grid item in an `auto`
   track sizes to its content, which for a bare input is far narrower than the
   field should be. */
.search-form-compact { width: 100%; margin-right: 0; }
.search-form-compact .search-button { padding: 0 0.85rem; }

/* ---- getting-started recipes ------------------------------------------- */

.recipe-grid {
  display: grid; gap: var(--gap);
  grid-template-columns: repeat(auto-fit, minmax(18rem, 1fr));
}
.recipe {
  --facet: 14px;
  background: var(--surface); border: 1px solid var(--edge);
  padding: 1.25rem 1.35rem 1.35rem;
  clip-path: polygon(0 0, calc(100% - var(--facet)) 0, 100% var(--facet), 100% 100%, 0 100%);
}
.recipe h3 { font-size: var(--step-1); margin: 0 0 0.3em; }
.recipe p { color: var(--ink-muted); font-size: 0.9rem; margin: 0 0 0.85rem; }
.recipe .code-block { margin: 0; }
.recipe .code-block pre { white-space: pre-wrap; overflow-wrap: anywhere; }

/* ============================================================================
   Sections
   ========================================================================= */

.section { padding: var(--bay) 0; }

.page-header { padding: clamp(2rem, 4vw, 3rem) 0 0; }
.page-header h1 { margin-bottom: 0.25em; }
.page-header p { color: var(--ink-muted); font-size: var(--step-1); }

.section-head {
  display: flex; align-items: baseline; gap: 1rem;
  margin-bottom: 1.5rem;
}
.section-head h2 { margin: 0; }
.section-head .rule { flex: 1; height: 1px; background: var(--edge); }

.view-all-link {
  display: inline-flex; align-items: center; gap: 0.4rem;
  min-height: var(--tap);
  color: var(--beryl); font-weight: 500; font-size: 0.95rem;
}
.view-all-link:hover { text-decoration: underline; }

/* ============================================================================
   Shard cards
   ========================================================================= */

.shard-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(17rem, 1fr));
  gap: var(--gap);
}
.shard-list { display: grid; gap: var(--gap); }

.shard-card {
  --facet: 16px;
  position: relative;
  background: var(--surface);
  border: 1px solid var(--edge);
  padding: var(--pad) var(--pad) 1.1rem;
  clip-path: polygon(0 0, calc(100% - var(--facet)) 0, 100% var(--facet), 100% 100%, 0 100%);
  transition: border-color 0.14s ease, background 0.14s ease;
}
/* The whole card is the target, so the whole card reacts, and it reacts the
   same way to a pointer as it does to keyboard focus landing inside it. */
.shard-card:hover, .shard-card:focus-within { border-color: var(--beryl); }
/* The cut corner catches light, the way a real bevel would. Decorative only:
   without pointer-events:none it would swallow clicks in the top-right
   corner, which is now part of the card's click target. */
.shard-card::after {
  content: "";
  position: absolute; top: 0; right: 0;
  width: calc(var(--facet) + 8px); height: calc(var(--facet) + 8px);
  background: linear-gradient(225deg, var(--beryl) 0 50%, transparent 50%);
  opacity: 0.28; transition: opacity 0.14s ease;
  pointer-events: none;
}
.shard-card:hover::after, .shard-card:focus-within::after { opacity: 0.9; }

.shard-card-header { display: flex; align-items: baseline; flex-wrap: wrap; gap: 0.55rem; }

.shard-name {
  font: 600 var(--step-1)/1.25 var(--sans);
  letter-spacing: 0; margin: 0;
}
.shard-name a { color: var(--ink); }
.shard-name a:hover { color: var(--beryl); }

/* ---- whole-card click target ------------------------------------------- */

/* The title link stretches over the card with a pseudo-element instead of the
   card being wrapped in an anchor. That keeps one tab stop per card and keeps
   the link's accessible name the shard name rather than the whole card body.
   Known tradeoff: the overlay sits over the description and meta text, so
   drag-selecting that text is harder than it was. */
.shard-name a::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 0;
}

/* Every other link in the card rides above the overlay so it keeps its own
   hit area, hover and focus. The title link is the exception: it has to stay
   unpositioned or its ::after would resolve against the title text instead of
   the card, and the overlay would cover only the title. */
.shard-card a { position: relative; z-index: 1; }
.shard-card .shard-name a { position: static; }

/* Not outline:none without a replacement. The outline moves from the anchor,
   which is only as wide as the title, onto the overlay, which is the card, so
   the ring is drawn where the click target actually is. Offset inward because
   the card is clip-path'd at the facet, so an outward ring would be cut away.
   Still 2px var(--focus). */
.shard-name a:focus-visible { outline: none; }
.shard-name a:focus-visible::after {
  outline: 2px solid var(--focus);
  outline-offset: -2px;
}

.shard-description {
  color: var(--ink-muted); font-size: 0.9rem; line-height: 1.55;
  margin: 0.55rem 0 1.1rem;
}

.shard-meta {
  display: flex; align-items: center; flex-wrap: wrap; gap: 1rem;
  border-top: 1px solid var(--edge); padding-top: 0.75rem;
  font: 400 0.78rem/1 var(--mono); color: var(--ink-muted);
}
.shard-meta .icon { color: var(--ore); margin-right: 0.35rem; }
.shard-stars, .shard-dependents { display: inline-flex; align-items: center; }

/* A signal we have not measured, as opposed to one measured as zero. Muted
   and italic so it reads as an absence at a glance and never gets mistaken
   for a count. Used for star counts, which are fetched from the host and can
   legitimately be missing; never for dependents, which we compute ourselves
   and therefore always know. */
.stat-unknown {
  font-style: italic; color: var(--ink-muted); opacity: 0.8;
  font-weight: 400; letter-spacing: 0; text-transform: none;
}
/* The homepage stat slot is set in 700 condensed at --step-3. An unmeasured
   value must not wear the typography of a figure, so it drops to body size. */
.intro-stats .stat-unknown { font: italic 400 var(--step-0)/1.55 var(--sans); }

/* The one action in an otherwise informational row, so it gets the link colour
   and a real target height. The icon keeps --ore like the counts beside it. */
.shard-docs-link {
  display: inline-flex; align-items: center;
  min-height: var(--tap);
  color: var(--beryl);
}
.shard-docs-link:hover { text-decoration: underline; }
.shard-license { margin-left: auto; }

/* ---- version pill ------------------------------------------------------ */

.version-number, .badge {
  display: inline-block;
  font: 500 0.72rem/1.55 var(--mono);
  padding: 0.05rem 0.45rem;
  color: var(--beryl); background: var(--beryl-wash);
  border: 1px solid transparent;
}
.badge-dev { color: var(--ink-muted); background: var(--surface-sunk); }

/* ============================================================================
   Shard detail
   ========================================================================= */

.shard-header { padding: clamp(1.75rem, 3vw, 2.5rem) 0 0; }
.shard-title-block { display: flex; align-items: baseline; flex-wrap: wrap; gap: 0.75rem; }
.shard-title { font-size: var(--step-4); margin: 0; }
.shard-description-large { font-size: var(--step-1); color: var(--ink-muted); max-width: 62ch; }

.shard-stats-block {
  display: flex; flex-wrap: wrap; gap: 1.5rem;
  font: 400 0.85rem/1 var(--mono); color: var(--ink-muted);
  padding: 0.9rem 0 0;
}
.shard-stats-block .icon { color: var(--ore); margin-right: 0.35rem; }

.shard-content {
  display: grid; grid-template-columns: minmax(0, 1fr) 19rem;
  gap: clamp(1.5rem, 3vw, 2.5rem);
  padding: 2rem 0 0;
}
.shard-main { min-width: 0; }
.shard-sidebar { min-width: 0; display: grid; gap: var(--gap); align-content: start; }

.shard-section {
  --facet: 16px;
  background: var(--surface); border: 1px solid var(--edge);
  padding: clamp(1.25rem, 2.5vw, 1.9rem);
  margin-bottom: var(--gap);
  clip-path: polygon(0 0, calc(100% - var(--facet)) 0, 100% var(--facet), 100% 100%, 0 100%);
}
.shard-section h2 { font-size: var(--step-2); margin-bottom: 0.75rem; }

.sidebar-section {
  --facet: 12px;
  background: var(--surface); border: 1px solid var(--edge);
  padding: 1.15rem 1.25rem;
  clip-path: polygon(0 0, calc(100% - var(--facet)) 0, 100% var(--facet), 100% 100%, 0 100%);
}
.sidebar-section h3 {
  font: 500 0.7rem/1 var(--mono);
  letter-spacing: 0.13em; text-transform: uppercase;
  color: var(--ink-muted); margin-bottom: 0.85rem;
}

.code-block {
  background: var(--surface-sunk); border: 1px solid var(--edge);
  padding: 0.9rem 1rem; margin: 0.85rem 0; overflow-x: auto;
}
.code-block pre { margin: 0; font: 400 0.85rem/1.5 var(--mono); }

.readme-content { font-size: 0.95rem; line-height: 1.6; }
.readme-raw {
  margin: 0; padding: 1rem;
  background: var(--surface-sunk); border: 1px solid var(--edge);
  font: 400 0.8rem/1.5 var(--mono);
  white-space: pre-wrap; overflow-wrap: anywhere;
  max-height: 40rem; overflow-y: auto;
}

.dependency-list, .version-list, .link-list, .footer-links { list-style: none; margin: 0; padding: 0; }
.dependency-list li, .version-list li {
  display: flex; align-items: center; gap: 0.6rem;
  min-height: var(--tap);
  padding: 0.4rem 0; border-bottom: 1px solid var(--edge);
  font-size: 0.9rem;
}
.dependency-list li:last-child, .version-list li:last-child { border-bottom: 0; }
.dependency-link { color: var(--beryl); }
.dependency-link:hover { text-decoration: underline; }
.version-date, .version-list-footer { font: 400 0.75rem var(--mono); color: var(--ink-faint); margin-left: auto; }

.link-list a {
  display: flex; align-items: center; min-height: var(--tap);
  color: var(--beryl); font-size: 0.92rem;
}
.link-list a:hover { text-decoration: underline; }

/* ============================================================================
   Browse filters
   ========================================================================= */

.filters-section {
  --facet: 12px;
  background: var(--surface); border: 1px solid var(--edge);
  padding: 1.25rem; margin: 1.5rem 0;
  clip-path: polygon(0 0, calc(100% - var(--facet)) 0, 100% var(--facet), 100% 100%, 0 100%);
}
.filters-row { display: flex; flex-wrap: wrap; gap: 1rem; align-items: flex-end; }
.filter-group { display: grid; gap: 0.35rem; min-width: 9rem; }
.filter-group label {
  font: 500 0.7rem/1 var(--mono);
  letter-spacing: 0.1em; text-transform: uppercase; color: var(--ink-muted);
}
.filter-select, .filter-input {
  min-height: 2.5rem;
  border: 1px solid var(--edge-strong);
  background: var(--surface); color: var(--ink);
  padding: 0.45rem 0.6rem; font: 400 0.92rem var(--sans);
}

.search-results-count { font: 400 0.85rem var(--mono); color: var(--ink-muted); margin: 0 0 1rem; }

/* ============================================================================
   Buttons, pagination, states
   ========================================================================= */

.button {
  display: inline-flex; align-items: center; justify-content: center; gap: 0.45rem;
  min-height: 2.5rem; padding: 0.6rem 1.35rem;
  border: 1px solid var(--ink); background: var(--surface); color: var(--ink);
  font: 600 0.92rem var(--sans); cursor: pointer;
}
.button:hover { background: var(--surface-sunk); }
.button-primary { background: var(--beryl); border-color: var(--beryl); color: var(--on-beryl); }
.button-primary:hover { background: var(--beryl-deep); border-color: var(--beryl-deep); }

.pagination { display: flex; align-items: center; gap: 1rem; padding: 2rem 0 0; }
.pagination-link {
  display: inline-flex; align-items: center; min-height: var(--tap);
  padding: 0.4rem 0.8rem;
  border: 1px solid var(--edge-strong); color: var(--ink);
  font-size: 0.9rem;
}
.pagination-link:hover { border-color: var(--beryl); color: var(--beryl); }
.pagination-info { font: 400 0.82rem var(--mono); color: var(--ink-muted); }

.empty-state, .error-page {
  text-align: center; padding: clamp(3rem, 8vw, 5rem) 1.5rem;
  color: var(--ink-muted);
}
.empty-state h3, .error-page h1 { color: var(--ink); }
.error-message { color: var(--danger); font-weight: 500; }

/* ============================================================================
   Footer
   ========================================================================= */

.site-footer {
  border-top: 1px solid var(--edge); background: var(--surface);
  padding: 2.25rem 0; margin-top: var(--bay);
  font-size: 0.9rem; color: var(--ink-muted);
}
.footer-links { display: flex; flex-wrap: wrap; gap: 1.5rem; }
.footer-links a {
  display: inline-flex; align-items: center; min-height: var(--tap);
  color: var(--ink-muted);
}
.footer-links a:hover { color: var(--beryl); text-decoration: underline; }

/* ============================================================================
   Responsive
   ========================================================================= */

@media (max-width: 60rem) {
  .shard-content { grid-template-columns: minmax(0, 1fr); }
  .navbar { gap: 1rem; }
  .search-form-compact { width: min(20rem, 100%); }
}

@media (max-width: 48rem) {
  /* Brand and menu share the first row, search takes its own full-width row so
     it never squeezes below a usable size. Expressed in grid terms because the
     bar is a grid: `flex-wrap` and `order` were inert here and the field kept
     its centre column, which at this width is barely wider than the button. */
  .navbar {
    grid-template-columns: auto minmax(0, 1fr);
    gap: 0.75rem 1rem;
    padding-top: 0.75rem; padding-bottom: 0.75rem;
  }
  .search-form-compact { grid-column: 1 / -1; grid-row: 2; max-width: none; margin-right: 0; }
  .navbar-menu { justify-self: end; }
}

@media (max-width: 40rem) {
  :root { --bay: 2.75rem; }
  .navbar-menu { margin-left: 0; width: 100%; gap: 1.1rem; }
  .intro { gap: 1.25rem; }
  .intro-stats { gap: 1.75rem; }
  .search-form { max-width: none; }
  .shard-license { margin-left: 0; }
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { transition-duration: 0.01ms !important; animation-duration: 0.01ms !important; }
}

/* ============================================================================
   Job ad (CrystalGigs)

   Paid placement, so it is drawn as an inset rather than as one more section
   of this site: sunk ground, a rule along the top, and the disclosure line
   before anything else. It deliberately does not reuse the card styles this
   site uses for its own content, because an ad dressed as a listing is a dark
   pattern.

   The component renders nothing when there is nothing to advertise, so none
   of these rules need an empty state.
   ========================================================================= */

.job-ad {
  /* Same gutters and shell as .container, so the strip lines up with the
     content above it without having to be wrapped in one. */
  width: min(calc(var(--shell) - 3rem), calc(100% - 3rem));
  margin: var(--bay) auto 0;
  padding: 1.1rem var(--pad) 1.25rem;
  background: var(--surface-sunk);
  border: 1px solid var(--edge);
  border-top: 2px solid var(--edge-strong);
  clip-path: polygon(0 0, calc(100% - var(--facet)) 0, 100% var(--facet), 100% 100%, 0 100%);
}

.job-ad-head {
  display: flex; align-items: baseline; flex-wrap: wrap;
  gap: 0.35rem 0.75rem;
  margin-bottom: 0.9rem;
}

/* The disclosure comes first in the markup and reads first, because a reader
   should know this is an ad before they read the offer. */
.job-ad-disclosure {
  order: -1; flex-basis: 100%;
  margin: 0 0 0.15rem;
  font: 600 var(--step--1)/1.2 var(--mono);
  letter-spacing: 0.09em; text-transform: uppercase;
  color: var(--ink-muted);   /* 5.28:1 on --surface-sunk light, 8.61:1 dark */
}
.job-ad-icon { margin-right: 0.4rem; color: var(--ink-faint); }

.job-ad-heading {
  margin: 0;
  font: 700 var(--step-1)/1.2 var(--cond);
  letter-spacing: -0.01em;
  color: var(--ink);
}

.job-ad-list {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr));
  gap: var(--gap);
  list-style: none; margin: 0; padding: 0;
}

.job-ad-item {
  display: flex; flex-direction: column; align-items: flex-start;
  gap: 0.3rem;
  padding: 0.85rem 1rem;
  background: var(--surface);
  border: 1px solid var(--edge);
}
.job-ad-item-featured { border-left: 3px solid var(--beryl); }

.job-ad-title {
  display: inline-flex; align-items: center; min-height: var(--tap);
  font: 600 var(--step-0)/1.3 var(--sans);
  color: var(--ink);
  /* Underlined, because the accent alone does not carry meaning. */
  text-decoration: underline; text-underline-offset: 2px;
}
.job-ad-title:hover { color: var(--beryl); }

.job-ad-featured {
  padding: 0.05rem 0.45rem;
  font: 600 var(--step--1)/1.5 var(--mono);
  color: var(--beryl);            /* 5.11:1 light, 7.87:1 dark on --beryl-wash */
  background: var(--beryl-wash);
  /* The wash alone barely separates from the card, so the hairline draws it. */
  border: 1px solid var(--beryl);
}

.job-ad-meta {
  display: flex; flex-wrap: wrap; gap: 0 0.5rem;
  margin: 0;
  font-size: var(--step--1);
  color: var(--ink-muted);
}
/* Separators are drawn, never typed, so a screen reader reads the fields as a
   list of facts rather than announcing punctuation between them. */
.job-ad-meta span + span::before { content: "\00b7"; margin-right: 0.5rem; }

@media (max-width: 40rem) {
  .job-ad { padding: 1rem 1.1rem 1.15rem; }
  .job-ad-list { grid-template-columns: minmax(0, 1fr); }
}


/* ============================================================================
   Shard page: version picker, manifest, notices

   The detail page has to make emptiness legible. Most of this registry is
   minimal repositories, so "no tags", "no README", "no manifest yet" are the
   normal states and each one is a rendered sentence rather than a gap. The
   rules below are therefore mostly about giving those sentences a shape that
   reads as deliberate.

   Palette is the existing tokens, unchanged. Every surface keeps the one cut
   corner. Icons are Font Awesome glyphs, decorative and aria-hidden, with the
   meaning always carried by adjacent text.
   ========================================================================= */

/* The repository a shard is, under its name. Mono because it is an
   identifier, not prose, and two shards called "router" are told apart by
   exactly this line. */
.shard-identity {
  margin: 0.45rem 0 0;
  font: 400 0.82rem/1.4 var(--mono);
  color: var(--ink-faint);
  word-break: break-all;
}

.stat-item { display: inline-flex; align-items: baseline; gap: 0.3rem; }
.stat-item strong { font-weight: 600; color: var(--ink); }

/* ---- version picker ----------------------------------------------------- */

/* A disclosure, not a select: the options are links, so every release has its
   own URL and the control works with no script at all. */
.version-picker { position: relative; }

.version-picker-summary {
  --facet: 10px;
  display: inline-flex; align-items: center; gap: 0.6rem;
  min-height: var(--tap); padding: 0.35rem 0.7rem;
  border: 1px solid var(--edge-strong);
  background: var(--surface);
  cursor: pointer;
  clip-path: polygon(0 0, calc(100% - var(--facet)) 0, 100% var(--facet), 100% 100%, 0 100%);
}
/* The default triangle is replaced by the caret below, which can be positioned
   and rotated. Both vendor spellings, or Safari keeps drawing its own. */
.version-picker-summary { list-style: none; }
.version-picker-summary::-webkit-details-marker { display: none; }
.version-picker-summary::marker { content: ""; }
.version-picker-summary:hover { border-color: var(--beryl); }

.version-picker-current { font: 500 0.95rem/1 var(--mono); color: var(--ink); }
.version-picker-count { font: 400 0.75rem/1 var(--mono); color: var(--ink-muted); }
.version-picker-caret { font-size: 0.7rem; color: var(--ink-muted); transition: transform 120ms ease; }
.version-picker[open] .version-picker-caret { transform: rotate(180deg); }

/* Overlaid rather than inline: opening the list must not shove the page
   contents down, or the reader loses their place every time they check what
   versions exist. Capped and scrollable because a mature shard has hundreds. */
.version-picker-list {
  --facet: 12px;
  position: absolute; z-index: 20;
  top: calc(100% + 0.35rem); left: 0;
  min-width: 17rem; max-width: min(26rem, 90vw);
  max-height: 22rem; overflow-y: auto;
  margin: 0; padding: 0; list-style: none;
  background: var(--surface);
  border: 1px solid var(--edge-strong);
  clip-path: polygon(0 0, calc(100% - var(--facet)) 0, 100% var(--facet), 100% 100%, 0 100%);
}

.version-picker-item { border-bottom: 1px solid var(--edge); }
.version-picker-item:last-child { border-bottom: 0; }

.version-picker-option {
  display: flex; align-items: center; flex-wrap: wrap; gap: 0.45rem;
  min-height: var(--tap); padding: 0.5rem 0.8rem;
  font: 400 0.85rem/1.4 var(--sans);
  color: var(--ink); text-decoration: none;
}
a.version-picker-option:hover { background: var(--surface-sunk); color: var(--beryl); }

/* The row already on screen, and any row on a shard we could never identify.
   Not a link, so it gets the muted treatment that says so without needing a
   label to explain it. */
.version-picker-option-static { color: var(--ink-muted); cursor: default; }
.version-picker-item[aria-current="true"] {
  background: var(--beryl-wash);
  border-left: 3px solid var(--beryl);
}

.version-picker-label { font: 500 0.85rem/1.4 var(--mono); }

/* Stands in for the picker when there is nothing to pick. Same size and
   position, so the header does not reflow between a shard with releases and
   one without. */
.version-pill {
  --facet: 10px;
  display: inline-flex; align-items: center;
  min-height: var(--tap); padding: 0.35rem 0.7rem;
  font: 400 0.8rem/1 var(--mono);
  border: 1px dashed var(--edge-strong);
  color: var(--ink-muted); background: var(--surface-sunk);
}

.badge-latest { color: var(--beryl); background: var(--beryl-wash); border: 1px solid var(--beryl); }
.badge-yanked { color: var(--danger); background: var(--surface-sunk); border: 1px solid var(--danger); }
.badge-unindexed { color: var(--ink-muted); background: var(--surface-sunk); border: 1px dashed var(--edge-strong); }

/* ---- notices ------------------------------------------------------------ */

/* What is wrong or missing, said before the reader scrolls looking for it.
   The rule down the left is the whole treatment: no tinted panel, because a
   sparse shard is not an error and must not be dressed as one. */
.shard-notice {
  display: flex; align-items: flex-start; gap: 0.7rem;
  padding: 0.85rem 1rem;
  margin: 0 0 var(--gap);
  background: var(--surface-sunk);
  border: 1px solid var(--edge);
  border-left: 3px solid var(--edge-strong);
  font-size: 0.92rem;
}
.shard-notice p { margin: 0; }
.shard-notice-icon { margin-top: 0.2rem; color: var(--ink-muted); }
.shard-notice-info { border-left-color: var(--beryl); }
.shard-notice-info .shard-notice-icon { color: var(--beryl); }
.shard-notice-warn { border-left-color: var(--warn); }
.shard-notice-warn .shard-notice-icon { color: var(--warn); }

/* ---- manifest and metadata --------------------------------------------- */

.shard-subhead {
  font: 500 0.72rem/1 var(--mono);
  letter-spacing: 0.12em; text-transform: uppercase;
  color: var(--ink-muted);
  margin: 1.4rem 0 0.5rem;
}
.shard-subhead:first-of-type { margin-top: 0.35rem; }

/* Label and value, side by side while there is room to read them that way. */
.manifest-facts, .meta-facts {
  display: grid; grid-template-columns: minmax(6rem, auto) minmax(0, 1fr);
  gap: 0.4rem 1rem;
  margin: 0;
}
.manifest-facts dt, .meta-facts dt {
  font: 500 0.72rem/1.6 var(--mono);
  letter-spacing: 0.08em; text-transform: uppercase;
  color: var(--ink-muted);
}
.manifest-facts dd, .meta-facts dd { margin: 0; min-width: 0; word-break: break-word; }
.meta-facts { gap: 0.3rem 0.8rem; font-size: 0.9rem; }

.manifest-facts code, .meta-facts code {
  font: 400 0.82rem/1.5 var(--mono);
  background: var(--surface-sunk); border: 1px solid var(--edge);
  padding: 0.05rem 0.35rem;
}

.manifest-list { list-style: none; margin: 0; padding: 0; display: grid; gap: 0.25rem; }

/* ---- dependency rows --------------------------------------------------- */

.dependency-requirement { font: 400 0.8rem/1 var(--mono); color: var(--ink-muted); }
/* The source shorthand, which is what says whether a requirement tracks a
   release or somebody's branch. Pushed right so the names stay scannable. */
.dependency-source {
  margin-left: auto;
  font: 400 0.75rem/1.4 var(--mono);
  color: var(--ink-faint);
  text-align: right; word-break: break-all;
}

@media (max-width: 40rem) {
  .manifest-facts, .meta-facts { grid-template-columns: minmax(0, 1fr); gap: 0.1rem; }
  .manifest-facts dt, .meta-facts dt { margin-top: 0.55rem; }
  .version-picker-list { min-width: min(17rem, 88vw); }
  .dependency-source { margin-left: 0; text-align: left; flex-basis: 100%; }
}