/* ib-shell.css — the page canvas, in ONE place.
   ---------------------------------------------------------------------------
   THE POINT OF THIS FILE: changing the site background is a one-line edit.
   Every static shell (188 of them: calculators, guides, steroid spokes, info
   pages, homepage) used to declare its own `--bg`. They had drifted to three
   different light values (#f0f0f4 / #fafafb / #f1f2f5). They now declare none —
   the value lives here and only here.

   Named `ib-*.css` on purpose: next.config.ts applies
   `Cache-Control: no-cache, must-revalidate` to `/:file(ib-[^/]+\.css)`.
   Rename it to anything else and deploys will serve a stale stylesheet.

   Linked in <head> BEFORE ib-nav.css. That ordering is load-bearing: ib-nav.css
   owns `.ib-dropdown-menu`, and anything here that ties on specificity must lose
   to it, not win.

   No flash: a <head> stylesheet is render-blocking, so the browser waits rather
   than painting the wrong colour. Verified by serving this sheet with a 2.5s
   delay and sampling the first paint — no white frame. The wait itself is
   already paid for: ib-nav.css and ib-chat.css are fetched in parallel on the
   same connection with the same cache policy.

   The 40px grid backdrop and the two radial glows that used to sit on
   body::before / body::after were removed from all 188 pages. Do not reintroduce
   a backdrop here: a z-index:-1 pseudo-element joins the ROOT stacking context
   and paints UNDERNEATH body's own background box, so an opaque body silently
   erases it — which is exactly how the grid became invisible in light mode
   without anyone noticing.

   Embeds are unaffected: app.js sets `background: transparent` inline on
   <html> and <body> when window.__embed, and an inline style beats this sheet.
*/

/* ── The canvas ───────────────────────────────────────────────────────────── */
/* CHANGE THE BACKGROUND HERE — one line per theme.

   DARK is the base, LIGHT is the [data-theme="light"] override. That order is
   NOT a style preference, it is required:
     - The 188 static shells set data-theme synchronously in <head>, so either
       polarity works for them.
     - The Next.js routes (/account, /admin, /community, /partner/blog) render
       with NO data-theme attribute until ThemeSync runs on the client. With
       light as the base they painted a LIGHT canvas under globals.css's
       dark-default text tokens — light-on-light, unreadable, until hydration.
   Dark-base matches what globals.css (:root = dark) and the old shells already
   assumed. Verified: /community with no data-theme now resolves #0e0e10. */
