/* BloggerBear public frontend -- minimal, clean, readable. This is a
   portfolio-project public blog, not a design showpiece. */

:root {
  color-scheme: light dark;
  --max-width: 720px;
  --fg: #1a1a1a;
  --bg: #ffffff;
  --muted: #6b6b6b;
  --accent: #2a5db0;
  --border: #e2e2e2;
}

@media (prefers-color-scheme: dark) {
  :root {
    --fg: #e8e8e8;
    --bg: #16181c;
    --muted: #9a9a9a;
    --accent: #7aa7ff;
    --border: #2c2f36;
  }
}

* {
  box-sizing: border-box;
}

/* html's height: 100% is what lets body's min-height: 100vh below
   actually reach the true bottom of the viewport on every browser --
   without it, some mobile browsers' dynamic toolbar/address-bar resize
   behavior can leave a gap under a 100vh body. */
html {
  height: 100%;
}

/* Sticky-footer layout: body is a column flexbox spanning at least the
   full viewport height; main is the one flexible child (flex: 1 0
   auto), so it grows to fill any leftover space on short pages -- that
   pushes .site-footer down to sit flush with the bottom of the
   viewport, instead of floating up right under a short page's content.
   On a page long enough to scroll, main simply doesn't grow past its
   content size and the footer sits right after it as normal, scrolling
   away with everything else -- nothing here pins the footer in place
   while scrolling, only while it would otherwise be too high up. */
body {
  margin: 0;
  padding: 0 16px;
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  /* Fallback declarations before the var()-based ones: every current
     browser supports CSS custom properties, but if this ever runs
     somewhere that genuinely doesn't (or a typo'd/missing --bg/--fg
     token slips through some future edit), the page still renders
     readable light-mode text on a white background rather than
     unstyled black-on-transparent. The var() line below always wins
     when supported, per normal CSS cascade rules -- this isn't an
     @supports branch, just declaration order. */
  background: #ffffff;
  background: var(--bg);
  color: #1a1a1a;
  color: var(--fg);
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif,
    "Apple Color Emoji", "Segoe UI Emoji";
  font-size: 17px;
  line-height: 1.55;
}

/* Skip link -- WCAG 2.4.1 (Bypass Blocks). The first focusable element
   on the page; visually hidden until it receives keyboard focus, so
   keyboard/screen-reader users can jump past both nav regions straight
   to #content without affecting the visual layout for anyone else. */
.skip-link {
  position: absolute;
  top: -100%;
  left: 8px;
  z-index: 100;
  padding: 10px 16px;
  /* --fg/--bg, inverted, rather than --accent + a fixed white text
     color: --accent is a much lighter blue in dark mode (#7aa7ff),
     which fails WCAG AA contrast (~2.4:1, needs 4.5:1) against white
     text -- easy to miss since it looks fine in light mode alone. The
     --fg/--bg pair is already chosen for strong mutual contrast in
     both themes, so inverting it here is guaranteed safe in both. */
  background: var(--fg);
  color: var(--bg);
  border-radius: 0 0 4px 4px;
  text-decoration: none;
  font-weight: 600;
}

.skip-link:focus {
  top: 0;
}

/* The programmatic focus target on route change (see app.js's route())
   -- intentionally no visible ring here: #content is a landmark, not an
   interactive control, and receiving focus is only there to move the
   keyboard/screen-reader cursor to new content, not to look like a
   clickable outline. Every actual interactive element (links, buttons,
   the feedback textarea, the skip link above) keeps its normal focus
   indicator -- nothing here suppresses those. */
#content:focus {
  outline: none;
}

/* The header is two groups of links: the site's own sections (Trending
   Everywhere / Musings / Stats -- fixed, in the HTML, so they work even if
   the API is down) and the topics (fetched, at most NAV_TOPIC_LIMIT). On a
   wide screen that is the title and the sections on one row, the topics on
   a row of their own. On a phone (max-width 640px, below) everything
   stacks: the sections become a row of tap-sized buttons, the topics a
   vertical list under a "Topics" label.

   The header is not sticky on phones any more: stacked, it is too tall to
   keep glued to the top of a small screen, and the footer's "Back to top"
   link (see .back-to-top) is the way back up. id="top" is its target. */
header {
  max-width: var(--max-width);
  width: 100%;
  margin: 0 auto;
  padding: 20px 0 12px;
  border-bottom: 1px solid var(--border);
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px 20px;
  flex-shrink: 0;
}

.site-title {
  display: flex;
  align-items: center;
  gap: 10px;
  font-size: 1.4rem;
  font-weight: 700;
  color: var(--fg);
  text-decoration: none;
}

