/*
 * Palette: "light blue, contrasty" -- matches the site's icon
 * (public/assets/icon-512.png: a minimalist cartoon caricature of Warren
 * Buffett -- bald head, oversized glasses, dark suit and red tie -- inside
 * a circular badge, navy outline on a light-blue wash). A prior round's
 * icon was a different illustration (an isometric castle/moat) in this
 * same navy-outline/light-blue-wash language; this round's icon swap kept
 * the palette unchanged and just replaced the artwork inside the badge, so
 * every color derivation below still holds.
 * --surface-page carries the icon's light blue wash across the whole site;
 * --surface-chart stays crisp white so cards/panels pop against it (the
 * "contrasty" part -- a flat off-white-on-off-white theme, which this
 * replaces, doesn't have that pop); --text-primary keys off the icon's
 * navy outline color almost exactly, so headings and body text read as
 * the same ink used to draw the icon. --series-1 (the primary chart/
 * button accent) is a punchier, more saturated blue than anything in the
 * icon itself -- the icon's blues are intentionally soft and desaturated,
 * which reads fine as illustration but goes muddy as a small UI accent
 * against white, so --series-1 stays in the same hue family without
 * literally sampling the artwork. --series-2..8 (multi-series chart
 * colors) and --diverging-neg (red, "down") are left as categorical/
 * semantic colors, not brand colors -- they exist to be *distinguishable*
 * from each other and from blue, not to match it. --success-text (green)
 * and --warning-text (amber) round out a green/amber/red good-caution-bad
 * trio with --diverging-neg for the Valuation tabs' judgment-call coloring
 * (Reverse DCF's CAGR sanity bands, Fair Price / Book's current-vs-fair
 * comparison, and Basic/Two-Stage DCF's Overvalued/Undervalued-By) --
 * these are deliberately distinct from --diverging-pos/-neg's neutral
 * arithmetic-sign convention used elsewhere (e.g. .chart-cagr-pos/-neg).
 *
 * A later round (the Rare Elephants rebrand) replaced this badge's artwork
 * a second time -- icon-512.png/icon-192.png/favicon.ico no longer show
 * the Buffett caricature described above at all; they now show the
 * wordmark's own traced "E" glyph centered in the same circular badge (see
 * public/assets/icon-source.svg's own comments, and src/partials/
 * header.php's docblock for how that glyph was traced). The Buffett
 * caricature itself wasn't deleted -- it lives on as one of three
 * homepage-hero mascots alongside new Munger/Lynch ones (mascot-
 * buffett.png, see public/index.php and src/Quotes.php) -- it's just no
 * longer the app icon/favicon. Every color derivation below is unaffected
 * either way, since only the artwork inside the badge changed, not the
 * badge's own navy-outline/light-blue-wash colors this whole palette keys
 * off.
 */
:root {
  color-scheme: light;
  --surface-page: #e7f2fb;
  --surface-chart: #ffffff;
  --text-primary: #0d2b4a;
  --text-secondary: #45607d;
  --text-muted: #7891ab;
  --gridline: #d7e6f2;
  --baseline: #b7d0e6;
  --success-text: #0a7a3d;
  --warning-text: #a66a00;
  --border-hairline: rgba(13, 43, 74, 0.14);
  /* Net Cash Flow's "use" side (Cash from Investing/Financing, Net Cash
     Flow itself) -- a dedicated rose/pink, deliberately not reusing
     --diverging-neg (Profit Flow's red "spent" color) to avoid visually
     coupling the two alluvial charts; matches --series-5's rose hue
     exactly since that's already this palette's pink. */
  --alluvial-use: #e87ba4;

  --series-1: #1663c7;
  --series-2: #eb6834;
  --series-3: #1baf7a;
  --series-4: #eda100;
  --series-5: #e87ba4;
  --series-6: #008300;
  --series-7: #4a3aa7;
  --series-8: #e34948;

  --diverging-pos: #1663c7;
  --diverging-neg: #e34948;
  --diverging-neutral: #eaf1f8;
}

@media (prefers-color-scheme: dark) {
  :root:where(:not([data-theme="light"])) {
    color-scheme: dark;
    --surface-page: #0a1826;
    --surface-chart: #122840;
    --text-primary: #eaf4fc;
    --text-secondary: #a9c4dc;
    --text-muted: #7592ac;
    --gridline: #1f3c58;
    --baseline: #2c4d6c;
    --success-text: #34c774;
    --warning-text: #e0a530;
    --border-hairline: rgba(234, 244, 252, 0.12);
    --alluvial-use: #d55181;

    --series-1: #4a9eef;
    --series-2: #d95926;
    --series-3: #199e70;
    --series-4: #c98500;
    --series-5: #d55181;
    --series-6: #008300;
    --series-7: #9085e9;
    --series-8: #e66767;

    --diverging-pos: #4a9eef;
    --diverging-neg: #e66767;
    --diverging-neutral: #23405c;
  }
}

:root[data-theme="dark"] {
  color-scheme: dark;
  --surface-page: #0a1826;
  --surface-chart: #122840;
  --text-primary: #eaf4fc;
  --text-secondary: #a9c4dc;
  --text-muted: #7592ac;
  --gridline: #1f3c58;
  --baseline: #2c4d6c;
  --success-text: #34c774;
  --warning-text: #e0a530;
  --border-hairline: rgba(234, 244, 252, 0.12);
  --alluvial-use: #d55181;

  --series-1: #4a9eef;
  --series-2: #d95926;
  --series-3: #199e70;
  --series-4: #c98500;
  --series-5: #d55181;
  --series-6: #008300;
  --series-7: #9085e9;
  --series-8: #e66767;

  --diverging-pos: #4a9eef;
  --diverging-neg: #e66767;
  --diverging-neutral: #23405c;
}

* {
  box-sizing: border-box;
}

html,
body {
  padding: 0;
  margin: 0;
}

/* Sticky-footer layout: body is a full-viewport-height flex column so
   .site-footer (see further below) always sits flush with the bottom of
   the viewport, even on a short page with little content above it --
   without this, a short page (e.g. the homepage) left the footer
   floating right after the hero instead of "all the way at the bottom"
   as explicitly requested. main.container (below) is the flexed middle
   piece that absorbs any leftover vertical space; .site-header and
   .site-footer both stay their natural (non-flexed) height. */
body {
  display: flex;
  flex-direction: column;
  min-height: 100vh;
  background: var(--surface-page);
  color: var(--text-primary);
  font-family: system-ui, -apple-system, "Segoe UI", sans-serif;
  font-size: 15px;
  line-height: 1.5;
}

a {
  color: inherit;
}

.container {
  max-width: 1080px;
  margin: 0 auto;
  padding: 0 24px;
}

/* Extra bottom breathing room on every page's <main> so the fixed-position
   .mascot-corner widget (further below) never sits directly on top of the
   last bit of in-flow content -- it's positioned outside document flow,
   so without this the page's real bottom edge and the widget's fixed
   corner would otherwise coincide. */
main.container {
  padding-bottom: 110px;
  /* Grows to fill whatever vertical space .site-header + .site-footer
     don't need, pushing the footer down to the viewport's bottom edge on
     a short page -- see body's own comment above for the full picture. */
  flex: 1 0 auto;
}

/* The header's own hairline border-bottom was removed site-wide at
   explicit request ("in the top bar remove the visible line across the
   top. When viewing a ticker I also want that line to not be visible") --
   a single change here covers both, since every page (homepage and every
   company.php ticker page alike) shares this one .site-header. */
.site-header {
  padding: 16px 0;
  flex-shrink: 0;
}

.brand-wordmark {
  text-decoration: none;
  font-size: 30px;
  font-weight: 700;
  letter-spacing: -0.01em;
  line-height: 1;
  display: inline-flex;
  align-items: baseline;
}

/* Homepage-only, larger copy of the same wordmark, shown directly above
   the search box in place of the header's own top-left copy (which is
   suppressed there -- see header.php's $hideHeaderWordmark) per explicit
   request ("I want the RareElephants icon move over top of the search
   bar"). .hero already centers its children via text-align, so an
   inline-flex element like .brand-wordmark needs no extra centering
   rules here -- just the bigger size and some breathing room before the
   search box below it. */
.hero-wordmark {
  font-size: 56px;
  margin: 0 0 24px;
}

/* Scales down on narrow viewports -- at the full 56px size "Elephants"
   overflows a phone-width screen since .brand-wordmark's own inline-flex
   layout doesn't wrap mid-word. */
@media (max-width: 480px) {
  .hero-wordmark {
    font-size: 34px;
  }
}

.brand-wordmark-fast {
  color: var(--text-primary);
}

.brand-wordmark-rest {
  color: var(--series-1);
}

