diff --git a/DECISIONS.md b/DECISIONS.md
new file mode 100644
index 0000000..07a4fdd
--- /dev/null
+++ b/DECISIONS.md
@@ -0,0 +1,33 @@
+# Product & architecture decisions
+
+A running log of decisions that aren't obvious from the code and would
+otherwise be re-litigated. Newest first.
+
+## 2026-06-08 — PDF to CSV and Reconcile stay in the bundle, under a "Finance" group
+
+**Decision:** `10_pdf_extractor` (PDF to CSV) and `11_reconciler` (Reconcile
+Two Files) remain part of the DataTools suite. In the sidebar they are
+segregated into their own **Finance** section, distinct from the
+file-cleaning tools.
+
+**Context / why this needed deciding:**
+- Both tools sit outside the documented 9-script cleaning architecture
+ (TECHNICAL.md / USER-GUIDE.md stop at the orchestrator).
+- They occupy the "reconciliation / manual data-entry" territory the
+ product's honest-positioning note explicitly placed outside a
+ file-cleaning tool's scope.
+- A journey-level UX review flagged that every extra tool in the main
+ sidebar raises the "which tool do I need?" load for a non-technical
+ buyer, so tools serving a different job should live in a clearly
+ different place.
+
+**Resolution:** Keep them in-bundle (they're built, useful, and ship
+today) but group them under "Finance" so the cleaning flow stays
+uncluttered. Revisit only if a separate finance-focused product emerges.
+
+**Implications:**
+- `tools_registry.py`: Reconcile + PDF to CSV carry a `finance` section.
+- Sidebar order: Start here → Data Cleaners → Transformations →
+ Automations → Finance → Coming soon.
+- This is the source-of-truth realization of the `layout-review/`
+ mockups (see `layout-review/shell.js`).
diff --git a/src/gui/app.py b/src/gui/app.py
index 9632a31..28c33e3 100644
--- a/src/gui/app.py
+++ b/src/gui/app.py
@@ -82,6 +82,8 @@ def _build_navigation() -> dict[str, list]:
"cleaners": [],
"transformations": [],
"automations": [],
+ "finance": [],
+ "coming_soon": [],
}
# Resolve the tool name through ``tool_name`` (i18n lookup) instead
# of using the registry's English ``tool.name`` field, otherwise the
@@ -96,16 +98,16 @@ def _build_navigation() -> dict[str, list]:
)
)
- # Home is now surfaced under the new "Analysis" section as
- # "File Analysis" — the home page's content (importing files,
- # running the analyzer, browsing findings) is itself a data-analysis
- # workflow, so grouping it next to Reconcile keeps the sidebar's
- # mental model coherent. ``default=True`` still points at this
- # page so first-visit lands here regardless of section placement.
+ # Home is the product's front door: "Start here". It's surfaced as a
+ # standalone, unlabeled top entry (in the "" section, ahead of the
+ # hidden Activate/Logs/Close pages) so it reads as the obvious
+ # starting point above the tool groups rather than one item among
+ # equals. The companion CSS in ``hide_streamlit_chrome`` gives its
+ # nav link accent emphasis. ``default=True`` lands first-visit here.
home = st.Page(
_home_page,
- title=_t("nav.file_analysis_title") or "File Analysis",
- icon=":material/insert_chart_outlined:",
+ title=_t("nav.start_here_title") or "Start here",
+ icon=":material/play_circle:",
default=True,
url_path="home",
)
@@ -136,17 +138,20 @@ def _build_navigation() -> dict[str, list]:
url_path="close",
)
- # Activate / Logs / Close stay in the unlabeled section (key ``""``)
- # so the CSS in ``hide_streamlit_chrome`` keeps hiding them by
- # ``href``. Home moved out of that bucket into "Analysis" — the
- # unlabeled section now contains ONLY hidden pages, so no orphan
- # entry appears above the "Analysis" header in the sidebar.
+ # Home leads the unlabeled section (key ``""``) so "Start here" sits
+ # at the very top with no section header above it. Activate / Logs /
+ # Close follow in the same unlabeled bucket and stay hidden by their
+ # ``href`` via the CSS in ``hide_streamlit_chrome``. Section order
+ # below is the journey order: cleaners (pipeline order) →
+ # transformations → automations → finance → coming soon (last, so
+ # not-yet-shipped tools never interleave with working ones).
return {
- "": [activate, logs, close],
- section_label("analysis"): [home, *by_section["analysis"]],
+ "": [home, activate, logs, close],
section_label("cleaners"): by_section["cleaners"],
section_label("transformations"): by_section["transformations"],
section_label("automations"): by_section["automations"],
+ section_label("finance"): by_section["finance"],
+ section_label("coming_soon"): by_section["coming_soon"],
}
diff --git a/src/gui/components/_legacy.py b/src/gui/components/_legacy.py
index bb09d3d..09e714c 100644
--- a/src/gui/components/_legacy.py
+++ b/src/gui/components/_legacy.py
@@ -95,6 +95,18 @@ footer {
[data-testid="stSidebarNav"] a[href$="/close/"] {
display: none !important;
}
+/* "Start here" front-door nav item — accent emphasis so the obvious
+ entry point reads at a glance above the tool groups. Targets the Home
+ link by href; accent values mirror theme.py (§3 color scale). */
+[data-testid="stSidebarNav"] a[href$="/home"],
+[data-testid="stSidebarNav"] a[href$="/home/"] {
+ background: #fef4ed !important;
+ font-weight: 600 !important;
+}
+[data-testid="stSidebarNav"] a[href$="/home"]:hover,
+[data-testid="stSidebarNav"] a[href$="/home/"]:hover {
+ background: #fde4d3 !important;
+}
/* Reclaim top padding lost from hidden header. Streamlit's default
block-container padding-top is ~6rem (room for the header it ships).
We hide the header so reclaim that space — the page title should sit
@@ -2168,14 +2180,37 @@ def render_tool_header(tool_id: str) -> None:
button as a defense-in-depth so the label can never wrap, no
matter how the column ends up sized.
"""
- col_title, col_help = st.columns([8, 2])
+ col_title, col_help = st.columns([7, 3])
with col_title:
st.title(_t(f"tools.{tool_id}.page_title"))
with col_help:
- # Spacer pushes the popover button down so it sits closer to
- # the title's baseline than to its top — without the spacer the
- # button floats above the big title text.
- st.write("")
+ # Local-first reassurance + Help, right-aligned opposite the
+ # title. The "Runs 100% locally" privacy pill is shown on every
+ # working tool page (where the user is actively feeding in a
+ # customer list) and omitted on not-yet-shipped "Coming Soon"
+ # tools, which process nothing. When the pill is shown it also
+ # serves as the spacer that nudges the popover down toward the
+ # title baseline; without it we keep the explicit spacer.
+ from src.gui.tools_registry import tool_by_id as _tool_by_id
+ _tool = _tool_by_id(tool_id)
+ if _tool is None or _tool.status == "Ready":
+ import html as _html
+ st.markdown(
+ '