:root                        { --bg: #0e0e10; }
html[data-theme="light"]     { --bg: #fafafb; }

/* ── Universal primitives ─────────────────────────────────────────────────────
   Each of these was byte-identical in every file that carried it (verified by
   hashing the normalised rule text, not by inspection):
     @font-face      188/188 files, 1 variant
     box-sizing       46/46  non-guide files (guides get it from ib-guide.css)
     #root:empty      34/34  files that mount the React calculator

   NOT hoisted here, on purpose:
     html,body{...}  45 identical + cycle-plotter (which only sets min-height).
                     A global `html,body{color:var(--white)}` would tie on
                     specificity with cycle-plotter's own `body{color:var(--cp-text)}`
                     and, loading later, WIN — repainting its text.
     :root tokens    4 variants across 46 files (--border/--border2 alphas differ
                     between the calc and guide families). Needs reconciliation
                     before it can be shared.                                    */
@font-face{font-family:'Inter Fallback';src:local('Arial');ascent-override:90.00%;descent-override:22.43%;line-gap-override:0.00%;size-adjust:107.12%}

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

/* ── The content column ───────────────────────────────────────────────────────
   ONE reading measure for every page. It had six: guides 760px, calculators 720px,
   info pages 660px, the two index pages 980px, reviews 820px, cycle-plotter 860px —
   with three different top paddings.

   880px, not 760px. `.static-page` is NOT the guide-article measure (guides render
   their prose inside .guide-main-content and never carry this class) — it is the
   crawlable SEO block that sits BELOW the calculator, in a page whose desktop layout
   already gives up 280px to the rail. At 760px that block read as a narrow ribbon
   stranded in a wide page. 880px is ~95 characters at the 15px body size: still
   inside the readable range, and it uses the space the rail leaves.

   `background: transparent` came from the calc pages, where .static-page is the SEO
   prose BELOW the React #root; it is inert everywhere else.

   Lives in ib-shell.css because all 186 pages link it, and it loads AFTER
   ib-guide.css / ib-static.css, so this is the value that wins.
   `.embed-mode .static-page` (0,2,0) in ib-static.css still overrides it in embeds. */
.static-page { max-width: 880px; margin: 0 auto; padding: 40px 20px 80px; background: transparent; }
/* The mobile side-padding MUST live here, after the shorthand above. It used to sit
   in two places at two breakpoints — `calculators` at 720px and ib-guide.css at
   640px — both of which load BEFORE this sheet. A `padding` shorthand here beats
   their `padding-left/right` on source order, so leaving them where they were
   silently widened the gutters on mobile. Unified to 767px, the site breakpoint. */
@media (max-width: 767px) { .static-page { padding-left: 16px; padding-right: 16px; } }

/* Pre-hydration loader. `content` keeps the \2026 ellipsis escape. */
#root:empty{min-height:100vh;display:flex;align-items:center;justify-content:center}
#root:empty::after{content:'Loading calculator\2026';font-family:var(--mono,monospace);font-size:13px;color:rgba(240,240,242,0.4)}
[data-theme="light"] #root:empty::after{color:rgba(20,20,28,0.4)}

html, body { background: var(--bg); }

/* ── The site footer ──────────────────────────────────────────────────────────
   ONE footer, one definition. The markup is stamped into all 186 shells by
   scripts/gen-footer.mjs (run in prebuild); the look is here and ONLY here.

   Deliberately theme-INDEPENDENT: the footer is a Deep Teal slab (#075e56, the
   `teal-numeral` token in DESIGN.md) in both light and night mode, so it reads as
   the base of the instrument rather than as more page. Because the slab never
   changes, its text never needs to: every colour below is a white alpha, and there
   are no [data-theme] or .embed-light branches to keep in sync. If you find one,
   it is a leftover — delete it, don't extend it.

   Contrast on #075e56: 0.82-alpha white = 5.9:1, 0.62 = 3.4:1 (large/secondary
   text only), the .ib-footer-col-head at 0.66 is 10px uppercase tracking — treat
   4.5:1 as the floor for anything a user has to READ, which the links and body
   copy clear. Do not drop the link alpha below 0.8.

   The brand mark is teal on teal here, so it is repainted white via currentColor —
   that is why the stamped <svg> carries no fill= attributes. */
.ib-footer{background:#075e56;margin-top:60px;border-top:none;}
.ib-footer-inner{max-width:1180px;margin:0 auto;padding:40px 36px 32px;}
.ib-footer-grid{display:grid;grid-template-columns:1.6fr 1fr 1fr 1fr;gap:40px;margin-bottom:32px;}
/* The homepage lists every calculator, grouped by family, instead of the curated subset
   the other 186 pages carry — it is the site's strongest internal-linking surface, so it
   earns the extra column. Same slab, same type, same mobile collapse. */
.ib-footer-grid-5{grid-template-columns:1.6fr 1fr 1fr 1fr 1fr;gap:32px;}
/* Six columns once GLP-1 split out of Peptide into its own footer family. Tighter
   gap so brand + five link columns still fit the 1180px slab; same mobile collapse. */
.ib-footer-grid-6{grid-template-columns:1.6fr 1fr 1fr 1fr 1fr 1fr;gap:24px;}
.ib-footer-brand{min-width:0;}
.ib-footer .ib-brand{display:flex;align-items:center;gap:9px;text-decoration:none;}
/* The mark is teal, and it now sits ON teal. Repaint it white.
   The child selectors are not redundant: home.js's <BrandLogo> hard-codes fill="#0fbcad"
   on each rect/circle as a PRESENTATION ATTRIBUTE, and an inherited `fill` on the parent
   <svg> does not beat a child's own attribute — only a CSS rule matching that child does.
   (The stamped footer's inline SVG carries no fill=, so it would inherit either way.) */
.ib-footer .ib-brand svg{color:#ffffff;flex-shrink:0;}
.ib-footer .ib-brand svg rect,.ib-footer .ib-brand svg circle,.ib-footer .ib-brand svg path{fill:currentColor;}
/* `.ib-brand .ib-brand-text`, not `.ib-brand-text`: ib-nav.css carries
   `[data-theme="light"] .ib-brand-text{color:#1a1a1e}` (0,2,0) and loads AFTER this
   sheet, so a 2-class selector here TIES and loses on source order — the wordmark
   stayed near-black on the teal slab. Three classes (0,3,0) wins outright, in both
   themes, without reaching for !important. Same reason for the `strong`, which
   ib-nav.css paints teal. */
.ib-footer .ib-brand .ib-brand-text{font-family:'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;font-size:15px;letter-spacing:-0.01em;color:#ffffff;}
.ib-footer .ib-brand .ib-brand-text strong{color:#ffffff;font-weight:600;}
.ib-footer-brand-desc{margin-top:14px;font-family:'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;font-size:13px;color:rgba(255,255,255,0.72);line-height:1.6;max-width:300px;}
.ib-footer-col-head{font-family:'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;font-size:10px;color:rgba(255,255,255,0.66);letter-spacing:0.12em;margin-bottom:12px;}
.ib-footer-col{display:flex;flex-direction:column;gap:7px;min-width:0;}
.ib-footer-col a{font-family:'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;font-size:13px;color:rgba(255,255,255,0.82);text-decoration:none;transition:color 0.15s;}
.ib-footer-col a:hover{color:#ffffff;text-decoration:underline;}
.ib-footer-bottom{padding-top:18px;border-top:1px solid rgba(255,255,255,0.18);display:flex;justify-content:space-between;align-items:center;font-family:'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;font-size:11px;color:rgba(255,255,255,0.72);gap:12px;}
.ib-footer-bottom strong{color:#ffffff;font-weight:600;}

/* Mobile — this is the "Safari bug" guard, do not remove it.
   The 4-column grid (1.6fr 1fr 1fr 1fr) sizes each track to `auto` = min-content at
   minimum, and "BPC-157 + TB-500 Blend" cannot wrap below its longest word. Four of
   those plus 40px gaps forced the footer to ~485px, which is wider than a 390px
   viewport — so the whole PAGE gained a horizontal scrollbar, not just the footer.
   Two guards, because either alone can fail:
     1. collapse to 2 columns and shrink the gap (fixes the common case);
     2. `min-width:0` + `overflow-wrap` on the tracks, so a long link can break rather
        than set an unshrinkable floor (fixes the narrow-viewport tail).
   Verify at 390px with `document.documentElement.scrollWidth <= innerWidth`. */
@media (max-width: 767px) {
  .ib-footer-inner { padding: 32px 18px 28px; }
  .ib-footer-grid { grid-template-columns: 1fr 1fr; gap: 28px 20px; margin-bottom: 24px; }
  .ib-footer-brand { grid-column: 1 / -1; }
  .ib-footer-brand-desc { max-width: none; }
  .ib-footer-col a { overflow-wrap: anywhere; }
  /* 44px touch targets for the stacked footer nav links (mobile only — desktop keeps
     the compact list). The link fills its row; the column drops its gap since the
     44px rows now provide their own separation. */
  .ib-footer-col { gap: 0; }
  .ib-footer-col a { display: flex; align-items: center; min-height: 44px; }
  .ib-footer-bottom { flex-direction: column; align-items: flex-start; gap: 8px; }
}

/* ── Segmented control — CANONICAL, site-wide ────────────────────────────────────
   Moved here from ib-calc.css so every surface can use it. It lived in a sheet that
   app.js INJECTS at runtime, so it did not exist on guides, /account or /community,
   and the sidebar had to keep a private copy of the same pixels. ib-shell.css is
   <link>ed by all 188 static shells and by app/layout.tsx, so this is reachable
   everywhere.

   Markup: .ib-seg > .ib-seg-pill + .ib-seg-btn(.ib-seg-active). Compact unit toggles
   (mg/mcg, ng/dL) add .ib-seg-unit. NEVER build a custom side-by-side pill.

   LIGHT IS THE BASE; dark overrides via [data-theme="dark"].

   The active colour resolves --accent-numeral, then --accent, then brand teal. The
   first two exist only on calculator pages (app.js sets --accent-numeral per calc);
   the literal is the fallback everywhere else. ib-calc.css still carries the
   calculator-scoped overrides (.ib-calc-page hover, and the light !important navy),
   and it loads AFTER this sheet, so those still win on calculators. */
.ib-seg {
  position: relative;
  display: flex;
  gap: 0;
  padding: 0;
  overflow: hidden;
  border-radius: 8px;
  background: #e6e6ee;
  border: 1px solid rgba(0, 0, 0, 0.10);
  box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.14);
}
[data-theme="dark"] .ib-seg {
  background: #0f0f12;
  border-color: rgba(255, 255, 255, 0.08);
  box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.6);
}
.ib-seg-pill {
  position: absolute;
  top: 0;
  bottom: 0;
  left: 0;
  border-radius: 6px;
  pointer-events: none;
  z-index: 0;
  background: #ffffff;
  box-shadow: 0 2px 5px rgba(0, 0, 0, 0.22);
  transition: transform 0.4s cubic-bezier(0.33, 1, 0.68, 1);
}
[data-theme="dark"] .ib-seg-pill {
  background: #2e2e36;
  box-shadow: 0 2px 5px rgba(0, 0, 0, 0.6);
}
@media (prefers-reduced-motion: reduce) {
  .ib-seg-pill { transition: none; }
}
.ib-seg-btn {
  flex: 1;
  position: relative;
  z-index: 1;
  min-width: 0;
  padding: 8px 8px;
  border-radius: 6px;
  border: none;
  background: transparent;
  cursor: pointer;
  text-align: center;
  font-family: Inter, -apple-system, BlinkMacSystemFont, sans-serif;
  font-size: 11px;
  font-weight: 500;
  /* The INACTIVE segment label. 0.48 measured 3.10:1 on the light track and 4.12:1 on the
     night track — both fail AA (11px is not large text). An unselected tab is still a label
     you must read to choose it; it is not decoration, and WCAG's "inactive component"
     exception covers DISABLED controls, not merely unselected ones. 0.66 → 4.9:1 light. */
  color: rgba(20, 20, 28, 0.66);
  -webkit-tap-highlight-color: transparent;
  transition: color 0.2s ease, transform 0.18s ease, background 0.2s ease, box-shadow 0.2s ease;
}
[data-theme="dark"] .ib-seg-btn { color: rgba(240, 240, 242, 0.62); }
.ib-seg-btn.ib-seg-active { font-weight: 600; color: var(--accent-numeral, var(--accent, #0fbcad)); }
/* The LIGHT fallback cannot be Instrument Teal. The active label sits on the pill, which is
   pure white — and #0fbcad on white is 2.38:1, a straight WCAG AA failure for 11px text.
   The rule above only avoids it by resolving --accent-numeral first, and app.js is the ONLY
   thing that sets that. So every light surface with a segmented control that ISN'T a
   calculator was failing: the plotter's rail, and anything else that adopts the component.
   (The left rail escaped by hard-coding navy for itself — a fix for one caller, not for the
   control.) The fallback is now DESIGN.md's Teal Numeral, the token that exists precisely
   because "Instrument Teal only reaches 2.4:1 on white": #075e56 measures 7.65:1 there.
   Calculators are untouched — they set --accent-numeral, so it never reaches the fallback.
   Dark needs no equivalent: Instrument Teal is 7.43:1 on the night pill. */
[data-theme="light"] .ib-seg-btn.ib-seg-active { color: var(--accent-numeral, #075e56); }
.ib-seg-lbl { display: inline-block; }

/* Hover on an inactive segment. This belongs to the CONTROL, so it lives with the control.
   It used to sit in ib-calc.css scoped to `.ib-calc-page`/`.ib-trt-page` — classes app.js
   puts on <html> on calculator pages only. The segmented control is site-wide (it is the
   rail's Explore / Saved Doses toggle on EVERY page), so the tab lit up under the cursor on
   a calculator and did nothing on the plotter, the guides, /account or the home page. Same
   markup, same sheets, different behaviour — which is exactly how a shared component rots.
   Unscoped here, every surface behaves the same and there is one rule to change. */
@media (hover: hover) {
  .ib-seg-btn:not(.ib-seg-active):hover { background: rgba(0, 0, 0, 0.10); box-shadow: 0 2px 7px rgba(0, 0, 0, 0.16); }
}

/* ── The focus ring ───────────────────────────────────────────────────────────────
   The design system had NO focus style. Keyboard users were getting the browser's default
   0.67px `auto` outline — technically visible, entirely undesigned, and invisible against
   several of our surfaces. For a tool people use mid-task, one-handed, in poor light, the
   focus ring is not a detail.

   :where() so the whole thing has ZERO specificity: it can never out-rank a component's own
   focus treatment (e.g. the calculator inputs' teal glow), it only fills the gap where none
   exists. `outline-offset` keeps it clear of the control's own border.

   The colour is the accent, which is legible on every surface we ship and is already the
   language for "this is the live element". */
:where(.ib-inp-field, .ib-cta, .ib-btn-icon, .ib-btn-ghost, .ib-seg-btn, .ib-btn-compact):focus-visible {
  outline: 2px solid var(--accent-numeral, var(--accent, #0fbcad));
  outline-offset: 2px;
}

/* Compact unit toggle (mg/mcg, ng/dL, …): the same segmented control as the mode
   toggles, but content-width so it sits inline in a field header. Fixed-width
   halves keep the sliding pill aligned regardless of label length. */
.ib-seg-unit { flex: none; }
.ib-seg-unit .ib-seg-btn { flex: 0 0 42px; padding-left: 0; padding-right: 0; }

/* 44px on touch. The segmented control is the most-used control on the site — the calculators'
   mode and unit toggles, and the rail's Explore / Saved Doses tabs on all 189 pages — and it
   measured 29px tall, the smallest real target in the product. The compact unit variant is
   also 42px WIDE, so it needs the basis raised too, or mg/mcg stays 2px short.
   See the block in ib-calc.css for why `pointer: coarse`. */
@media (pointer: coarse), (max-width: 767px) {
  .ib-seg-btn { min-height: 44px; }
  .ib-seg-unit .ib-seg-btn { flex-basis: 44px; }
}

/* Segmented branch: the sliding pill's position is DATA (which of N options is
   selected); each segment's compact padding is design. */
.ib-seg-pill-n { width: calc(100% / var(--seg-n)); transform: translateX(calc(var(--seg-i) * 100%)); }
.ib-seg-btn-chip { padding: 9px 4px; font-size: 12px; }
/* 44px touch target for segmented-control tabs on the calculators (Per week / Every N
   Days, mg/mcg, etc.). Scoped to html.ib-calc-page so the cycle-plotter's deliberately
   compact chip toggles are left untouched. The buttons already centre their content. */
html.ib-calc-page .ib-seg-btn { min-height: 44px; }

/* ── Calculator page layout ───────────────────────────────────────────────────────
   The last survivors of app.js's injected #ib-drawer-styles. That block held 30
   selectors; 27 of them matched nothing (measured across 4 calculators x 2 viewports)
   because they styled the nav's off-canvas drawer, which is deleted: .ib-calc-rail,
   .ib-rail-md*, .ib-rail-mhead*, .ib-nav--drawer *, and app.js's own .ib-proto-rail.

   Here rather than in ib-calc.css on purpose. app.js's comment on that block read:
   "useLayoutEffect (not useEffect): these styles must land BEFORE the first paint --
   painting the shell/rail unstyled and restyling a frame later reflowed the whole calc
   layout (~0.94 desktop CLS)". ib-calc.css is <link>ed at RUNTIME by app.js:545, which
   cannot make that promise; ib-shell.css is render-blocking in all 188 shells. Moving
   these to ib-calc.css would reintroduce exactly the layout shift the effect existed
   to prevent. .ib-calc-shell only ever appears on a calculator page, so it costs
   nothing elsewhere. */
.ib-calc-shell { display: flex; align-items: flex-start; max-width: 1180px; margin: 0 auto; width: 100%; box-sizing: border-box; }
.ib-calc-main { flex: 1; min-width: 0; }
@media (max-width: 767px) { .ib-calc-shell { display: block; } }