/* The "subtle elephant" letter (explicit request: "Incorporate a subtle
   elephant into one of the letters"). A later round replaced the earlier
   hand-drawn version (a plain block E with a stroked trunk) with a
   faithful vector trace of a user-supplied reference logo -- see
   header.php's own comment on this glyph for the tracing pipeline and why
   it's two separate `<path>` elements rather than one. Sized in em units
   (width/height set directly on the SVG element in header.php, matching
   its own viewBox aspect ratio) so it scales with the surrounding text and
   stays crisp at any zoom level, unlike a raster icon would.
   .brand-wordmark-e-body ties to the same blue as "lephants"
   (.brand-wordmark-rest) and .brand-wordmark-e-eye ties to the same dark
   navy as "Rare" (.brand-wordmark-fast) -- both theme-variable-driven
   (not a hardcoded second color) so light/dark mode still adapt
   automatically, replacing the old single-color currentColor/fill
   approach now that the traced glyph genuinely has two differently-
   colored parts rather than one solid letterform with a punched-out eye. */
.brand-wordmark-e {
  display: inline-block;
  vertical-align: -0.05em;
}

.brand-wordmark-e-body {
  fill: var(--series-1);
}

.brand-wordmark-e-eye {
  fill: var(--text-primary);
}

.site-header-inner {
  display: flex;
  align-items: center;
  justify-content: flex-start;
  gap: 24px;
  flex-wrap: wrap;
  row-gap: 8px;
}

/* The dark-mode toggle always sits at the header's far right edge (per
   explicit request). margin-left: auto rather than justify-content:
   space-between on .site-header-inner, since .brand-wordmark and the
   conditional .search-box before this div don't always add up to a fixed
   width (the search box only renders on a company page -- see
   header.php), and a lone flex child sits at flex-start, not the far
   edge, under space-between with nothing else after it in the row. */
.site-header-actions {
  display: flex;
  align-items: center;
  gap: 16px;
  margin-left: auto;
  flex-shrink: 0;
}

.header-nav-link {
  color: var(--text-secondary);
  font-size: 14px;
  text-decoration: none;
  white-space: nowrap;
}

.header-nav-link:hover {
  color: var(--text-primary);
}

.theme-toggle {
  border: 1px solid var(--border-hairline);
  background: var(--surface-chart);
  color: var(--text-secondary);
  border-radius: 8px;
  padding: 6px 12px;
  font-size: 13px;
  cursor: pointer;
}

.theme-toggle:hover {
  color: var(--text-primary);
}

.chart-card {
  background: var(--surface-chart);
  border: 1px solid var(--border-hairline);
  border-radius: 12px;
  padding: 20px 20px 8px;
  margin-bottom: 24px;
}

.chart-card-header {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  margin-bottom: 4px;
  gap: 12px;
  flex-wrap: wrap;
}

.chart-card-title {
  font-size: 15px;
  font-weight: 600;
  color: var(--text-primary);
  margin: 0;
}

.chart-card-subtitle {
  font-size: 12px;
  color: var(--text-muted);
  margin: 2px 0 12px;
}

.chart-card-year {
  font-size: 12px;
  font-weight: 400;
  color: var(--text-muted);
}

/* --- Combined dual-pie card (Asset & Liabilities Breakdown) ------------ */

.dual-pie-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 24px;
}

@media (max-width: 720px) {
  .dual-pie-grid {
    grid-template-columns: 1fr;
  }
}

.dual-pie-item-title {
  font-size: 13px;
  font-weight: 600;
  color: var(--text-secondary);
  margin: 12px 0 8px;
}

.dual-pie-item .dual-pie-item-title {
  margin-top: 0;
}

.pie-total-line {
  font-size: 13px;
  color: var(--text-secondary);
  margin: 0 0 10px;
}

.pie-total-line strong {
  color: var(--text-primary);
}

.dual-pie-empty {
  font-size: 13px;
  color: var(--text-muted);
  padding: 32px 8px;
  text-align: center;
  border: 1px dashed var(--border-hairline);
  border-radius: 8px;
}

.view-toggle {
  display: flex;
  gap: 4px;
}

.view-toggle button {
  border: 1px solid var(--border-hairline);
  background: transparent;
  color: var(--text-secondary);
  border-radius: 6px;
  padding: 4px 10px;
  font-size: 12px;
  cursor: pointer;
}

.view-toggle button[aria-pressed="true"] {
  background: var(--series-1);
  border-color: var(--series-1);
  color: white;
}

.data-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 13px;
  font-variant-numeric: tabular-nums;
}

.data-table th,
.data-table td {
  text-align: right;
  padding: 6px 10px;
  border-bottom: 1px solid var(--gridline);
  white-space: nowrap;
}

.data-table th:first-child,
.data-table td:first-child {
  text-align: left;
}

.data-table thead th {
  color: var(--text-muted);
  font-weight: 500;
  font-size: 12px;
}

.csv-download-btn {
  margin-top: 10px;
  border: 1px solid var(--border-hairline);
  background: transparent;
  color: var(--text-secondary);
  border-radius: 6px;
  padding: 5px 12px;
  font-size: 12px;
  cursor: pointer;
}

.csv-download-btn:hover {
  background: var(--surface-chart);
  color: var(--text-primary);
}

.legend-row {
  display: flex;
  gap: 16px;
  flex-wrap: wrap;
  font-size: 12px;
  color: var(--text-secondary);
  margin-bottom: 8px;
}

.legend-item {
  display: flex;
  align-items: center;
  gap: 6px;
}

.legend-toggle {
  cursor: pointer;
  user-select: none;
}

.legend-checkbox {
  margin: 0;
  cursor: pointer;
}

.legend-item--dim {
  opacity: 0.45;
}

.legend-swatch {
  width: 10px;
  height: 10px;
  border-radius: 2px;
  display: inline-block;
}

.search-box {
  position: relative;
  flex: 1;
  max-width: 420px;
  /* A flex item's default min-width: auto floors it at its content's
     intrinsic minimum width (a text <input> defaults to a sizable one)
     rather than actually letting `flex: 1`'s flex-shrink shrink it -- on
     a narrow (mobile) viewport that intrinsic floor, plus
     .brand-wordmark and .site-header-actions alongside it in
     .site-header-inner, added up to wider than the viewport itself,
     pushing the theme-toggle button off the right edge into horizontal
     page scroll. 160px replaces that intrinsic floor with a small,
     deliberate one instead of removing it to 0 -- paired with
     .site-header-inner's own flex-wrap (above), that makes the search
     box wrap down onto its OWN full-width row once there's under 160px
     of space left on the wordmark's row, rather than (min-width: 0's
     actual effect) getting squeezed down to a barely-usable sliver
     alongside it. */
  min-width: 160px;
}

.search-input {
  width: 100%;
  padding: 8px 12px;
  border-radius: 8px;
  border: 1px solid var(--border-hairline);
  background: var(--surface-chart);
  color: var(--text-primary);
  font-size: 14px;
}

.search-results {
  position: absolute;
  top: calc(100% + 4px);
  left: 0;
  right: 0;
  background: var(--surface-chart);
  border: 1px solid var(--border-hairline);
  border-radius: 8px;
  overflow: hidden;
  z-index: 10;
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.12);
}

.search-result-row {
  display: flex;
  justify-content: space-between;
  gap: 12px;
  padding: 10px 12px;
  text-decoration: none;
  color: var(--text-primary);
}

.search-result-row:hover {
  background: var(--gridline);
}

.search-result-ticker {
  font-weight: 600;
}