/* .site-title picture: the logo is a <picture> (WebP source + SVG
   <img> fallback -- see index.html/error.html) wrapping the actual
   .site-logo <img>. picture is the direct flex child of .site-title
   here, not the img, so the shrink/display behavior needs to be set on
   it too -- setting it only on .site-logo below would leave the
   wrapping <picture> box to the flex algorithm's default sizing. */
.site-title picture {
  display: block;
  flex-shrink: 0;
}

.site-logo {
  display: block;
  width: 32px;
  height: 32px;
  border-radius: 6px;
  /* Purely decorative next to the "BloggerBear" text right beside it
     (see index.html's alt="") -- flex-shrink: 0 keeps it square even
     when the header wraps on narrow viewports. */
  flex-shrink: 0;
}

#nav,
#nav-site {
  display: flex;
  flex-wrap: wrap;
  gap: 4px 16px;
}

#nav a,
#nav-site a {
  color: var(--accent);
  text-decoration: none;
  font-size: 0.95rem;
}

#nav a:hover,
#nav-site a:hover {
  text-decoration: underline;
}

/* The site's own sections (Trending Everywhere, Musings, Stats) are
   outlined pills, so they read as "places on the site" next to the plain
   topic links below them. */
#nav-site {
  gap: 6px 10px;
}

#nav-site a {
  border: 1px solid var(--accent);
  border-radius: 999px;
  padding: 2px 12px;
}

/* The topics: their own row on a wide screen, with a small muted label. */
.nav-topics {
  flex: 1 0 100%;
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 4px 12px;
}

.nav-topics[hidden] {
  display: none;
}

.nav-label {
  color: var(--muted);
  font-size: 0.8rem;
  text-transform: uppercase;
  letter-spacing: 0.06em;
}

@media (max-width: 640px) {
  header {
    display: block;
    padding-bottom: 8px;
  }

  #nav-site {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: 8px;
    margin: 12px 0 4px;
  }

  #nav-site a {
    display: flex;
    align-items: center;
    justify-content: center;
    min-height: 44px;
    padding: 4px 8px;
    text-align: center;
    line-height: 1.2;
  }

  .nav-topics {
    display: block;
    margin-top: 12px;
    padding-top: 10px;
    border-top: 1px solid var(--border);
  }

  .nav-label {
    display: block;
    margin-bottom: 2px;
  }

  #nav {
    display: block;
  }

  #nav a {
    display: block;
    padding: 10px 0;
    border-bottom: 1px solid var(--border);
    font-size: 1rem;
  }

  #nav a:last-child {
    border-bottom: 0;
  }
}

main {
  max-width: var(--max-width);
  margin: 0 auto;
  padding: 24px 0 48px;
  /* The one flexible child in body's column flexbox -- grows to absorb
     any leftover vertical space on a short page (see the sticky-footer
     comment on body above), which is what actually keeps .site-footer
     glued to the bottom instead of it being an unrelated footer rule. */
  flex: 1 0 auto;
  width: 100%;
}

main h1 {
  font-size: 1.6rem;
  margin: 0 0 12px;
}

.section-heading {
  font-size: 1.15rem;
  margin: 0 0 12px;
}

/* --- Home hero -------------------------------------------------------- */

.hero {
  padding: 8px 0 28px;
  margin-bottom: 20px;
  border-bottom: 1px solid var(--border);
}

/* Bigger than the standard main h1 (1.6rem) above -- this is the one
   place on the site meant to read as a landing moment rather than a
   plain page title; every other route's h1 (an article title, "Terms
   of Service", ...) stays at the standard size. */
.hero h1 {
  font-size: 2rem;
  margin: 0 0 10px;
}

.hero .tagline {
  margin: 0 0 16px;
  max-width: 480px;
  color: var(--muted);
  font-size: 1.05rem;
  line-height: 1.5;
}

.find-out-more {
  display: inline-block;
  padding: 8px 18px;
  border: 1px solid var(--accent);
  border-radius: 999px;
  color: var(--accent);
  text-decoration: none;
  font-weight: 600;
  font-size: 0.95rem;
}

.find-out-more:hover {
  /* --fg/--bg, inverted, not --accent + fixed white text -- see the
     same contrast note on .skip-link and #site-notice-dismiss: --accent
     is a much lighter blue in dark mode (#7aa7ff), which fails WCAG AA
     against white text (~2.4:1, needs 4.5:1). The --fg/--bg pair is
     already guaranteed high-contrast in both themes. */
  background: var(--fg);
  border-color: var(--fg);
  color: var(--bg);
}

a {
  color: var(--accent);
}

.topic-list,
.article-list {
  list-style: none;
  padding: 0;
  margin: 0;
}

