/*
  Nunito, self-hosted.

  design-tokens.json#/web/font pins source: "self-hosted" and
  #/web/buildPolicy/forbidCdnScriptTags bans third-party runtime fetches, so
  the face is served from our own origin. The files were vendored once by
  scripts/vendor-fonts.mjs and are committed; the build never touches a network.

  Nunito ships as a variable font, so one file per subset covers the whole
  400-700 axis the contract asks for (weights 400 / 600 / 700). Two files,
  ~74 KB total, against web.assetBudget.fontBytes = 120 000.
*/

@font-face {
  font-family: "Nunito";
  font-style: normal;
  font-weight: 400 700;
  font-display: swap;
  src: url("/portal/fonts/nunito-latin.woff2") format("woff2");
  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: "Nunito";
  font-style: normal;
  font-weight: 400 700;
  font-display: swap;
  src: url("/portal/fonts/nunito-latin-ext.woff2") format("woff2");
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7,
    U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF,
    U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

/* GENERATED from product/design-tokens.json by scripts/build-contract.mjs.
   Do not edit. Every colour, space, radius and type value in the portal
   resolves through one of these. design-tokens.json#/$comment.web: the portal
   inherits color.* and colorLight.* (the shared iOS/Android app theme) unchanged
   and adds only web.* on top. brand.* is corporate identity and is scoped to
   marketing / signed-out surfaces via .brand-surface. */

:root {
  color-scheme: dark;

  /* --- app theme, dark (color.*) --- */
  --color-primary: #43A047;
  --color-primary-muted: #2E7D32;
  --color-accent: #8BC34A;
  --color-surface: #0E1411;
  --color-surface-raised: #18211C;
  --color-surface-sunken: #0A0F0C;
  --color-border: #26332B;
  --color-danger: #EF5350;
  --color-warning: #FFB300;
  --color-success: #43A047;
  --color-info: #4FC3F7;
  --color-text-primary: #F1F5F2;
  --color-text-secondary: #9DB0A3;
  --color-text-muted: #5F7167;
  --color-online: #43A047;
  --color-offline: #5F7167;
  --color-streaming: #4FC3F7;
  --color-privacy-on: #8BC34A;
  --color-focus: #4FC3F7;
  --color-scrim: #000000B3;
  --color-surface-glass: #18211CEB;

  /* --- spacing / radius / type / layout / control / motion --- */
  --space-xs: 4px;
  --space-sm: 8px;
  --space-md: 16px;
  --space-lg: 24px;
  --space-xl: 40px;
  --radius-sm: 6px;
  --radius-md: 10px;
  --radius-lg: 16px;
  --radius-pill: 999px;
  --type-family-body: Nunito, system-ui, sans-serif;
  --type-family-display: Nunito, system-ui, sans-serif;
  --type-family-mono: ui-monospace, SFMono-Regular, monospace;
  --type-title: 28px;
  --type-headline: 20px;
  --type-body: 15px;
  --type-caption: 12px;
  --type-mono: 14px;
  --type-line-height-body: 1.5;
  --type-weight-regular: 400;
  --type-weight-semibold: 600;
  --type-weight-bold: 700;
  --layout-page-padding-compact: 16px;
  --layout-page-padding-medium: 24px;
  --layout-page-padding-wide: 32px;
  --layout-content-measure: 760px;
  --layout-rail-width: 248px;
  --layout-bottom-nav-height: 64px;
  --layout-sticky-player-clearance: 112px;
  --layout-card-min-width: 280px;
  --layout-touch-target-min: 44px;
  --control-height: 44px;
  --control-height-large: 50px;
  --control-border-width: 1px;
  --control-focus-width: 3px;
  --control-disabled-opacity: 0.5;
  --motion-fast-ms: 120ms;
  --motion-standard-ms: 220ms;
  --motion-slow-ms: 400ms;
  --motion-easing: cubic-bezier(0.2, 0, 0, 1);
  --elevation-card: 0 1px 3px rgba(0,0,0,0.4);
  --elevation-raised: 0 4px 12px rgba(0,0,0,0.5);

  /* --- persistent mini-player (audioPlayer.bar / audioPlayer.waveform) --- */
  --player-radius: 22px;
  --player-inset: 20px;
  --player-padding-x: 20px;
  --player-padding-y: 14px;
  --player-gap: 14px;
  --player-state-icon: 44px;
  --player-interrupted: #FFB300;
  --player-spectrogram-on: #8BC34A;
  --player-waveform-top: #43A047;
  --player-waveform-bottom: #43A047B3;
  --player-waveform-size: 36px;
  --player-waveform-spacing: 3px;
  --player-waveform-animation-ms: 400ms;

  /* --- notification inbox / migration density --- */
  --notification-icon-size: 52px;
  --notification-icon-radius: 10px;
  --notification-unread-size: 9px;
  --new-species-highlight-ms: 2400ms;

  /* --- floating glass controls (glassControl.*) --- */
  --glass-control-size: 44px;
  --glass-control-icon: #FFFFFF;
  --glass-control-icon-size: 22px;
  --glass-control-shadow: 0 2px 6px rgba(0,0,0,0.2);

  /* --- wildlife detection model selector (inferenceSelector.*) --- */
  --inference-card-background: var(--color-surface-raised);
  --inference-card-selected-border: var(--color-focus);
  --inference-card-unselected-border: var(--color-border);
  --inference-card-disabled-opacity: 0.56;
  --inference-card-border-width: 1px;
  --inference-card-selected-border-width: 2px;
  --inference-card-radius: var(--radius-md);
  --inference-card-padding: var(--space-md);
  --inference-card-gap: 12px;
  --inference-card-min-height: 96px;
  --inference-card-icon-size: 24px;
  --inference-card-radio-size: 22px;
  --inference-grid-columns: 3;
  --inference-grid-columns-accessibility: 2;
  --inference-grid-columns-accessibility-breakpoint: accessibility1;
  --inference-grid-short-row-alignment: leading;
  --inference-grid-gap: 10px;
  --inference-grid-artwork-aspect-ratio: 1;
  --inference-grid-artwork-bleed: full;
  --inference-grid-label-top-gap: 6px;
  --inference-grid-label-gap: 2px;
  --inference-grid-status-band-padding: 3px;
  --inference-section-gap: var(--space-lg);
  --inference-section-control-gap: var(--space-sm);
  --inference-section-max-width: 720px;
  --inference-region-type: var(--type-caption);
  --inference-region-color: var(--color-text-secondary);
  --inference-accent-birdnet: #43A047;
  --inference-accent-perch: #4FC3F7;
  --inference-accent-hawkears: #D98E32;
  --inference-accent-tern-midatlantic: #FFC42C;
  --inference-accent-scott-mode: #8B5CF6;

  /* --- web-only additions (web.*) --- */
  --web-font-family: Nunito, system-ui, -apple-system, 'Segoe UI', sans-serif;
  --web-glass-backdrop: blur(20px) saturate(180%);
  --web-glass-bg: #18211CD6;
  --web-glass-bg-light: #FFFFFFD6;
  /* Denser material for the floating chrome (nav capsule, mini-player). */
  --web-chrome-bg: #18211CF5;
  --web-chrome-backdrop: blur(32px) saturate(180%);
  --web-glass-border: #FFFFFF1F;
  --web-scrim: linear-gradient(to bottom, rgba(0,0,0,0) 0%, rgba(0,0,0,0.65) 100%);
  --web-scrim-solid: #000000A6;

  /* --- web mini-player override (web.miniPlayer, S#722) --- */
  --player-padding-x: 16px;
  --player-padding-y: 8px;
  --player-gap: 10px;

  /* --- History › Detections cards (web.detectionCard, S#722) --- */
  --detection-card-min-width: 360px;
  --detection-card-thumb: 56px;

  /* --- History › Timeline (web.timeline + audioPlayer.timelineScrub, S#722) --- */
  --timeline-time-column-width: 72px;
  --timeline-gutter-width: 20px;
  --timeline-gutter-line-width: 2px;
  --timeline-gutter-line-color: #8E8E9326;
  --timeline-thumb-size: 52px;
  --timeline-thumb-radius: 10px;
  --timeline-name-width: 68px;
  --timeline-item-gap: 8px;
  --timeline-sun-row-background: #000000;
  --timeline-sun-row-text: #FFFFFF;
  --timeline-sun-row-time-text: #FFFFFFCC;
  --timeline-sun-row-padding-y: 8px;
  --timeline-sun-row-padding-x: 12px;
  --timeline-time-font-size: 13px;
  --timeline-empty-time-color: var(--color-text-muted);
  --timeline-row-height: 76px;
  --timeline-row-height-condensed: 44px;
  --timeline-playhead-color: #FF3B30;
  --timeline-playhead-thickness: 2px;
  --timeline-rail-width: 3px;
  --timeline-rail-color: #4FC3F7;
  --timeline-rail-anomaly-color: #FFB300;
  --timeline-rail-radius: 2px;
  --timeline-go-live-bg: #EF5350;
  --timeline-go-live-text: #FFFFFF;

  /* --- app chrome (web.shell) — the floating capsule, not a rail --- */
  --shell-top-bar-height: 52px;
  --shell-nav-capsule-radius: 999px;
  --shell-nav-capsule-padding: 6px;
  --shell-nav-item-radius: 999px;
  --shell-nav-item-padding-x: 14px;
  --shell-nav-item-height: 40px;
  --shell-brandmark-size: 20px;
  --shell-brandmark-icon-size: 32px;
  --shell-brandmark-icon-radius: 7px;
  --shell-bottom-capsule-inset: 16px;
  --shell-dock-gap: 8px;

  /* --- multi-column page layout (web.columns) --- */
  --columns-panel-column-min-width: 340px;
  --columns-tile-min-width: 220px;
  --columns-chart-min-width: 340px;
  --columns-cell-min-width: 150px;
  --columns-home-tile-min-width: 165px;
  --columns-home-tile-gap: 12px;
  --columns-home-tile-height: 160px;
  --columns-home-tile-radius: 14px;

  /* --- Home hero surface (web.hero) --- */
  --hero-background: #2E758A;
  --hero-on-background: #FFFFFF;
  --hero-on-background-secondary: #FFFFFFC7;
  --hero-control-background: #00000026;

  /* --- grouped-inset list (web.list) --- */
  --list-row-height: 52px;
  --list-row-padding-x: 16px;
  --list-divider-inset: 16px;
  --list-group-gap: 16px;

  /* --- web-only type steps (web.type) --- */
  --type-display: 34px;
  --type-section-title: 22px;
  --type-line-height-heading: 1.2;

  /* --- Live Map iframe (web.map) --- */
  --map-station-marker-size: 28px;
  --map-marker-size: 26px;
  --map-new-badge-size: 14px;
  --map-offline-opacity: 0.65;
  --map-offline-brightness: 0.9;
  --map-motus-tint: #FFCC00;
  --map-tag-tint: #FF3B30;
  --map-curated-tint: #AF52DE;
  --map-station-cluster-tint: #FF3B30;
  --map-star-tint: #FFCC00;
  --map-flash-dot: #FFCC00;
  --map-marker-ring: #FFFFFF;
  --map-pill-background: #000000B3;
  --map-flash-pill-background: #000000D9;
  --map-night-band: #0000001F;
  --map-control-size: 44px;
  --map-heat-stop0: #007AFF;
  --map-heat-stop25: #30B0C7;
  --map-heat-stop50: #34C759;
  --map-heat-stop70: #FFCC00;
  --map-heat-stop85: #FF9500;
  --map-heat-stop100: #FF3B30;
  --map-heat-opacity: 0.65;
  --map-species-count-background: #0000008C;

  /* --- corporate brand (brand.*) — marketing + signed-out only --- */
  --brand-primary: #027996;
  --brand-dark-teal: #01333C;
  --brand-gold: #FFC42C;
  --brand-cream: #FFF8EE;
  --brand-primary-hover: #01617A;
  --brand-on-primary: #FFF8EE;
  --brand-on-gold: #01333C;

  /* --- accessibility --- */
  --focus-ring: 0 0 0 3px #4FC3F7;

  /* --- a tag's track map (tagHistory.stationMarker / tagHistory.leg) --- */
  --track-marker-small: 32px;
  --track-marker-medium: 40px;
  --track-marker-large: 48px;
  --track-marker-fill: var(--color-info);
  --track-marker-label: var(--color-surface);
  --track-marker-selected: var(--color-focus);
  --track-marker-selected-width: 3px;
  --track-marker-emphasis-scale: 1.12;
  --track-leg-stroke: var(--color-text-secondary);
  --track-leg-width: 2px;
  --track-leg-dash: 6 5;
  --track-leg-arrow: 9px;
  --track-row-min-height: 56px;
  --track-camera-padding: 32px;
  --track-minimum-span-m: 800;
}

/* colorLight.* is the same app theme at light polarity. Only the keys the
   contract redefines are overridden; everything else inherits from :root. */
@media (prefers-color-scheme: light) {
  :root {
    color-scheme: light;
    --color-primary: #2E7D32;
    --color-accent: #5E8F20;
    --color-surface: #F6F8F5;
    --color-surface-raised: #FFFFFF;
    --color-surface-sunken: #EAF0EB;
    --color-border: #CDD8D0;
    --color-danger: #B3261E;
    --color-warning: #8A5A00;
    --color-info: #00658A;
    --color-success: #1B5E20;
    --color-text-primary: #142019;
    --color-text-secondary: #526158;
    --color-text-muted: #6E7D73;
    --color-focus: #006FB9;

    /* The frosted material has to flip too. web.glass.background is a dark
       translucent fill; leaving it in place under light-mode text put
       --color-text-primary (#142019) on a near-black card and made
       every panel heading unreadable. design-tokens.json ships
       web.glass.backgroundLight for precisely this polarity. */
    --web-glass-bg: #FFFFFFD6;
    --web-chrome-bg: #FFFFFFF5;
    --web-glass-border: #CDD8D0;
  }
}

/* backdrop-filter is not universal. design-tokens.json#/web/glass says text
   contrast must never depend on the blur landing, so fall back to the opaque
   surface token. */
@supports not (backdrop-filter: blur(1px)) {
  :root {
    --web-glass-bg: var(--color-surface-raised);
    --web-glass-bg-light: var(--color-surface-raised);
    --web-chrome-bg: var(--color-surface-raised);
  }
}

/* An explicit choice beats the OS guess. data-theme is set from the visitor's
   stored preference and mirrors the two blocks above. */
[data-theme="light"] {
  color-scheme: light;
  --color-primary: #2E7D32;
  --color-accent: #5E8F20;
  --color-surface: #F6F8F5;
  --color-surface-raised: #FFFFFF;
  --color-surface-sunken: #EAF0EB;
  --color-border: #CDD8D0;
  --color-danger: #B3261E;
  --color-warning: #8A5A00;
  --color-info: #00658A;
  --color-success: #1B5E20;
  --color-text-primary: #142019;
  --color-text-secondary: #526158;
  --color-text-muted: #6E7D73;
  --color-focus: #006FB9;
  --web-glass-bg: #FFFFFFD6;
  --web-chrome-bg: #FFFFFFF5;
  --web-glass-border: #CDD8D0;
}

[data-theme="dark"] {
  color-scheme: dark;
  --color-primary: #43A047;
  --color-primary-muted: #2E7D32;
  --color-accent: #8BC34A;
  --color-surface: #0E1411;
  --color-surface-raised: #18211C;
  --color-surface-sunken: #0A0F0C;
  --color-border: #26332B;
  --color-danger: #EF5350;
  --color-warning: #FFB300;
  --color-success: #43A047;
  --color-info: #4FC3F7;
  --color-text-primary: #F1F5F2;
  --color-text-secondary: #9DB0A3;
  --color-text-muted: #5F7167;
  --color-online: #43A047;
  --color-offline: #5F7167;
  --color-streaming: #4FC3F7;
  --color-privacy-on: #8BC34A;
  --color-focus: #4FC3F7;
  --color-scrim: #000000B3;
  --color-surface-glass: #18211CEB;
  --web-glass-bg: #18211CD6;
  --web-chrome-bg: #18211CF5;
  --web-glass-border: #FFFFFF1F;
}

@media (prefers-reduced-motion: reduce) {
  :root {
    --motion-fast-ms: 0ms;
    --motion-standard-ms: 0ms;
    --motion-slow-ms: 0ms;
    --player-waveform-animation-ms: 0ms;
  }
}

/* Responsive switches, generated so the breakpoint numbers come from
   design-tokens.json#/breakpoint rather than being typed into a stylesheet.
   components.css reads these; it never writes a media query of its own.

   ONE nav, ONE position (Session #653). The floating frosted capsule docks to
   the bottom of the viewport at every width and spans it, its destinations in
   equal columns (Session #685); the top bar carries only the brandmark and
   the account. What changes at 600px is the item shape:
   glyph over label on a phone, glyph beside label above it. The capsule's
   height is the same at both, so the stack above it does not move.

   The bottom stack, from the edge up: safe-area/inset, capsule, dockGap,
   mini-player, dockGap, feedback button. The mini-player's height changes (its
   spectrogram toggles), so MiniPlayer measures itself into
   --mini-player-clearance; with no player it is 0 and everything above the
   capsule drops down to it.

   Pages use their width from 600px: stacked panels flow into columns
   no narrower than web.columns.panelColumnMinWidth, and the station detail
   split goes two-up, widening its main column at 1024px. Content is
   capped at 1440px, and from 1440px at
   2560px. */
:root {
  --page-padding: 16px;
  --content-max: 1440px;
  --columns-detail: minmax(0, 1fr);
  --panel-columns: 1;
  --home-tile-min: var(--columns-home-tile-min-width);
  --nav-item-direction: column;
  --nav-item-gap: 0px;
  --nav-item-font-size: var(--type-caption);
  --nav-item-padding-x: var(--space-sm);
  --dock-bottom: max(var(--shell-bottom-capsule-inset), env(safe-area-inset-bottom));
  --dock-capsule-height: calc(var(--layout-touch-target-min) + var(--shell-nav-capsule-padding) * 2 + var(--control-border-width) * 2);
  --mini-player-clearance: 0px;
  --player-bottom: calc(var(--dock-bottom) + var(--dock-capsule-height) + var(--shell-dock-gap));
  --dock-stack-height: calc(var(--player-bottom) + var(--mini-player-clearance));
  --modal-align: flex-end;
  --modal-backdrop-padding: 0px;
  --modal-height: 100dvh;
  --modal-radius: 0px;
  --modal-padding: var(--space-md);
}

@media (min-width: 600px) {
  :root {
    --page-padding: 24px;
    --columns-detail: minmax(0, 3fr) minmax(0, 2fr);
    --panel-columns: 340px;
    --home-tile-min: var(--columns-tile-min-width);
    --nav-item-direction: row;
    --nav-item-gap: var(--space-xs);
    --nav-item-font-size: var(--type-body);
    --nav-item-padding-x: var(--shell-nav-item-padding-x);
    --modal-align: center;
    --modal-backdrop-padding: var(--page-padding);
    --modal-height: auto;
    --modal-radius: var(--radius-lg);
    --modal-padding: var(--space-lg);
  }
}

@media (min-width: 1024px) {
  :root {
    --page-padding: 32px;
    --columns-detail: minmax(0, 2fr) minmax(0, 1fr);
  }
}

/* Desktop (S#697): the content cap lifts from 1440px to
   2560px, so a big window is a desktop page, not a
   centred tablet column; the grids above add columns as the width allows. */
@media (min-width: 1440px) {
  :root {
    --content-max: 2560px;
    /* Masonry stacks (Settings) take at most four columns, each at least
       panelColumnMinWidth, so a wide window widens the columns rather than
       laying out more than the panels can fill. */
    --panel-columns: 4 340px;
  }
}

:root {
  --map-dock-clearance: calc(44px + 6px * 2 + 16px);
}