.search-result-name {
  color: var(--text-secondary);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.hero {
  padding: 64px 0 32px;
  text-align: center;
}

/* Originally an explicit request ("for the .hero h1 change the font-size
   to 16px, and the max-width to 300px.") targeting the speech-bubble text
   (see .hero-speech-bubble below), which was still a bare <h1> selector
   match at the time. The same two values were also set directly on
   .hero-speech-bubble itself just below, tag-agnostic, since the
   mascot-corner widget renders this as a <p> rather than an <h1> on every
   page except the homepage (see mascot_corner.php's own docblock) -- kept
   in sync so both rules compute to the same thing wherever both apply.

   That sync matters more than it looks: on the homepage the bubble IS an
   `<h1 class="hero-speech-bubble">`, so both this selector and
   .hero-speech-bubble match it at once, and `.hero h1`'s two-part
   selector (0,1,1 specificity) always wins the cascade over
   .hero-speech-bubble's single class (0,1,0) regardless of which rule
   comes later in the file. Two later direct follow-ups on
   .hero-speech-bubble alone -- "Make the text in the speech bubble
   14px." then "Change the max-width of the bubble to 250px. This change
   should affect the bubble on the home page and when viewing tickers." --
   would otherwise have silently done nothing on the homepage specifically
   (verified live: a real headless-browser render of the homepage still
   showed the old 16px/300px after only editing .hero-speech-bubble,
   because this higher-specificity rule was still winning there), even
   though the requested values were genuinely present in the stylesheet.
   Updating this rule's own values to match is what actually makes both
   follow-ups take effect on the homepage, not just on ticker pages. */
.hero h1 {
  font-size: 14px;
  max-width: 250px;
  margin: 0 0 12px;
}

/* Mascot + speech-bubble widget (task: "Where the header used to be add a
   buffet logo but larger, and have a speech bubble coming out of him that
   says 'Start with the A's.'"). Originally lived at the top of the
   homepage's .hero, with the bubble text itself doubling as the page's
   <h1>.

   A later round ("I want... the mascot/speech bubble moved to the bottom
   right corner" + "Make it so we see a mascot/bubble in the bottom right
   even when viewing tickers") moved this out of the homepage-only .hero
   entirely into its own shared, fixed-position .mascot-corner widget (see
   src/partials/mascot_corner.php) shown on every page -- these two
   classes (.hero-mascot / .hero-speech-bubble) are still the base
   styling for it, just no longer only reachable via .hero's own
   in-document-flow layout. See .mascot-corner further below for the
   fixed-position override and the icon/bubble-order flip that puts the
   icon nearest the actual corner. */
.hero-mascot {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  flex-wrap: wrap;
  margin: 0 0 28px;
}

.hero-mascot-icon {
  display: block;
  border-radius: 18px;
  flex-shrink: 0;
}

/* Background and text color are hardcoded (not the usual --surface-chart/
   --text-primary theme tokens) at an explicit request: the bubble should
   read as white/paper regardless of site theme, like a real speech
   bubble, rather than flipping to a dark fill with near-white text in
   dark mode the way every other card on the site intentionally does.

   font-size/max-width duplicate the .hero h1 rule above's values, kept in
   sync with it deliberately -- see that rule's own comment for why both
   exist, and why keeping them in sync (not just present in the
   stylesheet) is what actually makes a value change here take visible
   effect on the homepage. Two later, direct follow-ups ("Make the text
   in the speech bubble 14px." then "Change the max-width of the bubble
   to 250px. This change should affect the bubble on the home page and
   when viewing tickers.") shrank both values from the original 16px/
   300px down to the current 14px/250px, on both rules at once. */
.hero-speech-bubble {
  position: relative;
  display: inline-block;
  background: #ffffff;
  border: 1px solid var(--border-hairline);
  border-radius: 15px;
  padding: 11px 18px;
  margin: 0;
  font-size: 14px;
  max-width: 250px;
  font-weight: 700;
  line-height: 1.25;
  color: #0d2b4a;
  box-shadow: 0 2px 10px rgba(13, 43, 74, 0.08);
}

/* The <q> element supplies the wrapping quotation marks itself (locale-
   aware curly quotes, via the `quotes` property below) rather than the
   PHP template embedding literal quote characters -- so a quote file's
   own text never has to worry about escaping or nesting them. Italicized
   at the same explicit request that asked for the quotation marks, now
   that this line can be any of several mascots' quotes rather than
   always the same fixed headline (see src/Quotes.php). */
.hero-speech-bubble-text {
  font-style: italic;
  quotes: "\201C" "\201D";
}

/* The tail pointing back toward the mascot's head, drawn as a pair of CSS
   triangles (border-width trick) stacked on top of each other -- the outer
   (::before, slightly larger, --border-hairline colored) peeks out from
   behind the inner (the element itself, --surface-chart colored, matching
   the bubble's own fill) to fake a 1px outline on a shape that can't take a
   real `border` the way the bubble's rounded-rect body can. */
.hero-speech-bubble-tail {
  position: absolute;
  left: -10px;
  bottom: 13px;
  width: 0;
  height: 0;
  border-style: solid;
  border-width: 6px 11px 6px 0;
  /* Matches the bubble's own hardcoded white fill above, not
     --surface-chart -- same reasoning, so the tail doesn't turn a
     mismatched dark color against the bubble's now-always-white body. */
  border-color: transparent #ffffff transparent transparent;
}

.hero-speech-bubble-tail::before {
  content: "";
  position: absolute;
  top: -7px;
  left: -1px;
  width: 0;
  height: 0;
  border-style: solid;
  border-width: 7px 12px 7px 0;
  border-color: transparent var(--border-hairline) transparent transparent;
  z-index: -1;
}

/* Below ~560px the bubble wraps under the icon instead of sitting beside
   it -- a left-pointing tail stops making sense once they're stacked
   vertically rather than side by side, so it's hidden rather than left
   pointing at nothing. */
@media (max-width: 560px) {
  .hero-mascot {
    flex-direction: column;
  }

  .hero-speech-bubble-tail {
    display: none;
  }
}

/* Mascot-corner widget (see src/partials/mascot_corner.php) -- shown on
   every page, not just the homepage, per "Make it so we see a
   mascot/bubble in the bottom right even when viewing tickers." An
   earlier round made this `position: fixed` with public/assets/
   mascot_corner.js fading it to a low opacity at runtime whenever its box
   overlapped a .chart-card; a later round reversed that to plain
   in-document-flow placement at the very bottom of the page. Newer,
   more specific feedback reversed THAT in turn: "Move the mascot/bubble
   back to the way it was in the bottom right corner. When viewing a
   stock I don't want to see it transparently over the chart. I just want
   it to appear once you scroll down." So it's `position: fixed` again --
   with mascot_corner.js toggling a hard hide/show class instead of
   fading to a low-but-nonzero opacity, so the widget is always either
   fully invisible or fully visible, never a translucent overlay on top of
   chart content.

   The very next round of feedback was about the moment of first paint,
   not steady-state behavior: "When I refresh the page or click on
   another graph link the mascot 'flashes' on the right. I don't want to
   see that flash when I load a new page." The widget used to default to
   `opacity: 1` (visible) in CSS, with mascot_corner.js only hiding it a
   frame later via requestAnimationFrame once it had computed whether the
   widget overlaps a .chart-card -- on any page where it starts out
   overlapping (the common case, since a chart card usually sits right
   where this corner would be before a reader scrolls), that's a real
   visible-then-hidden flash on every load, not just a perception issue.
   Fixed by flipping the default: the widget now starts fully invisible
   (`opacity: 0`) the instant it's in the DOM, before any script has even
   run, and mascot_corner.js adds `.mascot-corner-visible` only once it
   has actually confirmed there's no overlap -- so there is no default
   "on" state to flash away from. See mascot_corner.js for why its very
   first visibility check also runs synchronously rather than through
   requestAnimationFrame, and main.container's padding-bottom rule
   further below, which keeps this fixed-position widget from covering
   the last bit of in-flow page content whenever it does end up shown. */
/* Explicit follow-up request: "when the mascot appears on the page I want
   him to slide out slowly from the right side of the page instead of
   just flash appearing." Previously this was a plain opacity fade (0.15s
   -- fast, and no motion at all, hence "flash"). Now the hidden state
   also carries a rightward translateX offset (effectively just off the
   right edge of the viewport, since the widget itself already sits
   flush against `right: 20px`), and both `opacity`/`transform` transition
   together over a much longer, slower duration -- so the widget visibly
   slides in from the right while it fades up, instead of blinking on in
   place. This is defined once, here on the shared base rule, rather than
   per reveal-mode: BOTH the homepage's 5s-timer reveal and a ticker
   page's overlap-triggered reveal just toggle the same
   .mascot-corner-visible class (see mascot_corner.js), so one CSS
   transition covers "whenever it appears" on every page automatically,
   with no JS changes needed. */
.mascot-corner {
  position: fixed;
  right: 20px;
  bottom: 20px;
  left: auto;
  padding: 0;
  text-align: right;
  z-index: 15;
  max-width: calc(100vw - 40px);
  pointer-events: none;
  opacity: 0;
  transform: translateX(60px);
  transition: opacity 0.9s ease-out, transform 0.9s ease-out;
}

.mascot-corner.mascot-corner-visible {
  opacity: 1;
  transform: translateX(0);
}

.mascot-corner .hero-mascot {
  margin: 0;
  pointer-events: auto;
}

/* Pinned to the corner, so the icon sits to the RIGHT of the bubble (tail
   pointing right, back toward the icon) instead of the in-flow default of
   icon-then-bubble -- mirrors the whole row so the icon reads as the
   badge anchoring the corner, with the bubble growing to its left rather
   than running off the right edge of the viewport.

   `gap` is widened well past the base rule's 8px specifically here --
   direct follow-up feedback: "move the bubble/tail over so it doesn't
   overlap with the mascot." The tail (see
   .mascot-corner .hero-speech-bubble-tail below) is a 30px-wide box that
   starts flush against the bubble's right edge and extends into this
   gap; at the old 8px gap it ran 22px past the gap and into the icon's
   own box. 38px gives the full 30px tail room to render, plus an 8px
   buffer so its tip stops clearly short of the icon instead of just
   grazing it -- still reading as "pointing toward the mascot" (same
   direction, same vertical center) without actually touching it. */
@media (min-width: 561px) {
  .mascot-corner .hero-mascot {
    flex-direction: row-reverse;
    gap: 38px;
  }
}

/* Comic-book-style tail, replacing the sideways mid-height triangle above
   (explicit request: "Fix the speech bubble so it has a point coming out
   of the bottom right going to the mascot. Similar to how you'd see in a
   comic book."). This is the ONLY .hero-speech-bubble-tail rendering
   actually reachable in practice -- mascot_corner.php's wrapper div
   always carries the .mascot-corner class (there's no longer a bare
   in-flow, non-corner rendering of this widget anywhere in the app), so
   this override, not the base rule above, is what every reader actually
   sees.

   A first pass anchored the tail's point BELOW the bubble (a box hanging
   off the bottom-right corner, its point aimed down-and-right). Direct
   follow-up feedback: "Change the speech bubble so the point coming out
   of the bubble points to the mascot" -- the mascot icon actually sits
   immediately to the bubble's RIGHT, vertically centered with it (see
   the row-reverse rule above), not below it, so a point aimed downward
   read as disconnected from the icon rather than pointing at it. Fixed
   by re-anchoring the box to extend out the bubble's right edge instead
   (`left: 100%` -- flush against that edge -- rather than hanging off
   the bottom).

   A second follow-up moved the tail's own vertical position: "Move the
   bubble tail so it's in the middle of the bubble." The box is now
   centered with `top: 50%; transform: translateY(-50%);` (rather than a
   fixed `bottom` offset) so it stays truly centered on the bubble's
   height regardless of how tall the bubble ends up being -- a longer
   quote that wraps to two lines grows the bubble, and a bottom-anchored
   offset would have drifted off-center in that case, while a
   percentage-based center stays correct. The polygon's own base/tip
   points were re-centered to match (a symmetric base spanning the box's
   middle 70% of its height, tip dead-center at 50%), so the point lands
   exactly level with both the box's and the bubble's vertical middle --
   which, since the icon is *also* vertically centered with the bubble
   (the row-reverse rule above), is exactly where the icon's own center
   sits too.

   Drawn as a `clip-path` triangle instead of the base rule's border trick
   -- a border-width triangle can only really point along an axis (left/
   right/up/down); a true diagonal comic-tail point needs an arbitrary
   polygon. */
.mascot-corner .hero-speech-bubble-tail {
  left: 100%;
  right: auto;
  top: 50%;
  bottom: auto;
  width: 30px;
  height: 18px;
  border: 0;
  background: #ffffff;
  clip-path: polygon(0% 15%, 0% 85%, 100% 50%);
  transform: translateY(-50%);
}

/* Flex items paint in an order-modified-document-order stacking context
   of their own (per the flexbox spec's painting-order rules), which put
   .hero-mascot-icon (an earlier flex item) ABOVE .hero-speech-bubble in
   paint order here. Originally added so the tail would show up on top of
   the icon where the two deliberately overlapped (caught by a debug
   render with the tail's clip-path removed and colored solid red: the
   icon visibly covered most of that box). The `gap` widening above (see
   its own comment) now keeps the tail clear of the icon entirely, so
   there's nothing left to paint over -- but the rule is left in place as
   a harmless safety net (a bubble already `position: relative`, promoted
   to its own explicit stacking level) in case a future tweak ever brings
   the two back into contact. */
.mascot-corner .hero-speech-bubble {
  z-index: 1;
}

/* Same 1px-larger-peeking-out-behind trick the base rule's own ::before
   uses (see its comment above) to fake an outline on a shape that can't
   take a real `border` -- just re-expressed for a clip-path polygon
   instead of a border-triangle: an absolutely-positioned box inset -1px
   on every side (so it's uniformly 1px bigger without needing its own
   width/height math), filled --border-hairline, clipped to the same
   polygon shape, and sat behind (z-index: -1) the white tail above. */
.mascot-corner .hero-speech-bubble-tail::before {
  top: -1px;
  left: -1px;
  right: -1px;
  bottom: -1px;
  width: auto;
  height: auto;
  border: 0;
  background: var(--border-hairline);
  clip-path: polygon(0% 0%, 55% 0%, 100% 100%);
}

@media (max-width: 420px) {
  .mascot-corner .hero-speech-bubble {
    max-width: 200px;
  }
}

/* :not(.hero-speech-bubble) matters now that the mascot-corner widget's
   bubble renders as a <p> (not an <h1>) on every page except the
   homepage (see mascot_corner.php) -- that <p> also happens to be a
   descendant of a .hero-classed div (see .mascot-corner), so without
   this exclusion this rule's higher specificity than .hero-speech-bubble
   alone would silently override that bubble's own hardcoded white-bubble
   text color and margin with these page-body ones instead. */
.hero p:not(.hero-speech-bubble) {
  color: var(--text-secondary);
  margin: 0 0 32px;
}

.hero .search-box {
  margin: 0 auto;
}

.ticker-chip-row {
  display: flex;
  gap: 8px;
  justify-content: center;
  flex-wrap: wrap;
  margin-top: 16px;
}

.ticker-chip {
  border: 1px solid var(--border-hairline);
  border-radius: 999px;
  padding: 6px 14px;
  font-size: 13px;
  text-decoration: none;
  color: var(--text-secondary);
}

.ticker-chip:hover {
  color: var(--text-primary);
  border-color: var(--text-secondary);
}

.company-header {
  padding: 32px 0 8px;
}

.company-header h1 {
  margin: 0 0 4px;
  font-size: 26px;
}

.company-header .meta {
  color: var(--text-secondary);
  font-size: 14px;
}

.meta-sep {
  color: var(--text-muted);
  margin: 0 4px;
}

/* --- business description (Item 1. Business, from the latest 10-K) ----- */

.business-panel {
  margin-top: 14px;
  padding-top: 14px;
  border-top: 1px solid var(--border-hairline);
  max-width: 860px;
}

.business-text {
  margin: 0 0 8px;
  font-size: 14px;
  line-height: 1.6;
  color: var(--text-secondary);
}

.business-more {
  margin-bottom: 8px;
}

.business-more summary {
  cursor: pointer;
  font-size: 13px;
  font-weight: 600;
  color: var(--series-1);
  list-style: none;
}

.business-more summary::-webkit-details-marker {
  display: none;
}

.business-more summary::before {
  content: "+ ";
}

.business-more[open] summary::before {
  content: "\2212 "; /* minus sign */
}

.business-more .business-text {
  margin-top: 8px;
}

.business-source {
  margin: 0;
  font-size: 12px;
  color: var(--text-muted);
}

.business-source a {
  color: var(--text-muted);
}

.charts-grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: 0;
  padding: 8px 0 64px;
}