.topic-list li,
.article-list li {
  padding: 10px 0;
  border-bottom: 1px solid var(--border);
}

.article-date {
  color: var(--muted);
  font-size: 0.85rem;
}

.topic-article-count {
  color: var(--muted);
  font-size: 0.85rem;
}

.article-meta {
  color: var(--muted);
  font-size: 0.9rem;
  margin: 0 0 20px;
}

.article-body p {
  margin: 0 0 1em;
}

/* Headings the article body itself contains (real <h2>-<h6>, see
   markdown.js: the article title is the page's one <h1>). Sized below the
   title (1.6rem) and stepping down. */
.article-body h2,
.article-body h3,
.article-body h4,
.article-body h5,
.article-body h6 {
  line-height: 1.3;
  margin: 1.6em 0 0.5em;
}

.article-body h2 {
  font-size: 1.3rem;
}

.article-body h3 {
  font-size: 1.15rem;
}

.article-body h4,
.article-body h5,
.article-body h6 {
  font-size: 1rem;
}

.article-body ul,
.article-body ol {
  margin: 0 0 1em;
  padding-left: 1.5em;
}

.article-body li {
  margin: 0.25em 0;
}

.article-body li > ul,
.article-body li > ol {
  margin: 0.25em 0 0.25em;
}

.article-body blockquote {
  margin: 0 0 1em;
  padding: 0 0 0 1em;
  border-left: 3px solid var(--border);
  color: var(--muted);
}

.article-body pre {
  margin: 0 0 1em;
  padding: 12px 14px;
  overflow-x: auto;
  border: 1px solid var(--border);
  border-radius: 6px;
  font-size: 0.9rem;
}

.article-body code {
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, "Liberation Mono", monospace;
  font-size: 0.92em;
}

.article-body hr {
  margin: 1.6em 0;
  border: 0;
  border-top: 1px solid var(--border);
}

.article-body a {
  overflow-wrap: anywhere;
}

/* Wide tables scroll inside their own box rather than stretching the page. */
.table-scroll {
  overflow-x: auto;
  margin: 0 0 1em;
}

.article-body table {
  border-collapse: collapse;
  font-size: 0.92rem;
}

.article-body th,
.article-body td {
  padding: 6px 12px;
  border: 1px solid var(--border);
  text-align: left;
  vertical-align: top;
}

.article-body th {
  background: var(--border);
}

.article-body .align-center {
  text-align: center;
}

.article-body .align-right {
  text-align: right;
}

/* --- Articles in the Pipeline ----------------------------------------
   A "terminal window" treatment for in-progress work (pending-review /
   researching) -- reuses the site's existing --bg/--fg/--border/--muted
   tokens rather than a hardcoded terminal-green palette, so it stays
   consistent (and correctly themed in both light and dark mode) instead
   of looking like a jarring, unrelated widget bolted onto the page. */

.pipeline-box {
  margin: 0 0 24px;
  border: 1px solid var(--border);
  border-radius: 6px;
  overflow: hidden;
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, "Liberation Mono", monospace;
}

.pipeline-titlebar {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 8px 14px;
  background: var(--border);
  border-bottom: 1px solid var(--border);
}

.pipeline-title {
  font-size: 0.85rem;
  font-weight: 600;
}

.pipeline-list {
  list-style: none;
  margin: 0;
  padding: 12px 14px;
}

.pipeline-list li {
  display: flex;
  align-items: baseline;
  gap: 12px;
  padding: 4px 0;
  color: var(--fg);
}

.pipeline-status {
  flex: 0 0 11rem;
}

.pipeline-item-title {
  color: var(--muted);
  min-width: 0;
  overflow-wrap: anywhere;
}

/* "checked 38 min ago": beside the title on a wide screen, on its own line below on a phone. */
.pipeline-item-meta {
  margin-left: auto;
  flex: 0 0 auto;
  color: var(--muted);
  font-size: 0.8rem;
  white-space: nowrap;
}

/* On a phone the fixed 11rem status column leaves the title a sliver: stack
   the status above its title instead. */
@media (max-width: 640px) {
  .pipeline-list li {
    flex-wrap: wrap;
    gap: 2px 12px;
  }

  /* Leaves room for the paw (the li::before) so it stays on the status line. */
  .pipeline-status {
    flex: 1 0 calc(100% - 3rem);
  }

  .pipeline-item-title {
    flex: 1 0 100%;
    padding-left: 1.9rem;
  }

  .pipeline-item-meta {
    flex: 1 0 100%;
    margin-left: 0;
    padding-left: 1.9rem;
  }
}

