/* Tabula Ageing — application shell.
   Linked by every page after the design-system token sheets.

   Fonts are self-hosted from fonts/ — no third-party request, no @import chain.
   Each family is one variable woff2 covering its whole weight range, so the three
   files below serve every weight the platform uses.

   font-display: optional is deliberate. It gives the browser a very short window
   to use the real font; if the file is not ready it keeps the metric-matched
   fallback for the life of that document and never swaps mid-view. Combined with
   the preloads in each page head and the speculation rules that prerender the
   next module, the result is: correct type from the first frame on any warm load,
   and on a cold throttled load a fallback that occupies exactly the same boxes —
   never a visible reflow, in either case. */

/* ---- the three design-system families, self-hosted --------------------- */
@font-face {
  font-family: "Bricolage Grotesque";
  src: url("fonts/bricolage-latin.woff2") format("woff2");
  font-weight: 400 800;
  font-style: normal;
  font-display: optional;
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "Instrument Sans";
  src: url("fonts/instrument-latin.woff2") format("woff2");
  font-weight: 400 700;
  font-style: normal;
  font-display: optional;
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "Instrument Sans";
  src: url("fonts/instrument-italic-latin.woff2") format("woff2");
  font-weight: 400 700;
  font-style: italic;
  font-display: optional;
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "JetBrains Mono";
  src: url("fonts/jetbrains-latin.woff2") format("woff2");
  font-weight: 400 600;
  font-style: normal;
  font-display: optional;
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
/* Greek, for ρ and β where they are set in the mono face. */
@font-face {
  font-family: "JetBrains Mono";
  src: url("fonts/jetbrains-greek.woff2") format("woff2");
  font-weight: 400 600;
  font-style: normal;
  font-display: optional;
  unicode-range: U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1, U+03A3-03FF;
}

/* ---- metric-matched fallbacks ------------------------------------------ */
/* No longer load-bearing now the fonts are local, but kept as insurance: each is
   size- and metric-adjusted to its webfont and sits directly after it in the
   stack, so any fallback occupies the same box and costs no reflow. */
@font-face {
  font-family: "Bricolage Fallback";
  src: local("Arial"), local("Helvetica"), local("Liberation Sans");
  size-adjust: 103%;
  ascent-override: 96%;
  descent-override: 24%;
  line-gap-override: 0%;
}
@font-face {
  font-family: "Instrument Fallback";
  src: local("Arial"), local("Helvetica"), local("Liberation Sans");
  size-adjust: 98%;
  ascent-override: 96%;
  descent-override: 24%;
  line-gap-override: 0%;
}
@font-face {
  font-family: "Mono Fallback";
  src: local("Menlo"), local("Consolas"), local("DejaVu Sans Mono"), local("Courier New");
  size-adjust: 100%;
  ascent-override: 102%;
  descent-override: 30%;
  line-gap-override: 0%;
}

:root {
  --font-display: "Bricolage Grotesque", "Bricolage Fallback", system-ui, sans-serif;
  --font-body: "Instrument Sans", "Instrument Fallback", system-ui, sans-serif;
  --font-mono: "JetBrains Mono", "Mono Fallback", ui-monospace, monospace;
}

/* ---- cross-document view transitions ----------------------------------- */
/* The ten module pages are separate documents. A cross-document transition holds
   the outgoing frame on screen until the incoming one has painted, so the white
   gap between documents is never rendered. Short and plain — a cross-fade.

   The enabling rule itself is NOT here: fx.js injects @view-transition only when
   the page is the top-level document. Cross-document transitions do not run in a
   frame, and an embedding host that owns navigation starts one and immediately
   skips it — which rejects a promise the page has no handle on and logs an error
   on every load. Injecting conditionally means the transition runs where it works
   and is never started where it cannot. These styles are inert until then. */
::view-transition-old(root),
::view-transition-new(root) {
  animation-duration: 140ms;
  animation-timing-function: linear;
}
@media (prefers-reduced-motion: reduce) {
  ::view-transition-old(root),
  ::view-transition-new(root) { animation: none; }
}

/* ---- critical chrome tokens -------------------------------------------- */
/* Mirrors of the token values the persistent chrome resolves against, so the
   header, module tab bar and rail have real colours in the first frame. Values
   are byte-identical to the token sheets; mirror any retune here. */
:root {
  --neutral-0: #ffffff;
  --neutral-25: #fcfaf7;
  --neutral-50: #faf7f2;
  --neutral-100: #f2eee7;
  --neutral-200: #e6e1d7;
  --neutral-300: #d4cec2;
  --neutral-400: #a9a299;
  --neutral-500: #867f76;
  --neutral-600: #6b655d;
  --neutral-950: #141210;
  --bg-page: var(--neutral-50);
  --surface-card: var(--neutral-0);
  --surface-code: var(--neutral-950);
  --border-hairline: rgba(20, 18, 16, .08);
  --border-default: var(--neutral-200);
  --text-primary: var(--neutral-950);
  --text-secondary: var(--neutral-600);
  --text-tertiary: var(--neutral-400);
  --text-link: var(--neutral-950);
  --brand-600: #e8330f;
  --radius-sm: 6px;
  --radius-md: 10px;
  --radius-lg: 14px;
  --radius-xl: 20px;
  --radius-pill: 999px;
  --shadow-sm: 0 1px 3px rgba(20, 18, 16, .06), 0 1px 2px rgba(20, 18, 16, .04);
  --shadow-lg: 0 12px 32px rgba(20, 18, 16, .10), 0 4px 8px rgba(20, 18, 16, .04);
  --type-overline: 600 11px/1.3 var(--font-body);
  --type-caption: 400 12px/1.45 var(--font-body);
  --type-label: 500 13px/1.35 var(--font-body);
  --type-body-s: 400 13px/1.5 var(--font-body);
  --type-body: 400 14.5px/1.55 var(--font-body);
  --type-button: 600 14px/1 var(--font-body);
  --type-code: 400 13px/1.6 var(--font-mono);
}

html { background: var(--bg-page); }
body {
  margin: 0;
  background: var(--bg-page);
  color: var(--text-primary);
  font: var(--type-body);
}

/* Content-region transition spinner: 150ms delay before it appears, scoped to the
   content region — never the viewport. */
@keyframes om-spin { to { transform: rotate(360deg); } }
@keyframes om-spin-in { from { opacity: 0 } to { opacity: 1 } }
.om-loading {
  display: grid;
  place-items: center;
  min-height: 320px;
  animation: om-spin-in 120ms linear 150ms both;
}
.om-loading::after {
  content: "";
  width: 22px;
  height: 22px;
  border-radius: 50%;
  box-sizing: border-box;
  border: 2px solid var(--accent-tint, rgba(20, 18, 16, .1));
  border-top-color: var(--accent, var(--text-primary));
  animation: om-spin .8s linear infinite;
}
@media (prefers-reduced-motion: reduce) {
  .om-loading, .om-loading::after { animation-duration: .01ms; animation-iteration-count: 1; }
}

/* Sort indicator: one arrow across every module's tables. The active column shows its
   direction at full strength; a sortable column that is not active shows a muted arrow
   only on hover, so the affordance is discoverable without adding permanent noise. */
.om-th .om-sortarrow { margin-left: 5px; transition: opacity 140ms linear; }
.om-th:hover .om-sortarrow { opacity: 1 !important; }


/* ---------- module-page entrance + scroll reveal ----------
   Driven by fx.js on the six module pages only (the homepage keeps its own hero code).
   Times are wall-clock offsets from the moment the page's content is first classified.
   Each element starts when the previous one is ~70% through its motion. */
:root{
  --mod-delay-title:0ms;    --mod-dur-title:220ms;
  --mod-delay-sub:30ms;     --mod-dur-sub:180ms;     /* controls are usable as soon as the data is ready */
  --mod-delay-panels:80ms;  --mod-stagger:20ms;
  --mod-dur-surface:220ms;                          /* quiet, brief scroll reveal */
}
/* Failsafe: the hidden state exists only once fx.js has armed the page, and only where
   motion is allowed. main is held for the few frames until fx.js has classified it. */
@media (prefers-reduced-motion: no-preference){
  html.om-mod-armed:not(.om-mod-ready) main{opacity:0}
  html.om-mod-ready [data-om-rv="p"]{opacity:0}
}

/* ---------- arm colours (Perturbation) ----------
   One meaning per colour: these mark treatment arms only. Signs keep the platform's
   red (positive) / blue (negative) and are never drawn in these. */
:root{
  --arm-inh:#248636;       /* miR-29 inhibitor */
  --arm-mim:#923D8E;       /* miR-29 mimic */
  --arm-ctrl:#686868;      /* control, pooled / default (dashed, open markers) */
  --arm-inh-ctrl:#A8CDAE;  /* inhibitor's own control, when both controls are separate series */
  --arm-mim-ctrl:#D4ADCE;  /* mimic's own control, when both controls are separate series */
}


/* ---------- the one header ----------
   Same markup on every page (only aria-current differs). Across documents the browser keeps it
   on screen: it is its own view-transition group with no animation, while the content cross-fades. */
html{background-color:#faf7f2}
.om-hdr{view-transition-name:site-header}
.om-brand-home{display:flex;align-items:center;gap:11px;text-decoration:none;flex:none;white-space:nowrap;color:var(--text-primary)}
.om-brand-icon{display:grid;place-items:center;width:34px;height:34px;flex:none;color:var(--text-primary)}
.om-brand-name{font:700 15px/1 var(--font-display);letter-spacing:-.02em;color:var(--text-primary)}
.ta-hdr-logo{display:flex;align-items:center;justify-content:center;flex:0 0 122px;width:122px;height:68px}
.ta-hdr-logo img{display:block;width:122px;height:44px;object-fit:contain}
::view-transition-group(site-header),::view-transition-old(site-header),::view-transition-new(site-header){animation:none}
.om-hdr-a:hover,.om-faq-link:hover,.om-about-btn:hover{color:var(--text-primary) !important}
.om-hdr-dd{display:none;position:relative;flex:none}
.om-hdr-dd>summary{list-style:none;cursor:pointer;padding:24px 0;white-space:nowrap}
.om-hdr-dd>summary::-webkit-details-marker{display:none}
.om-hdr-menu{position:absolute;top:60px;left:0;z-index:60;min-width:220px;padding:6px;background:#fff;border:1px solid var(--border-default);border-radius:var(--radius-md);box-shadow:var(--shadow-lg)}
.om-hdr-menu a{display:block;padding:9px 11px;border-radius:var(--radius-sm);text-decoration:none;font:var(--type-body);color:var(--text-primary)}
.om-hdr-menu a:hover{background:var(--neutral-100)}
.om-hdr-menu a[aria-current]{font-weight:600;background:var(--neutral-100)}
@media (max-width:1299px){.om-hdr{gap:20px !important;padding:0 20px !important}.om-hdr-mods{display:none !important}.om-hdr-mid{display:block}}
@media (max-width:799px){.ta-hdr-logo{display:none}}
@media (max-width:599px){.om-hdr{gap:12px !important;padding:0 14px !important}.om-hdr-mid,.om-about-nav{display:none !important}.om-hdr-all{display:block}.om-hdr-all .om-hdr-menu{left:auto;right:0}}

/* ---- narrow screens: two-column page grids stack into one column ----
   Inline grid tracks win over stylesheet rules, hence !important; children may shrink below their content width. */
.om-stack > *{min-width:0}
@media (max-width:900px){ .om-stack{grid-template-columns:minmax(0,1fr) !important} }

/* Module title rows share alignment; the artwork lives in the site header. */
.ta-mod-head{align-items:flex-start}

/* ---- phones: page gutters, titles and fixed-width controls fit the screen (layout-check findings at 320–430 px) ---- */
@media (max-width:599px){
  main{padding-left:16px !important;padding-right:16px !important;box-sizing:border-box}
  main h1{font-size:clamp(30px,10.5vw,48px) !important;overflow-wrap:anywhere}
  main [style*="min-width:2"],main [style*="min-width:3"],main [style*="min-width: 2"],main [style*="min-width: 3"]{min-width:0 !important;max-width:100%}
  main select{min-width:0;max-width:100%;text-overflow:ellipsis}
  /* control columns (species, tissue, method …): full row, never wider than the screen */
  main [data-pg-ctl]{flex:1 1 100% !important;min-width:0 !important;max-width:100%}
  main [data-pg-ctl] > div{min-width:0 !important;width:100%;box-sizing:border-box}
  main [data-pg-ctl] select{width:100%}
  main div[style*="flex-direction:column"],main div[style*="flex-direction: column"]{min-width:0;max-width:100%}
  main input{min-width:0}
  main .om-fx{max-width:100%;min-width:0}
  main .om-fx > *{min-width:0;overflow:hidden;text-overflow:ellipsis}
  /* fixed-size charts scale down to the column instead of pushing the page sideways */
  main figure{min-width:0;max-width:100%}
  main figure > svg,main figure > div > svg,main svg[width][viewBox]{max-width:100%;height:auto}
}

/* Ageing's extra Quick controls button stays compact at tablet widths. */
@media (min-width:600px) and (max-width:680px){.om-hdr:has(#om-qc-nav){gap:12px !important;padding:0 14px !important}.om-hdr:has(#om-qc-nav) .om-about-nav{gap:10px}}

/* ---- explorer controls: one clear visual language, without changing chart colours ---- */
main select:not([multiple]):not([size]){
  appearance:none;
  -webkit-appearance:none;
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12'%3E%3Cpath d='m3 4.5 3 3 3-3' fill='none' stroke='%236b655d' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E")!important;
  background-position:right 10px center!important;
  background-repeat:no-repeat!important;
  background-size:12px 12px!important;
  padding-right:30px!important;
  cursor:pointer;
}
main :is(button,input,select,textarea,a,[tabindex]):focus-visible,
.om-hdr :is(button,a,summary):focus-visible{
  outline:2px solid var(--accent,var(--brand-600))!important;
  outline-offset:3px;
  box-shadow:none!important;
}
main [role="radiogroup"] [role="radio"][aria-checked="true"]{
  color:color-mix(in srgb,var(--accent,var(--text-primary)) 80%,#17111e)!important;
}
main input[type="checkbox"]{accent-color:var(--accent,var(--brand-600))}
main :is(input[type="search"],select){transition:border-color 140ms ease,box-shadow 140ms ease}
main :is(input[type="search"],select):hover{border-color:color-mix(in srgb,var(--accent,var(--brand-600)) 35%,var(--border-default))!important}
/* Whole-card hover should not look like a selected result. Controls keep their feedback. */
main section.om-fx::before,main section.om-fx::after{display:none}
@media(prefers-reduced-motion:reduce){main :is(input[type="search"],select){transition:none}}
/* Scientific symbols have their own local subset. The primary Latin faces and
   their metrics remain unchanged; do not rely on a platform font for ρ, Δ or →.
   DejaVu Sans derivative; license and source hash are in fonts/. */
@font-face {
  font-family: "Tabula Symbols";
  src: url("fonts/tabula-symbols.woff2") format("woff2");
  font-weight: 400;
  font-style: normal;
  font-display: swap;
  unicode-range: U+0370-03FF, U+2032-2033, U+2190-22FF;
}
:root {
  --font-display: "Bricolage Grotesque", "Tabula Symbols", "Bricolage Fallback", system-ui, sans-serif;
  --font-body: "Instrument Sans", "Tabula Symbols", "Instrument Fallback", system-ui, sans-serif;
  --font-mono: "JetBrains Mono", "Tabula Symbols", "Mono Fallback", ui-monospace, monospace;
}