/* --- Annual/Quarterly period toggle ------------------------------------ */

.period-toggle {
  display: inline-flex;
  gap: 4px;
  margin: 12px 0 4px;
}

.period-toggle-link {
  border: 1px solid var(--border-hairline);
  background: var(--surface-chart);
  color: var(--text-secondary);
  border-radius: 6px;
  padding: 5px 12px;
  font-size: 13px;
  text-decoration: none;
}

.period-toggle-link:hover {
  color: var(--text-primary);
}

.period-toggle-link.active {
  background: var(--series-1);
  border-color: var(--series-1);
  color: white;
  font-weight: 600;
}

/* --- Shared row for both toggles on a bar-type chart page: Annual/
   Quarterly (.period-toggle) on the left, 5Y/10Y/MAX (.range-toggle) on
   the right — see company.php's $showPeriodToggle/$isBarPage branch.
   Wraps to two lines on a narrow viewport rather than squeezing/
   overlapping. On a page with only the period toggle (not a bar-type
   chart), .period-toggle renders outside this wrapper and keeps its own
   original top/bottom margins untouched. ------------------------------ */

.toggles-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 8px;
  margin: 12px 0 4px;
}

.toggles-row .period-toggle,
.toggles-row .range-toggle {
  margin: 0;
}

/* Groups Annual/Quarterly and (on a compare-capable page) the Compare
   search together on the left side of .toggles-row, level with 5Y/10Y/MAX
   on the right -- .toggles-row's own justify-content: space-between pushes
   this group and .range-toggle to opposite ends with exactly these two
   flex children. Wraps its own contents on a narrow viewport rather than
   overlapping the range toggle. */
.toggles-row-start {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 8px 16px;
}

.toggles-row-start .compare-row {
  margin: 0;
}

/* A compare error message (an unresolvable typed ticker) forces onto its
   own line within the left group rather than squeezing in beside the
   period toggle/compare form. */
.toggles-row-start .compare-error {
  flex-basis: 100%;
}

/* --- 5Y/10Y/MAX chart date-range toggle (bar-type chart pages only — see
   render_range_toggle()/company.php's $isBarPage). Same look as the
   Annual/Quarterly toggle just above; sits to the right of it in
   .toggles-row. ---------------------------------------------------------- */

.range-toggle {
  display: inline-flex;
  gap: 4px;
  margin: 8px 0 4px;
}

.range-toggle-link {
  border: 1px solid var(--border-hairline);
  background: var(--surface-chart);
  color: var(--text-secondary);
  border-radius: 6px;
  padding: 5px 12px;
  font-size: 13px;
  text-decoration: none;
}

.range-toggle-link:hover {
  color: var(--text-primary);
}

.range-toggle-link.active {
  background: var(--series-1);
  border-color: var(--series-1);
  color: white;
  font-weight: 600;
}

/* --- 5Y/10Y pan arrows (item 13) — flank the 5Y/10Y/MAX links inside the
   same .range-toggle row, sliding a fixed-size window one fiscal year at a
   time instead of changing its size. Same footprint/look as a
   .range-toggle-link so the row's height doesn't jump when they toggle
   between enabled/disabled. Always disabled on MAX and at either end of
   the available history — see render_range_toggle()'s docblock. -------- */

.range-pan-arrow {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border: 1px solid var(--border-hairline);
  background: var(--surface-chart);
  color: var(--text-secondary);
  border-radius: 6px;
  width: 28px;
  padding: 5px 0;
  font-size: 13px;
  line-height: 1;
  text-decoration: none;
}

a.range-pan-arrow:hover {
  color: var(--text-primary);
}