/* Paw prints instead of default bullet dots -- a ::before pseudo-element
   with the emoji as content, not a background-image asset. */
.pipeline-list li::before {
  content: "\1F43E";
  margin-right: 8px;
}

/* Decorative CSS-only spinner: a rotating ring, no image asset, no
   animation library. aria-hidden in the markup, not just visually
   decorative -- see app.js's renderPipelineSection. */
.pipeline-spinner {
  display: inline-block;
  width: 14px;
  height: 14px;
  border: 2px solid var(--muted);
  border-top-color: var(--accent);
  border-radius: 50%;
  animation: pipeline-spin 0.9s linear infinite;
  flex-shrink: 0;
}

@keyframes pipeline-spin {
  to {
    transform: rotate(360deg);
  }
}

/* A small bear-themed touch alongside the spinner -- a gentle bob, not a
   custom illustration (this repo has no bear artwork/sprite to animate). */
.pipeline-bear {
  display: inline-block;
  animation: pipeline-bob 1.6s ease-in-out infinite;
}

@keyframes pipeline-bob {
  0%,
  100% {
    transform: translateY(0);
  }
  50% {
    transform: translateY(-3px);
  }
}

/* WCAG 2.3.3 / respecting a user's OS-level motion preference -- both
   animations above freeze on their resting frame instead of looping. */
@media (prefers-reduced-motion: reduce) {
  .pipeline-spinner {
    animation: none;
  }

  .pipeline-bear {
    animation: none;
  }
}

.sources-footer {
  margin-top: 32px;
  padding-top: 16px;
  border-top: 1px solid var(--border);
  font-size: 0.9rem;
  color: var(--muted);
}

.sources-footer h2 {
  font-size: 1rem;
  margin: 0 0 8px;
  color: var(--fg);
}

.sources-footer ul {
  padding-left: 20px;
  margin: 0;
}

/* Compact one-line AI lineage summary (models / tokens / approved by /
   cost) -- muted like .article-date, on its own line under the date on
   both the article list and the article page, and baked into the static
   article pages (common/static_pages.py) with the same class. */
.lineage-summary {
  color: var(--muted);
  font-size: 0.8rem;
  margin: 2px 0 0;
  overflow-wrap: anywhere;
}

.article-meta + .lineage-summary {
  margin: -14px 0 20px;
}

/* Full lineage stats footer -- same treatment as .sources-footer, with a
   definition list for the label/value rows. */
.lineage-footer {
  margin-top: 32px;
  padding-top: 16px;
  border-top: 1px solid var(--border);
  font-size: 0.9rem;
  color: var(--muted);
}

.lineage-footer h2 {
  font-size: 1rem;
  margin: 0 0 8px;
  color: var(--fg);
}

.lineage-footer dl {
  margin: 0;
  display: grid;
  grid-template-columns: max-content 1fr;
  gap: 4px 16px;
}

.lineage-footer dt {
  font-weight: 600;
}

/* One column on a phone: label above value, not a squeezed two-column grid. */
@media (max-width: 640px) {
  .lineage-footer dl {
    grid-template-columns: 1fr;
    gap: 0;
  }

  .lineage-footer dd {
    margin-bottom: 8px;
  }
}

.lineage-footer dd {
  margin: 0;
  overflow-wrap: anywhere;
}

.feedback {
  margin-top: 32px;
  padding-top: 16px;
  border-top: 1px solid var(--border);
}

.feedback h2 {
  font-size: 1rem;
  margin: 0 0 12px;
  color: var(--fg);
}

/* Feedback closed (a locked article, a pause, the daily or rate limit): the form is replaced by
   this, with the reason. The static article pages start their buttons hidden and reveal them once
   the API says feedback is open, so the hidden attribute has to win over display: flex. (The decoy
   field on the same form is hidden with that same attribute and needs no rule of its own.) */
.feedback-buttons[hidden] {
  display: none;
}

/* --- Musings ---------------------------------------------------------
   A bear feeling the musing's mood sits where a bullet point would be (see moods.js). */

.musings-list {
  list-style: none;
  margin: 0;
  padding: 0;
}

.musing-item {
  display: flex;
  align-items: flex-start;
  gap: 14px;
  padding: 16px 0;
  border-bottom: 1px solid var(--border);
}

.musing-bear {
  flex: 0 0 auto;
  width: 56px;
  height: 56px;
}

.musing-body {
  min-width: 0;
}

.musing-text {
  margin: 0 0 6px;
}

.musing-mood {
  margin: 0 0 6px;
  color: var(--muted);
  font-size: 0.9rem;
}

.musing-meta {
  margin: 0;
  color: var(--muted);
  font-size: 0.85rem;
}

