"""dashboard/explain.py — the comprehension layer. Every analytical block in Readthrough answers four questions before it shows a number: what — what am I looking at, in one plain sentence (always visible) read — how do I interpret it; what does high/low/rising actually mean source — where the figure came from, so the reader can go check caveat — what this does NOT prove (optional, but present wherever the signal is heuristic rather than reported fact) ``what`` renders as a subtitle directly under the section header, so a reader who never clicks anything still knows what the panel is. ``read``/``source``/ ``caveat`` live behind a "How to read this" disclosure, so an expert is not slowed down by prose they already know. Explanations live here rather than in ``dashboard/i18n.py`` because they are paragraphs, not chrome labels — mixing them into the 396-key string table would make both harder to maintain. Usage:: from dashboard.explain import explained_header, explain_note st.markdown(explained_header("redline", t("cw_tab_language"), accent=AMBER), unsafe_allow_html=True) # or, for a sub-block that already has a header: st.markdown(explain_note("dso"), unsafe_allow_html=True) """ from __future__ import annotations import streamlit as st from dashboard.theme import ( BG, BG_MUTED, BORDER, TEXT, TEXT_MUTED, TEXT_FAINT, FS_META, FS_SECTION_LABEL, FS_EYEBROW, ) # ── Chrome labels for the disclosure itself ────────────────────────────────── _UI: dict[str, dict[str, str]] = { "how_to_read": { "en": "How to read this", "fr": "Comment lire ce bloc", "es": "Cómo leer esto", "de": "So lesen Sie das", }, "read_label": { "en": "Interpretation", "fr": "Interprétation", "es": "Interpretación", "de": "Interpretation", }, "source_label": { "en": "Source", "fr": "Source", "es": "Fuente", "de": "Quelle", }, "caveat_label": { "en": "What it does not prove", "fr": "Ce que cela ne prouve pas", "es": "Lo que no demuestra", "de": "Was es nicht beweist", }, } def _lang() -> str: try: return st.session_state.get("ui_lang", "en") except Exception: return "en" def _pick(bucket: dict[str, str] | None) -> str: if not bucket: return "" lang = _lang() return bucket.get(lang) or bucket.get("en") or "" def _ui(key: str) -> str: return _pick(_UI.get(key)) # ── The explanation registry ───────────────────────────────────────────────── # One entry per analytical block in the app. Keys are stable identifiers, not # display labels, so renaming a section never orphans its explanation. EXPLANATIONS: dict[str, dict[str, dict[str, str]]] = { # ══ The Brief — Storyline (continuity threads) ═══════════════════════════ "continuity_threads": { "what": { "en": "Specific issues raised in past quarters — tracked here across time to show whether they got better, worse, or were dropped.", "fr": "Des sujets précis soulevés lors de trimestres passés — suivis ici dans la durée pour voir s'ils se sont améliorés, aggravés, ou abandonnés.", "es": "Temas concretos planteados en trimestres anteriores — seguidos aquí en el tiempo para ver si mejoraron, empeoraron o se abandonaron.", "de": "Konkrete Themen aus früheren Quartalen — hier über die Zeit verfolgt, um zu zeigen, ob sie sich gebessert, verschlechtert oder erledigt haben.", }, "read": { "en": "Each thread starts when a material issue is first flagged and is re-evaluated every quarter with fresh evidence: 'ongoing' means it's still true with nothing new, 'escalated' means it got worse, 'resolved' means there is concrete evidence it was addressed. A thread not mentioned for two consecutive quarters is marked 'stale' automatically — companies rarely announce that a problem quietly went away.", "fr": "Chaque fil démarre quand un sujet important est signalé pour la première fois, puis est réévalué chaque trimestre avec de nouvelles preuves : « en cours » signifie que rien n'a changé, « aggravé » que la situation a empiré, « résolu » qu'il existe une preuve concrète que le sujet a été traité. Un fil non mentionné pendant deux trimestres consécutifs passe automatiquement en « caduc » — les entreprises annoncent rarement qu'un problème s'est discrètement résorbé.", "es": "Cada hilo comienza cuando se señala por primera vez un tema relevante y se reevalúa cada trimestre con nueva evidencia: 'en curso' significa que sigue igual, 'agravado' que empeoró, 'resuelto' que hay evidencia concreta de que se abordó. Un hilo no mencionado durante dos trimestres seguidos pasa automáticamente a 'caduco'.", "de": "Jeder Handlungsfaden beginnt, wenn ein wesentliches Thema erstmals angesprochen wird, und wird jedes Quartal mit neuer Evidenz neu bewertet: \"laufend\" heißt unverändert, \"verschärft\" heißt schlechter geworden, \"gelöst\" heißt konkret behoben. Ein zwei Quartale in Folge nicht erwähnter Faden wird automatisch als \"veraltet\" markiert.", }, "source": { "en": "Status changes are proposed by the AI from retrieved filing/transcript evidence each quarter, but staleness (two consecutive quarters unmentioned) is assigned by code, not the AI — a company cannot make a problem disappear just by not talking about it.", "fr": "Les changements de statut sont proposés par l'IA à partir des preuves extraites du document/de la transcription chaque trimestre, mais la caducité (deux trimestres consécutifs sans mention) est attribuée par le code, pas par l'IA — une entreprise ne peut pas faire disparaître un problème simplement en n'en parlant plus.", "es": "Los cambios de estado los propone la IA a partir de la evidencia recuperada cada trimestre, pero la caducidad (dos trimestres seguidos sin mención) la asigna el código, no la IA.", "de": "Statusänderungen werden von der KI anhand der jedes Quartal abgerufenen Evidenz vorgeschlagen, die Kennzeichnung als veraltet (zwei Quartale in Folge unerwähnt) erfolgt jedoch durch Code, nicht durch die KI.", }, "caveat": { "en": "This is an experimental AI judgment, not a fact. A 'resolved' or 'escalated' call can be wrong — always check the evidence quote before treating it as settled.", "fr": "Il s'agit d'un jugement expérimental de l'IA, pas d'un fait. Une évaluation « résolu » ou « aggravé » peut être erronée — vérifiez toujours la citation source avant de la considérer comme acquise.", "es": "Se trata de un juicio experimental de la IA, no de un hecho. Una calificación de 'resuelto' o 'agravado' puede ser errónea — verifique siempre la cita de evidencia antes de darla por buena.", "de": "Dies ist eine experimentelle KI-Einschätzung, keine Tatsache. Eine Einstufung als \"gelöst\" oder \"verschärft\" kann falsch sein — prüfen Sie stets das Belegzitat, bevor Sie sie als gesichert ansehen.", }, }, # ══ What Changed — Filing timeline (8-K) ═════════════════════════════════ "events": { "what": { "en": "Every material event this company formally reported to the SEC between earnings — executive departures, auditor changes, new debt, closed acquisitions.", "fr": "Chaque événement important que l'entreprise a officiellement déclaré à la SEC entre deux publications de résultats — départs de dirigeants, changement d'auditeur, nouvelle dette, acquisitions finalisées.", "es": "Cada hecho relevante que la empresa comunicó formalmente a la SEC entre resultados — salidas de directivos, cambio de auditor, nueva deuda, adquisiciones cerradas.", "de": "Jedes wesentliche Ereignis, das das Unternehmen zwischen zwei Quartalsberichten offiziell bei der SEC gemeldet hat — Vorstandsabgänge, Prüferwechsel, neue Schulden, abgeschlossene Übernahmen.", }, "read": { "en": "US companies must file an 8-K within four business days of a material event — long before it appears in the next quarterly report. The item code is the company's own classification, not ours: 5.02 is an executive or director change, 4.01 an auditor change, 1.01 a material agreement, 2.02 the earnings release itself. Clusters matter more than single events: a CFO departure plus an auditor change in the same quarter is a different story from either one alone.", "fr": "Une société américaine doit déposer un 8-K dans les quatre jours ouvrés suivant un événement important — bien avant qu'il n'apparaisse dans le rapport trimestriel suivant. Le code d'item est la classification faite par l'entreprise elle-même, pas par nous : 5.02 = changement de dirigeant ou d'administrateur, 4.01 = changement d'auditeur, 1.01 = accord matériel, 2.02 = publication des résultats. Les regroupements comptent plus que les événements isolés : un départ de CFO accompagné d'un changement d'auditeur le même trimestre ne raconte pas la même histoire que l'un des deux seul.", "es": "Una empresa estadounidense debe presentar un 8-K dentro de los cuatro días hábiles siguientes a un hecho relevante — mucho antes de que aparezca en el siguiente informe trimestral. El código de ítem es la clasificación de la propia empresa, no la nuestra: 5.02 = cambio de directivo o consejero, 4.01 = cambio de auditor, 1.01 = acuerdo material, 2.02 = publicación de resultados. Los agrupamientos importan más que los hechos aislados.", "de": "US-Unternehmen müssen ein 8-K innerhalb von vier Werktagen nach einem wesentlichen Ereignis einreichen — lange bevor es im nächsten Quartalsbericht erscheint. Der Item-Code ist die Einstufung des Unternehmens selbst: 5.02 = Wechsel im Vorstand/Aufsichtsrat, 4.01 = Prüferwechsel, 1.01 = wesentliche Vereinbarung, 2.02 = Ergebnisveröffentlichung. Häufungen sind aussagekräftiger als Einzelereignisse.", }, "source": { "en": "SEC EDGAR filing index — dates and item codes exactly as filed. Nothing here is inferred or generated.", "fr": "Index des dépôts SEC EDGAR — dates et codes d'item exactement tels que déposés. Rien ici n'est déduit ni généré.", "es": "Índice de presentaciones de SEC EDGAR — fechas y códigos de ítem tal como se presentaron. Nada aquí es inferido ni generado.", "de": "SEC-EDGAR-Einreichungsindex — Daten und Item-Codes exakt wie eingereicht. Nichts davon ist abgeleitet oder generiert.", }, "caveat": { "en": "A filing tells you an event happened, not whether it was good or bad. A CFO can leave for a better job; a company can change auditors for fee reasons. Read the filing before drawing a conclusion.", "fr": "Un dépôt vous dit qu'un événement a eu lieu, pas s'il est bon ou mauvais. Un CFO peut partir pour un meilleur poste ; une société peut changer d'auditeur pour une question d'honoraires. Lisez le document avant de conclure.", "es": "Una presentación indica que ocurrió un hecho, no si fue bueno o malo. Un CFO puede irse a un puesto mejor; una empresa puede cambiar de auditor por honorarios. Lea el documento antes de concluir.", "de": "Eine Einreichung sagt Ihnen, dass ein Ereignis stattfand — nicht, ob es gut oder schlecht war. Lesen Sie das Dokument, bevor Sie schlussfolgern.", }, }, # ══ What Changed — Redline (language diff) ═══════════════════════════════ # ══ The Brief — the article ══════════════════════════════════════════════ "narrative": { "what": { "en": "The quarter written out as an article, composed only from the facts on this page that passed source verification.", "fr": "Le trimestre raconté sous forme d'article, écrit uniquement à partir des faits de cette page ayant passé la vérification des sources.", "es": "El trimestre contado como un artículo, escrito solo a partir de los hechos de esta página que superaron la verificación de fuentes.", "de": "Das Quartal als Artikel geschrieben, ausschließlich aus den Fakten dieser Seite, die die Quellenprüfung bestanden haben.", }, "read": { "en": "Read it the way you would read a markets column: for the shape of the quarter, not for the numbers — those are already above and below it. The paragraph on language is the one worth slowing down for, because it is built from wording the company changed rather than from anything it announced. Every number in the text is a number one of the numbered sources states; nothing was computed, inferred or rounded on the way in.", "fr": "Lisez-le comme une chronique de marché : pour la forme du trimestre, pas pour les chiffres — ils sont déjà au-dessus et en dessous. Le paragraphe sur le langage mérite qu'on ralentisse : il est construit sur des formulations que l'entreprise a modifiées, pas sur ce qu'elle a annoncé. Chaque chiffre du texte est un chiffre énoncé par l'une des sources numérotées ; rien n'a été calculé, déduit ni arrondi en chemin.", "es": "Léalo como una columna de mercados: por la forma del trimestre, no por las cifras — ya están arriba y abajo. El párrafo sobre el lenguaje es el que merece detenerse, porque se construye sobre la redacción que la empresa cambió. Cada cifra del texto la enuncia una de las fuentes numeradas.", "de": "Lesen Sie ihn wie eine Marktkolumne: wegen der Form des Quartals, nicht wegen der Zahlen — die stehen bereits darüber und darunter. Der Absatz zur Sprache lohnt das Innehalten, denn er beruht auf Formulierungen, die das Unternehmen geändert hat. Jede Zahl im Text nennt eine der nummerierten Quellen.", }, "source": { "en": "A second model call that runs after verification and is shown nothing else: the verified facts, the code-computed wording changes, the reported metric trajectory. Any paragraph that cites no source, or that contains a number its sources do not state, is discarded before the page is built.", "fr": "Un second appel au modèle, exécuté après la vérification et à qui rien d'autre n'est montré : les faits vérifiés, les changements de formulation calculés par le code, la trajectoire des métriques publiées. Tout paragraphe sans source citée, ou contenant un chiffre que ses sources n'énoncent pas, est écarté avant la construction de la page.", "es": "Una segunda llamada al modelo, ejecutada tras la verificación y a la que no se le muestra nada más: los hechos verificados, los cambios de redacción calculados por el código y la trayectoria de las métricas publicadas. Todo párrafo sin fuente citada se descarta.", "de": "Ein zweiter Modellaufruf nach der Verifikation, dem nichts anderes gezeigt wird: die verifizierten Fakten, die im Code berechneten Formulierungsänderungen und der Verlauf der berichteten Kennzahlen. Jeder Absatz ohne zitierte Quelle wird verworfen.", }, "caveat": { "en": "The prose is written by a model. The facts under it are verified; the way they are joined into an argument is not, and a well-turned sentence is not evidence. Where the piece and the panels below it disagree, the panels are the record.", "fr": "La prose est écrite par un modèle. Les faits qui la soutiennent sont vérifiés ; la manière dont ils sont assemblés en raisonnement ne l'est pas, et une phrase bien tournée n'est pas une preuve. En cas de désaccord entre le papier et les panneaux ci-dessous, ce sont les panneaux qui font foi.", "es": "La prosa la escribe un modelo. Los hechos que la sostienen están verificados; la forma en que se unen en un argumento no lo está. Si el artículo y los paneles inferiores discrepan, los paneles son el registro.", "de": "Die Prosa schreibt ein Modell. Die Fakten darunter sind verifiziert; die Art ihrer Verknüpfung zu einem Argument nicht. Wenn Artikel und Panels darunter sich widersprechen, gelten die Panels.", }, }, "redline": { "what": { "en": "The exact wording this company changed in its filing since last quarter — risks it added, removed, or quietly rewrote.", "fr": "Les formulations exactes que l'entreprise a modifiées dans son document depuis le trimestre dernier — risques ajoutés, supprimés, ou discrètement réécrits.", "es": "La redacción exacta que la empresa cambió en su informe desde el trimestre pasado — riesgos añadidos, eliminados o reescritos discretamente.", "de": "Der exakte Wortlaut, den das Unternehmen seit dem letzten Quartal geändert hat — hinzugefügte, entfernte oder still umformulierte Risiken.", }, "read": { "en": "Legal teams rewrite filings deliberately — a sentence rarely changes by accident. A newly added risk factor is the company telling you, in writing, that something it was not worried about last quarter now worries it. A removed one usually means the threat receded. Reworded risks are the subtlest and often the most informative: watch for hedging that gets stronger (\"may\" → \"will likely\") or quantities that disappear. Comparing two 80-page documents by hand is why almost nobody reads this.", "fr": "Les services juridiques réécrivent les documents délibérément — une phrase change rarement par accident. Un facteur de risque nouvellement ajouté, c'est l'entreprise qui vous dit, par écrit, que quelque chose qui ne l'inquiétait pas le trimestre dernier l'inquiète maintenant. Un risque supprimé signifie en général que la menace a reculé. Les risques reformulés sont les plus subtils et souvent les plus instructifs : guettez les nuances qui se durcissent (« may » → « will likely ») ou les chiffres qui disparaissent. Comparer deux documents de 80 pages à la main, c'est la raison pour laquelle presque personne ne lit ça.", "es": "Los equipos legales reescriben los informes deliberadamente — una frase rara vez cambia por accidente. Un factor de riesgo recién añadido es la empresa diciéndole por escrito que algo que no le preocupaba el trimestre pasado ahora sí. Los riesgos reformulados son los más sutiles y a menudo los más informativos.", "de": "Rechtsabteilungen formulieren Berichte bewusst um — ein Satz ändert sich selten zufällig. Ein neu hinzugefügter Risikofaktor ist die schriftliche Mitteilung, dass etwas, das im Vorquartal keine Sorge war, es jetzt ist. Umformulierte Risiken sind die subtilsten und oft aufschlussreichsten.", }, "source": { "en": "Verbatim text from consecutive 10-K/10-Q filings, compared sentence by sentence in code. The before/after passages are quoted exactly — no AI rewriting.", "fr": "Texte verbatim de dépôts 10-K/10-Q consécutifs, comparés phrase par phrase par le code. Les passages avant/après sont cités exactement — aucune réécriture par IA.", "es": "Texto literal de presentaciones 10-K/10-Q consecutivas, comparadas frase por frase por código. Los pasajes antes/después se citan exactamente — sin reescritura por IA.", "de": "Wörtlicher Text aus aufeinanderfolgenden 10-K/10-Q-Berichten, im Code satzweise verglichen. Die Vorher-/Nachher-Passagen sind exakt zitiert — keine KI-Umschreibung.", }, "caveat": { "en": "Companies also rewrite boilerplate for legal housekeeping. A changed sentence is a lead to investigate, not a conclusion.", "fr": "Les entreprises réécrivent aussi des passages standard pour de simples raisons de forme juridique. Une phrase modifiée est une piste à creuser, pas une conclusion.", "es": "Las empresas también reescriben texto estándar por mantenimiento legal. Una frase modificada es una pista a investigar, no una conclusión.", "de": "Unternehmen überarbeiten auch Standardtexte aus rein rechtlichen Formgründen. Ein geänderter Satz ist ein Hinweis, keine Schlussfolgerung.", }, }, # ══ What Changed — Q&A pressure map ══════════════════════════════════════ "attention": { "what": { "en": "What professional analysts kept asking management about, call after call — and which questions never got a straight answer.", "fr": "Ce sur quoi les analystes professionnels ont interrogé la direction, call après call — et quelles questions n'ont jamais reçu de réponse claire.", "es": "Lo que los analistas profesionales siguieron preguntando a la dirección, llamada tras llamada — y qué preguntas nunca obtuvieron una respuesta clara.", "de": "Wonach professionelle Analysten das Management Call für Call gefragt haben — und welche Fragen nie klar beantwortet wurden.", }, "read": { "en": "Sell-side analysts talk to customers, suppliers and competitors all quarter. What they choose to spend their two questions on is the closest thing to a public read on where the informed money thinks the risk is. A theme that shows up on one call is noise; the same theme on three or four consecutive calls means the market has an unresolved doubt. Rows flagged as evasive are ones where the question recurred and the answer stayed non-committal — that combination is the signal, not either half alone.", "fr": "Les analystes sell-side parlent aux clients, fournisseurs et concurrents toute l'année. Ce sur quoi ils choisissent de dépenser leurs deux questions est ce qui se rapproche le plus d'une lecture publique de là où l'argent informé situe le risque. Un thème qui apparaît sur un call, c'est du bruit ; le même thème sur trois ou quatre calls consécutifs signifie que le marché a un doute non résolu. Les lignes marquées comme évasives sont celles où la question est revenue et où la réponse est restée non-engageante — c'est cette combinaison qui est le signal, pas l'une des deux moitiés seule.", "es": "Los analistas del sell-side hablan con clientes, proveedores y competidores todo el trimestre. En qué eligen gastar sus dos preguntas es lo más cercano a una lectura pública de dónde el dinero informado sitúa el riesgo. Un tema en una llamada es ruido; el mismo tema en tres o cuatro llamadas consecutivas significa una duda no resuelta.", "de": "Sell-Side-Analysten sprechen das ganze Quartal mit Kunden, Lieferanten und Wettbewerbern. Wofür sie ihre zwei Fragen einsetzen, kommt einer öffentlichen Einschätzung am nächsten, wo das informierte Geld das Risiko sieht. Ein Thema in einem Call ist Rauschen; dasselbe Thema in drei oder vier aufeinanderfolgenden Calls bedeutet einen ungelösten Zweifel.", }, "source": { "en": "Earnings call transcripts, parsed into analyst questions and management answers, then clustered by topic in code. Counts are literal question counts.", "fr": "Transcriptions des earnings calls, découpées en questions d'analystes et réponses de la direction, puis regroupées par thème par le code. Les compteurs sont des décomptes littéraux de questions.", "es": "Transcripciones de earnings calls, divididas en preguntas de analistas y respuestas de la dirección, y agrupadas por tema en código. Los recuentos son literales.", "de": "Earnings-Call-Transkripte, aufgeteilt in Analystenfragen und Management-Antworten, dann per Code nach Themen geclustert. Die Zahlen sind wörtliche Fragenzählungen.", }, "caveat": { "en": "Topic labels are generated by word-frequency clustering, not by an analyst. They can be blunt. The underlying question text is always shown so you can judge the grouping yourself.", "fr": "Les libellés de thème sont générés par regroupement de fréquences de mots, pas par un analyste. Ils peuvent être grossiers. Le texte de la question sous-jacente est toujours affiché pour que vous jugiez vous-même du regroupement.", "es": "Las etiquetas de tema se generan por agrupación de frecuencia de palabras, no por un analista. Pueden ser toscas. El texto de la pregunta siempre se muestra.", "de": "Themenbezeichnungen entstehen durch Worthäufigkeits-Clustering, nicht durch einen Analysten. Der zugrundeliegende Fragetext wird immer angezeigt.", }, }, # ══ Financials — Guidance track record ═══════════════════════════════════ "track_record": { "what": { "en": "How often this management team actually delivered what it promised — every guidance figure it published, scored against the result.", "fr": "À quelle fréquence cette équipe dirigeante a réellement tenu ses promesses — chaque objectif publié, confronté au résultat obtenu.", "es": "Con qué frecuencia este equipo directivo cumplió lo prometido — cada cifra de guidance publicada, contrastada con el resultado.", "de": "Wie oft dieses Management tatsächlich lieferte, was es versprach — jede veröffentlichte Prognose, am Ergebnis gemessen.", }, "read": { "en": "Guidance is not a neutral forecast — it is a number management chooses to publish, knowing it will be scored on it. A team that has hit its own targets eight quarters running has earned the benefit of the doubt on the ninth. A team that has missed twice in a row has not, and its current guidance deserves a discount. This is the context that makes the next quarter's target readable; without it, every guidance number looks equally credible.", "fr": "La guidance n'est pas une prévision neutre — c'est un chiffre que la direction choisit de publier en sachant qu'elle sera jugée dessus. Une équipe qui a tenu ses propres objectifs huit trimestres d'affilée a gagné le bénéfice du doute pour le neuvième. Une équipe qui a manqué deux fois de suite ne l'a pas, et sa guidance actuelle mérite une décote. C'est ce contexte qui rend lisible l'objectif du trimestre suivant ; sans lui, tous les chiffres de guidance semblent également crédibles.", "es": "El guidance no es una previsión neutral — es una cifra que la dirección elige publicar sabiendo que será juzgada por ella. Un equipo que cumplió sus objetivos ocho trimestres seguidos se ha ganado el beneficio de la duda. Uno que falló dos veces seguidas no, y su guidance actual merece un descuento.", "de": "Eine Prognose ist keine neutrale Vorhersage — sie ist eine Zahl, die das Management bewusst veröffentlicht, wohl wissend, dass es daran gemessen wird. Ein Team, das acht Quartale in Folge seine Ziele erreicht hat, verdient Vertrauensvorschuss. Eines, das zweimal in Folge verfehlt hat, nicht.", }, "source": { "en": "Guidance statements extracted from filings and earnings calls, matched against the reported result for the period they targeted.", "fr": "Déclarations de guidance extraites des documents et des earnings calls, confrontées au résultat publié pour la période visée.", "es": "Declaraciones de guidance extraídas de informes y earnings calls, contrastadas con el resultado publicado del periodo objetivo.", "de": "Prognoseaussagen aus Berichten und Earnings Calls, abgeglichen mit dem berichteten Ergebnis der Zielperiode.", }, "caveat": { "en": "Only guidance we could match to a reported outcome is scored. Vague qualitative promises (\"we expect momentum to continue\") are not counted, so the record covers the measurable half of what management said.", "fr": "Seule la guidance que nous avons pu rapprocher d'un résultat publié est notée. Les promesses qualitatives vagues (« nous anticipons une dynamique qui se poursuit ») ne sont pas comptées : le bilan couvre donc la moitié mesurable de ce que la direction a dit.", "es": "Solo se puntúa el guidance que pudimos emparejar con un resultado publicado. Las promesas cualitativas vagas no se cuentan.", "de": "Bewertet wird nur die Prognose, die einem berichteten Ergebnis zugeordnet werden konnte. Vage qualitative Versprechen zählen nicht.", }, }, # ══ Financials — GAAP vs adjusted EPS ════════════════════════════════════ "eps_quality": { "what": { "en": "The gap between the profit the company reports under accounting rules and the \"adjusted\" profit it asks the market to look at instead.", "fr": "L'écart entre le bénéfice que l'entreprise publie selon les règles comptables et le bénéfice « ajusté » qu'elle demande au marché de regarder à la place.", "es": "La diferencia entre el beneficio que la empresa publica según las normas contables y el beneficio «ajustado» que pide al mercado mirar en su lugar.", "de": "Die Lücke zwischen dem Gewinn nach Rechnungslegungsvorschriften und dem „bereinigten\" Gewinn, den das Unternehmen dem Markt stattdessen präsentiert.", }, "read": { "en": "Almost every large company reports two profit figures. GAAP is the audited, rule-bound one. \"Adjusted\" strips out costs management considers unrepresentative — restructuring, acquisition charges, and, most often, stock paid to employees. Headlines and analyst estimates almost always use the adjusted number. A stable gap is normal. A gap that widens quarter after quarter means a growing share of reported profit depends on what gets excluded — and the items being excluded are worth naming. Watch especially for \"one-time\" charges that appear every single quarter.", "fr": "Presque toutes les grandes entreprises publient deux chiffres de bénéfice. Le GAAP est celui qui est audité et encadré par les règles. L'« ajusté » retire les coûts que la direction juge non représentatifs — restructuration, charges d'acquisition et, le plus souvent, actions versées aux salariés. Les titres de presse et les estimations d'analystes utilisent presque toujours le chiffre ajusté. Un écart stable est normal. Un écart qui s'élargit trimestre après trimestre signifie qu'une part croissante du bénéfice publié dépend de ce qu'on exclut — et les éléments exclus méritent alors d'être nommés. Surveillez particulièrement les charges « exceptionnelles » qui reviennent à chaque trimestre.", "es": "Casi toda gran empresa publica dos cifras de beneficio. GAAP es la auditada y sujeta a normas. El «ajustado» elimina costes que la dirección considera no representativos — reestructuración, cargos por adquisiciones y, sobre todo, acciones pagadas a empleados. Los titulares y las estimaciones usan casi siempre la cifra ajustada. Una brecha estable es normal; una que se ensancha trimestre a trimestre no.", "de": "Fast jedes Großunternehmen berichtet zwei Gewinnzahlen. GAAP ist die geprüfte, regelgebundene. „Bereinigt\" streicht Kosten, die das Management für nicht repräsentativ hält — Restrukturierung, Übernahmekosten und meist Aktienvergütung. Schlagzeilen und Analystenschätzungen nutzen fast immer die bereinigte Zahl. Eine stabile Lücke ist normal; eine, die sich Quartal für Quartal weitet, nicht.", }, "source": { "en": "GAAP diluted EPS from the company's own XBRL filing data; adjusted EPS from the consensus-basis figure providers use. The gap is the arithmetic difference.", "fr": "BPA dilué GAAP issu des données XBRL déposées par l'entreprise ; BPA ajusté issu du chiffre en base consensus utilisé par les fournisseurs de données. L'écart est la différence arithmétique.", "es": "BPA diluido GAAP de los datos XBRL de la propia empresa; BPA ajustado de la cifra en base consenso. La brecha es la diferencia aritmética.", "de": "Verwässertes GAAP-EPS aus den XBRL-Daten des Unternehmens; bereinigtes EPS aus der Konsensbasis-Zahl. Die Lücke ist die arithmetische Differenz.", }, "caveat": { "en": "This measures the size of the adjustment, not whether it is justified. Excluding a genuine one-off restructuring can give a truer picture of the business; excluding recurring stock compensation usually does not. The gap tells you where to look, not what to conclude.", "fr": "Ceci mesure la taille de l'ajustement, pas sa légitimité. Exclure une restructuration réellement exceptionnelle peut donner une image plus juste de l'activité ; exclure une rémunération en actions récurrente, en général non. L'écart vous dit où regarder, pas quoi conclure.", "es": "Esto mide el tamaño del ajuste, no si está justificado. Excluir una reestructuración realmente puntual puede dar una imagen más fiel; excluir remuneración en acciones recurrente, normalmente no.", "de": "Dies misst die Größe der Bereinigung, nicht ihre Berechtigung. Die Lücke zeigt, wo man hinsehen sollte — nicht, was zu schlussfolgern ist.", }, }, # ══ Financials — Working capital ═════════════════════════════════════════ "working_capital": { "what": { "en": "Early-warning ratios from the balance sheet: how long customers take to pay, how long inventory sits, and how much revenue is already booked but not yet delivered.", "fr": "Ratios d'alerte précoce issus du bilan : combien de temps les clients mettent à payer, combien de temps les stocks dorment, et quelle part du chiffre d'affaires est déjà encaissée mais pas encore livrée.", "es": "Ratios de alerta temprana del balance: cuánto tardan los clientes en pagar, cuánto tiempo permanece el inventario y cuántos ingresos están cobrados pero no entregados.", "de": "Frühwarn-Kennzahlen aus der Bilanz: wie lange Kunden zum Zahlen brauchen, wie lange Lagerbestände liegen und wie viel Umsatz bereits verbucht, aber noch nicht geliefert ist.", }, "read": { "en": "These move before the income statement does, which is what makes them worth watching. Days sales outstanding rising means customers are paying more slowly — sometimes a sign the company is granting easier terms to close deals. Days inventory rising means goods are moving off shelves more slowly than they arrive. Deferred revenue is money already collected for work not yet delivered: for a subscription business, a falling balance is a demand warning that shows up a quarter or two before revenue reflects it. One quarter of movement is noise; a trend across three or four is the signal.", "fr": "Ces ratios bougent avant le compte de résultat, et c'est précisément ce qui les rend utiles à surveiller. Un délai de paiement clients (DSO) qui s'allonge signifie que les clients paient plus lentement — parfois le signe que l'entreprise accorde des conditions plus souples pour conclure des ventes. Des jours de stock en hausse signifient que la marchandise s'écoule moins vite qu'elle n'arrive. Le revenu différé, c'est de l'argent déjà encaissé pour des prestations non encore livrées : pour un modèle par abonnement, une baisse est un avertissement sur la demande qui apparaît un à deux trimestres avant que le chiffre d'affaires ne le reflète. Un trimestre de variation est du bruit ; une tendance sur trois ou quatre est un signal.", "es": "Estos se mueven antes que la cuenta de resultados, y por eso vale la pena vigilarlos. Un DSO al alza significa que los clientes pagan más despacio. Días de inventario al alza significa que la mercancía sale más lento de lo que entra. Los ingresos diferidos son dinero ya cobrado por trabajo no entregado: en un negocio de suscripción, una caída es un aviso de demanda uno o dos trimestres antes de que lo refleje la facturación.", "de": "Diese bewegen sich vor der Gewinn- und Verlustrechnung — genau das macht sie wertvoll. Steigende Debitorenlaufzeit heißt, Kunden zahlen langsamer. Steigende Lagerdauer heißt, Ware fließt langsamer ab, als sie eintrifft. Abgegrenzte Umsätze sind bereits kassiertes Geld für noch nicht erbrachte Leistungen: Bei Abo-Modellen ist ein Rückgang eine Nachfragewarnung, ein bis zwei Quartale bevor der Umsatz sie zeigt.", }, "source": { "en": "Quarterly balance sheet and cash flow statement line items as reported, divided by the same quarter's revenue. Deterministic arithmetic — no estimation.", "fr": "Postes du bilan et du tableau de flux de trésorerie trimestriels tels que publiés, rapportés au chiffre d'affaires du même trimestre. Arithmétique déterministe — aucune estimation.", "es": "Partidas del balance y del estado de flujos trimestrales tal como se publican, divididas por los ingresos del mismo trimestre. Aritmética determinista.", "de": "Quartalsbilanz- und Cashflow-Positionen wie berichtet, geteilt durch den Umsatz desselben Quartals. Deterministische Arithmetik.", }, "caveat": { "en": "Highly seasonal businesses move these ratios every year on schedule. Compare a quarter to the same quarter a year earlier, not just to the one before it. Companies with no inventory or no deferred revenue will show blanks — that is normal, not missing data.", "fr": "Les activités très saisonnières font bouger ces ratios chaque année de façon régulière. Comparez un trimestre au même trimestre de l'année précédente, pas seulement au trimestre qui le précède. Les entreprises sans stocks ou sans revenu différé afficheront des cases vides — c'est normal, ce ne sont pas des données manquantes.", "es": "Los negocios muy estacionales mueven estos ratios cada año de forma regular. Compare un trimestre con el mismo del año anterior. Empresas sin inventario o sin ingresos diferidos mostrarán casillas vacías — es normal.", "de": "Stark saisonale Geschäfte bewegen diese Kennzahlen jedes Jahr planmäßig. Vergleichen Sie ein Quartal mit dem Vorjahresquartal. Unternehmen ohne Lager oder abgegrenzte Umsätze zeigen leere Felder — das ist normal.", }, }, # ══ Financials — Stock-based compensation ════════════════════════════════ "sbc": { "what": { "en": "How much of this company's revenue is paid out to employees in shares rather than cash — and whether buybacks are creating value or just offsetting that dilution.", "fr": "Quelle part du chiffre d'affaires est versée aux salariés en actions plutôt qu'en liquide — et si les rachats d'actions créent de la valeur ou ne font qu'éponger cette dilution.", "es": "Qué parte de los ingresos se paga a empleados en acciones en lugar de efectivo — y si las recompras crean valor o solo compensan esa dilución.", "de": "Welcher Anteil des Umsatzes an Mitarbeiter in Aktien statt bar gezahlt wird — und ob Rückkäufe Wert schaffen oder nur die Verwässerung ausgleichen.", }, "read": { "en": "Stock compensation is a real cost: it hands existing shareholders a smaller slice of the company each year. Many firms announce large buybacks that, once you net out the new shares issued to employees, leave the share count flat or higher. The share count is the honest scoreboard — if it is not falling, the buyback did not shrink your ownership dilution, it merely cancelled it out.", "fr": "La rémunération en actions est un coût réel : elle laisse chaque année aux actionnaires existants une part plus petite de l'entreprise. Beaucoup de sociétés annoncent de gros rachats d'actions qui, une fois retranchées les actions nouvellement émises aux salariés, laissent le nombre de titres stable voire en hausse. Le nombre d'actions est le tableau de bord honnête — s'il ne baisse pas, le rachat n'a pas réduit votre dilution, il l'a seulement annulée.", "es": "La remuneración en acciones es un coste real: cada año deja a los accionistas existentes una porción menor de la empresa. Muchas firmas anuncian grandes recompras que, netas de las nuevas acciones emitidas a empleados, dejan el número de títulos plano o al alza. El recuento de acciones es el marcador honesto.", "de": "Aktienvergütung ist ein realer Kosten­faktor: Sie gibt bestehenden Aktionären jedes Jahr einen kleineren Anteil. Viele Firmen kündigen große Rückkäufe an, die — nach Abzug neu ausgegebener Mitarbeiteraktien — die Aktienzahl unverändert lassen. Die Aktienzahl ist der ehrliche Maßstab.", }, "source": { "en": "Stock compensation from the cash flow statement; diluted share count from the income statement. Both as reported each quarter.", "fr": "Rémunération en actions issue du tableau de flux de trésorerie ; nombre d'actions diluées issu du compte de résultat. Les deux tels que publiés chaque trimestre.", "es": "Remuneración en acciones del estado de flujos; número de acciones diluidas de la cuenta de resultados. Ambos tal como se publican.", "de": "Aktienvergütung aus der Kapitalflussrechnung; verwässerte Aktienzahl aus der GuV. Beide wie berichtet.", }, }, # ══ Financials — Surprise history / real expectations ════════════════════ "expectations": { "what": { "en": "Whether beating the published analyst estimate has actually moved this stock — the difference between the official bar and the one the market really sets.", "fr": "Si battre l'estimation publiée des analystes a réellement fait bouger ce titre — l'écart entre la barre officielle et celle que le marché fixe vraiment.", "es": "Si superar la estimación publicada ha movido realmente esta acción — la diferencia entre el listón oficial y el que fija el mercado.", "de": "Ob das Übertreffen der veröffentlichten Analystenschätzung diese Aktie tatsächlich bewegt hat — die Differenz zwischen der offiziellen Latte und der, die der Markt wirklich setzt.", }, "read": { "en": "The published consensus is not the bar the stock trades against. Professional investors carry their own, usually higher, expectation — the \"whisper number\". When a company beats consensus and the stock falls anyway, and does so repeatedly, that is direct evidence the real bar sits above the published one. Knowing that changes how you read the next beat: a small beat against a high private bar is a miss in disguise.", "fr": "Le consensus publié n'est pas la barre contre laquelle le titre se négocie. Les investisseurs professionnels portent leur propre attente, généralement plus haute — le « whisper number ». Quand une société bat le consensus et que le titre baisse quand même, et que cela se répète, c'est la preuve directe que la vraie barre est au-dessus de la barre publiée. Le savoir change la lecture du prochain « beat » : un petit dépassement face à une barre privée élevée est un échec déguisé.", "es": "El consenso publicado no es el listón contra el que cotiza la acción. Los inversores profesionales tienen su propia expectativa, normalmente más alta — el «whisper number». Cuando una empresa bate el consenso y la acción cae igualmente, y se repite, es prueba directa de que el listón real está por encima del publicado.", "de": "Der veröffentlichte Konsens ist nicht die Latte, gegen die die Aktie gehandelt wird. Profis haben ihre eigene, meist höhere Erwartung — die „Whisper Number\". Wenn ein Unternehmen den Konsens schlägt und die Aktie trotzdem fällt, und das wiederholt, ist das der direkte Beleg, dass die echte Latte höher liegt.", }, "source": { "en": "Reported vs estimated EPS per quarter, paired with the stock's own price move in the days after each release.", "fr": "BPA publié vs estimé par trimestre, apparié au mouvement de cours du titre dans les jours suivant chaque publication.", "es": "BPA publicado vs estimado por trimestre, emparejado con el movimiento del precio tras cada publicación.", "de": "Berichtetes vs. geschätztes EPS je Quartal, gepaart mit der Kursbewegung nach jeder Veröffentlichung.", }, "caveat": { "en": "A stock's move after earnings also reflects the market that day and whatever guidance came with the release. This is a pattern worth knowing, not a mechanism that predicts the next reaction.", "fr": "Le mouvement d'un titre après les résultats reflète aussi le marché du jour et la guidance publiée en même temps. C'est un schéma utile à connaître, pas un mécanisme qui prédit la prochaine réaction.", "es": "El movimiento tras resultados también refleja el mercado de ese día y el guidance que acompañó la publicación. Es un patrón útil, no un mecanismo predictivo.", "de": "Die Kursbewegung nach Zahlen spiegelt auch den Markt des Tages und die mitgelieferte Prognose. Ein wissenswertes Muster, kein Vorhersagemechanismus.", }, }, # ══ Signals feed ═════════════════════════════════════════════════════════ "signal_feed": { "what": { "en": "Every individual observation behind this quarter's read, listed one per card so you can check each one against its source.", "fr": "Chaque observation individuelle qui sous-tend la lecture du trimestre, listée une par carte pour que vous puissiez vérifier chacune contre sa source.", "es": "Cada observación individual tras la lectura del trimestre, una por tarjeta para que compruebe cada una con su fuente.", "de": "Jede einzelne Beobachtung hinter der Quartalseinschätzung, eine pro Karte, damit Sie jede an ihrer Quelle prüfen können.", }, "read": { "en": "Cards are ranked by how material the underlying change is, not chronologically. The colored left edge is the direction: green supports a positive read, red a cautious one, amber means it cuts both ways. Each card names its source document and how confident we are in the extraction — open the quote to see the original sentence.", "fr": "Les cartes sont classées par matérialité du changement sous-jacent, pas chronologiquement. Le liseré coloré à gauche indique le sens : vert soutient une lecture positive, rouge une lecture prudente, ambre signifie que cela joue dans les deux sens. Chaque carte nomme son document source et notre niveau de confiance dans l'extraction — ouvrez la citation pour voir la phrase d'origine.", "es": "Las tarjetas se ordenan por materialidad del cambio, no cronológicamente. El borde izquierdo indica la dirección: verde apoya una lectura positiva, rojo una cautelosa, ámbar corta en ambos sentidos. Cada tarjeta indica su documento fuente y nuestra confianza en la extracción.", "de": "Karten sind nach Wesentlichkeit der Änderung sortiert, nicht chronologisch. Der farbige linke Rand zeigt die Richtung: Grün stützt eine positive Lesart, Rot eine vorsichtige, Bernstein wirkt in beide Richtungen. Jede Karte nennt ihr Quelldokument und unsere Extraktionssicherheit.", }, }, } # ── Renderers ──────────────────────────────────────────────────────────────── def has_explanation(key: str) -> bool: return key in EXPLANATIONS def _disclosure(key: str) -> str: """The collapsible 'How to read this' body. Empty string if nothing to say.""" entry = EXPLANATIONS.get(key) if not entry: return "" rows: list[str] = [] for field, label_key, color in ( ("read", "read_label", TEXT), ("source", "source_label", TEXT_MUTED), ("caveat", "caveat_label", TEXT_MUTED), ): text = _pick(entry.get(field)) if not text: continue rows.append( f'
' f'
{_ui(label_key)}
' f'
{text}
' f'
' ) if not rows: return "" return ( f'
' f'' f'?' f'{_ui("how_to_read")}' f'' f'
' f'{"".join(rows)}' f'
' f'
' ) def explain_note(key: str, inline: bool = False) -> str: """Plain-language 'what is this' line plus the how-to-read disclosure. Args: key: registry key. inline: when True, omit the leading margin so the note can sit flush beneath an existing header rendered by the caller. """ entry = EXPLANATIONS.get(key) if not entry: return "" what = _pick(entry.get("what")) top_margin = "0" if inline else "10px" what_html = ( f'
{what}
' if what else "" ) return what_html + _disclosure(key) def explained_header( key: str, label: str, accent: str = TEXT_MUTED, icon: str = "", compact: bool = False, ) -> str: """A section header followed by its plain-language explanation. Drop-in replacement for ``components.section_header`` anywhere the block below it needs explaining. """ from dashboard.components import section_header return section_header(label, accent=accent, icon=icon, compact=compact) + explain_note(key) def metric_hint(text: str) -> str: """A one-line inline hint under a single metric or chart. For places where a full explanation block would be too heavy but the number still needs a plain-language anchor. """ if not text: return "" return ( f'
{text}
' ) def empty_state(title: str, body: str, hint: str = "") -> str: """A styled 'nothing to show, and here is why' panel. Blank panels are a major source of confusion — the reader cannot tell a broken feature from a genuinely empty result. Every block that can come back empty renders one of these instead of nothing. """ hint_html = ( f'
{hint}
' if hint else "" ) return ( f'
' f'
{title}
' f'
{body}
' f'{hint_html}' f'
' )