.range-pan-arrow--disabled {
  opacity: 0.35;
  cursor: default;
}

/* --- financial statement tables (Income Statement / Balance Sheet / Cash
   Flow) — full multi-year tables, not a chart/table toggle card. --------- */

.statement-card .chart-card-header {
  margin-bottom: 12px;
}

/* Bounds the table's horizontal scroll to this card instead of the whole
   page — combined with the period-column pagination (View.php's
   paginate_periods()), this is what keeps a long quarterly history from
   "running endlessly to the right." */
.statement-scroll {
  overflow-x: auto;
  padding-bottom: 12px;
  max-width: 100%;
}

.statement-table th,
.statement-table td {
  white-space: nowrap;
}

.statement-table th:first-child,
.statement-table td:first-child {
  min-width: 220px;
  max-width: 260px;
  white-space: normal;
  /* Sticky line-item column: stays put while the period columns scroll
     horizontally within .statement-scroll, so a long row label is never
     scrolled out of view along with the numbers it labels. */
  position: sticky;
  left: 0;
  background: var(--surface-chart);
  z-index: 1;
}

.statement-row-section td {
  padding-top: 16px;
  font-size: 11px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--text-muted);
  border-bottom: none;
}

.statement-row-subtotal td,
.statement-row-calculated td {
  font-weight: 700;
  border-top: 1px solid var(--text-secondary);
}

.statement-row-percent td {
  color: var(--text-muted);
  font-size: 12px;
}

.statement-footnote {
  margin: 12px 0 0;
  font-size: 12px;
  color: var(--text-muted);
}

.statement-pagination {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 16px;
  margin-top: 12px;
  padding-top: 12px;
  border-top: 1px solid var(--border-hairline);
}

.statement-pagination-link {
  font-size: 13px;
  color: var(--series-1);
  text-decoration: none;
  font-weight: 600;
}

.statement-pagination-link:hover {
  text-decoration: underline;
}

.statement-pagination-link.disabled {
  color: var(--text-muted);
  font-weight: 400;
  cursor: default;
}

.statement-pagination-status {
  font-size: 12px;
  color: var(--text-muted);
}

/* --- valuation (DCF/Fair P/B/Liquidation Value calculators) ------------- */

.valuation-stats-row,
.valuation-inputs-row {
  display: flex;
  gap: 24px;
  flex-wrap: wrap;
  margin: 16px 0;
}

.valuation-stat,
.valuation-input {
  display: flex;
  flex-direction: column;
  gap: 4px;
}

.valuation-stat-label {
  font-size: 12px;
  color: var(--text-muted);
}

.valuation-stat-value {
  font-size: 20px;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}

.valuation-input input {
  width: 120px;
  padding: 6px 10px;
  border-radius: 6px;
  border: 1px solid var(--border-hairline);
  background: var(--surface-page);
  color: var(--text-primary);
  font-size: 14px;
  font-variant-numeric: tabular-nums;
}

.valuation-result {
  display: flex;
  align-items: baseline;
  gap: 10px;
  margin-top: 8px;
  padding-top: 16px;
  border-top: 1px solid var(--border-hairline);
}

.valuation-result-label {
  font-size: 13px;
  color: var(--text-muted);
}

.valuation-result-value {
  font-size: 32px;
  font-weight: 700;
  color: var(--series-1);
  font-variant-numeric: tabular-nums;
}

/*
 * The Basic DCF and Two-Stage DCF tabs' Fair Value/Market Value/
 * Overvalued-or-Undervalued-By trio (and Fair Price / Book's Fair P/B/
 * Price-Book pair) render as multiple .valuation-result blocks side by
 * side in one row, rather than the single centered result the Reverse DCF
 * and Liquidation Value tabs show. All values in the row share one size
 * (--valuation-result-value's 32px) -- an earlier version rendered the
 * secondary figures smaller, but the user asked for all three the same
 * size, so that override was removed. No top border/padding of their own
 * on the individual blocks -- the row's shared border reads as one result
 * group, not several stacked cards.
 */
.valuation-results-row {
  display: flex;
  gap: 32px;
  flex-wrap: wrap;
  margin-top: 8px;
  padding-top: 16px;
  border-top: 1px solid var(--border-hairline);
}

.valuation-results-row .valuation-result {
  margin-top: 0;
  padding-top: 0;
  border-top: none;
}

/*
 * Generic good/caution/bad semantic coloring, reused across three distinct
 * judgment calls on the Valuation tabs: Basic/Two-Stage DCF's Overvalued-
 * or-Undervalued-By figure (valuation.js's applyFairValueComparison()),
 * Reverse DCF's Implied CAGR sanity bands (reverseCagrColorClass()), and
 * Fair Price / Book's current-vs-fair Price/Book comparison
 * (fairPbDiffColorClass()). Unlike .chart-cagr-pos/.chart-cagr-neg (a
 * neutral arithmetic-sign cue), these explicitly rank "good vs. bad" for
 * the user, so they draw from --success-text/--warning-text/--diverging-neg
 * rather than the blue/red diverging pair those use.
 */
.valuation-color-good {
  color: var(--success-text);
}

.valuation-color-caution {
  color: var(--warning-text);
}

.valuation-color-bad {
  color: var(--diverging-neg);
}

/* "Formula:" footnote at the bottom of every Valuation card (see
 * View.php's render_valuation_formula()) -- small and muted like
 * .chart-card-subtitle, with a slightly heavier "Formula:" label prefix. */
.valuation-formula {
  margin-top: 12px;
  font-size: 12px;
  line-height: 1.5;
  color: var(--text-muted);
}

.valuation-formula-label {
  font-weight: 600;
  color: var(--text-secondary);
}

/* --- metric nav (macrotrends-style two-tier horizontal menu) ----------- */

.metric-nav {
  margin-top: 12px;
}

/* Tier 1: main categories. Always all visible, plain horizontal tab row. */
.metric-nav-main {
  list-style: none;
  display: flex;
  flex-wrap: wrap;
  margin: 0;
  padding: 0;
  border-top: 1px solid var(--border-hairline);
  border-bottom: 1px solid var(--border-hairline);
}

.metric-nav-main-link {
  display: block;
  color: var(--text-secondary);
  font-size: 14px;
  font-weight: 600;
  text-decoration: none;
  padding: 13px 16px;
  border-bottom: 2px solid transparent;
  margin-bottom: -1px; /* sit on top of the row's own bottom border */
}

.metric-nav-main-link:hover {
  color: var(--text-primary);
}

.metric-nav-main-link.active {
  color: var(--text-primary);
  border-bottom-color: var(--series-1);
}

/* Tier 2: submenu for whichever category is active — only this one row
   ever renders, so nothing shows until a main category is selected. */
.metric-nav-sub {
  list-style: none;
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  margin: 0;
  padding: 10px 0;
  background: var(--surface-page);
}

.metric-nav-sub-link {
  display: flex;
  align-items: center;
  gap: 6px;
  font-size: 13px;
  color: var(--text-secondary);
  text-decoration: none;
  padding: 6px 12px;
  border-radius: 999px;
  border: 1px solid var(--border-hairline);
  background: var(--surface-chart);
  white-space: nowrap;
}

.metric-nav-sub-link:hover {
  color: var(--text-primary);
  border-color: var(--text-secondary);
}

.metric-nav-sub-link.unavailable {
  color: var(--text-muted);
}

/* Comes after .unavailable so an active-AND-unavailable item (you're
   looking at a "coming soon" chart right now) still gets readable white
   text on the blue fill, instead of the muted gray losing the tie. */
.metric-nav-sub-link.active {
  background: var(--series-1);
  border-color: var(--series-1);
  color: white;
  font-weight: 600;
}

.soon-badge {
  font-size: 10px;
  letter-spacing: 0.03em;
  text-transform: uppercase;
  color: var(--text-muted);
  border: 1px solid var(--border-hairline);
  border-radius: 999px;
  padding: 1px 6px;
}

.metric-nav-sub-link.active .soon-badge {
  border-color: rgba(255, 255, 255, 0.5);
  color: white;
}

/* "You are here" — states the same thing the nav highlighting shows,
   in plain text, directly above the chart. */
.metric-breadcrumb {
  font-size: 13px;
  color: var(--text-muted);
  padding: 12px 0 4px;
}

.metric-breadcrumb .crumb-sep {
  margin: 0 4px;
}

.coming-soon-panel {
  border: 1px dashed var(--border-hairline);
  border-radius: 12px;
  padding: 40px 32px;
  text-align: center;
  color: var(--text-secondary);
}

.coming-soon-title {
  font-size: 16px;
  font-weight: 600;
  color: var(--text-primary);
  margin: 0 0 8px;
}

.error-panel {
  border: 1px solid var(--border-hairline);
  border-radius: 12px;
  padding: 32px;
  text-align: center;
  color: var(--text-secondary);
  margin-top: 32px;
}

.tooltip-box {
  background: var(--surface-chart);
  border: 1px solid var(--border-hairline);
  border-radius: 8px;
  padding: 8px 12px;
  font-size: 12px;
  box-shadow: 0 4px 16px rgba(0, 0, 0, 0.12);
}

.tooltip-box .tt-label {
  color: var(--text-muted);
  margin-bottom: 4px;
}