@media (max-width: 640px) {
  .musing-bear {
    width: 44px;
    height: 44px;
  }
}

/* --- The bear-tummy toy (tummy.js) ----------------------------------------------
   Decoration around feedback: inline, never a modal, never a gate. It only wiggles for a reader who
   has not asked for reduced motion. */

.tummy-game {
  margin: 14px auto 0;
  text-align: center;
}

.tummy-button {
  display: inline-block;
  padding: 4px;
  border: 0;
  border-radius: 12px;
  background: none;
  cursor: pointer;
  touch-action: manipulation;
  -webkit-tap-highlight-color: transparent;
}

.tummy-button:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

.tummy-bear {
  display: block;
  width: 112px;
  height: 112px;
  user-select: none;
  -webkit-user-drag: none;
}

.tummy-note {
  margin: 4px 0 0;
  color: var(--muted);
  font-size: 0.85rem;
}

@media (prefers-reduced-motion: no-preference) {
  .tummy-wiggle .tummy-bear {
    animation: tummy-wiggle 0.35s ease-in-out;
  }

  @keyframes tummy-wiggle {
    0% {
      transform: rotate(0deg) scale(1);
    }
    25% {
      transform: rotate(-5deg) scale(1.04);
    }
    60% {
      transform: rotate(4deg) scale(1.02);
    }
    100% {
      transform: rotate(0deg) scale(1);
    }
  }
}

.feedback-closed {
  padding: 14px 16px;
  border: 1px dashed var(--border);
  border-radius: 8px;
  text-align: center;
}

.feedback-closed p {
  margin: 0 0 6px;
}

.feedback-closed-headline {
  font-size: 1.15rem;
  font-weight: 700;
}

.feedback-closed-reason {
  color: var(--muted);
  font-size: 0.9rem;
}

.feedback-closed-retry {
  color: var(--muted);
  font-size: 0.85rem;
}

.feedback-buttons {
  display: flex;
  gap: 8px;
  margin-bottom: 12px;
}

.feedback-buttons button {
  font: inherit;
  padding: 6px 14px;
  border: 1px solid var(--border);
  border-radius: 4px;
  background: var(--bg);
  color: var(--fg);
  cursor: pointer;
}

.feedback-buttons button:hover:not(:disabled) {
  border-color: var(--accent);
}

.feedback-buttons button:disabled {
  opacity: 0.5;
  cursor: not-allowed;
}

.feedback-comment-hint {
  font-size: 0.85rem;
  color: var(--muted);
  margin: 0 0 10px;
  max-width: 480px;
}

.feedback label {
  display: block;
  font-size: 0.9rem;
  color: var(--muted);
  margin-bottom: 12px;
}

.feedback textarea {
  display: block;
  width: 100%;
  max-width: 480px;
  margin-top: 6px;
  font: inherit;
  color: var(--fg);
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: 4px;
  padding: 8px;
}

.feedback-status {
  font-size: 0.9rem;
  color: var(--muted);
  min-height: 1.2em;
}

/* The footer is three centred rows -- Explore (the site sections), Legal, and
   RSS / Back to top -- with a paw print between every two links (a real
   <span class="paw" aria-hidden="true"> in the HTML, not CSS content, so it
   wraps with the text and screen readers never hear it). Each link and the paw
   after it share a nowrap span (.footer-item), so a wrapped line always starts
   with a link, never a paw. */
.site-footer {
  max-width: var(--max-width);
  width: 100%;
  margin: 0 auto;
  padding: 16px 0 32px;
  border-top: 1px solid var(--border);
  font-size: 0.85rem;
  color: var(--muted);
  text-align: center;
}

.footer-nav,
.legal-nav,
.footer-links {
  margin: 0 0 4px;
}

.footer-nav a,
.legal-nav a,
.footer-links a {
  display: inline-block;
  padding: 6px 0;
}

.footer-item {
  white-space: nowrap;
}

.paw {
  margin: 0 0.6em 0 0.15em;
  user-select: none;
}

/* Phone: a bigger type size and tap-sized links (44px). */
@media (max-width: 640px) {
  .site-footer {
    font-size: 0.95rem;
  }

  .footer-nav a,
  .legal-nav a,
  .footer-links a {
    padding: 11px 0;
  }
}

.footer-nav a,
.legal-nav a,
.site-footer a {
  color: var(--accent);
  text-decoration: none;
}

.footer-nav a:hover,
.legal-nav a:hover,
.site-footer a:hover {
  text-decoration: underline;
}

/* --- Site notice ---------------------------------------------------- */

