mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-08 12:32:43 +00:00
merge(W5): cited reference-range backbone + inline explainers
Add src/lib/reference-ranges.ts — a citation-backed per-metric range backbone with a four-state placement classifier — and expand the medical citations registry. Fill the empty metric-status normalRange fields, enrich metric explainers/tooltips with cited bands (SpO2 darker-skin caveat, device-honesty copy, steps 8,000 floor), label BP as ESH 2023 with ESC/AHA shown only as context, and surface pulse pressure + MAP on the BP detail from stored readings. General guidance, no brand attribution.
This commit is contained in:
commit
1b39e191a1
12 files changed
+985
-29
No files matched your search
+26
-8
@@ -2395,7 +2395,7 @@
|
||||
"explainer": {
|
||||
"trigger": "Was ist {title}?",
|
||||
"bloodPressureTitle": "Blutdruck",
|
||||
"bloodPressureBody": "Der Blutdruck ist die Kraft, die das Blut auf die Gefäßwände ausübt, angegeben als systolisch zu diastolisch (Spitzenwert beim Herzschlag über dem Ruhewert dazwischen). Dauerhaft hohe Werte belasten Herz und Gefäße auf Dauer.",
|
||||
"bloodPressureBody": "Der Blutdruck ist die Kraft, die das Blut auf die Gefäßwände ausübt, angegeben als systolisch zu diastolisch (Spitzenwert beim Herzschlag über dem Ruhewert dazwischen). Dauerhaft hohe Werte belasten Herz und Gefäße auf Dauer. Als allgemeine Orientierung gilt nach europäischer Leitlinie (ESH 2023) unter 120/80 als optimal und ab 140/90 als Bluthochdruck; die US-Leitlinie (ACC/AHA 2017) setzt mit 130–139/80–89 (Stadium 1) eine strengere Grenze. Einordnung immer aus einer ruhigen, sitzenden Messung, nie aus einem einzelnen Wert.",
|
||||
"pulseTitle": "Puls",
|
||||
"pulseBody": "Der Puls gibt an, wie oft das Herz pro Minute schlägt. Eine Einzelmessung spiegelt den Moment wider – in Ruhe liegt er meist bei 60–100 Schlägen pro Minute und steigt natürlicherweise bei Bewegung, Stress oder Koffein.",
|
||||
"weightTitle": "Gewicht",
|
||||
@@ -2413,7 +2413,7 @@
|
||||
"hrvTitle": "Herzfrequenzvariabilität (HRV)",
|
||||
"hrvBody": "Die HRV ist die kleine Schwankung der Zeit zwischen aufeinanderfolgenden Herzschlägen. Eine höhere Variabilität spiegelt meist ein gut erholtes, anpassungsfähiges Nervensystem wider, ein anhaltender Rückgang kann auf Erschöpfung, Stress oder mangelnde Erholung hinweisen.",
|
||||
"oxygenSaturationTitle": "Sauerstoffsättigung (SpO₂)",
|
||||
"oxygenSaturationBody": "Die SpO₂ ist der Prozentsatz der roten Blutkörperchen, die Sauerstoff transportieren. Werte von 95–100 % sind in Ruhe normal; Werte, die dauerhaft unter 90 % liegen, verdienen Aufmerksamkeit, kurze nächtliche Abfälle sind jedoch häufig.",
|
||||
"oxygenSaturationBody": "Die SpO₂ ist der Prozentsatz der roten Blutkörperchen, die Sauerstoff transportieren. Als allgemeine Orientierung sind Werte von 95–100 % in Ruhe normal (StatPearls/NIH 2023); Werte dauerhaft unter 90 % verdienen Aufmerksamkeit, kurze nächtliche Abfälle sind häufig und über ~98 % bringt nichts. Optische Sensoren können die wahre Sättigung bei dunklerer Haut überschätzen (US FDA 2024) — ein einzelner normnaher Wert ist daher keine Entwarnung; bei Sorge warm und ruhig erneut messen.",
|
||||
"bodyTemperatureTitle": "Körpertemperatur",
|
||||
"bodyTemperatureBody": "Die Körpertemperatur spiegelt deinen Kernwärmezustand wider, normalerweise um 37 °C mit kleinen Tagesschwankungen. Ein anhaltender Anstieg kann auf eine Infektion hindeuten; nächtliche Messungen am Handgelenk erfassen vor allem relative Änderungen, keinen klinischen Wert.",
|
||||
"activeEnergyTitle": "Aktive Energie",
|
||||
@@ -2485,14 +2485,32 @@
|
||||
"breathingDisturbancesTitle": "Atemaussetzer im Schlaf",
|
||||
"breathingDisturbancesBody": "Deine Apple Watch kann schätzen, wie oft deine Atmung im Schlaf unterbrochen war. Ein niedriger Index ist besser; ein von Apple als erhöht eingestufter Wert — vor allem über viele Nächte — sollte mit einer Ärztin oder einem Arzt besprochen werden.",
|
||||
"stepsTitle": "Schritte",
|
||||
"stepsBody": "Die Schrittzahl ist die Gesamtzahl der Schritte, die du täglich gehst. Sie ist das einfachste Maß für Alltagsbewegung — eine höhere Tageszahl spiegelt meist einen aktiveren Lebensstil wider, und eine stetige Gewohnheit zählt mehr als ein einzelner großer Tag.",
|
||||
"readinessBody": "Die Tagesform ist ein einzelner Tageswert dafür, wie bereit dein Körper für den Tag ist — er verbindet Ruhepuls, Herzfrequenzvariabilität, Schlaf und die Beständigkeit deiner letzten Nächte. Ein höherer Wert heißt erholt und belastbar; ein niedrigerer ist ein sanfter Hinweis, einen Gang zurückzuschalten.",
|
||||
"stepsBody": "Die Schrittzahl ist die Gesamtzahl der Schritte, die du täglich gehst. Sie ist das einfachste Maß für Alltagsbewegung, und eine stetige Gewohnheit zählt mehr als ein einzelner großer Tag. Als allgemeine Orientierung fällt das Sterblichkeitsrisiko deutlich Richtung etwa 8.000 Schritte pro Tag, mit weiterem Nutzen bis rund 12.000 (Saint-Maurice JAMA 2020); bei älteren Menschen liegt der optimale Wert niedriger. Die „10.000“ ist ein Werbeslogan, keine Forschung.",
|
||||
"readinessBody": "Die Tagesform ist ein einzelner Tageswert dafür, wie bereit dein Körper für den Tag ist — er verbindet Ruhepuls, Herzfrequenzvariabilität, Schlaf und die Beständigkeit deiner letzten Nächte. Es ist ein Geräte-Näherungswert, der an deinem eigenen Ausgangswert gemessen wird, keine klinische Messung, und zwischen Geräten nicht vergleichbar. Ein höherer Wert heißt erholt und belastbar; ein niedrigerer ist ein sanfter Hinweis, einen Gang zurückzuschalten.",
|
||||
"sleepScoreBody": "Dein Schlafscore fasst eine ganze Nacht in einer Zahl zusammen — aus Dauer, Effizienz, Regelmäßigkeit und der Verteilung von Leicht-, Tief- und REM-Schlaf. Achte eher auf das Muster über Wochen als auf einen perfekten Wert in einer einzelnen Nacht.",
|
||||
"recoveryBody": "Die Erholung zeigt, wie gut sich dein Körper regeneriert hat — gestützt auf nächtliche Signale wie Herzfrequenzvariabilität und Ruhepuls. Ein hoher Wert ist grünes Licht, mehr zu geben; ein niedrigerer deutet an, dass dein System noch aufbaut.",
|
||||
"strainBody": "Die Belastung erfasst, wie viel kardiovaskuläre und körperliche Last du deinem Körper zugemutet hast — von Workouts bis zu einem aktiven Tag auf den Beinen. Sobald genügend Verlauf vorliegt, wird sie an deinem eigenen üblichen Aufwand gemessen, nicht an einem fremden Maßstab.",
|
||||
"stressBody": "Stress ist hier eine Schätzung der körperlichen Last auf deinem Nervensystem, vor allem aus Mustern der Herzfrequenzvariabilität abgelesen — nicht daraus, wie du dich bewusst fühlst. Ein niedrigerer Wert deutet auf einen ruhigen, gut regulierten Zustand hin; ein höherer ist eine Einladung, Erholung einzuplanen.",
|
||||
"recoveryBody": "Die Erholung zeigt, wie gut sich dein Körper regeneriert hat — gestützt auf nächtliche Signale wie Herzfrequenzvariabilität und Ruhepuls. Es ist ein geräteabgeleiteter Näherungswert an deinem eigenen Ausgangswert, keine klinische Messung, und zwischen Geräten nicht vergleichbar. Ein hoher Wert ist grünes Licht, mehr zu geben; ein niedrigerer deutet an, dass dein System noch aufbaut.",
|
||||
"strainBody": "Die Belastung erfasst, wie viel kardiovaskuläre und körperliche Last du deinem Körper zugemutet hast — von Workouts bis zu einem aktiven Tag auf den Beinen. Es ist ein geräteabgeleiteter Index, gemessen an deinem eigenen üblichen Aufwand, keine klinische Messung, und der Kennwert ist zwischen Geräten nicht vergleichbar.",
|
||||
"stressBody": "Stress ist hier eine Geräte-Heuristik aus Mustern der Herzfrequenzvariabilität — keine Cortisol-Messung und nicht, wie du dich bewusst fühlst, und auch kein klinischer Stress-Score. Lies ihn an deinem eigenen Ausgangswert; zwischen Geräten ist er nicht vergleichbar. Ein niedrigerer Wert deutet auf einen ruhigen, gut regulierten Zustand hin; ein höherer ist eine Einladung, Erholung einzuplanen.",
|
||||
"recoveryPageTitle": "Erholung",
|
||||
"recoveryPageBody": "Die Erholung fasst die nächtlichen Signale deines Wearables zusammen – Recharge, Belastung, Load sowie deine Herzfrequenz- und Energiewerte des Tages – und zeigt, wie gut sich dein Körper erholt hat. Achte eher auf das Muster über mehrere Tage als auf einen einzelnen Wert; ein schwächerer Wert ist eine Einladung, kürzerzutreten, kein Alarm."
|
||||
"recoveryPageBody": "Die Erholung fasst die nächtlichen Signale deines Wearables zusammen – Recharge, Belastung, Load sowie deine Herzfrequenz- und Energiewerte des Tages – und zeigt, wie gut sich dein Körper erholt hat. Achte eher auf das Muster über mehrere Tage als auf einen einzelnen Wert; ein schwächerer Wert ist eine Einladung, kürzerzutreten, kein Alarm.",
|
||||
"pulsePressureTitle": "Pulsdruck",
|
||||
"pulsePressureBody": "Der Pulsdruck ist die Differenz zwischen deinem systolischen und diastolischen Wert (systolisch minus diastolisch). Als allgemeine Orientierung sind etwa 40 mmHg in Ruhe typisch; eine große Differenz über rund 60 mmHg im mittleren Alter kann auf versteifte Arterien hinweisen, eine kleine Differenz unter etwa 25 mmHg kann mit niedrigem Schlagvolumen einhergehen (StatPearls/NIH 2023). Ein Gesprächsanlass, keine Diagnose.",
|
||||
"meanArterialPressureTitle": "Mittlerer arterieller Druck",
|
||||
"meanArterialPressureBody": "Der mittlere arterielle Druck ist der durchschnittliche Druck, den deine Organe über einen Herzschlag sehen – grob der diastolische Wert plus ein Drittel der Differenz zum systolischen. Als allgemeine Orientierung liegt ein Ruhewert bei etwa 70–100 mmHg (StatPearls/NIH 2023). Die oft genannte Grenze von ~60 mmHg ist ein Konzept der Akutmedizin, kein Alarm für die Selbstmessung.",
|
||||
"cycleEducationBody": "Dein Zyklus durchläuft vier hormongesteuerte Phasen, und seine Länge und Regelmäßigkeit sind ein gut zugängliches Fenster auf die allgemeine Gesundheit. Als allgemeine Orientierung dauern Zyklen bei Erwachsenen typischerweise 21–35 Tage (klinische Referenzen nennen 24–38), mit einer Blutung von rund fünf Tagen (ACOG 2015). Die Lutealphase ist der stabile Anker, daher schätzt man den Eisprung am besten durch Rückrechnen von der nächsten erwarteten Periode – nie über einen festen „Tag 14“. Ein vorhergesagtes fruchtbares Fenster ist eine Schätzung, die mehrere erfasste Zyklen braucht und ungefähr bleibt, und die Basaltemperatur bestätigt den Eisprung im Nachhinein, statt ihn vorherzusagen. Dies ist keine Verhütungsmethode.",
|
||||
"sleepHygieneBody": "Einige evidenzbasierte Gewohnheiten unterstützen den Schlaf: gleichbleibende Zubett- und Aufstehzeiten auch am Wochenende, helles Morgenlicht und gedämpfte Abende, ein kühles, dunkles und ruhiges Zimmer sowie der Verzicht auf Koffein am Nachmittag, späten Alkohol und große späte Mahlzeiten (CDC 2024). Regelmäßigkeit sagt die Gesundheit mindestens so stark voraus wie die Dauer, und die Gesamtschlafzeit eines Wearables ist weit verlässlicher als seine Aufteilung in Tief-, Leicht- und REM-Schlaf.",
|
||||
"referenceGuidance": "Allgemeine Orientierung, keine medizinische Beratung. Referenzbereiche sind Populationswerte, die sich mit Alter, Geschlecht und Erkrankungen verschieben – dein eigener Verlauf über die Zeit führt die Einordnung.",
|
||||
"referenceState": {
|
||||
"within": "Dein letzter Wert liegt im allgemeinen Referenzbereich.",
|
||||
"slightlyOutside": "Dein letzter Wert liegt knapp außerhalb des üblichen Bereichs – behalte den Verlauf im Blick.",
|
||||
"outside": "Dein letzter Wert liegt außerhalb des üblichen Bereichs. Das ist eine Einordnungshilfe, keine Diagnose; besprich ein anhaltendes Muster mit einer Ärztin oder einem Arzt.",
|
||||
"insufficient": "Es gibt noch nicht genug Daten, oder dieses Signal hat keinen festen Populationsbereich – dein eigener Ausgangswert führt die Einordnung."
|
||||
}
|
||||
},
|
||||
"bpDerived": {
|
||||
"pulsePressure": "Der Pulsdruck (die Differenz zwischen deinen Werten) beträgt etwa {value} mmHg – rund 40 ist in Ruhe typisch.",
|
||||
"meanArterialPressure": "Der mittlere arterielle Druck (der Durchschnitt, den deine Organe sehen) liegt bei etwa {value} mmHg, normalerweise um 70–100.",
|
||||
"caveat": "Diese Werte werden als allgemeine Orientierung aus deiner letzten Messung abgeleitet, nicht als Diagnose."
|
||||
},
|
||||
"stats": {
|
||||
"label": "Zusammenfassende Statistik",
|
||||
|
||||
+32
-14
@@ -2395,7 +2395,7 @@
|
||||
"explainer": {
|
||||
"trigger": "What is {title}?",
|
||||
"bloodPressureTitle": "Blood pressure",
|
||||
"bloodPressureBody": "Blood pressure is the force your blood exerts on the artery walls, written as systolic over diastolic (the peak during a heartbeat over the resting value between beats). Consistently high readings strain the heart and blood vessels over time.",
|
||||
"bloodPressureBody": "Blood pressure is the force your blood exerts on the artery walls, written as systolic over diastolic (the peak during a heartbeat over the resting value between beats). Consistently high readings strain the heart and blood vessels over time. As general guidance, European guidance (ESH 2023) calls under 120/80 optimal and 140/90 or above hypertension; US guidance (ACC/AHA 2017) sets a stricter line, labelling 130–139/80–89 as Stage 1. Categorise from a calm seated reading, never a single number.",
|
||||
"pulseTitle": "Pulse",
|
||||
"pulseBody": "Your pulse is how many times your heart beats per minute. A spot reading reflects what you were doing in the moment — at rest it is typically 60–100 beats per minute, and it rises naturally with movement, stress, or caffeine.",
|
||||
"weightTitle": "Weight",
|
||||
@@ -2409,19 +2409,19 @@
|
||||
"medicationsTitle": "Medication adherence",
|
||||
"medicationsBody": "Adherence is how reliably you take your medication as prescribed, shown here as the share of expected doses you actually logged. Steady intake is often what makes a treatment work as intended.",
|
||||
"restingHrTitle": "Resting heart rate",
|
||||
"restingHrBody": "Resting heart rate is your heart rate when you are completely at rest, usually measured overnight. A lower resting rate generally points to better cardiovascular fitness, and a rising trend can be an early sign of stress or illness.",
|
||||
"restingHrBody": "Resting heart rate is your heart rate when you are completely at rest, usually measured overnight — distinct from your pulse during activity. A lower resting rate generally points to better cardiovascular fitness, and a rising trend can be an early sign of stress or illness. As general guidance, a normal adult resting rate is 60–100 beats per minute (AHA 2024); a trained heart can sit benignly at 40–60.",
|
||||
"hrvTitle": "Heart rate variability (HRV)",
|
||||
"hrvBody": "HRV is the tiny variation in time between consecutive heartbeats. Higher variability usually reflects a well-recovered, adaptable nervous system, while a sustained drop can signal fatigue, stress, or insufficient recovery.",
|
||||
"oxygenSaturationTitle": "Oxygen saturation (SpO₂)",
|
||||
"oxygenSaturationBody": "SpO₂ is the percentage of your red blood cells carrying oxygen. Readings of 95–100% are normal at rest; values that stay below 90% warrant attention, though brief nightly dips are common.",
|
||||
"oxygenSaturationBody": "SpO₂ is the percentage of your red blood cells carrying oxygen. As general guidance, readings of 95–100% are normal at rest (StatPearls/NIH 2023); values that stay below 90% warrant attention, though brief nightly dips are common and above ~98% adds no benefit. Optical readers can over-read true saturation in people with darker skin (US FDA 2024), so a single near-normal reading is not a clearance — retake when warm and still if concerned.",
|
||||
"bodyTemperatureTitle": "Body temperature",
|
||||
"bodyTemperatureBody": "Body temperature reflects your core thermal state, normally around 37 °C with small daily swings. A sustained rise can indicate infection, while overnight wrist readings mainly track relative changes rather than a clinical value.",
|
||||
"bodyTemperatureBody": "Body temperature reflects your core thermal state. As general guidance, the population mean is about 36.6 °C — not 37.0 °C — with a normal oral range of roughly 35.7–37.4 °C and a fever from 38.0 °C (J Gen Intern Med 2019). It swings about 0.5 °C across the day, and sites differ by up to ~1 °C, so tag where you measured. Overnight wrist readings track relative change, not a clinical fever value.",
|
||||
"activeEnergyTitle": "Active energy",
|
||||
"activeEnergyBody": "Active energy is the calories you burn through movement, on top of the baseline your body uses at rest. It is a direct measure of how physically active your day was.",
|
||||
"workoutsTitle": "Workouts",
|
||||
"workoutsBody": "Workouts are the structured exercise sessions your devices record, with type, duration, and energy. Tracking them shows how regular training accumulates over weeks and how it relates to your other health signals.",
|
||||
"respiratoryRateTitle": "Respiratory rate",
|
||||
"respiratoryRateBody": "Respiratory rate is the number of breaths you take per minute. At rest most adults breathe roughly twelve to twenty times a minute; a sustained rise can accompany illness, exertion, or stress.",
|
||||
"respiratoryRateBody": "Respiratory rate is the number of breaths you take per minute. As general guidance, at rest most adults breathe 12–20 times a minute (American Lung Association); it is meaningful only truly at rest or asleep. A sustained rise of about +3 above your own nightly baseline can be an early illness signal — clinical escalation cutoffs are hospital tools, not self-alarms.",
|
||||
"bodyWaterTitle": "Total body water",
|
||||
"bodyWaterBody": "Total body water is the share of your body weight made up of fluid, inside and around your cells. It shifts with hydration, food, and activity, so the trend over weeks tells you more than any single reading.",
|
||||
"boneMassTitle": "Bone mass",
|
||||
@@ -2433,7 +2433,7 @@
|
||||
"muscleMassTitle": "Muscle mass",
|
||||
"muscleMassBody": "Muscle mass is the estimated weight of the muscle in your body. More muscle supports strength, metabolism, and mobility, and it tends to need active effort to preserve as you age.",
|
||||
"visceralFatTitle": "Visceral fat",
|
||||
"visceralFatBody": "Visceral fat is the fat stored deep in the abdomen around your organs. Unlike fat under the skin, a high level is closely linked to heart disease and type 2 diabetes, so lower is generally better.",
|
||||
"visceralFatBody": "Visceral fat is the fat stored deep in the abdomen around your organs. Unlike fat under the skin, a high level is closely linked to heart disease and type 2 diabetes, so lower is generally better. The scale shows a unitless rating (about 1–12 is the healthy range, 13 and up elevated); this is a separate scale from the imaging cm² thresholds clinicians use and is not interchangeable with them.",
|
||||
"leanBodyMassTitle": "Lean body mass",
|
||||
"leanBodyMassBody": "Lean body mass is your total weight minus body fat — muscle, bone, organs, and water combined. Tracking it alongside weight shows whether changes come from muscle or from fat.",
|
||||
"flightsClimbedTitle": "Flights climbed",
|
||||
@@ -2453,7 +2453,7 @@
|
||||
"walkingSpeedTitle": "Walking speed",
|
||||
"walkingSpeedBody": "Walking speed is how fast you move on a level walk. It is a well-studied marker of overall health and mobility, and a steady or rising pace is a good sign.",
|
||||
"pulseWaveVelocityTitle": "Pulse wave velocity",
|
||||
"pulseWaveVelocityBody": "Pulse wave velocity measures how fast the pressure wave from each heartbeat travels along your arteries. A faster wave indicates stiffer arteries, which is linked to higher cardiovascular risk.",
|
||||
"pulseWaveVelocityBody": "Pulse wave velocity measures how fast the pressure wave from each heartbeat travels along your arteries. A faster wave indicates stiffer arteries, which is linked to higher cardiovascular risk. As general guidance, European clinical reference flags carotid-femoral velocity above 10 m/s (ESC/ESH 2018) — but a consumer estimate is a proxy, not interchangeable with that clinical measurement.",
|
||||
"vascularAgeTitle": "Vascular age",
|
||||
"vascularAgeBody": "Vascular age estimates how old your arteries behave compared with your actual age, based on arterial stiffness. A vascular age below your real age suggests healthier blood vessels.",
|
||||
"environmentalAudioTitle": "Environmental sound levels",
|
||||
@@ -2465,7 +2465,7 @@
|
||||
"daylightTitle": "Time in daylight",
|
||||
"daylightBody": "Time in daylight is how long you spend in outdoor light each day. Regular daylight supports a healthy sleep-wake rhythm and mood, and time outdoors is linked to eye health.",
|
||||
"bloodGlucoseTitle": "Blood glucose",
|
||||
"bloodGlucoseBody": "Blood glucose is the amount of sugar in your blood, the body's main energy source. It rises after eating and settles between meals; readings that stay high over time can signal diabetes or prediabetes.",
|
||||
"bloodGlucoseBody": "Blood glucose is the amount of sugar in your blood, the body's main energy source. It rises after eating and settles between meals. As general guidance for adults without diabetes, a fasting reading under 100 mg/dL is normal, 100–125 is prediabetes, and 126 or above falls in the diabetes range (ADA 2024). A single high reading does not diagnose — a clinician confirms with a repeat test, and people managing diabetes use different, clinician-set targets.",
|
||||
"skinTemperatureTitle": "Skin temperature",
|
||||
"skinTemperatureBody": "Skin temperature here is the small nightly change in your wrist temperature from your own baseline, not an absolute fever reading. Shifts can track sleep, environment, illness, or menstrual-cycle phase.",
|
||||
"cardioFitnessTitle": "Cardio fitness (VO₂ max)",
|
||||
@@ -2485,14 +2485,32 @@
|
||||
"breathingDisturbancesTitle": "Sleep breathing disturbances",
|
||||
"breathingDisturbancesBody": "Your Apple Watch can estimate how often your breathing was interrupted during sleep. A lower index is better; a level Apple flags as elevated, especially over many nights, is worth raising with a clinician.",
|
||||
"stepsTitle": "Steps",
|
||||
"stepsBody": "Step count is the total number of steps you take each day. It is the simplest measure of everyday movement — a higher daily count generally reflects a more active lifestyle, and a steady habit matters more than any single big day.",
|
||||
"readinessBody": "Readiness is a single daily read on how prepared your body is to take on the day, blending resting heart rate, heart-rate variability, sleep, and how steady your recent nights have been. A higher score means recovered and resourced; a lower one is a gentle cue to ease off.",
|
||||
"stepsBody": "Step count is the total number of steps you take each day. It is the simplest measure of everyday movement, and a steady habit matters more than any single big day. As general guidance, mortality risk falls steeply toward about 8,000 steps a day with continued benefit through roughly 12,000 (Saint-Maurice JAMA 2020); the optimal count is lower for older adults. The '10,000' figure is a marketing slogan, not research.",
|
||||
"readinessBody": "Readiness is a single daily read on how prepared your body is to take on the day, blending resting heart rate, heart-rate variability, sleep, and how steady your recent nights have been. It is a device proxy benchmarked against your own baseline, not a clinical measure, and scores are not comparable across devices. A higher score means recovered and resourced; a lower one is a gentle cue to ease off.",
|
||||
"sleepScoreBody": "Your sleep score distils a whole night into one number — duration, efficiency, regularity, and the balance of light, deep, and REM. Watch the pattern across weeks rather than chasing a perfect score on any single night.",
|
||||
"recoveryBody": "Recovery reflects how well your body has bounced back, drawing on overnight signals like heart-rate variability and resting heart rate. A strong score is a green light to push; a softer one suggests your system is still rebuilding.",
|
||||
"strainBody": "Strain captures how much cardiovascular and physical load you have put on your body, from workouts to an active day on your feet. Once you have enough history it is judged against your own typical effort, not someone else's baseline.",
|
||||
"stressBody": "Stress here is an estimate of physiological load on your nervous system, read mainly from heart-rate-variability patterns rather than how you consciously feel. A lower score points to a calm, well-regulated state; a higher one is an invitation to build in recovery.",
|
||||
"recoveryBody": "Recovery reflects how well your body has bounced back, drawing on overnight signals like heart-rate variability and resting heart rate. It is a device-derived proxy read against your own baseline, not a clinical measure, and is not comparable across devices. A strong score is a green light to push; a softer one suggests your system is still rebuilding.",
|
||||
"strainBody": "Strain captures how much cardiovascular and physical load you have put on your body, from workouts to an active day on your feet. It is a device-derived index judged against your own typical effort, not a clinical measure, and the headline number is not comparable across devices.",
|
||||
"stressBody": "Stress here is a device heuristic derived from heart-rate-variability patterns, not a cortisol measurement or how you consciously feel — and not a clinical stress score. Read it against your own baseline; it is not comparable across devices. A lower score points to a calm, well-regulated state; a higher one is an invitation to build in recovery.",
|
||||
"recoveryPageTitle": "Recovery",
|
||||
"recoveryPageBody": "Recovery brings together the overnight signals your wearable records — recharge, strain, load and your day's heart-rate and energy totals — to show how well your body has bounced back. Read the pattern across days rather than any single number; a softer reading is an invitation to ease off, not an alarm."
|
||||
"recoveryPageBody": "Recovery brings together the overnight signals your wearable records — recharge, strain, load and your day's heart-rate and energy totals — to show how well your body has bounced back. Read the pattern across days rather than any single number; a softer reading is an invitation to ease off, not an alarm.",
|
||||
"pulsePressureTitle": "Pulse pressure",
|
||||
"pulsePressureBody": "Pulse pressure is the gap between your systolic and diastolic readings (systolic minus diastolic). As general guidance, about 40 mmHg is typical at rest; a wide gap above roughly 60 mmHg in middle age can reflect stiffening arteries, while a narrow gap under about 25 mmHg can accompany low stroke volume (StatPearls/NIH 2023). It is a discussion prompt, not a diagnosis.",
|
||||
"meanArterialPressureTitle": "Mean arterial pressure",
|
||||
"meanArterialPressureBody": "Mean arterial pressure is the average pressure your organs see across a heartbeat, roughly the diastolic plus a third of the gap to systolic. As general guidance, a resting value sits around 70–100 mmHg (StatPearls/NIH 2023). The often-cited ~60 mmHg floor is an acute-care concept, not a consumer self-alarm.",
|
||||
"cycleEducationBody": "Your cycle runs through four phases driven by shifting hormones, and its length and regularity are an accessible window on overall health. As general guidance, adult cycles typically run 21–35 days (clinical references cite 24–38), with bleeding around five days (ACOG 2015). The luteal phase is the steady anchor, so ovulation is best estimated by counting back from your next expected period — never a fixed 'day 14'. A predicted fertile window is an estimate that needs several recorded cycles and stays approximate, and basal body temperature confirms ovulation after the fact rather than predicting it. This is not a contraceptive method.",
|
||||
"sleepHygieneBody": "A few evidence-based habits support sleep: keep consistent bed and wake times including weekends, get bright morning daylight and dim your evenings, keep the room cool, dark and quiet, and avoid afternoon caffeine, late alcohol and large late meals (CDC 2024). Regularity predicts health at least as strongly as duration, and a wearable's total sleep time is far more trustworthy than its deep, light and REM split.",
|
||||
"referenceGuidance": "General guidance, not medical advice. Reference ranges are population anchors that shift with your age, sex and health conditions — your own trend over time leads the read.",
|
||||
"referenceState": {
|
||||
"within": "Your recent value sits within the general reference range.",
|
||||
"slightlyOutside": "Your recent value sits just outside the usual range — worth keeping an eye on the trend.",
|
||||
"outside": "Your recent value sits outside the usual range. This is a placement aid, not a diagnosis; discuss a sustained pattern with a clinician.",
|
||||
"insufficient": "There isn't enough data yet, or this signal has no fixed population range — your own baseline carries the read."
|
||||
}
|
||||
},
|
||||
"bpDerived": {
|
||||
"pulsePressure": "Pulse pressure (the gap between your readings) is about {value} mmHg — roughly 40 is typical at rest.",
|
||||
"meanArterialPressure": "Mean arterial pressure (the average your organs see) is about {value} mmHg, normally around 70–100.",
|
||||
"caveat": "These are derived from your latest reading as general guidance, not a diagnosis."
|
||||
},
|
||||
"stats": {
|
||||
"label": "Summary statistics",
|
||||
|
||||
+19
-1
@@ -2492,7 +2492,25 @@
|
||||
"strainBody": "El esfuerzo capta cuánta carga cardiovascular y física le has impuesto a tu cuerpo, desde los entrenamientos hasta un día activo de pie. Cuando ya hay suficiente historial, se valora frente a tu propio esfuerzo habitual, no frente a un estándar ajeno.",
|
||||
"stressBody": "El estrés aquí es una estimación de la carga fisiológica sobre tu sistema nervioso, leída sobre todo a partir de patrones de variabilidad de la frecuencia cardíaca y no de cómo te sientes conscientemente. Una puntuación más baja apunta a un estado calmado y bien regulado; una más alta es una invitación a incorporar recuperación.",
|
||||
"recoveryPageTitle": "Recuperación",
|
||||
"recoveryPageBody": "La recuperación reúne las señales nocturnas que registra tu wearable —recarga, esfuerzo, carga y los totales de frecuencia cardiaca y energía del día— para mostrar cómo se ha recuperado tu cuerpo. Fíjate en el patrón a lo largo de los días más que en un solo número; un valor más bajo es una invitación a bajar el ritmo, no una alarma."
|
||||
"recoveryPageBody": "La recuperación reúne las señales nocturnas que registra tu wearable —recarga, esfuerzo, carga y los totales de frecuencia cardiaca y energía del día— para mostrar cómo se ha recuperado tu cuerpo. Fíjate en el patrón a lo largo de los días más que en un solo número; un valor más bajo es una invitación a bajar el ritmo, no una alarma.",
|
||||
"pulsePressureTitle": "Presión de pulso",
|
||||
"pulsePressureBody": "La presión de pulso es la diferencia entre tu valor sistólico y diastólico (sistólico menos diastólico). Como orientación general, unos 40 mmHg es lo típico en reposo; una diferencia amplia por encima de unos 60 mmHg en la mediana edad puede reflejar arterias más rígidas, mientras que una diferencia estrecha por debajo de unos 25 mmHg puede acompañar a un bajo volumen sistólico (StatPearls/NIH 2023). Es un motivo de conversación, no un diagnóstico.",
|
||||
"meanArterialPressureTitle": "Presión arterial media",
|
||||
"meanArterialPressureBody": "La presión arterial media es la presión promedio que ven tus órganos a lo largo de un latido, aproximadamente la diastólica más un tercio de la diferencia con la sistólica. Como orientación general, un valor en reposo ronda los 70–100 mmHg (StatPearls/NIH 2023). El umbral de ~60 mmHg que se suele citar es un concepto de cuidados agudos, no una alarma para el autocontrol.",
|
||||
"cycleEducationBody": "Tu ciclo atraviesa cuatro fases impulsadas por hormonas, y su duración y regularidad son una ventana accesible a la salud general. Como orientación general, los ciclos en personas adultas suelen durar 21–35 días (las referencias clínicas citan 24–38), con un sangrado de unos cinco días (ACOG 2015). La fase lútea es el ancla estable, así que la ovulación se estima mejor contando hacia atrás desde tu próxima regla prevista, nunca con un «día 14» fijo. Una ventana fértil predicha es una estimación que necesita varios ciclos registrados y sigue siendo aproximada, y la temperatura basal confirma la ovulación a posteriori en lugar de predecirla. No es un método anticonceptivo.",
|
||||
"sleepHygieneBody": "Algunos hábitos con respaldo de evidencia favorecen el sueño: mantener horarios constantes de acostarse y levantarse incluso los fines de semana, recibir luz brillante por la mañana y atenuar las noches, una habitación fresca, oscura y silenciosa, y evitar la cafeína por la tarde, el alcohol tardío y las comidas copiosas a última hora (CDC 2024). La regularidad predice la salud al menos tanto como la duración, y el tiempo total de sueño de un wearable es mucho más fiable que su reparto entre sueño profundo, ligero y REM.",
|
||||
"referenceGuidance": "Orientación general, no consejo médico. Los rangos de referencia son valores poblacionales que cambian con tu edad, sexo y condiciones de salud; tu propia tendencia a lo largo del tiempo guía la lectura.",
|
||||
"referenceState": {
|
||||
"within": "Tu valor reciente se sitúa dentro del rango de referencia general.",
|
||||
"slightlyOutside": "Tu valor reciente queda justo fuera del rango habitual; conviene vigilar la tendencia.",
|
||||
"outside": "Tu valor reciente queda fuera del rango habitual. Es una ayuda de ubicación, no un diagnóstico; comenta un patrón sostenido con un profesional sanitario.",
|
||||
"insufficient": "Aún no hay datos suficientes, o esta señal no tiene un rango poblacional fijo; tu propia base guía la lectura."
|
||||
}
|
||||
},
|
||||
"bpDerived": {
|
||||
"pulsePressure": "La presión de pulso (la diferencia entre tus valores) es de unos {value} mmHg; alrededor de 40 es lo típico en reposo.",
|
||||
"meanArterialPressure": "La presión arterial media (el promedio que ven tus órganos) es de unos {value} mmHg, normalmente entre 70 y 100.",
|
||||
"caveat": "Estos valores se derivan de tu última lectura como orientación general, no como diagnóstico."
|
||||
},
|
||||
"stats": {
|
||||
"label": "Estadísticas resumidas",
|
||||
|
||||
+19
-1
@@ -2492,7 +2492,25 @@
|
||||
"strainBody": "La charge mesure l'effort cardiovasculaire et physique que vous avez imposé à votre corps, des entraînements à une journée active debout. Dès qu'il y a assez d'historique, elle est évaluée par rapport à votre propre effort habituel, pas à un standard extérieur.",
|
||||
"stressBody": "Le stress est ici une estimation de la charge physiologique sur votre système nerveux, lue surtout à partir des schémas de variabilité de la fréquence cardiaque et non de ce que vous ressentez consciemment. Un score plus bas indique un état calme et bien régulé ; un score plus élevé invite à prévoir de la récupération.",
|
||||
"recoveryPageTitle": "Récupération",
|
||||
"recoveryPageBody": "La récupération rassemble les signaux nocturnes enregistrés par votre wearable — recharge, effort, charge ainsi que vos totaux de fréquence cardiaque et d'énergie du jour — pour montrer à quel point votre corps a récupéré. Observez la tendance sur plusieurs jours plutôt qu'un seul chiffre ; une valeur plus faible invite à lever le pied, ce n'est pas une alarme."
|
||||
"recoveryPageBody": "La récupération rassemble les signaux nocturnes enregistrés par votre wearable — recharge, effort, charge ainsi que vos totaux de fréquence cardiaque et d'énergie du jour — pour montrer à quel point votre corps a récupéré. Observez la tendance sur plusieurs jours plutôt qu'un seul chiffre ; une valeur plus faible invite à lever le pied, ce n'est pas une alarme.",
|
||||
"pulsePressureTitle": "Pression pulsée",
|
||||
"pulsePressureBody": "La pression pulsée est l'écart entre votre valeur systolique et diastolique (systolique moins diastolique). À titre indicatif général, environ 40 mmHg est typique au repos ; un écart large au-dessus d'environ 60 mmHg à l'âge moyen peut refléter des artères rigidifiées, tandis qu'un écart étroit en dessous d'environ 25 mmHg peut accompagner un faible volume d'éjection (StatPearls/NIH 2023). C'est un sujet de discussion, pas un diagnostic.",
|
||||
"meanArterialPressureTitle": "Pression artérielle moyenne",
|
||||
"meanArterialPressureBody": "La pression artérielle moyenne est la pression moyenne que voient vos organes sur un battement, soit grosso modo la diastolique plus un tiers de l'écart avec la systolique. À titre indicatif général, une valeur au repos se situe autour de 70–100 mmHg (StatPearls/NIH 2023). Le seuil de ~60 mmHg souvent cité est un concept de soins aigus, pas une alarme pour l'autosurveillance.",
|
||||
"cycleEducationBody": "Votre cycle traverse quatre phases pilotées par les hormones, et sa durée et sa régularité sont une fenêtre accessible sur la santé globale. À titre indicatif général, les cycles chez l'adulte durent typiquement 21–35 jours (les références cliniques citent 24–38), avec des règles d'environ cinq jours (ACOG 2015). La phase lutéale est l'ancrage stable, donc l'ovulation s'estime au mieux en comptant à rebours depuis vos prochaines règles prévues, jamais à un « jour 14 » fixe. Une fenêtre fertile prédite est une estimation qui demande plusieurs cycles enregistrés et reste approximative, et la température basale confirme l'ovulation après coup plutôt qu'elle ne la prédit. Ce n'est pas une méthode de contraception.",
|
||||
"sleepHygieneBody": "Quelques habitudes fondées sur les preuves favorisent le sommeil : des heures de coucher et de lever régulières, week-ends compris, une lumière vive le matin et des soirées tamisées, une chambre fraîche, sombre et calme, et éviter la caféine l'après-midi, l'alcool tardif et les repas copieux tard le soir (CDC 2024). La régularité prédit la santé au moins autant que la durée, et le temps de sommeil total d'un wearable est bien plus fiable que sa répartition entre sommeil profond, léger et paradoxal.",
|
||||
"referenceGuidance": "Indication générale, pas un avis médical. Les plages de référence sont des repères de population qui évoluent avec votre âge, votre sexe et vos conditions de santé ; votre propre tendance dans le temps guide la lecture.",
|
||||
"referenceState": {
|
||||
"within": "Votre valeur récente se situe dans la plage de référence générale.",
|
||||
"slightlyOutside": "Votre valeur récente est juste en dehors de la plage habituelle — il vaut la peine de surveiller la tendance.",
|
||||
"outside": "Votre valeur récente est en dehors de la plage habituelle. C'est une aide au repérage, pas un diagnostic ; parlez d'un schéma persistant à un professionnel de santé.",
|
||||
"insufficient": "Il n'y a pas encore assez de données, ou ce signal n'a pas de plage de population fixe — votre propre référence guide la lecture."
|
||||
}
|
||||
},
|
||||
"bpDerived": {
|
||||
"pulsePressure": "La pression pulsée (l'écart entre vos valeurs) est d'environ {value} mmHg ; environ 40 est typique au repos.",
|
||||
"meanArterialPressure": "La pression artérielle moyenne (la moyenne que voient vos organes) est d'environ {value} mmHg, normalement autour de 70–100.",
|
||||
"caveat": "Ces valeurs sont dérivées de votre dernière mesure à titre indicatif général, pas comme un diagnostic."
|
||||
},
|
||||
"stats": {
|
||||
"label": "Statistiques récapitulatives",
|
||||
|
||||
+19
-1
@@ -2492,7 +2492,25 @@
|
||||
"strainBody": "Lo sforzo misura quanto carico cardiovascolare e fisico hai imposto al tuo corpo, dagli allenamenti a una giornata attiva in piedi. Quando c'è abbastanza storico, viene valutato rispetto al tuo sforzo abituale, non rispetto a uno standard esterno.",
|
||||
"stressBody": "Lo stress qui è una stima del carico fisiologico sul tuo sistema nervoso, letto soprattutto dagli schemi di variabilità della frequenza cardiaca e non da come ti senti consapevolmente. Un punteggio più basso indica uno stato calmo e ben regolato; uno più alto è un invito a inserire del recupero.",
|
||||
"recoveryPageTitle": "Recupero",
|
||||
"recoveryPageBody": "Il recupero riunisce i segnali notturni registrati dal tuo wearable — ricarica, sforzo, carico e i totali di frequenza cardiaca ed energia della giornata — per mostrare quanto bene il tuo corpo si è ripreso. Guarda l'andamento su più giorni piuttosto che un singolo numero; un valore più basso è un invito a rallentare, non un allarme."
|
||||
"recoveryPageBody": "Il recupero riunisce i segnali notturni registrati dal tuo wearable — ricarica, sforzo, carico e i totali di frequenza cardiaca ed energia della giornata — per mostrare quanto bene il tuo corpo si è ripreso. Guarda l'andamento su più giorni piuttosto che un singolo numero; un valore più basso è un invito a rallentare, non un allarme.",
|
||||
"pulsePressureTitle": "Pressione differenziale",
|
||||
"pulsePressureBody": "La pressione differenziale è la differenza tra il valore sistolico e quello diastolico (sistolica meno diastolica). Come orientamento generale, circa 40 mmHg è tipico a riposo; un divario ampio sopra i 60 mmHg circa nella mezza età può riflettere arterie più rigide, mentre un divario stretto sotto i 25 mmHg circa può accompagnare una bassa gittata sistolica (StatPearls/NIH 2023). È uno spunto di discussione, non una diagnosi.",
|
||||
"meanArterialPressureTitle": "Pressione arteriosa media",
|
||||
"meanArterialPressureBody": "La pressione arteriosa media è la pressione media che i tuoi organi vedono in un battito, all'incirca la diastolica più un terzo del divario con la sistolica. Come orientamento generale, un valore a riposo si aggira sui 70–100 mmHg (StatPearls/NIH 2023). La soglia di ~60 mmHg spesso citata è un concetto di terapia intensiva, non un allarme per l'automisurazione.",
|
||||
"cycleEducationBody": "Il tuo ciclo attraversa quattro fasi guidate dagli ormoni, e la sua durata e regolarità sono una finestra accessibile sulla salute generale. Come orientamento generale, nei adulti i cicli durano tipicamente 21–35 giorni (i riferimenti clinici citano 24–38), con un sanguinamento di circa cinque giorni (ACOG 2015). La fase luteale è l'ancora stabile, quindi l'ovulazione si stima meglio contando a ritroso dalla prossima mestruazione attesa, mai con un «giorno 14» fisso. Una finestra fertile prevista è una stima che richiede più cicli registrati e resta approssimativa, e la temperatura basale conferma l'ovulazione a posteriori anziché prevederla. Non è un metodo contraccettivo.",
|
||||
"sleepHygieneBody": "Alcune abitudini basate sull'evidenza favoriscono il sonno: orari costanti per coricarsi e svegliarsi anche nel weekend, luce intensa al mattino e serate soffuse, una stanza fresca, buia e silenziosa, ed evitare la caffeina nel pomeriggio, l'alcol a tarda ora e i pasti abbondanti a fine giornata (CDC 2024). La regolarità predice la salute almeno quanto la durata, e il tempo di sonno totale di un wearable è molto più affidabile della sua suddivisione tra sonno profondo, leggero e REM.",
|
||||
"referenceGuidance": "Orientamento generale, non un consiglio medico. Gli intervalli di riferimento sono valori di popolazione che cambiano con la tua età, il sesso e le condizioni di salute; il tuo andamento nel tempo guida la lettura.",
|
||||
"referenceState": {
|
||||
"within": "Il tuo valore recente rientra nell'intervallo di riferimento generale.",
|
||||
"slightlyOutside": "Il tuo valore recente è appena fuori dall'intervallo abituale: vale la pena tenere d'occhio l'andamento.",
|
||||
"outside": "Il tuo valore recente è fuori dall'intervallo abituale. È un aiuto alla collocazione, non una diagnosi; parla di un andamento persistente con un medico.",
|
||||
"insufficient": "Non ci sono ancora dati sufficienti, oppure questo segnale non ha un intervallo di popolazione fisso: il tuo riferimento personale guida la lettura."
|
||||
}
|
||||
},
|
||||
"bpDerived": {
|
||||
"pulsePressure": "La pressione differenziale (il divario tra i tuoi valori) è di circa {value} mmHg; circa 40 è tipico a riposo.",
|
||||
"meanArterialPressure": "La pressione arteriosa media (la media che vedono i tuoi organi) è di circa {value} mmHg, normalmente intorno a 70–100.",
|
||||
"caveat": "Questi valori sono derivati dalla tua ultima misurazione come orientamento generale, non come diagnosi."
|
||||
},
|
||||
"stats": {
|
||||
"label": "Statistiche riepilogative",
|
||||
|
||||
+19
-1
@@ -2492,7 +2492,25 @@
|
||||
"strainBody": "Obciążenie ujmuje, ile wysiłku sercowo-naczyniowego i fizycznego nałożyłeś na swoje ciało — od treningów po aktywny dzień na nogach. Gdy zbierze się wystarczająco danych, jest oceniane względem Twojego własnego typowego wysiłku, a nie cudzej miary.",
|
||||
"stressBody": "Stres jest tu szacunkiem fizjologicznego obciążenia Twojego układu nerwowego, odczytywanym głównie ze wzorców zmienności rytmu serca, a nie z tego, jak się świadomie czujesz. Niższy wynik wskazuje na spokojny, dobrze regulowany stan; wyższy to zachęta, by wpleść regenerację.",
|
||||
"recoveryPageTitle": "Regeneracja",
|
||||
"recoveryPageBody": "Regeneracja łączy nocne sygnały rejestrowane przez Twoje urządzenie – recharge, obciążenie, load oraz dzienne sumy tętna i energii – aby pokazać, jak dobrze Twój organizm się zregenerował. Patrz na wzorzec w ciągu kilku dni, a nie na pojedynczą liczbę; niższy wynik to zaproszenie do zwolnienia tempa, a nie alarm."
|
||||
"recoveryPageBody": "Regeneracja łączy nocne sygnały rejestrowane przez Twoje urządzenie – recharge, obciążenie, load oraz dzienne sumy tętna i energii – aby pokazać, jak dobrze Twój organizm się zregenerował. Patrz na wzorzec w ciągu kilku dni, a nie na pojedynczą liczbę; niższy wynik to zaproszenie do zwolnienia tempa, a nie alarm.",
|
||||
"pulsePressureTitle": "Ciśnienie tętna",
|
||||
"pulsePressureBody": "Ciśnienie tętna to różnica między wartością skurczową a rozkurczową (skurczowe minus rozkurczowe). Jako ogólna wskazówka, około 40 mmHg jest typowe w spoczynku; szeroka różnica powyżej około 60 mmHg w średnim wieku może odzwierciedlać usztywnienie tętnic, a wąska różnica poniżej około 25 mmHg może towarzyszyć niskiej objętości wyrzutowej (StatPearls/NIH 2023). To powód do rozmowy, nie diagnoza.",
|
||||
"meanArterialPressureTitle": "Średnie ciśnienie tętnicze",
|
||||
"meanArterialPressureBody": "Średnie ciśnienie tętnicze to przeciętne ciśnienie, jakie widzą Twoje narządy w trakcie uderzenia serca, w przybliżeniu rozkurczowe plus jedna trzecia różnicy do skurczowego. Jako ogólna wskazówka, wartość spoczynkowa mieści się w okolicy 70–100 mmHg (StatPearls/NIH 2023). Często przywoływany próg ~60 mmHg to pojęcie z medycyny ostrej, nie alarm do samodzielnego pomiaru.",
|
||||
"cycleEducationBody": "Twój cykl przebiega przez cztery fazy sterowane hormonami, a jego długość i regularność to dostępne okno na ogólny stan zdrowia. Jako ogólna wskazówka, u osób dorosłych cykle trwają zwykle 21–35 dni (źródła kliniczne podają 24–38), z krwawieniem około pięciu dni (ACOG 2015). Faza lutealna jest stałą kotwicą, dlatego owulację najlepiej szacować, licząc wstecz od następnej spodziewanej miesiączki, nigdy według stałego „14. dnia”. Przewidziane okno płodne to oszacowanie, które wymaga kilku zarejestrowanych cykli i pozostaje przybliżone, a podstawowa temperatura ciała potwierdza owulację po fakcie, a nie ją przewiduje. To nie jest metoda antykoncepcji.",
|
||||
"sleepHygieneBody": "Kilka opartych na dowodach nawyków wspiera sen: stałe pory kładzenia się i wstawania także w weekendy, jasne światło rano i przyciemnione wieczory, chłodna, ciemna i cicha sypialnia oraz unikanie kofeiny po południu, alkoholu późnym wieczorem i obfitych posiłków przed snem (CDC 2024). Regularność przewiduje zdrowie co najmniej tak silnie jak długość snu, a całkowity czas snu z urządzenia jest znacznie bardziej wiarygodny niż jego podział na sen głęboki, lekki i REM.",
|
||||
"referenceGuidance": "Ogólna wskazówka, nie porada medyczna. Zakresy referencyjne to wartości populacyjne, które zmieniają się wraz z Twoim wiekiem, płcią i stanem zdrowia; to Twój własny trend w czasie prowadzi odczyt.",
|
||||
"referenceState": {
|
||||
"within": "Twoja ostatnia wartość mieści się w ogólnym zakresie referencyjnym.",
|
||||
"slightlyOutside": "Twoja ostatnia wartość jest tuż poza zwykłym zakresem – warto obserwować trend.",
|
||||
"outside": "Twoja ostatnia wartość jest poza zwykłym zakresem. To pomoc w umiejscowieniu, nie diagnoza; omów utrzymujący się wzorzec z lekarzem.",
|
||||
"insufficient": "Nie ma jeszcze wystarczających danych lub ten sygnał nie ma stałego zakresu populacyjnego – odczyt prowadzi Twoja własna wartość bazowa."
|
||||
}
|
||||
},
|
||||
"bpDerived": {
|
||||
"pulsePressure": "Ciśnienie tętna (różnica między Twoimi wartościami) wynosi około {value} mmHg – około 40 jest typowe w spoczynku.",
|
||||
"meanArterialPressure": "Średnie ciśnienie tętnicze (średnia, jaką widzą Twoje narządy) wynosi około {value} mmHg, zwykle w okolicy 70–100.",
|
||||
"caveat": "Te wartości są wyliczone z ostatniego pomiaru jako ogólna wskazówka, nie jako diagnoza."
|
||||
},
|
||||
"stats": {
|
||||
"label": "Statystyki podsumowujące",
|
||||
|
||||
@@ -170,6 +170,32 @@ export default function InsightsBlutdruckPage() {
|
||||
average). */}
|
||||
<MetricTargetSummary slug="blood-pressure" />
|
||||
|
||||
{/* v1.18.6 — free derived BP context: pulse pressure (SBP − DBP) and
|
||||
mean arterial pressure ((SBP + 2·DBP)/3), computed from the most
|
||||
recent reading. Pure derivation of stored BP — no schema, no extra
|
||||
fetch. Calm, cited, non-alarming. */}
|
||||
{(() => {
|
||||
const sbp = sysSummary?.latest ?? null;
|
||||
const dbp = diaSummary?.latest ?? null;
|
||||
if (sbp == null || dbp == null || sbp <= dbp) return null;
|
||||
const pp = Math.round(sbp - dbp);
|
||||
const map = Math.round((sbp + 2 * dbp) / 3);
|
||||
return (
|
||||
<p
|
||||
data-slot="bp-derived-caption"
|
||||
className="text-muted-foreground text-sm leading-relaxed"
|
||||
>
|
||||
{t("insights.subPage.bpDerived.pulsePressure", {
|
||||
value: pp,
|
||||
})}{" "}
|
||||
{t("insights.subPage.bpDerived.meanArterialPressure", {
|
||||
value: map,
|
||||
})}{" "}
|
||||
{t("insights.subPage.bpDerived.caveat")}
|
||||
</p>
|
||||
);
|
||||
})()}
|
||||
|
||||
{/* v1.12.4 — Einschätzung is the last content block; the stat strip
|
||||
(rendered by the shell) closes the spine below it. */}
|
||||
<SlugInsightStatusCard
|
||||
|
||||
@@ -3,10 +3,16 @@
|
||||
import { useEffect, useRef, type ReactNode } from "react";
|
||||
import Link from "next/link";
|
||||
import { usePathname } from "next/navigation";
|
||||
import { ListOrdered } from "lucide-react";
|
||||
import { Info, ListOrdered } from "lucide-react";
|
||||
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import {
|
||||
Tooltip,
|
||||
TooltipContent,
|
||||
TooltipProvider,
|
||||
TooltipTrigger,
|
||||
} from "@/components/ui/tooltip";
|
||||
import { CoachLaunchButton } from "@/components/insights/coach-launch-button";
|
||||
import { TargetAdjustButton } from "@/components/insights/target-adjust-button";
|
||||
import { TargetAdjustProvider } from "@/lib/insights/target-adjust-context";
|
||||
@@ -291,6 +297,33 @@ export function SubPageShell({
|
||||
className="text-muted-foreground text-sm leading-relaxed"
|
||||
>
|
||||
{t(`insights.subPage.explainer.${explainerMetric}Body`)}
|
||||
{/* v1.18.6 — a discreet general-guidance tooltip travels with
|
||||
every metric definition. The reference bands in the body
|
||||
above are population anchors, not medical advice; this
|
||||
caption pins that framing without crowding the prose. */}
|
||||
<TooltipProvider>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<button
|
||||
type="button"
|
||||
data-slot="metric-reference-guidance"
|
||||
className={cn(
|
||||
"text-muted-foreground hover:text-foreground ml-1.5 inline-flex",
|
||||
"align-middle focus-visible:ring-ring/50 rounded-full",
|
||||
"focus-visible:ring-2 focus-visible:outline-none",
|
||||
)}
|
||||
aria-label={t(
|
||||
"insights.subPage.explainer.referenceGuidance",
|
||||
)}
|
||||
>
|
||||
<Info className="size-3.5" aria-hidden="true" />
|
||||
</button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent className="max-w-xs text-pretty">
|
||||
{t("insights.subPage.explainer.referenceGuidance")}
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
</TooltipProvider>
|
||||
</p>
|
||||
) : null}
|
||||
{description && !explainerMetric ? (
|
||||
|
||||
@@ -0,0 +1,98 @@
|
||||
import { describe, it, expect } from "vitest";
|
||||
|
||||
import { CITATIONS } from "../medical-citations";
|
||||
import {
|
||||
REFERENCE_RANGES,
|
||||
REFERENCE_METRICS,
|
||||
classifyReference,
|
||||
getReferenceRange,
|
||||
isReferenceMetric,
|
||||
referenceCitation,
|
||||
referenceLabel,
|
||||
type ReferenceMetric,
|
||||
} from "../reference-ranges";
|
||||
|
||||
describe("reference-ranges backbone", () => {
|
||||
it("every band + conflict + headline resolves a real citation", () => {
|
||||
for (const metric of REFERENCE_METRICS) {
|
||||
const range = REFERENCE_RANGES[metric];
|
||||
expect(CITATIONS[range.referenceId], `${metric} referenceId`).toBeTruthy();
|
||||
expect(range.unit, `${metric} unit`).toBeTruthy();
|
||||
expect(range.guidanceCaveat, `${metric} guidanceCaveat`).toBeTruthy();
|
||||
for (const band of range.bands) {
|
||||
expect(CITATIONS[band.citation], `${metric} band ${band.label}`).toBeTruthy();
|
||||
// A band must pin at least one bound.
|
||||
expect(band.low != null || band.high != null, `${metric} ${band.label}`).toBe(true);
|
||||
}
|
||||
for (const conflict of range.conflicts ?? []) {
|
||||
expect(CITATIONS[conflict.citation], `${metric} conflict`).toBeTruthy();
|
||||
expect(conflict.note).toBeTruthy();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
it("keeps ESH 2023 as the BP headline anchor and surfaces US/EU context", () => {
|
||||
const bp = REFERENCE_RANGES.BLOOD_PRESSURE;
|
||||
expect(bp.referenceId).toBe("ESH_2023_HYPERTENSION");
|
||||
expect(referenceLabel("BLOOD_PRESSURE")).toContain("ESH 2023");
|
||||
// ACC/AHA + ESC 2024 ride only as context, never the headline.
|
||||
const conflictCitations = (bp.conflicts ?? []).map((c) => c.citation);
|
||||
expect(conflictCitations).toContain("ACC_AHA_2017_BP");
|
||||
expect(conflictCitations).toContain("ESC_2024_BP");
|
||||
});
|
||||
|
||||
it("classifies values into the four-state contract", () => {
|
||||
// Within the normal band.
|
||||
expect(classifyReference("RESTING_HEART_RATE", 70)).toBe("within");
|
||||
expect(classifyReference("BLOOD_GLUCOSE", 90)).toBe("within");
|
||||
// Adjacent watch band.
|
||||
expect(classifyReference("BLOOD_GLUCOSE", 110)).toBe("slightly-outside");
|
||||
// Attention band beyond the watch tier.
|
||||
expect(classifyReference("BLOOD_GLUCOSE", 150)).toBe("outside");
|
||||
// SpO2 below the healthy floor is attention.
|
||||
expect(classifyReference("OXYGEN_SATURATION", 88)).toBe("outside");
|
||||
expect(classifyReference("OXYGEN_SATURATION", 98)).toBe("within");
|
||||
});
|
||||
|
||||
it("returns insufficient for absent values and band-less metrics", () => {
|
||||
expect(classifyReference("RESTING_HEART_RATE", null)).toBe("insufficient");
|
||||
expect(classifyReference("RESTING_HEART_RATE", Number.NaN)).toBe("insufficient");
|
||||
// HRV carries no fixed population band — baseline-only.
|
||||
expect(REFERENCE_RANGES.HEART_RATE_VARIABILITY.bands).toHaveLength(0);
|
||||
expect(classifyReference("HEART_RATE_VARIABILITY", 42)).toBe("insufficient");
|
||||
});
|
||||
|
||||
it("steps backbone uses the canonical 8,000 green floor", () => {
|
||||
const steps = REFERENCE_RANGES.STEPS;
|
||||
expect(steps.bands[0].low).toBe(8000);
|
||||
expect(steps.referenceId).toBe("STEPS_SAINT_MAURICE_2020");
|
||||
});
|
||||
|
||||
it("guards + resolvers narrow correctly", () => {
|
||||
expect(isReferenceMetric("BLOOD_PRESSURE")).toBe(true);
|
||||
expect(isReferenceMetric("NOT_A_METRIC")).toBe(false);
|
||||
expect(getReferenceRange("NOT_A_METRIC")).toBeNull();
|
||||
expect(getReferenceRange("HBA1C")).toBe(REFERENCE_RANGES.HBA1C);
|
||||
expect(referenceCitation("BLOOD_PRESSURE")).toBe(
|
||||
CITATIONS.ESH_2023_HYPERTENSION,
|
||||
);
|
||||
});
|
||||
|
||||
it("no commercial brand appears in any band label or caveat", () => {
|
||||
const banned = /apple|withings|oura|whoop|garmin|fitbit|polar|samsung/i;
|
||||
for (const metric of REFERENCE_METRICS) {
|
||||
const range = REFERENCE_RANGES[metric];
|
||||
expect(range.guidanceCaveat).not.toMatch(banned);
|
||||
for (const band of range.bands) expect(band.label).not.toMatch(banned);
|
||||
for (const conflict of range.conflicts ?? []) {
|
||||
expect(conflict.note).not.toMatch(banned);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
it("metric ids stay a closed, non-empty set", () => {
|
||||
expect(REFERENCE_METRICS.length).toBeGreaterThan(10);
|
||||
const ids = new Set<ReferenceMetric>(REFERENCE_METRICS);
|
||||
expect(ids.size).toBe(REFERENCE_METRICS.length);
|
||||
});
|
||||
});
|
||||
@@ -155,6 +155,11 @@ const REGISTRY: Record<MetricStatusMetricId, MetricStatusMeta> = {
|
||||
normalRange: { low: 50, high: 100 },
|
||||
archetype: "physiological-vital",
|
||||
},
|
||||
// HRV — no universal "normal" band (ESC/NASPE 1996): only the personal
|
||||
// trend is meaningful and overnight wearable RMSSD is not interchangeable
|
||||
// with the clinical 24-h SDNN norms. No `normalRange` by design; the user's
|
||||
// own baseline leads and single-night swings of ~±10% are routine noise
|
||||
// (see `reference-ranges.ts HEART_RATE_VARIABILITY.guidanceCaveat`).
|
||||
HEART_RATE_VARIABILITY: {
|
||||
id: "HEART_RATE_VARIABILITY",
|
||||
measurementType: "HEART_RATE_VARIABILITY",
|
||||
@@ -163,6 +168,13 @@ const REGISTRY: Record<MetricStatusMetricId, MetricStatusMeta> = {
|
||||
direction: "higher-better",
|
||||
archetype: "physiological-vital",
|
||||
},
|
||||
// SpO₂ — healthy room-air 95–100% (StatPearls/NIH 2023). C4 equity note:
|
||||
// optical pulse oximeters can OVER-READ true saturation in people with
|
||||
// darker skin pigmentation (~3× more often than in lighter skin per the
|
||||
// US FDA 2024 review), so a single near-normal reading is not a clearance.
|
||||
// The band is unchanged; the caveat lives in the explainer copy +
|
||||
// `reference-ranges.ts OXYGEN_SATURATION.guidanceCaveat`. Do NOT set a
|
||||
// "higher is always better" goal — above ~98% adds no benefit.
|
||||
OXYGEN_SATURATION: {
|
||||
id: "OXYGEN_SATURATION",
|
||||
measurementType: "OXYGEN_SATURATION",
|
||||
@@ -181,13 +193,18 @@ const REGISTRY: Record<MetricStatusMetricId, MetricStatusMeta> = {
|
||||
normalRange: { low: 12, high: 20 },
|
||||
archetype: "physiological-vital",
|
||||
},
|
||||
// Body temperature — the population oral-equivalent mean is ~36.6 °C, not
|
||||
// 37.0 °C, with a normal band ~35.7–37.4 °C and fever ≥38.0 °C (J Gen
|
||||
// Intern Med systematic review, 2019). C6: the high anchor tightens
|
||||
// 37.5 → 37.2 so the band sits below the fever line; sites differ by up
|
||||
// to ~1 °C, so the read is a coarse placement only.
|
||||
BODY_TEMPERATURE: {
|
||||
id: "BODY_TEMPERATURE",
|
||||
measurementType: "BODY_TEMPERATURE",
|
||||
displayName: "Body temperature",
|
||||
unit: "°C",
|
||||
direction: "target-band",
|
||||
normalRange: { low: 36.1, high: 37.5 },
|
||||
normalRange: { low: 36.1, high: 37.2 },
|
||||
archetype: "physiological-vital",
|
||||
},
|
||||
SKIN_TEMPERATURE: {
|
||||
@@ -215,12 +232,17 @@ const REGISTRY: Record<MetricStatusMetricId, MetricStatusMeta> = {
|
||||
direction: "lower-better",
|
||||
archetype: "physiological-vital",
|
||||
},
|
||||
// Pulse-wave velocity — the cf-PWV >10 m/s organ-damage marker is the
|
||||
// European clinical reference (ESC/ESH 2018). A consumer estimate is a
|
||||
// PROXY, not interchangeable with clinical cf-PWV tonometry; the band is
|
||||
// a coarse "below 10 is the reference side" placement only.
|
||||
PULSE_WAVE_VELOCITY: {
|
||||
id: "PULSE_WAVE_VELOCITY",
|
||||
measurementType: "PULSE_WAVE_VELOCITY",
|
||||
displayName: "Pulse-wave velocity",
|
||||
unit: "m/s",
|
||||
direction: "lower-better",
|
||||
normalRange: { low: 0, high: 10 },
|
||||
archetype: "physiological-vital",
|
||||
},
|
||||
VASCULAR_AGE: {
|
||||
@@ -232,13 +254,18 @@ const REGISTRY: Record<MetricStatusMetricId, MetricStatusMeta> = {
|
||||
archetype: "physiological-vital",
|
||||
},
|
||||
// ── activity-fitness ──
|
||||
// Steps — C2/D4 reconcile: the canonical "green" floor is 8,000/day
|
||||
// (Saint-Maurice JAMA 2020: mortality risk falls steeply toward ~8k with
|
||||
// continued benefit through ~12k), aligning this display band with the
|
||||
// resolver + classifier so the cross-surface number stops contradicting.
|
||||
// The "10,000" target is a marketing slogan, not research.
|
||||
STEPS: {
|
||||
id: "STEPS",
|
||||
measurementType: "ACTIVITY_STEPS",
|
||||
displayName: "Steps",
|
||||
unit: "steps/day",
|
||||
direction: "higher-better",
|
||||
normalRange: { low: 7000, high: 10000 },
|
||||
normalRange: { low: 8000, high: 15000 },
|
||||
archetype: "activity-fitness",
|
||||
},
|
||||
ACTIVE_ENERGY: {
|
||||
|
||||
@@ -189,6 +189,293 @@ export const CITATIONS = {
|
||||
caveat:
|
||||
"Reference values for an idealised adult; individual variation broad.",
|
||||
},
|
||||
|
||||
// ── v1.18.6 — reference-range backbone sources ──
|
||||
|
||||
/**
|
||||
* AHA — All About Heart Rate (resting + target heart rates), 2024.
|
||||
* Normal adult resting 60–100 bpm; endurance athletes benignly 40–60.
|
||||
* Consumer-facing orientation; symptomatic concern is the trigger for
|
||||
* care, not the number alone.
|
||||
*/
|
||||
AHA_2024_RHR: {
|
||||
id: "aha-2024-rhr",
|
||||
name: "AHA 2024 Heart Rate",
|
||||
year: 2024,
|
||||
url: "https://www.heart.org/en/health-topics/high-blood-pressure/the-facts-about-high-blood-pressure/all-about-heart-rate-pulse",
|
||||
caveat:
|
||||
"General adult band; a trained heart can sit 40–60, and a new low with dizziness/fainting is the concern, not the number alone.",
|
||||
},
|
||||
|
||||
/**
|
||||
* StatPearls/NIH — Pulse Oximetry (2023). Healthy adult room-air SpO₂
|
||||
* 95–100% (bulk 96–99%); mild hypoxaemia 91–94%; clinically significant
|
||||
* <90%. Consumer wrist/finger optical readers are wellness-grade
|
||||
* (±2–4%) and a single low reading is often artefact.
|
||||
*/
|
||||
STATPEARLS_PULSE_OX: {
|
||||
id: "statpearls-pulse-ox",
|
||||
name: "StatPearls Pulse Oximetry",
|
||||
year: 2023,
|
||||
url: "https://www.ncbi.nlm.nih.gov/books/NBK470348/",
|
||||
caveat:
|
||||
"Healthy room-air band; consumer optical SpO₂ is ±2–4% and a single low reading is often artefact — sustained runs are the signal.",
|
||||
},
|
||||
|
||||
/**
|
||||
* 2017 ACC/AHA Guideline for the Prevention, Detection, Evaluation and
|
||||
* Management of High Blood Pressure in Adults. Lowers the US disease
|
||||
* line: Normal <120/<80; Elevated 120–129/<80; Stage 1 130–139 or
|
||||
* 80–89; Stage 2 ≥140 or ≥90. Surfaced only as the stricter COMPARISON
|
||||
* anchor next to the shipped ESH default — never the labelled band.
|
||||
*/
|
||||
ACC_AHA_2017_BP: {
|
||||
id: "acc-aha-2017-bp",
|
||||
name: "ACC/AHA 2017",
|
||||
year: 2017,
|
||||
url: "https://www.ahajournals.org/doi/10.1161/HYP.0000000000000065",
|
||||
caveat:
|
||||
"US guideline; labels 130–139/80–89 as Stage 1 — a stricter line than the ESH default, shown for placement only.",
|
||||
},
|
||||
|
||||
/**
|
||||
* 2024 ESC Guidelines for the management of elevated blood pressure and
|
||||
* hypertension. Adds an "Elevated" diagnostic band (120–139/70–89) and a
|
||||
* treated SBP target of 120–129. Disease line stays ≥140/90 office.
|
||||
* Displayed as context alongside the ESH 2023 default.
|
||||
*/
|
||||
ESC_2024_BP: {
|
||||
id: "esc-2024-bp",
|
||||
name: "ESC 2024",
|
||||
year: 2024,
|
||||
url: "https://academic.oup.com/eurheartj/article/45/38/3912/7741010",
|
||||
caveat:
|
||||
"European; introduces an 'Elevated' 120–139/70–89 band and a treated 120–129 target — context, not the shipped classification.",
|
||||
},
|
||||
|
||||
/**
|
||||
* StatPearls/NCBI — Physiology, Pulse Pressure (NBK482408). PP = SBP −
|
||||
* DBP; ~40 mmHg typical at rest, wide ≳60 mmHg marks large-artery
|
||||
* stiffening, narrow ≲25 mmHg can accompany low stroke volume. A
|
||||
* "discuss with a clinician" signal, not a diagnosis.
|
||||
*/
|
||||
STATPEARLS_PULSE_PRESSURE: {
|
||||
id: "statpearls-pulse-pressure",
|
||||
name: "StatPearls Pulse Pressure",
|
||||
year: 2023,
|
||||
url: "https://www.ncbi.nlm.nih.gov/books/NBK482408/",
|
||||
caveat:
|
||||
"Educational physiology reference; PP is a discussion prompt, never a standalone diagnosis.",
|
||||
},
|
||||
|
||||
/**
|
||||
* StatPearls/NCBI — Physiology, Mean Arterial Pressure (NBK538226).
|
||||
* MAP ≈ (SBP + 2·DBP)/3; resting band ~70–100 mmHg, ~60 mmHg the
|
||||
* conventional organ-perfusion floor — an acute-care concept, NOT a
|
||||
* consumer self-alarm threshold.
|
||||
*/
|
||||
STATPEARLS_MAP: {
|
||||
id: "statpearls-map",
|
||||
name: "StatPearls Mean Arterial Pressure",
|
||||
year: 2023,
|
||||
url: "https://www.ncbi.nlm.nih.gov/books/NBK538226/",
|
||||
caveat:
|
||||
"The ~60 mmHg floor is an acute-care perfusion concept, not a consumer alarm threshold.",
|
||||
},
|
||||
|
||||
/**
|
||||
* 2018 ESC/ESH Guidelines for the management of arterial hypertension.
|
||||
* Source of the carotid-femoral PWV >10 m/s organ-damage cut-off and
|
||||
* the BP grade bands still widely cited. The PWV number is European;
|
||||
* AHA/ACC do not enshrine an equivalent consumer PWV threshold.
|
||||
*/
|
||||
ESC_ESH_2018_PWV: {
|
||||
id: "esc-esh-2018-pwv",
|
||||
name: "ESC/ESH 2018",
|
||||
year: 2018,
|
||||
url: "https://academic.oup.com/eurheartj/article/39/33/3021/5079119",
|
||||
caveat:
|
||||
"cf-PWV >10 m/s is the European clinical reference; consumer PWV / vascular-age estimates are proxies, not clinical cf-PWV.",
|
||||
},
|
||||
|
||||
/**
|
||||
* US FDA — pulse-oximeter accuracy and limitations (Feb 2024 advisory
|
||||
* committee review + safety communication). Oximeters can overestimate
|
||||
* true saturation in people with darker skin, roughly 3× more often
|
||||
* than in lighter-skinned patients (occult hypoxaemia).
|
||||
*/
|
||||
FDA_2024_PULSE_OX: {
|
||||
id: "fda-2024-pulse-ox",
|
||||
name: "FDA 2024 Pulse Oximeter Review",
|
||||
year: 2024,
|
||||
url: "https://www.fda.gov/medical-devices/safety-communications/pulse-oximeter-accuracy-and-limitations-fda-safety-communication",
|
||||
caveat:
|
||||
"Documents over-reading in darker skin; a single near-normal reading is not a clearance — confirm when concerned.",
|
||||
},
|
||||
|
||||
/**
|
||||
* "Normal Body Temperature: A Systematic Review" — J Gen Intern Med,
|
||||
* 2019. Population oral-equivalent mean ~36.6 °C (normal ~35.7–37.4 °C),
|
||||
* superseding the conventional 37.0 °C set point. Fever ≥38.0 °C oral.
|
||||
*/
|
||||
JGIM_2019_TEMPERATURE: {
|
||||
id: "jgim-2019-temperature",
|
||||
name: "J Gen Intern Med 2019",
|
||||
year: 2019,
|
||||
url: "https://link.springer.com/article/10.1007/s11606-019-05148-7",
|
||||
caveat:
|
||||
"Oral-equivalent mean; sites differ by up to ~1 °C — tag the measurement site before comparing.",
|
||||
},
|
||||
|
||||
/**
|
||||
* Royal College of Physicians — National Early Warning Score 2 (NEWS2),
|
||||
* 2017. Respiratory-rate scoring: highest-risk ≤8 and ≥25/min. A
|
||||
* hospital deterioration tool, not a consumer alarm.
|
||||
*/
|
||||
RCP_2017_NEWS2: {
|
||||
id: "rcp-2017-news2",
|
||||
name: "RCP NEWS2 2017",
|
||||
year: 2017,
|
||||
url: "https://www.rcp.ac.uk/improving-care/resources/national-early-warning-score-news-2/",
|
||||
caveat:
|
||||
"Hospital deterioration score; the ≥25/min cutoff is clinical context, not a self-monitoring alarm.",
|
||||
},
|
||||
|
||||
/**
|
||||
* American Lung Association — resting respiratory rate reference
|
||||
* (12–20 breaths/min for healthy adults). Consumer-facing vital-sign
|
||||
* orientation.
|
||||
*/
|
||||
ALA_RESPIRATORY_RATE: {
|
||||
id: "ala-respiratory-rate",
|
||||
name: "American Lung Association",
|
||||
year: 2024,
|
||||
url: "https://www.lung.org/lung-health-diseases/how-lungs-work/breathing-rate",
|
||||
caveat:
|
||||
"General adult resting band; rates run higher in children and shift with activity, fever and emotion.",
|
||||
},
|
||||
|
||||
/**
|
||||
* Peer-reviewed visceral-adipose-tissue imaging literature (2014–2025):
|
||||
* CT/MRI/DXA VAT area >~100 cm² increased-risk, >~160 cm² high-risk.
|
||||
* Ethnicity-dependent; the consumer scale "rating" is NOT a cm² value.
|
||||
*/
|
||||
VAT_IMAGING_THRESHOLD: {
|
||||
id: "vat-imaging-threshold",
|
||||
name: "VAT imaging literature",
|
||||
year: 2025,
|
||||
url: "https://pubmed.ncbi.nlm.nih.gov/24008002/",
|
||||
caveat:
|
||||
"Imaging cm² thresholds; the consumer scale 'visceral fat rating' is a separate unitless scale, not interchangeable.",
|
||||
},
|
||||
|
||||
/**
|
||||
* Wilcox AJ, Dunson D, Baird DD. "The timing of the 'fertile window' in
|
||||
* the menstrual cycle." BMJ, 2000. The 6-day fertile window ends on
|
||||
* ovulation day; ovulation timing is highly variable even in regular
|
||||
* cyclers.
|
||||
*/
|
||||
WILCOX_2000_FERTILE_WINDOW: {
|
||||
id: "wilcox-2000-fertile-window",
|
||||
name: "Wilcox BMJ 2000",
|
||||
year: 2000,
|
||||
url: "https://www.bmj.com/content/321/7271/1259",
|
||||
caveat:
|
||||
"Fertile-window timing is highly variable; calendar estimates approximate and are not a contraceptive method.",
|
||||
},
|
||||
|
||||
/**
|
||||
* ACOG Committee Opinion 651 (2015) — "Menstruation in Girls and
|
||||
* Adolescents: Using the Menstrual Cycle as a Vital Sign." Adult cycle
|
||||
* 24–38 days, bleeding ~5 days (up to ~7).
|
||||
*
|
||||
* NEEDS-VERIFY ACOG — the cycle-length / bleeding-duration figures cited
|
||||
* in the `insights.subPage.explainer.cycleEducationBody` copy, and any
|
||||
* "when to see a clinician" thresholds (cycle <21 / >35, amenorrhea ≥3
|
||||
* months, heavy bleeding >7 days), must be confirmed against the live
|
||||
* ACOG FAQ before shipping that copy to users (automated fetch was
|
||||
* rate-limited during research).
|
||||
*/
|
||||
ACOG_CO651_2015: {
|
||||
id: "acog-co651-2015",
|
||||
name: "ACOG CO 651 2015",
|
||||
year: 2015,
|
||||
url: "https://www.acog.org/clinical/clinical-guidance/committee-opinion/articles/2015/12/menstruation-in-girls-and-adolescents-using-the-menstrual-cycle-as-a-vital-sign",
|
||||
caveat:
|
||||
"Cycle as a vital sign; exact clinician-consultation thresholds should be confirmed against the live ACOG FAQ.",
|
||||
},
|
||||
|
||||
/**
|
||||
* Phillips AJK, et al. "Irregular sleep/wake patterns are associated
|
||||
* with poorer academic performance and delayed circadian and sleep/wake
|
||||
* timing." Sci Rep, 2017 — origin of the Sleep Regularity Index (SRI).
|
||||
* UK Biobank (Windred 2023/24) tied higher regularity to lower mortality.
|
||||
*/
|
||||
PHILLIPS_2017_SRI: {
|
||||
id: "phillips-2017-sri",
|
||||
name: "Phillips Sci Rep 2017",
|
||||
year: 2017,
|
||||
url: "https://www.nature.com/articles/s41598-017-03171-4",
|
||||
caveat:
|
||||
"Sleep-regularity construct; no universal good/bad threshold — present as a personal trend.",
|
||||
},
|
||||
|
||||
/**
|
||||
* US CDC — About Sleep / tips for better sleep (2024). Evidence-based
|
||||
* sleep-hygiene behaviours (consistent schedule, morning light, dim
|
||||
* evenings, caffeine timing, cool dark room).
|
||||
*/
|
||||
CDC_2024_SLEEP: {
|
||||
id: "cdc-2024-sleep",
|
||||
name: "CDC 2024 Sleep",
|
||||
year: 2024,
|
||||
url: "https://www.cdc.gov/sleep/about/index.html",
|
||||
caveat: "General sleep-hygiene guidance; individual needs vary.",
|
||||
},
|
||||
|
||||
/**
|
||||
* WHO — Obesity: preventing and managing the global epidemic (TRS 894),
|
||||
* 2000. The BMI classification still in use: underweight <18.5, normal
|
||||
* 18.5–24.9, overweight 25.0–29.9, obesity ≥30. Asian-population risk
|
||||
* rises at lower cut-offs (WHO 2004).
|
||||
*/
|
||||
WHO_2000_BMI: {
|
||||
id: "who-2000-bmi",
|
||||
name: "WHO 2000 BMI",
|
||||
year: 2000,
|
||||
url: "https://www.who.int/publications/i/item/WHO_TRS_894",
|
||||
caveat:
|
||||
"Population screen, not a diagnosis; Asian-population risk rises at lower cut-offs and BMI does not distinguish muscle from fat.",
|
||||
},
|
||||
|
||||
/**
|
||||
* WHO/IDF — Definition and Diagnosis of Diabetes Mellitus and
|
||||
* Intermediate Hyperglycaemia, 2006. Impaired fasting glucose floor of
|
||||
* 110 mg/dL (vs ADA's 100); the diabetes line (≥126) is shared.
|
||||
*/
|
||||
WHO_IDF_2006_GLUCOSE: {
|
||||
id: "who-idf-2006-glucose",
|
||||
name: "WHO/IDF 2006",
|
||||
year: 2006,
|
||||
url: "https://www.who.int/publications/i/item/definition-and-diagnosis-of-diabetes-mellitus-and-intermediate-hyperglycaemia",
|
||||
caveat:
|
||||
"Sets the impaired-fasting-glucose floor at 110 mg/dL; the diabetes threshold (≥126) is shared with ADA.",
|
||||
},
|
||||
|
||||
/**
|
||||
* ESC/NASPE Task Force — Heart Rate Variability: Standards of
|
||||
* Measurement, Physiological Interpretation and Clinical Use.
|
||||
* Circulation / Eur Heart J, 1996. Defines SDNN/RMSSD norms and the
|
||||
* prognostic 24-h SDNN bands; day-to-day swings of ~±10% are routine.
|
||||
*/
|
||||
ESC_NASPE_1996_HRV: {
|
||||
id: "esc-naspe-1996-hrv",
|
||||
name: "ESC/NASPE HRV Standards 1996",
|
||||
year: 1996,
|
||||
url: "https://www.ahajournals.org/doi/10.1161/01.CIR.93.5.1043",
|
||||
caveat:
|
||||
"Clinical 24-h SDNN norms are NOT interchangeable with an overnight wearable RMSSD; only the personal trend is meaningful.",
|
||||
},
|
||||
} as const satisfies Record<string, Citation>;
|
||||
|
||||
export type CitationId = keyof typeof CITATIONS;
|
||||
|
||||
@@ -0,0 +1,377 @@
|
||||
/**
|
||||
* v1.18.6 — citation-backed reference-range backbone.
|
||||
*
|
||||
* A single structured source of truth for the population reference bands
|
||||
* the UI surfaces (explainer captions, metric tiles, the glossary, the
|
||||
* "where your value sits" tooltip). Each metric carries:
|
||||
* - the canonical display unit,
|
||||
* - one or more named `bands` (low/high half-open, with the body + year
|
||||
* that publishes them via a `CitationId`),
|
||||
* - the primary `referenceId` (the body the headline band is sourced to),
|
||||
* - an optional `conflicts[]` of alternative-guideline context (e.g. the
|
||||
* BP US/EU disagreement — shown as placement context, never the
|
||||
* shipped classification),
|
||||
* - a `guidanceCaveat` that frames the band as general guidance.
|
||||
*
|
||||
* This module is intentionally read-only data + pure helpers — no DB, no
|
||||
* LLM, no per-user state. The metric-status registry fills its coarse
|
||||
* `normalRange` from these bands; the BP detail surface reads the PP / MAP
|
||||
* anchors; the explainers read `referenceLabel()` for the cited sentence.
|
||||
*
|
||||
* Every band traces to `src/lib/medical-citations.ts`. No commercial brand
|
||||
* is named anywhere. Ranges are population anchors that shift with age,
|
||||
* sex and condition — the user's own baseline always leads the read; these
|
||||
* are a placement aid, not a diagnosis. Where authoritative bodies
|
||||
* disagree the disagreement is surfaced (`conflicts`), never silently
|
||||
* resolved.
|
||||
*/
|
||||
import { CITATIONS, type CitationId, citationLabel } from "./medical-citations";
|
||||
|
||||
/**
|
||||
* A single named reference band. `low`/`high` are inclusive display
|
||||
* anchors in the metric's unit; either bound may be omitted for an
|
||||
* open-ended band (e.g. "≥7 h", "<100 mg/dL").
|
||||
*/
|
||||
export interface ReferenceBand {
|
||||
/** Stable, human-readable band label ("Normal", "Elevated", "Fever"). */
|
||||
readonly label: string;
|
||||
readonly low?: number;
|
||||
readonly high?: number;
|
||||
/** The body + year that publishes this band. */
|
||||
readonly citation: CitationId;
|
||||
}
|
||||
|
||||
/**
|
||||
* An alternative-guideline framing shown as CONTEXT next to the default
|
||||
* band — never the shipped classification. The BP US/EU split is the
|
||||
* canonical use.
|
||||
*/
|
||||
export interface ReferenceConflict {
|
||||
/** Short note describing the alternative framing (general education). */
|
||||
readonly note: string;
|
||||
readonly citation: CitationId;
|
||||
}
|
||||
|
||||
export interface ReferenceRange {
|
||||
/** Canonical display unit (matches the metric tile + registry). */
|
||||
readonly unit: string;
|
||||
/** Ordered population bands; the first is the "good"/normal placement. */
|
||||
readonly bands: readonly ReferenceBand[];
|
||||
/** The body the headline band is sourced to (drives the cited sentence). */
|
||||
readonly referenceId: CitationId;
|
||||
/** Alternative-guideline context, shown for placement only. */
|
||||
readonly conflicts?: readonly ReferenceConflict[];
|
||||
/** One-sentence general-guidance frame surfaced under the band. */
|
||||
readonly guidanceCaveat: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* The reference-metric slugs the backbone covers. Stable string keys (not
|
||||
* the DB enum) so the UI, the registry and the explainer can all read one
|
||||
* vocabulary. A superset of the universal metrics + the cardiovascular
|
||||
* derivations the explainers cite.
|
||||
*/
|
||||
export type ReferenceMetric =
|
||||
| "BLOOD_PRESSURE"
|
||||
| "PULSE_PRESSURE"
|
||||
| "MEAN_ARTERIAL_PRESSURE"
|
||||
| "PULSE_WAVE_VELOCITY"
|
||||
| "RESTING_HEART_RATE"
|
||||
| "HEART_RATE_VARIABILITY"
|
||||
| "OXYGEN_SATURATION"
|
||||
| "RESPIRATORY_RATE"
|
||||
| "BODY_TEMPERATURE"
|
||||
| "BLOOD_GLUCOSE"
|
||||
| "HBA1C"
|
||||
| "BMI"
|
||||
| "STEPS"
|
||||
| "VISCERAL_FAT"
|
||||
| "SLEEP_DURATION";
|
||||
|
||||
export const REFERENCE_RANGES: Record<ReferenceMetric, ReferenceRange> = {
|
||||
// ── Blood pressure — ESH 2023 is the SHIPPED default (do not flip). ──
|
||||
// ACC/AHA 2017 + ESC 2024 are surfaced only as placement CONTEXT.
|
||||
BLOOD_PRESSURE: {
|
||||
unit: "mmHg",
|
||||
bands: [
|
||||
// ESH grade bands (office, mmHg, systolic anchors shown).
|
||||
{ label: "Optimal", high: 120, citation: "ESH_2023_HYPERTENSION" },
|
||||
{ label: "Normal", low: 120, high: 129, citation: "ESH_2023_HYPERTENSION" },
|
||||
{ label: "High-normal", low: 130, high: 139, citation: "ESH_2023_HYPERTENSION" },
|
||||
{ label: "Hypertension", low: 140, citation: "ESH_2023_HYPERTENSION" },
|
||||
],
|
||||
referenceId: "ESH_2023_HYPERTENSION",
|
||||
conflicts: [
|
||||
{
|
||||
note: "ACC/AHA (US) labels 130–139/80–89 as Stage 1 hypertension — a stricter line than the European disease threshold of 140/90.",
|
||||
citation: "ACC_AHA_2017_BP",
|
||||
},
|
||||
{
|
||||
note: "ESC 2024 adds an 'Elevated' band at 120–139/70–89 and a treated target of 120–129.",
|
||||
citation: "ESC_2024_BP",
|
||||
},
|
||||
],
|
||||
guidanceCaveat:
|
||||
"Categorised from a standardised seated reading or a validated home average — never a single reading. Targets individualise by age and condition.",
|
||||
},
|
||||
|
||||
// Pulse pressure = SBP − DBP. Pure derivation of stored BP.
|
||||
PULSE_PRESSURE: {
|
||||
unit: "mmHg",
|
||||
bands: [
|
||||
{ label: "Narrow", high: 25, citation: "STATPEARLS_PULSE_PRESSURE" },
|
||||
{ label: "Typical at rest", low: 25, high: 60, citation: "STATPEARLS_PULSE_PRESSURE" },
|
||||
{ label: "Wide", low: 60, citation: "STATPEARLS_PULSE_PRESSURE" },
|
||||
],
|
||||
referenceId: "STATPEARLS_PULSE_PRESSURE",
|
||||
guidanceCaveat:
|
||||
"A wide gap (≳60) in middle age can reflect arterial stiffening; a narrow gap (≲25) can accompany low stroke volume. A discussion prompt, not a diagnosis.",
|
||||
},
|
||||
|
||||
// Mean arterial pressure ≈ (SBP + 2·DBP)/3.
|
||||
MEAN_ARTERIAL_PRESSURE: {
|
||||
unit: "mmHg",
|
||||
bands: [
|
||||
{ label: "Resting band", low: 70, high: 100, citation: "STATPEARLS_MAP" },
|
||||
],
|
||||
referenceId: "STATPEARLS_MAP",
|
||||
guidanceCaveat:
|
||||
"The average pressure your organs see across a heartbeat. The ~60 mmHg floor is an acute-care concept, not a consumer self-alarm.",
|
||||
},
|
||||
|
||||
PULSE_WAVE_VELOCITY: {
|
||||
unit: "m/s",
|
||||
bands: [
|
||||
{ label: "Reference", high: 10, citation: "ESC_ESH_2018_PWV" },
|
||||
{ label: "Elevated stiffness", low: 10, citation: "ESC_ESH_2018_PWV" },
|
||||
],
|
||||
referenceId: "ESC_ESH_2018_PWV",
|
||||
guidanceCaveat:
|
||||
"The cf-PWV >10 m/s organ-damage marker is a European clinical reference measured by tonometry; a consumer estimate is a proxy, not interchangeable.",
|
||||
},
|
||||
|
||||
// ── Resting heart rate — uses RESTING_HEART_RATE, never workout pulse. ──
|
||||
// Bands are authored normal-first so `classifyReference` treats the
|
||||
// population-normal band as the "within" placement. The athletic band
|
||||
// (40–60) is benign for a trained heart but sits below the standard
|
||||
// normal range — the caveat copy carries that nuance.
|
||||
RESTING_HEART_RATE: {
|
||||
unit: "bpm",
|
||||
bands: [
|
||||
{ label: "Normal adult resting", low: 60, high: 100, citation: "AHA_2024_RHR" },
|
||||
{ label: "Athletic (benign)", low: 40, high: 60, citation: "AHA_2024_RHR" },
|
||||
],
|
||||
referenceId: "AHA_2024_RHR",
|
||||
guidanceCaveat:
|
||||
"Measured at rest, ideally on waking. A trained heart can sit 40–60 bpm; the trend over weeks beats any single reading. Caffeine, stress, fever and medication all shift it.",
|
||||
},
|
||||
|
||||
HEART_RATE_VARIABILITY: {
|
||||
unit: "ms",
|
||||
bands: [],
|
||||
referenceId: "ESC_NASPE_1996_HRV",
|
||||
guidanceCaveat:
|
||||
"There is no universal 'normal' — only your own trend is meaningful. Single-night swings of about ±10% are routine; track a 7-day rolling average and read changes smaller than that as noise.",
|
||||
},
|
||||
|
||||
// ── SpO2 — healthy room-air band + darker-skin over-read caveat (C4). ──
|
||||
OXYGEN_SATURATION: {
|
||||
unit: "%",
|
||||
bands: [
|
||||
{ label: "Healthy room air", low: 95, high: 100, citation: "STATPEARLS_PULSE_OX" },
|
||||
{ label: "Mild hypoxaemia", low: 91, high: 94, citation: "STATPEARLS_PULSE_OX" },
|
||||
],
|
||||
referenceId: "STATPEARLS_PULSE_OX",
|
||||
guidanceCaveat:
|
||||
"95–100% is normal at rest; above ~98% adds no benefit. Optical readers can over-read true saturation in people with darker skin, so a single near-normal reading is not a clearance — confirm when warm, still and concerned.",
|
||||
},
|
||||
|
||||
RESPIRATORY_RATE: {
|
||||
unit: "breaths/min",
|
||||
bands: [
|
||||
{ label: "Normal resting adult", low: 12, high: 20, citation: "ALA_RESPIRATORY_RATE" },
|
||||
{ label: "Worth attention", low: 22, citation: "RCP_2017_NEWS2" },
|
||||
],
|
||||
referenceId: "ALA_RESPIRATORY_RATE",
|
||||
guidanceCaveat:
|
||||
"Meaningful only truly at rest or asleep. A sustained rise of about +3 above your own nightly baseline can be an early illness signal; clinical escalation cutoffs are hospital tools, not self-alarms.",
|
||||
},
|
||||
|
||||
// ── Body temperature — evidence-based 36.6 °C mean, fever ≥38 (C6). ──
|
||||
BODY_TEMPERATURE: {
|
||||
unit: "°C",
|
||||
bands: [
|
||||
{ label: "Normal oral range", low: 35.7, high: 37.4, citation: "JGIM_2019_TEMPERATURE" },
|
||||
{ label: "Fever", low: 38.0, citation: "JGIM_2019_TEMPERATURE" },
|
||||
],
|
||||
referenceId: "JGIM_2019_TEMPERATURE",
|
||||
guidanceCaveat:
|
||||
"The population mean is ~36.6 °C, not 37.0 °C, and swings ~0.5 °C across the day. Sites differ by up to ~1 °C, so tag the measurement site. Wearable wrist readings track relative change, not a fever value.",
|
||||
},
|
||||
|
||||
// ── Glucose — GENERAL non-diabetic bands only (diabetes opt-in = W6). ──
|
||||
BLOOD_GLUCOSE: {
|
||||
unit: "mg/dL",
|
||||
bands: [
|
||||
{ label: "Normal fasting", high: 100, citation: "ADA_2024_GLYCEMIC" },
|
||||
{ label: "Prediabetes (fasting)", low: 100, high: 125, citation: "ADA_2024_GLYCEMIC" },
|
||||
{ label: "Diabetes range (fasting)", low: 126, citation: "ADA_2024_GLYCEMIC" },
|
||||
],
|
||||
referenceId: "ADA_2024_GLYCEMIC",
|
||||
conflicts: [
|
||||
{
|
||||
note: "WHO/IDF set the prediabetes fasting floor at 110 mg/dL rather than 100; the diabetes line (≥126) is shared.",
|
||||
citation: "WHO_IDF_2006_GLUCOSE",
|
||||
},
|
||||
],
|
||||
guidanceCaveat:
|
||||
"Fasting bands for adults without diabetes. A single high reading does not diagnose — a clinician confirms with a repeat test. People managing diabetes use different, clinician-set targets.",
|
||||
},
|
||||
|
||||
HBA1C: {
|
||||
unit: "%",
|
||||
bands: [
|
||||
{ label: "Normal", high: 5.7, citation: "ADA_2024_GLYCEMIC" },
|
||||
{ label: "Prediabetes", low: 5.7, high: 6.4, citation: "ADA_2024_GLYCEMIC" },
|
||||
{ label: "Diabetes", low: 6.5, citation: "ADA_2024_GLYCEMIC" },
|
||||
],
|
||||
referenceId: "ADA_2024_GLYCEMIC",
|
||||
guidanceCaveat:
|
||||
"Reflects the average glucose over ~2–3 months. Unreliable with altered red-cell turnover; when discordant with glucose, glucose takes precedence.",
|
||||
},
|
||||
|
||||
BMI: {
|
||||
unit: "kg/m²",
|
||||
bands: [
|
||||
{ label: "Underweight", high: 18.5, citation: "WHO_2020_PA" },
|
||||
{ label: "Normal", low: 18.5, high: 24.9, citation: "WHO_2020_PA" },
|
||||
{ label: "Overweight", low: 25.0, high: 29.9, citation: "WHO_2020_PA" },
|
||||
{ label: "Obesity", low: 30.0, citation: "WHO_2020_PA" },
|
||||
],
|
||||
referenceId: "WHO_2000_BMI",
|
||||
guidanceCaveat:
|
||||
"A population-level screen, not a diagnosis — it does not distinguish muscle from fat or where fat sits. Asian-population risk rises at lower cut-offs.",
|
||||
},
|
||||
|
||||
// ── Steps — canonical green floor = 8,000 (C2/D4 reconcile). ──
|
||||
STEPS: {
|
||||
unit: "steps/day",
|
||||
bands: [
|
||||
{ label: "Higher mortality benefit", low: 8000, citation: "STEPS_SAINT_MAURICE_2020" },
|
||||
],
|
||||
referenceId: "STEPS_SAINT_MAURICE_2020",
|
||||
guidanceCaveat:
|
||||
"Mortality risk falls steeply toward ~8,000 steps/day with continued benefit through ~12,000; the optimal count is lower for older adults. The '10,000' figure is a marketing slogan, not research.",
|
||||
},
|
||||
|
||||
VISCERAL_FAT: {
|
||||
unit: "rating",
|
||||
bands: [
|
||||
{ label: "Healthy rating", low: 1, high: 12, citation: "VAT_IMAGING_THRESHOLD" },
|
||||
{ label: "Elevated rating", low: 13, citation: "VAT_IMAGING_THRESHOLD" },
|
||||
],
|
||||
referenceId: "VAT_IMAGING_THRESHOLD",
|
||||
guidanceCaveat:
|
||||
"The consumer scale 'rating' (≈1–12 healthy) is a unitless scale, not the imaging cm² value (>100 cm² increased risk). High visceral fat is closely tied to heart disease and type 2 diabetes.",
|
||||
},
|
||||
|
||||
// ── Sleep — adult 7–9 h; total sleep time beats the stage split. ──
|
||||
SLEEP_DURATION: {
|
||||
unit: "h",
|
||||
bands: [
|
||||
{ label: "Adult 18–64", low: 7, high: 9, citation: "AASM_2015_SLEEP" },
|
||||
{ label: "Older adult 65+", low: 7, high: 8, citation: "AASM_2015_SLEEP" },
|
||||
],
|
||||
referenceId: "AASM_2015_SLEEP",
|
||||
guidanceCaveat:
|
||||
"Both short and long habitual sleep carry risk. Regularity predicts health at least as strongly as duration, and a wearable's total sleep time is far more trustworthy than its deep/REM split.",
|
||||
},
|
||||
};
|
||||
|
||||
/** All reference-metric slugs the backbone covers. */
|
||||
export const REFERENCE_METRICS = Object.keys(
|
||||
REFERENCE_RANGES,
|
||||
) as ReferenceMetric[];
|
||||
|
||||
/** Type guard narrowing an arbitrary string to a covered reference metric. */
|
||||
export function isReferenceMetric(value: string): value is ReferenceMetric {
|
||||
return Object.prototype.hasOwnProperty.call(REFERENCE_RANGES, value);
|
||||
}
|
||||
|
||||
/** Resolve the reference range for a metric, or null when uncovered. */
|
||||
export function getReferenceRange(metric: string): ReferenceRange | null {
|
||||
return isReferenceMetric(metric) ? REFERENCE_RANGES[metric] : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Localised "ESH 2023 (2023)"-style label for the headline band's source.
|
||||
* The citation name is canonical and not translated (locale-agnostic).
|
||||
*/
|
||||
export function referenceLabel(metric: ReferenceMetric): string {
|
||||
return citationLabel(REFERENCE_RANGES[metric].referenceId);
|
||||
}
|
||||
|
||||
/**
|
||||
* The four-state "where your value sits" phrasing contract (UI-1). A
|
||||
* deterministic placement of a value against the metric's population
|
||||
* bands, ALWAYS framed as general guidance. Consumers map each state to a
|
||||
* cited i18n string; the state itself never asserts a diagnosis.
|
||||
*
|
||||
* - `within` — inside the normal/good band.
|
||||
* - `slightly-outside`— in an adjacent watch band (e.g. high-normal,
|
||||
* prediabetes, mild hypoxaemia).
|
||||
* - `outside` — in a band the guideline flags for attention.
|
||||
* - `insufficient` — no value, or the metric carries no population band
|
||||
* (device-derived / baseline-only signals).
|
||||
*/
|
||||
export type ReferencePlacement =
|
||||
| "within"
|
||||
| "slightly-outside"
|
||||
| "outside"
|
||||
| "insufficient";
|
||||
|
||||
/**
|
||||
* Place a value against a metric's population bands. Returns
|
||||
* `insufficient` when the value is absent or the metric has no fixed band
|
||||
* (HRV and other device-derived signals defer wholly to the personal
|
||||
* baseline). Pure + side-effect free; the user's own baseline still leads
|
||||
* the read — this is only a coarse placement aid.
|
||||
*/
|
||||
export function classifyReference(
|
||||
metric: ReferenceMetric,
|
||||
value: number | null | undefined,
|
||||
): ReferencePlacement {
|
||||
const range = REFERENCE_RANGES[metric];
|
||||
if (value == null || !Number.isFinite(value) || range.bands.length === 0) {
|
||||
return "insufficient";
|
||||
}
|
||||
// Find the first band whose half-open interval contains the value.
|
||||
// Bands are authored in ascending order; the FIRST band is the
|
||||
// normal/good placement, so its membership maps to `within`.
|
||||
for (let i = 0; i < range.bands.length; i++) {
|
||||
const band = range.bands[i];
|
||||
const aboveLow = band.low == null || value >= band.low;
|
||||
const belowHigh = band.high == null || value <= band.high;
|
||||
if (aboveLow && belowHigh) {
|
||||
if (i === 0) return "within";
|
||||
// The band immediately adjacent to normal is the watch tier; any
|
||||
// band beyond it is the attention tier.
|
||||
return i === 1 ? "slightly-outside" : "outside";
|
||||
}
|
||||
}
|
||||
// Value sits beyond every authored band (e.g. below an open-low normal,
|
||||
// or above the top band's open-high). Treat an under-shoot of the first
|
||||
// band's low as within when the first band is open-low; otherwise the
|
||||
// value is outside the covered range → attention.
|
||||
const first = range.bands[0];
|
||||
if (first.low != null && value < first.low) return "outside";
|
||||
return "outside";
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience: the citation entry backing a metric's headline band, for
|
||||
* surfaces that want the url + caveat rather than the formatted label.
|
||||
*/
|
||||
export function referenceCitation(metric: ReferenceMetric) {
|
||||
return CITATIONS[REFERENCE_RANGES[metric].referenceId];
|
||||
}
|
||||
Reference in new issue
Block a user