.tooltip-box .tt-row {
  display: flex;
  align-items: center;
  gap: 6px;
  color: var(--text-primary);
}

.tt-pct {
  color: var(--text-muted);
  font-size: 11px;
}

.tt-yoy {
  font-size: 11px;
  font-weight: 500;
}

.tt-yoy-pos {
  color: var(--success-text);
}

.tt-yoy-neg {
  color: var(--diverging-neg);
}

/* --- vanilla SVG chart engine (charts.js) ------------------------------ */

.chart-plot {
  position: relative;
  width: 100%;
}

.chart-plot svg {
  display: block;
  width: 100%;
  height: 100%;
  overflow: visible;
}

.chart-plot .axis-label {
  fill: var(--text-muted);
  font-size: 11px;
  font-family: system-ui, -apple-system, "Segoe UI", sans-serif;
}

.chart-plot .gridline {
  stroke: var(--gridline);
  stroke-width: 1;
}

.chart-plot .baseline {
  stroke: var(--baseline);
  stroke-width: 1;
}

.chart-plot .hover-line {
  stroke: var(--text-muted);
  stroke-width: 1;
  stroke-dasharray: 3 3;
  opacity: 0;
  pointer-events: none;
}

.chart-plot .hit-layer {
  fill: transparent;
  cursor: crosshair;
}

.chart-plot .chart-selection-band {
  fill: var(--series-1);
  fill-opacity: 0.08;
}

.chart-plot .chart-selection-line {
  stroke: var(--series-1);
  stroke-width: 1.5;
  stroke-dasharray: 2 3;
  opacity: 0.6;
}

/*
 * The two draggable CAGR-selection "poles" (charts.js's startDrag()) —
 * grip circles at the top of each chart-selection-line, drawn just above
 * the plot area itself. pointer-events: all so the (otherwise fill-less-
 * looking, since the fill is a CSS var applied here rather than an SVG
 * attribute) circle still catches pointerdown even where its fill looks
 * subtle; cursor: grab/grabbing gives the drag affordance away before the
 * user even touches it.
 */
.chart-plot .chart-cagr-handle {
  fill: var(--series-1);
  stroke: var(--surface-chart);
  stroke-width: 2;
  cursor: grab;
  pointer-events: all;
  touch-action: none;
}

.chart-plot .chart-cagr-handle:hover {
  fill: var(--text-primary);
}

.chart-plot .chart-cagr-handle--active {
  fill: var(--text-primary);
  cursor: grabbing;
}

/* The on-chart stock-split marker (a dashed vertical line + "N:1 split"
 * label, Share-Based Metrics tab only) that used to live here
 * (.split-marker-line/.split-marker-label) was removed entirely at a
 * later explicit request -- split detection/adjustment itself is
 * unaffected (see Metrics::detectSplitsFromShares()), only this visual
 * annotation is gone. See SplitFiles.php for the etc/splits/
 * {ticker}_auto.csv file that replaced it as the way to inspect what the
 * algorithm detected for a given ticker.
 */

.chart-cagr-readout {
  margin-top: 8px;
  padding: 8px 12px;
  border: 1px solid var(--border-hairline);
  border-radius: 8px;
  font-size: 12px;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px 16px;
}

.chart-cagr-readout:empty {
  display: none;
  margin-top: 0;
  padding: 0;
  border: none;
}

.chart-cagr-title {
  color: var(--text-muted);
  font-weight: 500;
  flex-basis: 100%;
}

.chart-cagr-row {
  display: flex;
  align-items: center;
  gap: 6px;
  color: var(--text-secondary);
}

.chart-cagr-pos {
  color: var(--diverging-pos);
}

.chart-cagr-neg {
  color: var(--diverging-neg);
}

.chart-cagr-na {
  color: var(--text-muted);
}

.chart-cagr-empty {
  color: var(--text-muted);
}

/* Sits to the right of the main CAGR row within the same flex-wrapping
   .chart-cagr-readout container (e.g. EPS/FCF Per Share's Cyclically
   Adjusted CAGR reading next to the plain CAGR reading; Cash Flow
   History's SBC trend CAGR next to Operating/Free Cash Flow's) rather
   than being forced onto its own full-width line underneath — a left
   border stands in for the old row-separator border-top, now read as a
   vertical divider between the two readings instead of a horizontal one.
   Still wraps onto its own line on a narrow viewport since the container
   itself is flex-wrap: wrap. */
.chart-cagr-row--trend {
  padding-left: 14px;
  margin-left: 2px;
  border-left: 1px dashed var(--border-hairline);
}

.chart-tooltip {
  position: absolute;
  pointer-events: none;
  opacity: 0;
  transition: opacity 0.08s ease-out;
  z-index: 20;
  transform: translate(-50%, -110%);
  white-space: nowrap;
}

.empty-note {
  color: var(--text-muted);
  font-size: 13px;
  padding: 24px 0;
  text-align: center;
}

/* --- pie charts (Net Cash Flow/Asset/Liabilities Breakdown) and the
   alluvial Profit Flow/Net Cash Flow diagrams — both single-snapshot chart
   types rendered by renderPieChart()/renderAlluvialChart() in charts.js,
   alongside render_pie_card()/render_alluvial_card() in View.php. No
   separate legend row any more — each slice's label/percent is drawn
   in-slice (see .pie-slice-label below) or, for a slice too small to
   label, available on hover via the shared chart tooltip. ---------------- */

.pie-slice-label {
  font-family: system-ui, -apple-system, "Segoe UI", sans-serif;
  font-size: 12px;
  font-weight: 600;
  fill: #fff;
  paint-order: stroke;
  stroke: rgba(0, 0, 0, 0.55);
  stroke-width: 3px;
  stroke-linejoin: round;
}

.pie-slice-label tspan:last-child {
  font-weight: 500;
  font-size: 11px;
}

/* --- pie year selector (mirrors .period-toggle above) ------------------ */

.year-toggle {
  display: inline-flex;
  flex-wrap: wrap;
  gap: 4px;
  margin: 2px 0 14px;
}

.year-toggle-link {
  border: 1px solid var(--border-hairline);
  background: var(--surface-chart);
  color: var(--text-secondary);
  border-radius: 6px;
  padding: 4px 10px;
  font-size: 12px;
  text-decoration: none;
}

.year-toggle-link:hover {
  color: var(--text-primary);
}

.year-toggle-link.active {
  background: var(--series-1);
  border-color: var(--series-1);
  color: white;
  font-weight: 600;
}

.alluvial-plot {
  overflow: visible;
}

.alluvial-node {
  stroke: var(--surface-chart);
  stroke-width: 1;
}

.alluvial-node--root {
  fill: var(--success-text);
}

.alluvial-node--kept {
  fill: var(--success-text);
}

.alluvial-node--spent {
  fill: var(--diverging-neg);
}

.alluvial-node--use {
  fill: var(--alluvial-use);
}

.alluvial-link {
  stroke: none;
  fill-opacity: 0.32;
}

.alluvial-link:hover {
  fill-opacity: 0.55;
}

/* The single-source "entering" flow stub (see charts.js's synthetic
   entry-stub comment) shares Profit Flow's Revenue node's own green --
   both were gray for one round, then switched back to green (matching
   --kept) per explicit follow-up feedback once tried live. */
.alluvial-link--root,
.alluvial-link--kept {
  fill: var(--success-text);
}

.alluvial-link--spent {
  fill: var(--diverging-neg);
}

.alluvial-link--use {
  fill: var(--alluvial-use);
}

.alluvial-label {
  fill: var(--text-secondary);
  font-size: 11px;
}

.alluvial-label tspan:last-child {
  fill: var(--text-primary);
  font-weight: 600;
}

/* --- compare feature ---------------------------------------------------- */

.compare-row {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
  margin: 0 0 12px;
}

.compare-chips {
  display: flex;
  gap: 8px;
  flex-wrap: wrap;
}

.compare-chip {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  border: 1px solid var(--border-hairline);
  border-radius: 999px;
  padding: 5px 10px 5px 12px;
  font-size: 13px;
  text-decoration: none;
  color: var(--text-primary);
  background: var(--surface-chart);
}

.compare-chip:hover {
  border-color: var(--text-secondary);
}

.compare-chip-remove {
  color: var(--text-muted);
  font-size: 14px;
  line-height: 1;
}

.compare-form {
  display: flex;
  align-items: center;
  gap: 6px;
}

.compare-input {
  width: 160px;
  padding: 6px 10px;
  border-radius: 999px;
  border: 1px solid var(--border-hairline);
  background: var(--surface-page);
  color: var(--text-primary);
  font-size: 13px;
}

.compare-submit {
  border: 1px solid var(--border-hairline);
  background: transparent;
  color: var(--text-secondary);
  border-radius: 999px;
  padding: 6px 14px;
  font-size: 13px;
  cursor: pointer;
}

/* The compare box reuses the site-wide .search-box/.search-input/
   .search-results pattern (see public/assets/search.js's data-mode="compare"
   handling) but sits inline inside .compare-form's flex row rather than the
   header's flex:1-up-to-420px layout, so it's sized down to just wrap its
   160px-wide .compare-input instead of stretching to fill the row. */