.site-notice {
  /* In-flow, not a fixed overlay -- sits right below the header and
     pushes the rest of the page down while visible, so it can never
     obscure content lower on the page (unlike a fixed bottom bar, which
     would need extra reserved space to avoid covering short pages'
     footers -- see the comment in index.html). */
  max-width: var(--max-width);
  margin: 0 auto;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: 12px 20px;
  padding: 14px 16px;
  background: var(--bg);
  color: var(--fg);
  border-bottom: 1px solid var(--border);
}

/* Bugfix: [hidden]'s default UA-stylesheet `display: none` has the same
   specificity as the class selector above, and author CSS always wins
   that tie regardless of source order -- without this, app.js's
   `notice.hidden = true` set the attribute correctly but had zero visual
   effect, so the "Got it" button never actually dismissed anything. */
.site-notice[hidden] {
  display: none;
}

.site-notice p {
  margin: 0;
  max-width: 640px;
  font-size: 0.9rem;
  line-height: 1.5;
}

.site-notice a {
  color: var(--accent);
}

#site-notice-dismiss {
  flex-shrink: 0;
  font: inherit;
  font-weight: 600;
  /* Comfortably above the WCAG 2.5.8 (AA) 24x24px minimum target size,
     closer to the 44x44px AAA guidance -- this is often the first thing
     a visitor taps, so it should be an easy, unambiguous target. */
  padding: 10px 20px;
  border: 1px solid var(--fg);
  border-radius: 4px;
  /* --fg/--bg, inverted, not --accent + fixed white text -- see the
     same contrast note on .skip-link above (--accent is too light
     against white text in dark mode to meet WCAG AA). */
  background: var(--fg);
  color: var(--bg);
  cursor: pointer;
}

#site-notice-dismiss:hover {
  opacity: 0.9;
}

/* --- Stats page -------------------------------------------------------
   Public cost/token statistics (app.js's renderStats). Colors are the
   site's own custom properties throughout, so light/dark mode is inherited
   rather than re-implemented. Marks follow the chart specs used across
   this project: thin columns (capped at 24px in app.js) with a 4px rounded
   data-end, a hairline recessive grid, text in text tokens (never the
   series color), and a hit target wider than the mark. */

.stats-note {
  color: var(--muted);
  font-size: 0.85rem;
  margin: 0 0 20px;
}

.stats-tiles {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
  gap: 12px;
  margin: 0 0 8px;
}

.stat-tile {
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding: 12px 14px;
  border: 1px solid var(--border);
  border-radius: 6px;
}

.stat-label {
  color: var(--muted);
  font-size: 0.85rem;
}

.stat-value {
  font-size: 1.6rem;
  font-weight: 600;
  line-height: 1.2;
}

.stat-sub {
  color: var(--muted);
  font-size: 0.8rem;
}

.stats-chart {
  position: relative;
  margin: 8px 0 12px;
}

.stats-chart-svg {
  display: block;
  width: 100%;
  height: auto;
}

.stats-grid {
  stroke: var(--border);
  stroke-width: 1;
}

.stats-axis-label {
  fill: var(--muted);
  font-size: 11px;
}

.stats-bar {
  fill: var(--accent);
  opacity: 0.8;
}

.stats-hit {
  fill: transparent;
}

.stats-day:hover .stats-bar,
.stats-day:focus-within .stats-bar {
  opacity: 1;
}

.stats-hit:focus-visible {
  outline: 2px solid var(--fg);
  outline-offset: -2px;
}

.chart-tooltip {
  position: absolute;
  top: 0;
  z-index: 1;
  display: flex;
  flex-direction: column;
  gap: 1px;
  padding: 6px 10px;
  background: var(--bg);
  color: var(--fg);
  border: 1px solid var(--border);
  border-radius: 4px;
  font-size: 0.8rem;
  white-space: nowrap;
  pointer-events: none;
  transform: translateX(-50%);
}

/* Same gotcha as .site-notice above: this display: flex would otherwise
   beat the browser's default [hidden] { display: none }. */
.chart-tooltip[hidden] {
  display: none;
}

.chart-tooltip strong {
  font-size: 0.95rem;
}

.chart-tooltip span {
  color: var(--muted);
}

.stats-details {
  margin: 0 0 8px;
}

.stats-details summary {
  cursor: pointer;
  color: var(--accent);
  margin-bottom: 8px;
}

.stats-table-wrap {
  overflow-x: auto;
  margin: 0 0 8px;
}

.stats-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 0.9rem;
}

.stats-table th,
.stats-table td {
  padding: 6px 10px;
  border-bottom: 1px solid var(--border);
  text-align: left;
}

.stats-table th {
  color: var(--muted);
  font-weight: 600;
  font-size: 0.8rem;
}