.compare-search-box {
  flex: 0 0 auto;
  max-width: none;
}

/* Compare-mode suggestion rows are <button>s (see search.js), which need
   the usual form-control reset to look like the <a>-based rows everywhere
   else on the site. */
button.search-result-row {
  display: flex;
  width: 100%;
  border: none;
  background: none;
  font: inherit;
  text-align: left;
  cursor: pointer;
}

.compare-submit:hover {
  color: var(--text-primary);
  border-color: var(--text-secondary);
}

.compare-error {
  font-size: 12px;
  color: var(--text-muted);
  margin: 0 0 12px;
}

.compare-hint {
  font-size: 12px;
  color: var(--text-muted);
  margin: -6px 0 12px;
}

code {
  background: var(--surface-chart);
  border: 1px solid var(--border-hairline);
  border-radius: 4px;
  padding: 1px 6px;
  font-size: 0.92em;
}

/* --- screener ------------------------------------------------------------ */

.page-header {
  padding: 32px 0 8px;
}

.page-header h1 {
  margin: 0 0 4px;
  font-size: 26px;
}

.page-subtitle {
  color: var(--text-secondary);
  font-size: 14px;
  max-width: 760px;
  line-height: 1.5;
  margin: 0 0 20px;
}

.screener-toolbar {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
  margin: 0 0 14px;
}

.screener-search-input {
  flex: 1;
  min-width: 220px;
  max-width: 360px;
  padding: 8px 12px;
  border-radius: 8px;
  border: 1px solid var(--border-hairline);
  background: var(--surface-chart);
  color: var(--text-primary);
  font-size: 14px;
}

.screener-select {
  padding: 8px 10px;
  border-radius: 8px;
  border: 1px solid var(--border-hairline);
  background: var(--surface-chart);
  color: var(--text-primary);
  font-size: 14px;
}

.screener-clear-btn {
  border: 1px solid var(--border-hairline);
  background: transparent;
  color: var(--text-secondary);
  border-radius: 999px;
  padding: 7px 14px;
  font-size: 13px;
  cursor: pointer;
}

.screener-clear-btn:hover {
  color: var(--text-primary);
  border-color: var(--text-secondary);
}

.screener-preset-row {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
  margin: 0 0 14px;
}

.screener-preset-btn {
  border: 1px solid var(--border-hairline);
  background: transparent;
  color: var(--text-secondary);
  border-radius: 999px;
  padding: 7px 14px;
  font-size: 13px;
  cursor: pointer;
}

.screener-preset-btn:hover {
  color: var(--text-primary);
  border-color: var(--text-secondary);
}

.screener-preset-btn[aria-pressed="true"] {
  background: var(--series-1);
  border-color: var(--series-1);
  color: white;
}

/* Custom is always the last button in .screener-preset-row (see the HTML
   comment above it in render_screener_table()) -- pushed to the far right
   of the row regardless of how many preset buttons precede it. */
.screener-custom-toggle-btn {
  margin-left: auto;
}

.screener-custom-panel {
  margin: 0 0 18px;
}

.screener-buffett-panel {
  padding: 14px;
  margin: 0 0 18px;
  border: 1px solid var(--series-1);
  border-radius: 12px;
  background: var(--surface-chart);
}

.screener-buffett-intro {
  margin: 0 0 12px;
  color: var(--text-secondary);
  font-size: 13px;
  line-height: 1.5;
  max-width: 760px;
}

.screener-buffett-stale-notice {
  margin: 0 0 12px;
  padding: 10px 12px;
  border-left: 3px solid var(--diverging-neg);
  background: var(--surface-page);
  color: var(--text-primary);
  font-size: 13px;
  line-height: 1.5;
  max-width: 760px;
}

.screener-buffett-fields {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(230px, 1fr));
  gap: 10px 16px;
  margin: 0 0 12px;
}

.screener-buffett-field {
  display: flex;
  flex-direction: column;
  gap: 4px;
}

.screener-buffett-field-label {
  font-size: 12px;
  color: var(--text-secondary);
  display: flex;
  align-items: baseline;
  gap: 6px;
}

.screener-buffett-operator {
  color: var(--text-muted);
  font-variant-numeric: tabular-nums;
}

.screener-buffett-field-input {
  display: flex;
  align-items: center;
  gap: 6px;
  color: var(--text-muted);
  font-size: 13px;
}

.screener-buffett-field-input input {
  width: 100%;
  min-width: 0;
  padding: 6px 8px;
  border-radius: 6px;
  border: 1px solid var(--border-hairline);
  background: var(--surface-page);
  color: var(--text-primary);
  font-size: 13px;
  font-variant-numeric: tabular-nums;
}

.screener-buffett-reset-btn {
  border: 1px solid var(--border-hairline);
  background: transparent;
  color: var(--text-secondary);
  border-radius: 999px;
  padding: 6px 12px;
  font-size: 12px;
  cursor: pointer;
}

.screener-buffett-reset-btn:hover {
  color: var(--text-primary);
  border-color: var(--text-secondary);
}

.screener-buffett-col {
  display: none;
}

.screener-table.screener-buffett-active .screener-buffett-col {
  display: table-cell;
}

.screener-count {
  margin-left: auto;
  font-size: 13px;
  color: var(--text-muted);
}

.screener-metric-filters {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(230px, 1fr));
  gap: 10px 16px;
  padding: 14px;
  margin: 0 0 18px;
  border: 1px solid var(--border-hairline);
  border-radius: 12px;
  background: var(--surface-chart);
}

.screener-metric-filter {
  display: flex;
  flex-direction: column;
  gap: 4px;
}

.screener-metric-filter-label {
  font-size: 12px;
  color: var(--text-secondary);
}

.screener-metric-filter-select {
  width: 100%;
  min-width: 0;
  padding: 6px 8px;
  border-radius: 6px;
  border: 1px solid var(--border-hairline);
  background: var(--surface-page);
  color: var(--text-primary);
  font-size: 13px;
  font-variant-numeric: tabular-nums;
}

.screener-table-scroll {
  overflow-x: auto;
  padding-bottom: 12px;
  max-width: 100%;
}

.screener-table th:first-child,
.screener-table td:first-child {
  min-width: 60px;
}

.screener-name-cell {
  text-align: left !important;
  max-width: 240px;
  overflow: hidden;
  text-overflow: ellipsis;
}

.screener-table th.screener-sortable {
  cursor: pointer;
  user-select: none;
}

.screener-table th.screener-sortable:hover {
  color: var(--text-primary);
}

.screener-table th.screener-sort-asc::after {
  content: ' \25B2';
  font-size: 9px;
}

.screener-table th.screener-sort-desc::after {
  content: ' \25BC';
  font-size: 9px;
}

.screener-no-results-cell {
  text-align: center !important;
  color: var(--text-muted);
  padding: 24px 10px !important;
}

.screener-pagination {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 14px;
  margin: 16px 0 0;
}

.screener-pagination-link {
  border: 1px solid var(--border-hairline);
  background: transparent;
  color: var(--text-secondary);
  border-radius: 999px;
  padding: 6px 14px;
  font-size: 13px;
  cursor: pointer;
}

.screener-pagination-link:hover:not(:disabled) {
  color: var(--text-primary);
  border-color: var(--text-secondary);
}

.screener-pagination-link:disabled {
  opacity: 0.4;
  cursor: default;
}

.screener-pagination-status {
  font-size: 13px;
  color: var(--text-muted);
  font-variant-numeric: tabular-nums;
}

/* --- Financial Statements -> SEC Filings page (render_filings_card() in
   View.php / public/assets/filings.js) ------------------------------- */

.filings-toolbar {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  gap: 16px;
  margin: 0 0 14px;
}

.filings-filters {
  display: flex;
  flex-wrap: wrap;
  gap: 14px;
}

.filings-filter {
  display: flex;
  flex-direction: column;
  gap: 4px;
}

.filings-filter-label {
  font-size: 11px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.02em;
  color: var(--text-muted);
}

.filings-filter-select {
  padding: 7px 10px;
  border-radius: 8px;
  border: 1px solid var(--border-hairline);
  background: var(--surface-chart);
  color: var(--text-primary);
  font-size: 13px;
  min-width: 140px;
}

.filings-filter-apply {
  padding: 7px 14px;
  border-radius: 8px;
  border: 1px solid var(--border-hairline);
  background: var(--surface-chart);
  color: var(--text-primary);
  font-size: 13px;
  cursor: pointer;
}

.filings-search-box {
  margin: 0 0 14px;
  max-width: 420px;
}

.filings-search-input {
  width: 100%;
  padding: 8px 12px;
  border-radius: 8px;
  border: 1px solid var(--border-hairline);
  background: var(--surface-chart);
  color: var(--text-primary);
  font-size: 14px;
}

.filings-form-badge {
  display: inline-block;
  padding: 2px 8px;
  border-radius: 999px;
  border: 1px solid var(--border-hairline);
  background: var(--surface-chart);
  font-size: 12px;
  font-weight: 600;
  color: var(--text-primary);
}

.filings-actions {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: 6px;
  white-space: nowrap;
}

.filings-icon-link {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 30px;
  height: 30px;
  border-radius: 8px;
  color: var(--text-secondary);
  text-decoration: none;
}