.stats-table .num {
  text-align: right;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

/* --- What BloggerBear is wearing (Stats page) ------------------------------------------------------
   Rarity colours follow the usual game convention: grey common, green uncommon, blue rare, purple epic,
   orange legendary. Condition runs green, yellow, orange, red as durability falls. Colour is never the only
   signal (the words are always shown), and both sets are chosen to keep their contrast on the page in light
   and dark mode. The tooltip is always dark, like a game tooltip, and carries its own brighter set. */

:root {
  --rarity-common: #6b6b6b;
  --rarity-uncommon: #1a7f37;
  --rarity-rare: #0b62c4;
  --rarity-epic: #7b22c9;
  --rarity-legendary: #b45300;
  --dur-good: #1a7f37;
  --dur-fair: #8a6100;
  --dur-worn: #b34700;
  --dur-critical: #c62828;
  --dur-unknown: var(--muted);
  --gear-track: #d6d6d6;
}

@media (prefers-color-scheme: dark) {
  :root {
    --rarity-common: #b0b0b0;
    --rarity-uncommon: #4ade80;
    --rarity-rare: #4da3ff;
    --rarity-epic: #c084fc;
    --rarity-legendary: #ff9a2e;
    --dur-good: #4ade80;
    --dur-fair: #facc15;
    --dur-worn: #fb923c;
    --dur-critical: #f87171;
    --gear-track: #3a3e47;
  }
}

.rarity-common { --gear-color: var(--rarity-common); }
.rarity-uncommon { --gear-color: var(--rarity-uncommon); }
.rarity-rare { --gear-color: var(--rarity-rare); }
.rarity-epic { --gear-color: var(--rarity-epic); }
.rarity-legendary { --gear-color: var(--rarity-legendary); }

.dur-good { --dur-color: var(--dur-good); }
.dur-fair { --dur-color: var(--dur-fair); }
.dur-worn { --dur-color: var(--dur-worn); }
.dur-critical { --dur-color: var(--dur-critical); }
.dur-unknown { --dur-color: var(--dur-unknown); }

.gear {
  margin: 8px 0 24px;
}

/* The paper doll: armor down each side of the bear, three a side. */
.gear-doll {
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  gap: 14px 12px;
  align-items: start;
  justify-items: center;
  max-width: 560px;
  margin: 16px auto 8px;
}

.gear-slot-helmet { grid-column: 1; grid-row: 1; }
.gear-slot-chest { grid-column: 1; grid-row: 2; }
.gear-slot-gloves { grid-column: 1; grid-row: 3; }
.gear-slot-sword { grid-column: 3; grid-row: 1; }
.gear-slot-shield { grid-column: 3; grid-row: 2; }
.gear-slot-boots { grid-column: 3; grid-row: 3; }

.gear-bear {
  grid-column: 2;
  grid-row: 1 / span 3;
  align-self: center;
}

.gear-bear img {
  display: block;
  width: 160px;
  max-width: 100%;
  height: auto;
}

.gear-rings {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 12px;
  margin: 8px 0 16px;
}

.gear-slot {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  width: 104px;
  text-align: center;
}

.gear-slot-box {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 6px;
  padding: 8px;
  border: 2px solid var(--gear-color, var(--border));
  border-radius: 12px;
  background: transparent;
  color: var(--gear-color, var(--muted));
  font: inherit;
}

button.gear-slot-box {
  cursor: pointer;
}

button.gear-slot-box:focus-visible {
  outline: 3px solid var(--accent);
  outline-offset: 3px;
}

.gear-empty .gear-slot-box {
  border-style: dashed;
  border-color: var(--border);
  color: var(--muted);
}

.gear-slot-label {
  margin-top: 4px;
  font-size: 0.85rem;
  font-weight: 600;
}

.gear-slot-sub {
  font-size: 0.75rem;
  color: var(--muted);
  overflow-wrap: anywhere;
}

.gear-icon {
  display: block;
  width: 56px;
  height: 56px;
}

.gear-icon .gear-fill {
  fill: currentColor;
  fill-opacity: 0.85;
  stroke: currentColor;
  stroke-width: 1.5;
  stroke-linejoin: round;
}

.gear-icon .gear-cut {
  fill: var(--bg);
  stroke: none;
}

.gear-icon .gear-line {
  fill: none;
  stroke: var(--bg);
  stroke-width: 1.6;
  stroke-linecap: round;
}

.gear-icon .gear-band {
  fill: none;
  stroke: currentColor;
  stroke-width: 5;
}

.gear-icon .gear-spark {
  fill: currentColor;
}

.gear-icon .gear-halo {
  fill: none;
  stroke: currentColor;
  stroke-width: 1;
  stroke-opacity: 0.6;
}

.gear-icon .gear-ray {
  fill: none;
  stroke: currentColor;
  stroke-width: 1.5;
  stroke-linecap: round;
}

.gear-icon-empty .gear-fill {
  fill: none;
  stroke-dasharray: 3 3;
  stroke-opacity: 0.7;
}

.gear-icon-empty .gear-cut {
  fill: none;
}

.gear-icon-empty .gear-line,
.gear-icon-empty .gear-band {
  stroke: currentColor;
  stroke-opacity: 0.5;
}

/* The better the gear, the more it shines. */
.rarity-epic .gear-icon {
  filter: drop-shadow(0 0 2px var(--gear-color));
}

.rarity-legendary .gear-icon {
  filter: drop-shadow(0 0 4px var(--gear-color));
}

.gear-icon-bag {
  display: inline-block;
  width: 28px;
  height: 28px;
  flex: none;
}

.gear-icon-bag .gear-fill {
  fill: currentColor;
  fill-opacity: 0.25;
  stroke: currentColor;
  stroke-width: 1.4;
}

.gear-icon-bag .gear-line {
  stroke: currentColor;
}

.gear-bar {
  display: block;
  width: 44px;
  height: 6px;
}

.gear-bar-track {
  fill: var(--gear-track);
}

.gear-bar-fill {
  fill: var(--dur-color);
}

.gear-bag {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  margin: 4px 0 12px;
  color: var(--muted);
}

.gear-legend {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 4px 16px;
  margin: 0 0 12px;
  padding: 0;
  list-style: none;
  font-size: 0.9rem;
  font-weight: 600;
}

.gear-legend-item {
  color: var(--gear-color);
}

.gear-list {
  margin: 8px 0;
  padding-left: 1.2em;
  font-size: 0.9rem;
}

.gear-list li {
  margin-bottom: 8px;
}

.gear-list strong {
  color: var(--gear-color);
}

.gear-list-desc {
  display: block;
  color: var(--muted);
}

/* The tooltip is always dark, like a game tooltip, so it carries its own brighter colours. */
.gear-tip {
  --rarity-common: #b0b0b0;
  --rarity-uncommon: #4ade80;
  --rarity-rare: #4da3ff;
  --rarity-epic: #c084fc;
  --rarity-legendary: #ff9a2e;
  --dur-good: #4ade80;
  --dur-fair: #facc15;
  --dur-worn: #fb923c;
  --dur-critical: #f87171;
  --dur-unknown: #b8bdc8;
  --gear-track: #3a3e47;
  position: absolute;
  top: 100%;
  z-index: 20;
  width: min(300px, 80vw);
  padding: 12px 14px;
  border: 1px solid var(--gear-color);
  border-radius: 8px;
  background: #12141a;
  color: #e8e8e8;
  box-shadow: 0 6px 24px rgba(0, 0, 0, 0.45);
  font-size: 0.9rem;
  line-height: 1.4;
  text-align: left;
}

.gear-tip[hidden] {
  display: none;
}

.gear-align-left .gear-tip { left: 0; }
.gear-align-right .gear-tip { right: 0; }
.gear-align-center .gear-tip { left: 50%; transform: translateX(-50%); }

.gear-tip p {
  margin: 0;
}

.gear-tip-name {
  color: var(--gear-color);
  font-size: 1.05rem;
  font-weight: 700;
}

.gear-tip-rarity {
  color: var(--gear-color);
  font-size: 0.8rem;
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
}

.gear-tip .gear-tip-desc {
  margin: 8px 0;
}

.gear-tip-facts {
  margin: 0;
}

.gear-tip-row {
  display: flex;
  gap: 8px;
  margin-top: 4px;
}

.gear-tip-row dt {
  flex: none;
  min-width: 5.5em;
  color: #b8bdc8;
}

.gear-tip-row dd {
  margin: 0;
}

.gear-tip-condition {
  color: var(--dur-color);
}

.gear-tip .gear-bar {
  width: 100%;
  max-width: 160px;
  margin-top: 4px;
}

/* On a phone the tooltip is a panel along the bottom of the screen, where it cannot run off an edge. */
@media (max-width: 600px) {
  .gear-tip,
  .gear-align-left .gear-tip,
  .gear-align-right .gear-tip,
  .gear-align-center .gear-tip {
    position: fixed;
    top: auto;
    right: 16px;
    bottom: 16px;
    left: 16px;
    width: auto;
    transform: none;
  }

  .gear-slot {
    width: 84px;
  }

  .gear-icon {
    width: 44px;
    height: 44px;
  }

  .gear-bear img {
    width: 110px;
  }
}