.filings-icon-link:hover,
.filings-icon-link:focus-visible {
  color: var(--series-1);
  background: var(--surface-chart);
}

.filings-no-results {
  padding: 16px 0 0;
}

/* --- Company Overview (hand-maintained Markdown writeup — see
   MarkdownRenderer.php/CompanyBreakdown.php) -------------------------- */

.company-overview-card {
  padding-bottom: 20px;
}

.md-body {
  font-size: 14px;
  line-height: 1.6;
  color: var(--text-secondary);
}

.md-body h1,
.md-body h2,
.md-body h3,
.md-body h4,
.md-body h5,
.md-body h6 {
  color: var(--text-primary);
  font-weight: 700;
  margin: 24px 0 10px;
  line-height: 1.3;
}

.md-body h1:first-child,
.md-body h2:first-child,
.md-body h3:first-child {
  margin-top: 4px;
}

.md-body h1 { font-size: 22px; }
.md-body h2 { font-size: 18px; border-bottom: 1px solid var(--border-hairline); padding-bottom: 6px; }
.md-body h3 { font-size: 15px; }
.md-body h4,
.md-body h5,
.md-body h6 { font-size: 14px; }

.md-body p {
  margin: 0 0 14px;
}

.md-body strong {
  color: var(--text-primary);
  font-weight: 700;
}

.md-body a {
  color: var(--series-1);
  text-decoration: underline;
}

.md-body hr {
  border: none;
  border-top: 1px solid var(--border-hairline);
  margin: 20px 0;
}

.md-body ul,
.md-body ol {
  margin: 0 0 14px;
  padding-left: 24px;
}

.md-body li {
  margin: 4px 0;
}

.md-body li > ul,
.md-body li > ol {
  margin: 4px 0 4px;
}

.md-body blockquote {
  margin: 0 0 14px;
  padding: 8px 14px;
  border-left: 3px solid var(--series-1);
  background: var(--surface-page);
  border-radius: 0 8px 8px 0;
  color: var(--text-secondary);
}

.md-body code {
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 12.5px;
  background: var(--surface-page);
  border: 1px solid var(--border-hairline);
  border-radius: 4px;
  padding: 1px 5px;
  color: var(--text-primary);
}

.md-code {
  background: var(--surface-page);
  border: 1px solid var(--border-hairline);
  border-radius: 8px;
  padding: 12px 14px;
  overflow-x: auto;
  margin: 0 0 14px;
}

.md-code code {
  background: none;
  border: none;
  padding: 0;
  font-size: 12px;
  line-height: 1.5;
  white-space: pre;
  color: var(--text-secondary);
}

.md-table-wrap {
  overflow-x: auto;
  margin: 0 0 16px;
}

.md-table {
  border-collapse: collapse;
  width: 100%;
  font-size: 13px;
}

.md-table th,
.md-table td {
  text-align: left;
  padding: 8px 12px;
  border-bottom: 1px solid var(--border-hairline);
  vertical-align: top;
}

.md-table th {
  color: var(--text-primary);
  font-weight: 700;
  white-space: nowrap;
  border-bottom: 2px solid var(--border-hairline);
}

.md-table tbody tr:hover {
  background: var(--surface-page);
}

/* --- Moat Score (hand-maintained qualitative scorecard — see
   MoatScores.php) ------------------------------------------------------ */

/* Deliberately its own class rather than reusing .md-table-wrap: that
   wrapper sets overflow-x: auto for arbitrary (potentially very wide)
   hand-authored Markdown tables, but per the CSS overflow spec, setting
   overflow-x to anything but visible forces the computed overflow-y to
   auto as well -- which was clipping .moat-term-tip's hover/focus tooltip
   (it pops upward via `bottom: calc(100% + 8px)`, see below) for any row
   near the top of the table, most visibly the first two categories
   (Intangible Assets, Cost Advantage). The moat score table only ever has
   three fixed, short-header columns and doesn't need horizontal
   scrolling the way a deployer's arbitrary Company Overview table might,
   so this wrapper simply omits overflow-x instead of trying to keep
   horizontal scroll while un-clipping vertical overflow (not achievable
   with plain overflow-x/-y, since the browser computes overflow-y: auto
   the moment overflow-x isn't visible). */
.moat-table-wrap {
  margin: 0 0 16px;
}

.moat-total {
  display: flex;
  align-items: center;
  gap: 12px;
  flex-wrap: wrap;
  margin: 4px 0 20px;
  padding: 12px 16px;
  background: var(--surface-page);
  border-radius: 10px;
}

.moat-total-label {
  font-size: 13px;
  font-weight: 600;
  color: var(--text-secondary);
}

.moat-total-figure {
  font-size: 20px;
  font-weight: 700;
  color: var(--text-primary);
}

.moat-total .moat-score-bar {
  flex: 1 1 160px;
  max-width: 320px;
}

.moat-score-table th:nth-child(2),
.moat-score-table td:nth-child(2) {
  width: 110px;
}

.moat-factor-cell {
  white-space: nowrap;
}

.moat-blurb-cell {
  white-space: normal;
  min-width: 320px;
  color: var(--text-secondary);
}

.moat-score-cell {
  white-space: nowrap;
}

.moat-score-figure {
  font-weight: 700;
  display: block;
  margin-bottom: 4px;
}

.moat-score-text-good { color: var(--success-text); }
.moat-score-text-caution { color: var(--warning-text); }
.moat-score-text-bad { color: var(--diverging-neg); }
.moat-score-text-neutral { color: var(--text-primary); }

.moat-score-bar {
  display: inline-block;
  width: 90px;
  height: 6px;
  border-radius: 4px;
  background: var(--gridline);
  overflow: hidden;
}

.moat-score-bar-fill {
  display: block;
  height: 100%;
  border-radius: 4px;
}

.moat-score-band-good { background: var(--success-text); }
.moat-score-band-caution { background: var(--warning-text); }
.moat-score-band-bad { background: var(--diverging-neg); }
.moat-score-band-neutral { background: var(--series-1); }

/* Hover/focus definition tooltip on a Moat Factor category name — a
   plain-English explanation of what that moat category means in general
   (see MoatScores::CATEGORY_DEFINITIONS), distinct from that row's own
   company-specific "Analysis" blurb shown directly in the table. Pure
   CSS (no JS) so it also works via keyboard focus, not just a mouse
   hover — see the tabindex="0"/aria-describedby pairing in
   render_moat_score_card(). */
.moat-term {
  position: relative;
  border-bottom: 1px dotted var(--text-muted);
  cursor: help;
}

.moat-term-tip {
  display: none;
  position: absolute;
  left: 0;
  bottom: calc(100% + 8px);
  z-index: 5;
  width: 260px;
  max-width: 60vw;
  padding: 10px 12px;
  border-radius: 8px;
  background: var(--text-primary);
  color: var(--surface-chart);
  font-size: 12px;
  font-weight: 400;
  line-height: 1.45;
  white-space: normal;
  box-shadow: 0 6px 18px rgba(0, 0, 0, 0.18);
}

.moat-term:hover .moat-term-tip,
.moat-term:focus .moat-term-tip,
.moat-term:focus-visible .moat-term-tip {
  display: block;
}

/* --- site-wide footer ----------------------------------------------------
 * Explicit request: "Create these pages I want the links for them to be
 * all the way at the bottom" -- a real <footer>, added to every page via
 * src/partials/footer.php right after </main> closes, linking to the four
 * new static pages (About Us, Contact Us, Terms of Service, Privacy
 * Policy). Deliberately plain -- small muted-color links, no border or
 * background of its own -- so it reads as boilerplate site furniture
 * rather than competing with the page's actual content above it. A first
 * version had a border-top hairline here and no sticky-footer layout (see
 * body/main.container above), so on a short page like the homepage the
 * footer floated right after the hero instead of sitting at the very
 * bottom of the viewport -- direct follow-up request: "Remove the bar
 * above the links and they need to be all the way at the bottom of the
 * page." Fixed on both counts: the border is gone, and body/main.container
 * now do the actual bottom-pinning via flexbox. That same follow-up also
 * asked the links themselves be centered rather than left-aligned (see
 * .site-footer-nav's own `justify-content: center` below) -- .container's
 * own centering (max-width + margin: 0 auto) only centers the footer's
 * outer box on a wide viewport, not the row of links inside it. */
.site-footer {
  flex-shrink: 0;
  padding: 20px 0 28px;
}

.site-footer-nav {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 8px 20px;
}

.site-footer-nav a {
  color: var(--text-secondary);
  font-size: 13px;
  text-decoration: none;
}

.site-footer-nav a:hover,
.site-footer-nav a:focus-visible {
  color: var(--series-1);
  text-decoration: underline;
}

/* --- static pages (About Us / Contact Us / Terms of Service / Privacy
 * Policy) -- reuses .chart-card's white-card-on-light-blue-page look and
 * .md-body's existing prose typography (built for the Company Overview
 * markdown feature) rather than introducing a third set of heading/
 * paragraph rules, and caps prose width the same way .page-subtitle
 * already does elsewhere on the site, so a long line of body text doesn't
 * stretch uncomfortably wide on a desktop viewport. */
.static-page-card .md-body {
  max-width: 760px;
}
