is there a research paper that has compiled good and bad prognostic factor of major adult psychiatric disorders?

Yes, there are a few review papers and meta-analyses that have compiled prognostic factors across multiple psychiatric disorders. The most relevant tend to be systematic reviews covering schizophrenia, bipolar disorder, major depression, and anxiety disorders - sometimes together in a single comparative paper.
Searching sources...
Searching PubMed

prognostic factors psychiatric disorders systematic review

Searching PubMed

outcome predictors schizophrenia bipolar depression prognosis review

Searching the Web

systematic review prognostic factors major adult psychiatric disorders compiled good bad prognosis

Let me now pull up the most relevant specific papers to get their details.
Searching PubMed

prognostic factors schizophrenia bipolar depression anxiety disorders review

Reading Article

PMID: 36417817

Reading Article

PMID: 20444581

The PMID 37957292 looks like exactly what you're asking for - an umbrella review of predictors across mental disorders. Let me get its full details.
Reading Article

PMID: 37957292

Reading Article

PMID: 29843662

I found the key paper. Here is a thorough answer:

Yes - There Is a Well-Matched Paper

The best single paper for what you're describing is:
"An umbrella review of candidate predictors of response, remission, recovery, and relapse across mental disorders" Solmi M, Cortese S, Vita G, De Prisco M, Radua J, Dragioti E, et al. Molecular Psychiatry, 2023 PMID: 37957292 | DOI: 10.1038/s41380-023-02298-3 | Free via PMC: PMC10730397
This is an umbrella review (a systematic review of systematic reviews) - the highest-level evidence synthesis possible. It is almost exactly what you're looking for.

What the Paper Covers

  • 117 systematic reviews, 403 studies, nearly 300,000 individuals with DSM/ICD-defined mental disorders
  • Disorders covered: depressive disorders (39%), substance-use disorders (18%), schizophrenia-spectrum (12%), and others
  • Outcomes mapped: response, remission, recovery, and relapse
  • It extracted multivariable candidate predictors (MCPs) - not just isolated associations

Key Findings: Good vs. Bad Prognostic Factors

Bad prognostic factors (transdiagnostic - across multiple disorders):
  • Male sex
  • Younger age at onset
  • Higher disease-specific symptom burden
  • Higher depressive symptoms (even as a comorbidity in non-mood disorders)
  • Higher anxiety symptoms
  • Greater global illness severity
  • More previous episodes
  • Negative life events
  • Comorbid mental and/or physical disorders
Good prognostic factors (transdiagnostic):
  • Female sex
  • Higher education
  • Social contacts and employment
  • Absence of negative life events
  • Early access to and initiation of treatment
  • Lower disease-specific symptoms at baseline
  • Better quality of life / functioning at baseline
Disorder-specific highlights:
DisorderGood prognostic MCPsBad prognostic MCPs
DepressionFemale sex, less depressive symptoms, better functioning, fewer negative life eventsRecurrent episodes, residual symptoms, childhood maltreatment
SchizophreniaFewer positive symptomsHigher depressive symptoms, more positive symptoms at baseline
Substance use disorderMarital status, higher education-

Other Relevant Papers

If you want disorder-specific deep dives, these supplement the umbrella review:
  1. Schizophrenia long-term prognosis: Molstrom et al. 2022, Schizophr Res (PMID 36417817) - 20-year follow-up meta-analysis; found ~24% full recovery, challenging the myth of inevitable deterioration.
  2. Bipolar disorder course and outcome predictors: Treuer & Tohen 2010, Eur Psychiatry (PMID 20444581) - covers age of onset, comorbidities, personality, and functioning as predictors.
  3. Social support as a transdiagnostic prognostic factor: Wang et al. 2018, BMC Psychiatry (PMID 29843662) - systematic review showing poor perceived social support predicts worse outcomes in depression, schizophrenia, bipolar, and anxiety.
  4. Major depressive disorder prognosis review: Available at nature.com - covers biomarkers, duration of untreated illness, early improvement, and comorbidities as outcome predictors.

Bottom Line

The Solmi et al. 2023 umbrella review (PMID 37957292) is the most authoritative compiled resource covering good and bad prognostic factors across adult psychiatric disorders in a single paper. It is open-access via PubMed Central (PMC10730397), so you can read the full text for free. The main caveat noted by the authors themselves is that most underlying studies had high risk of bias, so predictions should be interpreted cautiously.

any similar studies for OCD and other anxiety related disorders?

There are indeed specific prognostic reviews for OCD and anxiety-related disorders. Key factors identified include symptom severity at baseline, comorbidities, insight level, and early treatment response. Let me search for the best compiled evidence.
Searching sources...
Searching PubMed

prognostic factors OCD obsessive compulsive disorder outcome predictors review

Searching PubMed

prognosis predictors anxiety disorders PTSD panic GAD outcome review

Searching the Web

systematic review prognostic factors OCD anxiety disorders PTSD good bad outcome predictors compiled

Good hits. Let me now pull the key papers' full abstracts in parallel.
Reading Article

PMID: 30745676

Reading Article

PMID: 25295427

Searching PubMed

prognostic factors PTSD panic disorder social anxiety GAD long-term outcome predictors

Searching PubMed

OCD course outcome predictors treatment response adult review

Reading Article

PMID: 27282021

Searching PubMed

prognostic factors PTSD outcome predictors review

Searching PubMed

long-term prognosis anxiety disorders panic social phobia GAD predictors outcome

Reading Article

PMID: 38884956

Searching PubMed

anxiety disorders course prognosis outcome natural history review

I now have enough to give a comprehensive answer. Here is a full synthesis:

Prognostic Factor Reviews for OCD and Anxiety-Related Disorders

There is no single umbrella review for anxiety disorders equivalent to the Solmi et al. 2023 paper for all psychiatric disorders - but several strong disorder-specific reviews exist. Here is the best evidence by condition:

1. OCD - Best Papers

A. Long-term outcome and predictors (Meta-Analysis)

"Long-term outcome of obsessive-compulsive disorder in adults: a meta-analysis" Sharma E, Thennarasu K, Reddy YCJ. J Clin Psychiatry, 2014 PMID: 25295427
This is the most rigorous quantitative synthesis. 17 studies, 1,265 adults, mean follow-up ~5 years.
Key finding: Pooled remission rate was 53% - much better than commonly assumed.
Bad prognostic factors (meta-regression):
FactorEffect
Earlier age of onsetSignificant negative association with remission
Longer illness durationWorse remission
Higher baseline Y-BOCS score (greater severity)Worse remission
Male genderWorse remission

B. Course, outcome and predictors (Narrative Review)

"Course and outcome of obsessive-compulsive disorder" Sharma E, Math SB. Indian J Psychiatry, 2019 PMID: 30745676 | Free PMC full text: PMC6343417
A comprehensive narrative review covering both clinical and neurobiological predictors.
Good prognostic factors in OCD:
  • Short illness duration at presentation
  • Low symptom severity
  • Early and intensive treatment
  • Long-term maintenance treatment
  • Good insight into obsessions
  • Absence of comorbid personality disorders
Bad prognostic factors in OCD:
  • Hoarding and symmetry symptom subtypes
  • Poor insight / overvalued ideation
  • Comorbid depression (severe)
  • Comorbid personality disorders (especially schizotypal, borderline)
  • Unemployment / being unmarried
  • Family dysfunction / high expressed emotion
  • Longer duration of untreated illness

C. Predictors of response to SRIs (Review)

"Predictors of response to serotonin reuptake inhibitors in OCD" Hazari N, Narayanaswamy JC, Arumugham SS. Expert Rev Neurother, 2016 PMID: 27282021
Covers clinical, neuropsychological, neuroimaging, and genetic predictors specifically for SRI pharmacotherapy.
Poor SRI response predicted by:
  • Poor insight into obsessions
  • Symmetry/hoarding and contamination/washing symptom dimensions
  • Certain comorbid personality disorders
  • Altered orbitofrontal cortex, anterior cingulate, caudate and thalamus volumes/activity on imaging
  • Variants in serotonergic and glutamatergic signalling genes

2. PTSD - Best Paper

"A systematic review and meta-analysis of predictors of response to trauma-focused psychotherapy for PTSD" Keyan D, Garland N, et al. Psychological Bulletin, 2024 PMID: 38884956
114 studies, nearly 62,000 patients - the largest compiled prognostic dataset for PTSD.
Poor prognostic factors for PTSD treatment response:
  • Male sex
  • Non-Caucasian ethnicity
  • Older age
  • Early trauma onset (childhood/complex PTSD)
  • Greater cumulative trauma exposure
  • Combat trauma history
  • Comorbid sleep disturbance, chronic pain, alcohol use disorder
  • Lower social support
  • Lower pre-treatment fear psychophysiology (less fear activation = less extinction learning)
  • Comorbid depression and anger dysregulation
Better prognosis: Higher baseline fear reactivity, stronger executive control, good social support, single adult-onset trauma without comorbidities.

3. Anxiety Disorders (Panic, Social Anxiety, GAD) - Key Points

There is no single compiled review equivalent to the OCD or PTSD papers above for all anxiety disorders together, but established knowledge from large cohort studies and narrative reviews shows:
Common good prognostic factors across anxiety disorders:
  • Acute/later onset (vs. childhood onset)
  • Milder baseline symptom severity
  • Absence of comorbid mood disorder or substance use
  • Good social support
  • Early treatment initiation
  • Higher education and employment
Common bad prognostic factors:
  • Chronic course with early onset
  • Avoidance behaviors entrenched over years
  • Comorbid depression (strongest predictor of chronicity across all anxiety disorders)
  • Comorbid personality disorder
  • Neuroticism as a trait
  • Significant functional impairment at baseline
The Solmi et al. 2023 umbrella review (from the previous answer) does include anxiety disorders as part of its transdiagnostic analysis, so for a single-source compiled comparison, that remains the best cross-disorder reference.

Summary Table

DisorderBest Review PaperPMID
OCD - long-term prognosisSharma et al. 2014 meta-analysis25295427
OCD - course/outcome narrativeSharma & Math 201930745676
OCD - SRI response predictorsHazari et al. 201627282021
PTSD - psychotherapy predictorsKeyan et al. 202438884956
All psychiatric disorders (transdiagnostic)Solmi et al. 202337957292
A key gap in the literature: there is no single umbrella review specifically compiling good/bad prognostic factors for panic disorder, social anxiety disorder, and GAD together - this remains an area where evidence is scattered across individual naturalistic cohort studies rather than synthesised reviews.

can you compile these good and bad prognostic factors into a checklist that i can use in clinical setting

Asking for Preferences

What format would you like the checklist in? · Which disorders should the checklist cover? · How should the checklist be structured?

Reading File
Running Command

mkdir -p /tmp/workspace/psych-checklist && ls /tmp/workspace/psych-checklist/

Writing File

~/psych-checklist/build_checklist.py

from reportlab.lib.pagesizes import A4
from reportlab.lib import colors
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
from reportlab.lib.units import cm, mm
from reportlab.platypus import (
    SimpleDocTemplate, Paragraph, Spacer, Table, TableStyle,
    HRFlowable, KeepTogether, PageBreak
)
from reportlab.lib.enums import TA_LEFT, TA_CENTER, TA_JUSTIFY
from reportlab.platypus import Flowable
from reportlab.pdfgen import canvas as pdfcanvas

OUTPUT = "/tmp/workspace/psych-checklist/Psychiatric_Prognostic_Checklist.pdf"

# ── Colours ──────────────────────────────────────────────────────────────────
DARK_NAVY   = colors.HexColor("#1a2b4a")
TEAL        = colors.HexColor("#007b7f")
GOOD_GREEN  = colors.HexColor("#d4edda")
BAD_RED     = colors.HexColor("#f8d7da")
GOOD_DARK   = colors.HexColor("#155724")
BAD_DARK    = colors.HexColor("#721c24")
HEADER_BG   = colors.HexColor("#2c3e6b")
SUBHDR_BG   = colors.HexColor("#e8f4f8")
WHITE       = colors.white
LIGHT_GREY  = colors.HexColor("#f5f5f5")
MID_GREY    = colors.HexColor("#cccccc")
TICK_GREEN  = colors.HexColor("#28a745")
CROSS_RED   = colors.HexColor("#dc3545")

# ── Styles ────────────────────────────────────────────────────────────────────
styles = getSampleStyleSheet()

title_style = ParagraphStyle(
    "TitleStyle", parent=styles["Title"],
    fontSize=20, textColor=WHITE, alignment=TA_CENTER,
    spaceAfter=4, fontName="Helvetica-Bold"
)
subtitle_style = ParagraphStyle(
    "SubtitleStyle", parent=styles["Normal"],
    fontSize=10, textColor=colors.HexColor("#c8d8f0"),
    alignment=TA_CENTER, spaceAfter=2, fontName="Helvetica-Oblique"
)
section_style = ParagraphStyle(
    "SectionStyle", parent=styles["Normal"],
    fontSize=13, textColor=WHITE, fontName="Helvetica-Bold",
    alignment=TA_LEFT, spaceAfter=0, leftIndent=6
)
col_header_good = ParagraphStyle(
    "ColGood", parent=styles["Normal"],
    fontSize=10, textColor=GOOD_DARK, fontName="Helvetica-Bold",
    alignment=TA_CENTER
)
col_header_bad = ParagraphStyle(
    "ColBad", parent=styles["Normal"],
    fontSize=10, textColor=BAD_DARK, fontName="Helvetica-Bold",
    alignment=TA_CENTER
)
item_style = ParagraphStyle(
    "ItemStyle", parent=styles["Normal"],
    fontSize=8.5, textColor=colors.HexColor("#222222"),
    fontName="Helvetica", leading=12, leftIndent=4
)
source_style = ParagraphStyle(
    "SourceStyle", parent=styles["Normal"],
    fontSize=7, textColor=colors.HexColor("#555555"),
    fontName="Helvetica-Oblique", alignment=TA_LEFT, leftIndent=4
)
footer_style = ParagraphStyle(
    "FooterStyle", parent=styles["Normal"],
    fontSize=7.5, textColor=colors.HexColor("#555555"),
    fontName="Helvetica-Oblique", alignment=TA_CENTER
)
transdiag_label = ParagraphStyle(
    "TransdiagLabel", parent=styles["Normal"],
    fontSize=8, textColor=colors.HexColor("#6c3483"),
    fontName="Helvetica-Bold", alignment=TA_LEFT
)
note_style = ParagraphStyle(
    "NoteStyle", parent=styles["Normal"],
    fontSize=8, textColor=colors.HexColor("#333333"),
    fontName="Helvetica", leading=11, leftIndent=6
)

def checkbox(text, style):
    return Paragraph(f"☐  {text}", style)

def make_factor_row(good_items, bad_items, row_idx):
    """Returns a list of two-column table rows, one item per row."""
    max_len = max(len(good_items), len(bad_items))
    rows = []
    for i in range(max_len):
        g = checkbox(good_items[i], item_style) if i < len(good_items) else Paragraph("", item_style)
        b = checkbox(bad_items[i], item_style) if i < len(bad_items) else Paragraph("", item_style)
        rows.append([g, b])
    return rows

def build_disorder_section(disorder_name, good_factors, bad_factors, source_text, emoji=""):
    elements = []

    # Section header bar
    header_data = [[Paragraph(f"  {emoji}  {disorder_name}", section_style)]]
    header_table = Table(header_data, colWidths=[18.5*cm])
    header_table.setStyle(TableStyle([
        ("BACKGROUND", (0,0), (-1,-1), HEADER_BG),
        ("ROWBACKGROUNDS", (0,0), (-1,-1), [HEADER_BG]),
        ("TOPPADDING", (0,0), (-1,-1), 7),
        ("BOTTOMPADDING", (0,0), (-1,-1), 7),
        ("LEFTPADDING", (0,0), (-1,-1), 6),
        ("BOX", (0,0), (-1,-1), 0.5, colors.HexColor("#1a2b4a")),
    ]))
    elements.append(header_table)

    # Column sub-headers
    col_headers = [
        [Paragraph("✅  GOOD PROGNOSTIC FACTORS", col_header_good),
         Paragraph("⚠️  POOR PROGNOSTIC FACTORS", col_header_bad)]
    ]
    col_hdr_table = Table(col_headers, colWidths=[9.25*cm, 9.25*cm])
    col_hdr_table.setStyle(TableStyle([
        ("BACKGROUND", (0,0), (0,0), GOOD_GREEN),
        ("BACKGROUND", (1,0), (1,0), BAD_RED),
        ("TOPPADDING", (0,0), (-1,-1), 5),
        ("BOTTOMPADDING", (0,0), (-1,-1), 5),
        ("LEFTPADDING", (0,0), (-1,-1), 4),
        ("RIGHTPADDING", (0,0), (-1,-1), 4),
        ("LINEBELOW", (0,0), (-1,-1), 0.5, MID_GREY),
    ]))
    elements.append(col_hdr_table)

    # Factor rows
    max_len = max(len(good_factors), len(bad_factors))
    data = []
    for i in range(max_len):
        g = checkbox(good_factors[i], item_style) if i < len(good_factors) else Paragraph("", item_style)
        b = checkbox(bad_factors[i], item_style) if i < len(bad_factors) else Paragraph("", item_style)
        data.append([g, b])

    factors_table = Table(data, colWidths=[9.25*cm, 9.25*cm])
    row_colors = []
    for i in range(max_len):
        bg = colors.HexColor("#f0faf0") if i % 2 == 0 else colors.HexColor("#fdf5f5")
        row_colors.append(("ROWBACKGROUNDS", (0,i), (-1,i), [bg]))

    ts = TableStyle([
        ("LEFTPADDING", (0,0), (-1,-1), 5),
        ("RIGHTPADDING", (0,0), (-1,-1), 5),
        ("TOPPADDING", (0,0), (-1,-1), 3),
        ("BOTTOMPADDING", (0,0), (-1,-1), 3),
        ("VALIGN", (0,0), (-1,-1), "TOP"),
        ("LINEAFTER", (0,0), (0,-1), 0.5, MID_GREY),
        ("BOX", (0,0), (-1,-1), 0.5, MID_GREY),
    ])
    # Alternating row bg
    for i in range(max_len):
        bg = colors.HexColor("#f2fbf2") if i % 2 == 0 else colors.HexColor("#fef5f5")
        ts.add("BACKGROUND", (0,i), (0,i), bg)
        ts.add("BACKGROUND", (1,i), (1,i), colors.HexColor("#fef5f5") if i % 2 == 0 else colors.HexColor("#fff8f8"))

    factors_table.setStyle(ts)
    elements.append(factors_table)

    # Source citation
    src_data = [[Paragraph(f"Source: {source_text}", source_style)]]
    src_table = Table(src_data, colWidths=[18.5*cm])
    src_table.setStyle(TableStyle([
        ("BACKGROUND", (0,0), (-1,-1), colors.HexColor("#f0f0f0")),
        ("TOPPADDING", (0,0), (-1,-1), 3),
        ("BOTTOMPADDING", (0,0), (-1,-1), 3),
        ("LEFTPADDING", (0,0), (-1,-1), 6),
        ("BOX", (0,0), (-1,-1), 0.3, MID_GREY),
    ]))
    elements.append(src_table)
    elements.append(Spacer(1, 0.35*cm))

    return elements


# ── DATA ─────────────────────────────────────────────────────────────────────

disorders = [
    {
        "name": "SCHIZOPHRENIA",
        "emoji": "🧠",
        "good": [
            "Female sex",
            "Later / acute onset of illness",
            "Predominantly positive symptoms",
            "Good premorbid functioning",
            "Short duration of untreated psychosis (DUP)",
            "Good insight into illness",
            "Strong social support network",
            "Employment / higher education",
            "Early and intensive treatment initiation",
            "Absence of negative symptoms at baseline",
            "No comorbid substance use disorder",
            "Married / stable intimate relationship",
        ],
        "bad": [
            "Male sex",
            "Insidious / early onset",
            "Predominantly negative symptoms",
            "Poor premorbid functioning",
            "Long duration of untreated psychosis",
            "Poor insight / anosognosia",
            "Social isolation / lack of support",
            "Unemployment / low education",
            "Delayed treatment initiation",
            "Prominent cognitive deficits",
            "Comorbid substance use disorder",
            "Family history of schizophrenia",
        ],
        "source": "Solmi et al. 2023 (Mol Psychiatry, PMID 37957292); Molstrom et al. 2022 (Schizophr Res, PMID 36417817)"
    },
    {
        "name": "BIPOLAR DISORDER",
        "emoji": "🔄",
        "good": [
            "Later age of onset",
            "Fewer prior mood episodes",
            "Good psychosocial functioning at baseline",
            "Long-term symptomatic remission achieved",
            "Good treatment adherence",
            "Strong social / family support",
            "Absence of psychotic features",
            "No comorbid anxiety or substance use",
            "Higher education / employment",
            "Female sex (for certain outcomes)",
        ],
        "bad": [
            "Early age of onset",
            "High number of prior episodes",
            "Poor interepisode functioning",
            "Rapid cycling course",
            "Psychotic features present",
            "Comorbid ADHD",
            "Comorbid anxiety disorder",
            "Comorbid substance use disorder",
            "Comorbid personality disorder",
            "Younger age at first treatment",
            "Cognitive impairment",
            "Poor medication adherence",
        ],
        "source": "Treuer & Tohen 2010 (Eur Psychiatry, PMID 20444581); Solmi et al. 2023 (PMID 37957292)"
    },
    {
        "name": "MAJOR DEPRESSIVE DISORDER (MDD)",
        "emoji": "🌧️",
        "good": [
            "Female sex",
            "Single depressive episode (not recurrent)",
            "Acute onset with identifiable trigger",
            "Mild-to-moderate baseline severity",
            "Early improvement within first 2 weeks of Rx",
            "Good social support",
            "Higher education / employment",
            "Absence of residual symptoms at remission",
            "Early and adequate treatment",
            "No childhood maltreatment history",
            "Better quality of life / functioning at baseline",
        ],
        "bad": [
            "Recurrent episode history",
            "Residual depressive symptoms after treatment",
            "Childhood maltreatment / adverse events",
            "Comorbid anxiety disorder",
            "Comorbid physical illness (chronic pain, etc.)",
            "Comorbid personality disorder",
            "High baseline symptom severity",
            "Long duration of untreated depression",
            "Poor social support / social isolation",
            "Negative life events",
            "Suicidality at presentation",
            "Low socioeconomic status",
        ],
        "source": "Solmi et al. 2023 (PMID 37957292); Prognosis & improved outcomes in MDD (Transl Psychiatry 2019)"
    },
    {
        "name": "OBSESSIVE-COMPULSIVE DISORDER (OCD)",
        "emoji": "🔁",
        "good": [
            "Short duration of illness at presentation",
            "Low symptom severity (low Y-BOCS score)",
            "Good insight into obsessions",
            "Later age of onset",
            "Female sex",
            "Early and intensive treatment",
            "Long-term maintenance treatment",
            "Good social support / functional family",
            "Absence of comorbid personality disorder",
            "Absence of severe comorbid depression",
            "Employment / married status",
            "Pure obsessional or checking subtype",
        ],
        "bad": [
            "Early age of onset",
            "Long illness duration at presentation",
            "High baseline Y-BOCS score",
            "Poor insight / overvalued ideation",
            "Male sex",
            "Hoarding symptom subtype",
            "Symmetry / ordering subtype",
            "Contamination-washing subtype (for SRI response)",
            "Comorbid schizotypal or borderline PD",
            "Comorbid severe depression",
            "Unemployment / single / unmarried",
            "Family dysfunction / high expressed emotion",
            "Long duration of untreated OCD",
        ],
        "source": "Sharma et al. 2014 meta-analysis (J Clin Psychiatry, PMID 25295427); Sharma & Math 2019 (Indian J Psychiatry, PMID 30745676); Hazari et al. 2016 (PMID 27282021)"
    },
    {
        "name": "PTSD",
        "emoji": "⚡",
        "good": [
            "Single, adult-onset trauma (no childhood trauma)",
            "Female sex (for treatment response)",
            "Good social support",
            "Higher education",
            "Strong executive control / cognitive function",
            "High fear psychophysiology at baseline (extinction learning capacity)",
            "Early initiation of trauma-focused therapy",
            "Absence of comorbid depression / alcohol use",
            "Single trauma type (not complex/multiple traumas)",
            "Caucasian ethnicity (study-specific finding)",
        ],
        "bad": [
            "Male sex",
            "Childhood / early-onset trauma (complex PTSD)",
            "Greater cumulative trauma exposure",
            "Combat trauma history",
            "Poor / absent social support",
            "Comorbid depression",
            "Comorbid alcohol / substance use disorder",
            "Comorbid chronic pain",
            "Comorbid sleep disorder",
            "Older age",
            "Non-Caucasian ethnicity (study-specific finding)",
            "Lower pre-treatment fear activation",
            "Higher anger dysregulation",
            "Lower quality of life at baseline",
        ],
        "source": "Keyan et al. 2024 systematic review & meta-analysis (Psychol Bull, PMID 38884956); Solmi et al. 2023 (PMID 37957292)"
    },
    {
        "name": "ANXIETY DISORDERS (Panic, Social Anxiety, GAD)",
        "emoji": "😰",
        "good": [
            "Later / adult onset (not childhood-onset)",
            "Milder baseline symptom severity",
            "Good social support",
            "Absence of comorbid mood disorder",
            "Early treatment initiation",
            "Higher education / employment",
            "Limited avoidance behaviour at presentation",
            "Single anxiety disorder (no comorbid anxiety)",
            "Good treatment adherence",
            "Absence of neuroticism trait",
            "Better baseline quality of life / functioning",
        ],
        "bad": [
            "Childhood / early onset",
            "Severe baseline symptoms",
            "Comorbid major depressive disorder",
            "Comorbid personality disorder",
            "Comorbid substance use disorder",
            "Multiple comorbid anxiety disorders",
            "Entrenched avoidance behaviours",
            "High neuroticism trait",
            "Poor social support",
            "Low socioeconomic status",
            "Negative life events",
            "Long duration of untreated disorder",
        ],
        "source": "Solmi et al. 2023 (PMID 37957292); Wang et al. 2018 (BMC Psychiatry, PMID 29843662); General anxiety literature"
    },
]

# ── TRANSDIAGNOSTIC SUMMARY DATA ──────────────────────────────────────────────
transdiag_good = [
    "Female sex",
    "Higher education",
    "Social contacts and employment",
    "Early access to and initiation of treatment",
    "Absence of negative life events",
    "Lower disease-specific symptom burden at baseline",
    "Better quality of life / functioning at baseline",
    "Absence of comorbid mental disorders",
    "Absence of comorbid physical illness",
    "Good treatment adherence",
]
transdiag_bad = [
    "Male sex",
    "Younger age at onset",
    "Higher disease-specific symptom burden",
    "Higher depressive symptoms (even as comorbidity)",
    "Higher anxiety symptoms",
    "Greater global illness severity",
    "More prior episodes / relapses",
    "Negative life events",
    "Comorbid mental disorders",
    "Comorbid physical disorders",
    "Social isolation",
    "Unemployment / low education",
]

# ── PAGE TEMPLATE ─────────────────────────────────────────────────────────────
def add_page_background(canvas, doc):
    canvas.saveState()
    w, h = A4
    # Top header band
    canvas.setFillColor(DARK_NAVY)
    canvas.rect(0, h - 2.8*cm, w, 2.8*cm, fill=1, stroke=0)
    # Teal accent strip
    canvas.setFillColor(TEAL)
    canvas.rect(0, h - 3.0*cm, w, 0.22*cm, fill=1, stroke=0)
    # Bottom footer band
    canvas.setFillColor(colors.HexColor("#f0f0f0"))
    canvas.rect(0, 0, w, 1.2*cm, fill=1, stroke=0)
    canvas.setFillColor(MID_GREY)
    canvas.rect(0, 1.2*cm, w, 0.05*cm, fill=1, stroke=0)
    # Footer text
    canvas.setFont("Helvetica-Oblique", 7.5)
    canvas.setFillColor(colors.HexColor("#555555"))
    canvas.drawCentredString(w/2, 0.45*cm,
        "Clinical Psychiatric Prognostic Checklist  |  For clinical use only - not a diagnostic tool  |  Sources: Solmi et al. 2023 · Sharma et al. 2014 · Keyan et al. 2024 · Treuer & Tohen 2010")
    canvas.drawRightString(w - 1.5*cm, 0.45*cm, f"Page {doc.page}")
    canvas.restoreState()


# ── BUILD ─────────────────────────────────────────────────────────────────────
doc = SimpleDocTemplate(
    OUTPUT,
    pagesize=A4,
    topMargin=3.2*cm,
    bottomMargin=1.6*cm,
    leftMargin=1.5*cm,
    rightMargin=1.5*cm,
    title="Psychiatric Prognostic Checklist",
    author="Orris AI",
    subject="Clinical Prognostic Factors in Adult Psychiatric Disorders",
)

story = []

# ── TITLE BLOCK ──
title_data = [
    [Paragraph("Clinical Prognostic Checklist", title_style)],
    [Paragraph("Adult Psychiatric Disorders  ·  Good &amp; Poor Prognostic Factors", subtitle_style)],
    [Paragraph("Based on systematic reviews and meta-analyses (2010–2024)", subtitle_style)],
]
title_table = Table(title_data, colWidths=[18.5*cm])
title_table.setStyle(TableStyle([
    ("BACKGROUND", (0,0), (-1,-1), DARK_NAVY),
    ("TOPPADDING", (0,0), (-1,-1), 4),
    ("BOTTOMPADDING", (0,0), (-1,-1), 4),
    ("LEFTPADDING", (0,0), (-1,-1), 8),
]))
story.append(title_table)
story.append(Spacer(1, 0.3*cm))

# ── HOW TO USE ──
how_data = [[
    Paragraph(
        "<b>HOW TO USE:</b>  For each patient, tick applicable factors in both columns. "
        "A cluster of poor prognostic factors should prompt more intensive monitoring, "
        "early escalation of treatment, and active social support planning. "
        "This checklist is a clinical aide - it does not replace clinical judgement.",
        note_style
    )
]]
how_table = Table(how_data, colWidths=[18.5*cm])
how_table.setStyle(TableStyle([
    ("BACKGROUND", (0,0), (-1,-1), colors.HexColor("#fffbe6")),
    ("BOX", (0,0), (-1,-1), 0.5, colors.HexColor("#e0c050")),
    ("TOPPADDING", (0,0), (-1,-1), 6),
    ("BOTTOMPADDING", (0,0), (-1,-1), 6),
    ("LEFTPADDING", (0,0), (-1,-1), 8),
    ("RIGHTPADDING", (0,0), (-1,-1), 8),
]))
story.append(how_table)
story.append(Spacer(1, 0.4*cm))

# ── TRANSDIAGNOSTIC SECTION ──
td_header_data = [[Paragraph("  🌐  TRANSDIAGNOSTIC FACTORS  (apply across ALL disorders)", section_style)]]
td_header_table = Table(td_header_data, colWidths=[18.5*cm])
td_header_table.setStyle(TableStyle([
    ("BACKGROUND", (0,0), (-1,-1), colors.HexColor("#4a235a")),
    ("TOPPADDING", (0,0), (-1,-1), 7),
    ("BOTTOMPADDING", (0,0), (-1,-1), 7),
    ("LEFTPADDING", (0,0), (-1,-1), 6),
    ("BOX", (0,0), (-1,-1), 0.5, colors.HexColor("#2d1440")),
]))
story.append(td_header_table)

td_col_hdr = [[
    Paragraph("✅  GOOD (applies across disorders)", col_header_good),
    Paragraph("⚠️  POOR (applies across disorders)", col_header_bad)
]]
td_col_hdr_table = Table(td_col_hdr, colWidths=[9.25*cm, 9.25*cm])
td_col_hdr_table.setStyle(TableStyle([
    ("BACKGROUND", (0,0), (0,0), GOOD_GREEN),
    ("BACKGROUND", (1,0), (1,0), BAD_RED),
    ("TOPPADDING", (0,0), (-1,-1), 5),
    ("BOTTOMPADDING", (0,0), (-1,-1), 5),
    ("LEFTPADDING", (0,0), (-1,-1), 4),
    ("LINEBELOW", (0,0), (-1,-1), 0.5, MID_GREY),
]))
story.append(td_col_hdr_table)

max_td = max(len(transdiag_good), len(transdiag_bad))
td_data = []
for i in range(max_td):
    g = checkbox(transdiag_good[i], item_style) if i < len(transdiag_good) else Paragraph("", item_style)
    b = checkbox(transdiag_bad[i], item_style) if i < len(transdiag_bad) else Paragraph("", item_style)
    td_data.append([g, b])

td_table = Table(td_data, colWidths=[9.25*cm, 9.25*cm])
ts_td = TableStyle([
    ("LEFTPADDING", (0,0), (-1,-1), 5),
    ("RIGHTPADDING", (0,0), (-1,-1), 5),
    ("TOPPADDING", (0,0), (-1,-1), 3),
    ("BOTTOMPADDING", (0,0), (-1,-1), 3),
    ("VALIGN", (0,0), (-1,-1), "TOP"),
    ("LINEAFTER", (0,0), (0,-1), 0.5, MID_GREY),
    ("BOX", (0,0), (-1,-1), 0.5, MID_GREY),
])
for i in range(max_td):
    ts_td.add("BACKGROUND", (0,i), (0,i), colors.HexColor("#f2fbf2") if i%2==0 else colors.HexColor("#e8f8e8"))
    ts_td.add("BACKGROUND", (1,i), (1,i), colors.HexColor("#fef5f5") if i%2==0 else colors.HexColor("#fdeaea"))
td_table.setStyle(ts_td)
story.append(td_table)

td_src_data = [[Paragraph("Source: Solmi et al. 2023 – Umbrella review, 117 SRs, 299,888 patients (Mol Psychiatry, PMID 37957292)", source_style)]]
td_src_table = Table(td_src_data, colWidths=[18.5*cm])
td_src_table.setStyle(TableStyle([
    ("BACKGROUND", (0,0), (-1,-1), colors.HexColor("#f0f0f0")),
    ("TOPPADDING", (0,0), (-1,-1), 3),
    ("BOTTOMPADDING", (0,0), (-1,-1), 3),
    ("LEFTPADDING", (0,0), (-1,-1), 6),
    ("BOX", (0,0), (-1,-1), 0.3, MID_GREY),
]))
story.append(td_src_table)
story.append(Spacer(1, 0.4*cm))

# ── DISORDER SECTIONS ──
for d in disorders:
    section_elements = build_disorder_section(
        d["name"], d["good"], d["bad"], d["source"], d["emoji"]
    )
    story.extend(section_elements)

# ── NOTES PAGE ──
story.append(PageBreak())
notes_header = [[Paragraph("  📋  CLINICAL NOTES", section_style)]]
notes_hdr_table = Table(notes_header, colWidths=[18.5*cm])
notes_hdr_table.setStyle(TableStyle([
    ("BACKGROUND", (0,0), (-1,-1), colors.HexColor("#2c3e6b")),
    ("TOPPADDING", (0,0), (-1,-1), 7),
    ("BOTTOMPADDING", (0,0), (-1,-1), 7),
    ("LEFTPADDING", (0,0), (-1,-1), 6),
]))
story.append(notes_hdr_table)
story.append(Spacer(1, 0.3*cm))

line_data = []
for _ in range(22):
    line_data.append([Paragraph("", item_style), HRFlowable(width="100%", thickness=0.4, color=MID_GREY)])

notes_lines = []
for i in range(22):
    notes_lines.append([Paragraph("", item_style)])
    notes_lines.append([HRFlowable(width="100%", thickness=0.4, color=MID_GREY)])
    notes_lines.append([Spacer(1, 0.45*cm)])

for item in notes_lines:
    story.append(item[0])

# ── REFERENCES ──
story.append(Spacer(1, 0.5*cm))
ref_header = [[Paragraph("  📚  KEY REFERENCES", section_style)]]
ref_hdr_table = Table(ref_header, colWidths=[18.5*cm])
ref_hdr_table.setStyle(TableStyle([
    ("BACKGROUND", (0,0), (-1,-1), colors.HexColor("#2c3e6b")),
    ("TOPPADDING", (0,0), (-1,-1), 6),
    ("BOTTOMPADDING", (0,0), (-1,-1), 6),
    ("LEFTPADDING", (0,0), (-1,-1), 6),
]))
story.append(ref_hdr_table)
story.append(Spacer(1, 0.2*cm))

refs = [
    "1. Solmi M, et al. (2023). An umbrella review of candidate predictors of response, remission, recovery, and relapse across mental disorders. Molecular Psychiatry. PMID: 37957292",
    "2. Molstrom IM, et al. (2022). The prognosis of schizophrenia: A systematic review and meta-analysis with meta-regression of 20-year follow-up studies. Schizophrenia Research. PMID: 36417817",
    "3. Treuer T & Tohen M. (2010). Predicting the course and outcome of bipolar disorder: a review. European Psychiatry. PMID: 20444581",
    "4. Sharma E, Thennarasu K, Reddy YCJ. (2014). Long-term outcome of OCD in adults: a meta-analysis. Journal of Clinical Psychiatry. PMID: 25295427",
    "5. Sharma E & Math SB. (2019). Course and outcome of obsessive-compulsive disorder. Indian Journal of Psychiatry. PMID: 30745676",
    "6. Hazari N, et al. (2016). Predictors of response to serotonin reuptake inhibitors in OCD. Expert Review of Neurotherapeutics. PMID: 27282021",
    "7. Keyan D, et al. (2024). A systematic review and meta-analysis of predictors of response to trauma-focused psychotherapy for PTSD. Psychological Bulletin. PMID: 38884956",
    "8. Wang J, et al. (2018). Associations between loneliness/social support and outcomes of mental health problems: a systematic review. BMC Psychiatry. PMID: 29843662",
]
for ref in refs:
    story.append(Paragraph(ref, source_style))
    story.append(Spacer(1, 0.15*cm))

# ── BUILD PDF ──
doc.build(story, onFirstPage=add_page_background, onLaterPages=add_page_background)
print("PDF created successfully:", OUTPUT)
Running Command

cd /tmp/workspace/psych-checklist && python build_checklist.py 2>&1

Running Command

ls -lh /tmp/workspace/psych-checklist/Psychiatric_Prognostic_Checklist.pdf

Your clinical checklist PDF is ready:

Psychiatric Prognostic Checklist

PDF Document · PDF


Here is what the PDF contains:
Structure (A4, multi-page):
  • A colour-coded dark navy header with document title and source attribution
  • A "How to Use" instruction box at the top
  • A Transdiagnostic section first - factors that apply across all disorders (from the Solmi et al. 2023 umbrella review)
  • 6 disorder-specific sections, each with a two-column layout:
    • Good prognostic factors (green background) with tick boxes
    • ⚠️ Poor prognostic factors (red background) with tick boxes
  • Disorders covered: Schizophrenia, Bipolar Disorder, MDD, OCD, PTSD, and Anxiety Disorders (Panic/Social Anxiety/GAD)
  • Source citation strip under each disorder section
  • A blank Clinical Notes page at the end
  • Full References list with PMIDs
How to use in clinic: Tick applicable factors for each patient in both columns. A cluster of poor prognostic factors should prompt earlier treatment escalation, closer follow-up, and active social support planning. It is designed as a clinical aide - not a scoring tool.

are there any biomarkers that can be added to the list of each disorder

Great question. Biomarkers for psychiatric prognosis are an active research area. Let me search for the best evidence across all 6 disorders simultaneously.
Searching sources...
Searching PubMed

biomarkers prognosis schizophrenia bipolar depression outcome predictors review

Searching PubMed

biomarkers OCD PTSD anxiety disorder treatment outcome predictors review

Searching the Web

prognostic biomarkers psychiatric disorders schizophrenia bipolar depression OCD PTSD CRP BDNF cortisol neuroimaging review 2022 2023 2024

Excellent - two key sources found. Let me pull full details from the most comprehensive ones in parallel.
Reading Article

PMID: 39456983

Reading Article

PMID: 39954922

Reading Web Page

https://pmc.ncbi.nlm.nih.gov/articles/PMC10168176

Searching PubMed

inflammatory biomarkers CRP IL-6 depression schizophrenia prognosis treatment outcome

Good. Now let me extract the disorder-specific biomarker content from the comprehensive PMC review, and also pull the OCD/anxiety-specific biomarker consensus paper.
Searching PubMed

biological markers OCD PTSD anxiety disorders consensus neurochemistry neuroimaging

Searching PubMed

inflammatory biomarkers CRP interleukin depression prognosis treatment response meta-analysis

Reading Web Page

https://link.springer.com/article/10.1186/s12888-025-07429-4

Your credits are exhausted. You can purchase a top-up or subscribe to a plan to continue using Orris. If you're already subscribed, your daily credits will reset tomorrow.

are there any biomarkers that can be added to the list of each disorder

Your credits are exhausted. You can purchase a top-up or subscribe to a plan to continue using Orris. If you're already subscribed, your daily credits will reset tomorrow.

are there any biomarkers that can be added to the list of each disorder

Yes - there is a solid (though still evolving) evidence base for prognostic biomarkers across these disorders. Let me search the best available evidence now.
Searching sources...
Searching PubMed

biomarkers prognosis schizophrenia bipolar major depression outcome treatment response review

Searching PubMed

biological markers OCD PTSD anxiety disorders consensus neuroimaging neurochemistry

Searching the Web

prognostic biomarkers schizophrenia bipolar depression OCD PTSD anxiety BDNF CRP cortisol neuroimaging systematic review 2023 2024

Two key sources confirmed. Let me pull full details from the most comprehensive ones.
Reading Article

PMID: 27419272

Reading Article

PMID: 27403679

Reading Web Page

https://pmc.ncbi.nlm.nih.gov/articles/PMC10168176

I now have enough from key sources. Let me build the updated PDF with the biomarker sections added.
Writing File

~/psych-checklist/build_checklist_v2.py

from reportlab.lib.pagesizes import A4
from reportlab.lib import colors
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
from reportlab.lib.units import cm, mm
from reportlab.platypus import (
    SimpleDocTemplate, Paragraph, Spacer, Table, TableStyle,
    HRFlowable, PageBreak
)
from reportlab.lib.enums import TA_LEFT, TA_CENTER

OUTPUT = "/tmp/workspace/psych-checklist/Psychiatric_Prognostic_Checklist_v2.pdf"

# ── Colours ──────────────────────────────────────────────────────────────────
DARK_NAVY   = colors.HexColor("#1a2b4a")
TEAL        = colors.HexColor("#007b7f")
GOOD_GREEN  = colors.HexColor("#d4edda")
BAD_RED     = colors.HexColor("#f8d7da")
GOOD_DARK   = colors.HexColor("#155724")
BAD_DARK    = colors.HexColor("#721c24")
HEADER_BG   = colors.HexColor("#2c3e6b")
WHITE       = colors.white
MID_GREY    = colors.HexColor("#cccccc")
BIOMARKER_BG = colors.HexColor("#eaf4fb")
BIOMARKER_HDR = colors.HexColor("#1a6b8a")
BIO_GOOD    = colors.HexColor("#1a6b8a")
BIO_BAD     = colors.HexColor("#7b3a10")
BIO_GOOD_BG = colors.HexColor("#d6eef7")
BIO_BAD_BG  = colors.HexColor("#fde8d0")

# ── Styles ────────────────────────────────────────────────────────────────────
styles = getSampleStyleSheet()

title_style = ParagraphStyle("TitleStyle", parent=styles["Title"],
    fontSize=20, textColor=WHITE, alignment=TA_CENTER,
    spaceAfter=4, fontName="Helvetica-Bold")
subtitle_style = ParagraphStyle("SubtitleStyle", parent=styles["Normal"],
    fontSize=10, textColor=colors.HexColor("#c8d8f0"),
    alignment=TA_CENTER, spaceAfter=2, fontName="Helvetica-Oblique")
section_style = ParagraphStyle("SectionStyle", parent=styles["Normal"],
    fontSize=13, textColor=WHITE, fontName="Helvetica-Bold",
    alignment=TA_LEFT, spaceAfter=0, leftIndent=6)
col_header_good = ParagraphStyle("ColGood", parent=styles["Normal"],
    fontSize=10, textColor=GOOD_DARK, fontName="Helvetica-Bold", alignment=TA_CENTER)
col_header_bad = ParagraphStyle("ColBad", parent=styles["Normal"],
    fontSize=10, textColor=BAD_DARK, fontName="Helvetica-Bold", alignment=TA_CENTER)
bio_col_good = ParagraphStyle("BioColGood", parent=styles["Normal"],
    fontSize=9, textColor=BIO_GOOD, fontName="Helvetica-Bold", alignment=TA_CENTER)
bio_col_bad = ParagraphStyle("BioColBad", parent=styles["Normal"],
    fontSize=9, textColor=BIO_BAD, fontName="Helvetica-Bold", alignment=TA_CENTER)
item_style = ParagraphStyle("ItemStyle", parent=styles["Normal"],
    fontSize=8.5, textColor=colors.HexColor("#222222"),
    fontName="Helvetica", leading=12, leftIndent=4)
bio_item_style = ParagraphStyle("BioItemStyle", parent=styles["Normal"],
    fontSize=8, textColor=colors.HexColor("#1a3a4a"),
    fontName="Helvetica", leading=11, leftIndent=4)
source_style = ParagraphStyle("SourceStyle", parent=styles["Normal"],
    fontSize=7, textColor=colors.HexColor("#555555"),
    fontName="Helvetica-Oblique", alignment=TA_LEFT, leftIndent=4)
note_style = ParagraphStyle("NoteStyle", parent=styles["Normal"],
    fontSize=8, textColor=colors.HexColor("#333333"),
    fontName="Helvetica", leading=11, leftIndent=6)
warning_style = ParagraphStyle("WarnStyle", parent=styles["Normal"],
    fontSize=7.5, textColor=colors.HexColor("#7b3a10"),
    fontName="Helvetica-Oblique", leading=10, leftIndent=4)

def checkbox(text, style):
    return Paragraph(f"☐  {text}", style)

def diamond(text, style):
    return Paragraph(f"◇  {text}", style)

def build_disorder_section(disorder_name, good_factors, bad_factors,
                            bio_good, bio_bad, source_text, bio_source, emoji=""):
    elements = []

    # Section header
    header_data = [[Paragraph(f"  {emoji}  {disorder_name}", section_style)]]
    header_table = Table(header_data, colWidths=[18.5*cm])
    header_table.setStyle(TableStyle([
        ("BACKGROUND", (0,0), (-1,-1), HEADER_BG),
        ("TOPPADDING", (0,0), (-1,-1), 7),
        ("BOTTOMPADDING", (0,0), (-1,-1), 7),
        ("LEFTPADDING", (0,0), (-1,-1), 6),
        ("BOX", (0,0), (-1,-1), 0.5, DARK_NAVY),
    ]))
    elements.append(header_table)

    # ── CLINICAL FACTORS columns ──
    col_hdr = [[Paragraph("✅  GOOD PROGNOSTIC FACTORS", col_header_good),
                Paragraph("⚠️  POOR PROGNOSTIC FACTORS", col_header_bad)]]
    col_hdr_table = Table(col_hdr, colWidths=[9.25*cm, 9.25*cm])
    col_hdr_table.setStyle(TableStyle([
        ("BACKGROUND", (0,0), (0,0), GOOD_GREEN),
        ("BACKGROUND", (1,0), (1,0), BAD_RED),
        ("TOPPADDING", (0,0), (-1,-1), 5),
        ("BOTTOMPADDING", (0,0), (-1,-1), 5),
        ("LEFTPADDING", (0,0), (-1,-1), 4),
        ("LINEBELOW", (0,0), (-1,-1), 0.5, MID_GREY),
    ]))
    elements.append(col_hdr_table)

    max_len = max(len(good_factors), len(bad_factors))
    data = []
    for i in range(max_len):
        g = checkbox(good_factors[i], item_style) if i < len(good_factors) else Paragraph("", item_style)
        b = checkbox(bad_factors[i], item_style) if i < len(bad_factors) else Paragraph("", item_style)
        data.append([g, b])

    factors_table = Table(data, colWidths=[9.25*cm, 9.25*cm])
    ts = TableStyle([
        ("LEFTPADDING", (0,0), (-1,-1), 5), ("RIGHTPADDING", (0,0), (-1,-1), 5),
        ("TOPPADDING", (0,0), (-1,-1), 3), ("BOTTOMPADDING", (0,0), (-1,-1), 3),
        ("VALIGN", (0,0), (-1,-1), "TOP"),
        ("LINEAFTER", (0,0), (0,-1), 0.5, MID_GREY),
        ("BOX", (0,0), (-1,-1), 0.5, MID_GREY),
    ])
    for i in range(max_len):
        ts.add("BACKGROUND", (0,i), (0,i), colors.HexColor("#f2fbf2") if i%2==0 else colors.HexColor("#e8f8e8"))
        ts.add("BACKGROUND", (1,i), (1,i), colors.HexColor("#fef5f5") if i%2==0 else colors.HexColor("#fdeaea"))
    factors_table.setStyle(ts)
    elements.append(factors_table)

    # ── BIOMARKER SUBSECTION ──
    bio_hdr_data = [[Paragraph("  🔬  BIOMARKER INDICATORS  (research-grade; use to inform, not replace, clinical judgement)", 
                               ParagraphStyle("BioHdr", parent=styles["Normal"],
                                   fontSize=9, textColor=WHITE, fontName="Helvetica-Bold",
                                   alignment=TA_LEFT))]]
    bio_hdr_table = Table(bio_hdr_data, colWidths=[18.5*cm])
    bio_hdr_table.setStyle(TableStyle([
        ("BACKGROUND", (0,0), (-1,-1), BIOMARKER_HDR),
        ("TOPPADDING", (0,0), (-1,-1), 5),
        ("BOTTOMPADDING", (0,0), (-1,-1), 5),
        ("LEFTPADDING", (0,0), (-1,-1), 6),
    ]))
    elements.append(bio_hdr_table)

    bio_col_hdr = [[Paragraph("🔵  FAVOURABLE BIOMARKER PROFILE", bio_col_good),
                    Paragraph("🔴  UNFAVOURABLE BIOMARKER PROFILE", bio_col_bad)]]
    bio_col_hdr_table = Table(bio_col_hdr, colWidths=[9.25*cm, 9.25*cm])
    bio_col_hdr_table.setStyle(TableStyle([
        ("BACKGROUND", (0,0), (0,0), BIO_GOOD_BG),
        ("BACKGROUND", (1,0), (1,0), BIO_BAD_BG),
        ("TOPPADDING", (0,0), (-1,-1), 4),
        ("BOTTOMPADDING", (0,0), (-1,-1), 4),
        ("LEFTPADDING", (0,0), (-1,-1), 4),
        ("LINEBELOW", (0,0), (-1,-1), 0.5, MID_GREY),
    ]))
    elements.append(bio_col_hdr_table)

    bio_max = max(len(bio_good), len(bio_bad))
    bio_data = []
    for i in range(bio_max):
        g = diamond(bio_good[i], bio_item_style) if i < len(bio_good) else Paragraph("", bio_item_style)
        b = diamond(bio_bad[i], bio_item_style) if i < len(bio_bad) else Paragraph("", bio_item_style)
        bio_data.append([g, b])

    bio_table = Table(bio_data, colWidths=[9.25*cm, 9.25*cm])
    bio_ts = TableStyle([
        ("LEFTPADDING", (0,0), (-1,-1), 5), ("RIGHTPADDING", (0,0), (-1,-1), 5),
        ("TOPPADDING", (0,0), (-1,-1), 3), ("BOTTOMPADDING", (0,0), (-1,-1), 3),
        ("VALIGN", (0,0), (-1,-1), "TOP"),
        ("LINEAFTER", (0,0), (0,-1), 0.5, MID_GREY),
        ("BOX", (0,0), (-1,-1), 0.5, MID_GREY),
    ])
    for i in range(bio_max):
        bio_ts.add("BACKGROUND", (0,i), (0,i), colors.HexColor("#eaf4fb") if i%2==0 else colors.HexColor("#dceef8"))
        bio_ts.add("BACKGROUND", (1,i), (1,i), colors.HexColor("#fef0e0") if i%2==0 else colors.HexColor("#fde5c8"))
    bio_table.setStyle(bio_ts)
    elements.append(bio_table)

    # Sources strip
    src_text = f"Clinical sources: {source_text}  |  Biomarker sources: {bio_source}"
    src_data = [[Paragraph(src_text, source_style)]]
    src_table = Table(src_data, colWidths=[18.5*cm])
    src_table.setStyle(TableStyle([
        ("BACKGROUND", (0,0), (-1,-1), colors.HexColor("#f0f0f0")),
        ("TOPPADDING", (0,0), (-1,-1), 3), ("BOTTOMPADDING", (0,0), (-1,-1), 3),
        ("LEFTPADDING", (0,0), (-1,-1), 6),
        ("BOX", (0,0), (-1,-1), 0.3, MID_GREY),
    ]))
    elements.append(src_table)
    elements.append(Spacer(1, 0.35*cm))
    return elements


# ── DATA ─────────────────────────────────────────────────────────────────────

disorders = [
    {
        "name": "SCHIZOPHRENIA",
        "emoji": "🧠",
        "good": [
            "Female sex",
            "Later / acute onset of illness",
            "Predominantly positive symptoms",
            "Good premorbid functioning",
            "Short duration of untreated psychosis (DUP)",
            "Good insight into illness",
            "Strong social support network",
            "Employment / higher education",
            "Early and intensive treatment initiation",
            "Absence of negative symptoms at baseline",
            "No comorbid substance use disorder",
            "Married / stable intimate relationship",
        ],
        "bad": [
            "Male sex",
            "Insidious / early onset",
            "Predominantly negative symptoms",
            "Poor premorbid functioning",
            "Long duration of untreated psychosis",
            "Poor insight / anosognosia",
            "Social isolation / lack of support",
            "Unemployment / low education",
            "Delayed treatment initiation",
            "Prominent cognitive deficits",
            "Comorbid substance use disorder",
            "Family history of schizophrenia",
        ],
        "bio_good": [
            "Higher baseline BDNF levels (→ better antipsychotic response)",
            "BDNF Val/Val genotype (vs. Met carrier)",
            "Striatal connectivity index (SCI) - intact connectivity",
            "Normal caudate / putamen volume on MRI",
            "Low inflammatory markers (CRP, IL-6) at baseline",
            "Normal anterior cingulate cortex (ACC) volume",
            "Normal prefrontal cortex activation on fMRI",
            "Low polygenic risk score for schizophrenia",
        ],
        "bio_bad": [
            "Low baseline serum BDNF",
            "BDNF Met/Met or Val/Met genotype",
            "Elevated striatal functional abnormalities (FSA index)",
            "Reduced caudate volume on structural MRI",
            "Elevated CRP / IL-6 (high inflammatory state)",
            "Reduced ACC and orbitofrontal cortex volume",
            "Hypofrontality on fMRI (reduced PFC activation)",
            "High polygenic risk score for schizophrenia",
            "Elevated homocysteine levels",
            "Low serum folate / B12",
        ],
        "source": "Solmi et al. 2023 (PMID 37957292); Molstrom et al. 2022 (PMID 36417817)",
        "bio_source": "Liberona et al. 2024 (PMID 39456983); Candidate biomarkers review (PMC10168176)"
    },
    {
        "name": "BIPOLAR DISORDER",
        "emoji": "🔄",
        "good": [
            "Later age of onset",
            "Fewer prior mood episodes",
            "Good psychosocial functioning at baseline",
            "Long-term symptomatic remission achieved",
            "Good treatment adherence",
            "Strong social / family support",
            "Absence of psychotic features",
            "No comorbid anxiety or substance use",
            "Higher education / employment",
            "Female sex (for certain outcomes)",
        ],
        "bad": [
            "Early age of onset",
            "High number of prior episodes",
            "Poor interepisode functioning",
            "Rapid cycling course",
            "Psychotic features present",
            "Comorbid ADHD",
            "Comorbid anxiety disorder",
            "Comorbid substance use disorder",
            "Comorbid personality disorder",
            "Cognitive impairment",
            "Poor medication adherence",
        ],
        "bio_good": [
            "Higher baseline BDNF (→ better lithium response)",
            "BDNF Val/Val genotype",
            "Low polygenic risk score for MDD / schizophrenia",
            "Episodic clinical course on HPA axis testing",
            "Normal cortisol awakening response",
            "Normal lithium erythrocyte / plasma ratio",
            "Low CRP / IL-6 inflammatory markers",
            "Preserved hippocampal volume on MRI",
        ],
        "bio_bad": [
            "Low serum BDNF",
            "High polygenic risk score for schizophrenia or MDD",
            "Elevated cortisol (HPA dysregulation)",
            "Elevated CRP / IL-6 / TNF-alpha",
            "Reduced hippocampal volume on MRI",
            "Reduced subgenual ACC volume",
            "Elevated homocysteine",
            "Telomere shortening (surrogate for biological ageing)",
            "Abnormal lithium erythrocyte:plasma ratio",
        ],
        "source": "Treuer & Tohen 2010 (PMID 20444581); Solmi et al. 2023 (PMID 37957292)",
        "bio_source": "Scott et al. 2025 (PMID 39954922); Liberona et al. 2024 (PMID 39456983)"
    },
    {
        "name": "MAJOR DEPRESSIVE DISORDER (MDD)",
        "emoji": "🌧️",
        "good": [
            "Female sex",
            "Single depressive episode (not recurrent)",
            "Acute onset with identifiable trigger",
            "Mild-to-moderate baseline severity",
            "Early improvement within first 2 weeks of Rx",
            "Good social support",
            "Higher education / employment",
            "Absence of residual symptoms at remission",
            "Early and adequate treatment",
            "No childhood maltreatment history",
            "Better quality of life / functioning at baseline",
        ],
        "bad": [
            "Recurrent episode history",
            "Residual depressive symptoms after treatment",
            "Childhood maltreatment / adverse events",
            "Comorbid anxiety disorder",
            "Comorbid physical illness (chronic pain, etc.)",
            "Comorbid personality disorder",
            "High baseline symptom severity",
            "Long duration of untreated depression",
            "Poor social support / social isolation",
            "Negative life events",
            "Suicidality at presentation",
        ],
        "bio_good": [
            "Higher serum BDNF at baseline (→ antidepressant response)",
            "BDNF Val/Val genotype",
            "Low baseline CRP / IL-6 (better SSRI response)",
            "Normal hippocampal volume on MRI",
            "Higher subgenual ACC activity on PET/fMRI",
            "Normal HPA axis cortisol pattern",
            "Rising BDNF levels after 2 weeks of treatment",
            "Normal thyroid function (TSH within range)",
            "Normal folate / B12 levels",
        ],
        "bio_bad": [
            "Low serum BDNF at baseline",
            "High CRP / IL-6 / TNF-alpha (inflammatory subtype → poor SSRI response)",
            "Reduced hippocampal volume (esp. with recurrence)",
            "Reduced subgenual ACC volume / activity",
            "Elevated baseline cortisol / flattened diurnal cortisol",
            "Hypothyroidism or subclinical hypothyroidism",
            "Low folate / B12 (associated with non-response)",
            "Elevated homocysteine",
            "Reduced anterior cingulate cortex theta activity on EEG",
            "High polygenic risk score for MDD",
        ],
        "source": "Solmi et al. 2023 (PMID 37957292); Prognosis in MDD (Transl Psychiatry 2019)",
        "bio_source": "Candidate biomarkers review (PMC10168176); Liberona et al. 2024 (PMID 39456983)"
    },
    {
        "name": "OBSESSIVE-COMPULSIVE DISORDER (OCD)",
        "emoji": "🔁",
        "good": [
            "Short duration of illness at presentation",
            "Low symptom severity (low Y-BOCS score)",
            "Good insight into obsessions",
            "Later age of onset",
            "Female sex",
            "Early and intensive treatment",
            "Long-term maintenance treatment",
            "Good social support / functional family",
            "Absence of comorbid personality disorder",
            "Pure obsessional or checking subtype",
        ],
        "bad": [
            "Early age of onset",
            "Long illness duration at presentation",
            "High baseline Y-BOCS score",
            "Poor insight / overvalued ideation",
            "Male sex",
            "Hoarding or symmetry symptom subtype",
            "Contamination-washing subtype (for SRI response)",
            "Comorbid schizotypal or borderline PD",
            "Comorbid severe depression",
            "Unemployment / single / unmarried",
            "Family dysfunction / high expressed emotion",
        ],
        "bio_good": [
            "Elevated CSF / blood 5-HIAA levels at baseline (→ better clomipramine response)",
            "Normal orbitofrontal cortex (OFC) volume",
            "Normal caudate activity on fMRI at rest",
            "Normal anterior cingulate cortex volume",
            "Normal serotonin transporter (5-HTT) binding on SPECT",
            "Higher glutamate levels in OFC (→ predicts CBT response in some studies)",
        ],
        "bio_bad": [
            "Low baseline CSF 5-HIAA (poor SRI response predictor)",
            "Reduced OFC volume / hyperactivity on fMRI",
            "Caudate hyperactivity (normalises with effective Rx)",
            "Poor insight correlates with orbitofrontal dysfunction",
            "Elevated cerebellar activity on fMRI",
            "Altered glutamatergic signalling in OFC / ACC",
            "Serotonin transporter gene (SLC6A4) polymorphisms",
            "Dopamine receptor D4 gene variants",
        ],
        "source": "Sharma et al. 2014 (PMID 25295427); Sharma & Math 2019 (PMID 30745676)",
        "bio_source": "Hazari et al. 2016 (PMID 27282021); Bandelow et al. 2017 (PMID 27419272)"
    },
    {
        "name": "PTSD",
        "emoji": "⚡",
        "good": [
            "Single, adult-onset trauma (no childhood trauma)",
            "Female sex (for treatment response)",
            "Good social support",
            "Higher education",
            "Strong executive control / cognitive function",
            "High fear psychophysiology at baseline",
            "Early initiation of trauma-focused therapy",
            "Absence of comorbid depression / alcohol use",
            "Single trauma type (not complex/multiple traumas)",
        ],
        "bad": [
            "Male sex",
            "Childhood / early-onset trauma (complex PTSD)",
            "Greater cumulative trauma exposure",
            "Combat trauma history",
            "Poor / absent social support",
            "Comorbid depression",
            "Comorbid alcohol / substance use disorder",
            "Comorbid chronic pain",
            "Comorbid sleep disorder",
            "Older age at treatment",
            "Higher anger dysregulation",
        ],
        "bio_good": [
            "Larger hippocampal volume (→ better fear extinction)",
            "Higher baseline glucocorticoid receptor sensitivity",
            "Normal HPA axis cortisol response",
            "Normal amygdala reactivity (not hyperreactive)",
            "Higher resting-state connectivity: PFC-amygdala",
            "Normal startle habituation on neurophysiology",
            "Normal error-related negativity (ERN) on EEG",
            "Low polygenic risk score for PTSD",
        ],
        "bio_bad": [
            "Reduced hippocampal volume (strongest structural biomarker)",
            "Reduced amygdala volume",
            "Reduced total brain / corpus callosum structural connectivity",
            "Low baseline cortisol / enhanced glucocorticoid suppression",
            "Elevated CRP / inflammatory markers",
            "Amygdala hyperreactivity to trauma cues on fMRI",
            "Impaired PFC inhibitory control of amygdala",
            "Elevated resting-state sympathetic tone (heart rate variability)",
            "High polygenic risk score for PTSD",
            "Dissociative subtype neurobiological profile",
        ],
        "source": "Keyan et al. 2024 (PMID 38884956); Solmi et al. 2023 (PMID 37957292)",
        "bio_source": "Bandelow et al. 2016/2017 (PMID 27403679, 27419272); PMC10168176"
    },
    {
        "name": "ANXIETY DISORDERS (Panic, Social Anxiety, GAD)",
        "emoji": "😰",
        "good": [
            "Later / adult onset (not childhood-onset)",
            "Milder baseline symptom severity",
            "Good social support",
            "Absence of comorbid mood disorder",
            "Early treatment initiation",
            "Higher education / employment",
            "Limited avoidance behaviour at presentation",
            "Single anxiety disorder (no comorbid anxiety)",
            "Good treatment adherence",
            "Absence of high neuroticism trait",
        ],
        "bad": [
            "Childhood / early onset",
            "Severe baseline symptoms",
            "Comorbid major depressive disorder",
            "Comorbid personality disorder",
            "Comorbid substance use disorder",
            "Multiple comorbid anxiety disorders",
            "Entrenched avoidance behaviours",
            "High neuroticism trait",
            "Poor social support",
            "Long duration of untreated disorder",
        ],
        "bio_good": [
            "Error-related negativity (ERN) - higher ERN predicts GAD first onset (screening tool)",
            "Normal amygdala volume / reactivity",
            "Intact resting-state PFC-amygdala connectivity",
            "Normal HPA axis cortisol (less dysregulation = better Tx response)",
            "Normal oxytocin levels (social anxiety)",
            "Normal GABA levels in PFC on MRS",
            "Low CRP / inflammatory markers at baseline",
        ],
        "bio_bad": [
            "Elevated ERN (biomarker of GAD risk / severity)",
            "Amygdala hyperreactivity to threat cues on fMRI",
            "Reduced insula volume (panic disorder)",
            "HPA axis dysregulation / elevated morning cortisol",
            "Reduced GABA in prefrontal cortex on MRS",
            "Low oxytocin (social anxiety disorder)",
            "Elevated CRP / IL-6 (complicates GAD course)",
            "SERT gene (SLC6A4) short allele (5-HTTLPR s/s genotype)",
            "Reduced benzodiazepine receptor binding on PET",
        ],
        "source": "Solmi et al. 2023 (PMID 37957292); Wang et al. 2018 (PMID 29843662)",
        "bio_source": "Bandelow et al. 2016/2017 (PMID 27403679, 27419272); PMC10168176"
    },
]

transdiag_good = [
    "Female sex",
    "Higher education",
    "Social contacts and employment",
    "Early access to treatment",
    "Absence of negative life events",
    "Lower disease-specific symptom burden at baseline",
    "Better quality of life / functioning at baseline",
    "Absence of comorbid mental or physical disorders",
    "Good treatment adherence",
]
transdiag_bad = [
    "Male sex",
    "Younger age at onset",
    "Higher disease-specific symptom burden",
    "Higher depressive symptoms (even as comorbidity)",
    "Higher anxiety symptoms",
    "Greater global illness severity",
    "More prior episodes / relapses",
    "Negative life events",
    "Social isolation / unemployment",
]
transdiag_bio_good = [
    "Higher serum BDNF (favours treatment response across mood/psychosis)",
    "BDNF Val/Val genotype",
    "Low CRP / IL-6 (low inflammatory burden)",
    "Normal HPA axis cortisol pattern",
    "Normal hippocampal volume",
    "Good cognitive function / executive control",
]
transdiag_bio_bad = [
    "Low serum BDNF",
    "Elevated CRP / IL-6 / TNF-alpha",
    "HPA axis dysregulation (elevated baseline cortisol)",
    "Reduced hippocampal volume",
    "High polygenic risk scores",
    "Elevated homocysteine",
    "Telomere shortening / accelerated biological ageing",
]


# ── PAGE TEMPLATE ─────────────────────────────────────────────────────────────
def add_page_background(canvas, doc):
    canvas.saveState()
    w, h = A4
    canvas.setFillColor(DARK_NAVY)
    canvas.rect(0, h - 2.8*cm, w, 2.8*cm, fill=1, stroke=0)
    canvas.setFillColor(TEAL)
    canvas.rect(0, h - 3.0*cm, w, 0.22*cm, fill=1, stroke=0)
    canvas.setFillColor(colors.HexColor("#f0f0f0"))
    canvas.rect(0, 0, w, 1.2*cm, fill=1, stroke=0)
    canvas.setFillColor(MID_GREY)
    canvas.rect(0, 1.2*cm, w, 0.05*cm, fill=1, stroke=0)
    canvas.setFont("Helvetica-Oblique", 7)
    canvas.setFillColor(colors.HexColor("#555555"))
    canvas.drawCentredString(w/2, 0.45*cm,
        "Clinical Psychiatric Prognostic Checklist v2  |  Clinical use only - not a diagnostic tool  |  Biomarkers are research-grade; confirm availability in your institution")
    canvas.drawRightString(w - 1.5*cm, 0.45*cm, f"Page {doc.page}")
    canvas.restoreState()


# ── BUILD ─────────────────────────────────────────────────────────────────────
doc = SimpleDocTemplate(OUTPUT, pagesize=A4,
    topMargin=3.2*cm, bottomMargin=1.6*cm,
    leftMargin=1.5*cm, rightMargin=1.5*cm,
    title="Psychiatric Prognostic Checklist v2",
    author="Orris AI",
    subject="Clinical Prognostic Factors + Biomarkers in Adult Psychiatric Disorders")

story = []

# Title block
title_data = [
    [Paragraph("Clinical Prognostic Checklist  (v2 + Biomarkers)", title_style)],
    [Paragraph("Adult Psychiatric Disorders  ·  Good &amp; Poor Prognostic Factors  ·  Biomarker Indicators", subtitle_style)],
    [Paragraph("Based on systematic reviews, meta-analyses and consensus statements (2010–2025)", subtitle_style)],
]
title_table = Table(title_data, colWidths=[18.5*cm])
title_table.setStyle(TableStyle([
    ("BACKGROUND", (0,0), (-1,-1), DARK_NAVY),
    ("TOPPADDING", (0,0), (-1,-1), 4), ("BOTTOMPADDING", (0,0), (-1,-1), 4),
    ("LEFTPADDING", (0,0), (-1,-1), 8),
]))
story.append(title_table)
story.append(Spacer(1, 0.25*cm))

# How to use
how_data = [[Paragraph(
    "<b>HOW TO USE:</b>  Tick applicable clinical factors (☐) and note relevant biomarkers (◇) for your patient. "
    "A cluster of poor prognostic factors plus unfavourable biomarker profile should prompt intensive monitoring, "
    "early treatment escalation, and proactive social support planning. "
    "<b>Biomarker availability varies by institution</b> - use where accessible. "
    "This checklist is a clinical aide and does not replace clinical judgement.",
    note_style)]]
how_table = Table(how_data, colWidths=[18.5*cm])
how_table.setStyle(TableStyle([
    ("BACKGROUND", (0,0), (-1,-1), colors.HexColor("#fffbe6")),
    ("BOX", (0,0), (-1,-1), 0.5, colors.HexColor("#e0c050")),
    ("TOPPADDING", (0,0), (-1,-1), 6), ("BOTTOMPADDING", (0,0), (-1,-1), 6),
    ("LEFTPADDING", (0,0), (-1,-1), 8), ("RIGHTPADDING", (0,0), (-1,-1), 8),
]))
story.append(how_table)
story.append(Spacer(1, 0.3*cm))

# Biomarker caveat box
caveat_data = [[Paragraph(
    "⚠️  <b>BIOMARKER CAVEAT:</b>  Psychiatric biomarkers are mostly in early validation stages. "
    "Most are not yet approved as standalone clinical tools. "
    "BDNF, CRP, cortisol, and thyroid function are readily available. "
    "Neuroimaging (MRI, fMRI, PET) and genetic markers require specialist access. "
    "Evidence strength is variable across disorders - strongest for schizophrenia, MDD and PTSD.",
    warning_style)]]
caveat_table = Table(caveat_data, colWidths=[18.5*cm])
caveat_table.setStyle(TableStyle([
    ("BACKGROUND", (0,0), (-1,-1), colors.HexColor("#fdf0e0")),
    ("BOX", (0,0), (-1,-1), 0.5, colors.HexColor("#c07030")),
    ("TOPPADDING", (0,0), (-1,-1), 5), ("BOTTOMPADDING", (0,0), (-1,-1), 5),
    ("LEFTPADDING", (0,0), (-1,-1), 8), ("RIGHTPADDING", (0,0), (-1,-1), 8),
]))
story.append(caveat_table)
story.append(Spacer(1, 0.3*cm))

# Transdiagnostic section
td_header_data = [[Paragraph("  🌐  TRANSDIAGNOSTIC FACTORS  (apply across ALL disorders)", section_style)]]
td_header_table = Table(td_header_data, colWidths=[18.5*cm])
td_header_table.setStyle(TableStyle([
    ("BACKGROUND", (0,0), (-1,-1), colors.HexColor("#4a235a")),
    ("TOPPADDING", (0,0), (-1,-1), 7), ("BOTTOMPADDING", (0,0), (-1,-1), 7),
    ("LEFTPADDING", (0,0), (-1,-1), 6),
    ("BOX", (0,0), (-1,-1), 0.5, colors.HexColor("#2d1440")),
]))
story.append(td_header_table)

# TD clinical columns
td_col_hdr = [[Paragraph("✅  GOOD (across disorders)", col_header_good),
               Paragraph("⚠️  POOR (across disorders)", col_header_bad)]]
td_col_hdr_table = Table(td_col_hdr, colWidths=[9.25*cm, 9.25*cm])
td_col_hdr_table.setStyle(TableStyle([
    ("BACKGROUND", (0,0), (0,0), GOOD_GREEN), ("BACKGROUND", (1,0), (1,0), BAD_RED),
    ("TOPPADDING", (0,0), (-1,-1), 5), ("BOTTOMPADDING", (0,0), (-1,-1), 5),
    ("LEFTPADDING", (0,0), (-1,-1), 4),
]))
story.append(td_col_hdr_table)

max_td = max(len(transdiag_good), len(transdiag_bad))
td_data = []
for i in range(max_td):
    g = checkbox(transdiag_good[i], item_style) if i < len(transdiag_good) else Paragraph("", item_style)
    b = checkbox(transdiag_bad[i], item_style) if i < len(transdiag_bad) else Paragraph("", item_style)
    td_data.append([g, b])
td_table = Table(td_data, colWidths=[9.25*cm, 9.25*cm])
ts_td = TableStyle([
    ("LEFTPADDING",(0,0),(-1,-1),5), ("RIGHTPADDING",(0,0),(-1,-1),5),
    ("TOPPADDING",(0,0),(-1,-1),3), ("BOTTOMPADDING",(0,0),(-1,-1),3),
    ("VALIGN",(0,0),(-1,-1),"TOP"),
    ("LINEAFTER",(0,0),(0,-1),0.5,MID_GREY),
    ("BOX",(0,0),(-1,-1),0.5,MID_GREY),
])
for i in range(max_td):
    ts_td.add("BACKGROUND",(0,i),(0,i), colors.HexColor("#f2fbf2") if i%2==0 else colors.HexColor("#e8f8e8"))
    ts_td.add("BACKGROUND",(1,i),(1,i), colors.HexColor("#fef5f5") if i%2==0 else colors.HexColor("#fdeaea"))
td_table.setStyle(ts_td)
story.append(td_table)

# TD biomarker columns
td_bio_hdr = [[Paragraph("  🔬  TRANSDIAGNOSTIC BIOMARKERS", ParagraphStyle("TDBioHdr",
    parent=styles["Normal"], fontSize=9, textColor=WHITE, fontName="Helvetica-Bold"))]]
td_bio_hdr_table = Table(td_bio_hdr, colWidths=[18.5*cm])
td_bio_hdr_table.setStyle(TableStyle([
    ("BACKGROUND", (0,0), (-1,-1), BIOMARKER_HDR),
    ("TOPPADDING", (0,0), (-1,-1), 5), ("BOTTOMPADDING", (0,0), (-1,-1), 5),
    ("LEFTPADDING", (0,0), (-1,-1), 6),
]))
story.append(td_bio_hdr_table)

td_bio_col = [[Paragraph("🔵  FAVOURABLE", bio_col_good), Paragraph("🔴  UNFAVOURABLE", bio_col_bad)]]
td_bio_col_table = Table(td_bio_col, colWidths=[9.25*cm, 9.25*cm])
td_bio_col_table.setStyle(TableStyle([
    ("BACKGROUND",(0,0),(0,0),BIO_GOOD_BG), ("BACKGROUND",(1,0),(1,0),BIO_BAD_BG),
    ("TOPPADDING",(0,0),(-1,-1),4), ("BOTTOMPADDING",(0,0),(-1,-1),4),
    ("LEFTPADDING",(0,0),(-1,-1),4),
]))
story.append(td_bio_col_table)

max_tdb = max(len(transdiag_bio_good), len(transdiag_bio_bad))
tdb_data = []
for i in range(max_tdb):
    g = diamond(transdiag_bio_good[i], bio_item_style) if i < len(transdiag_bio_good) else Paragraph("", bio_item_style)
    b = diamond(transdiag_bio_bad[i], bio_item_style) if i < len(transdiag_bio_bad) else Paragraph("", bio_item_style)
    tdb_data.append([g, b])
tdb_table = Table(tdb_data, colWidths=[9.25*cm, 9.25*cm])
ts_tdb = TableStyle([
    ("LEFTPADDING",(0,0),(-1,-1),5), ("RIGHTPADDING",(0,0),(-1,-1),5),
    ("TOPPADDING",(0,0),(-1,-1),3), ("BOTTOMPADDING",(0,0),(-1,-1),3),
    ("VALIGN",(0,0),(-1,-1),"TOP"),
    ("LINEAFTER",(0,0),(0,-1),0.5,MID_GREY),
    ("BOX",(0,0),(-1,-1),0.5,MID_GREY),
])
for i in range(max_tdb):
    ts_tdb.add("BACKGROUND",(0,i),(0,i), colors.HexColor("#eaf4fb") if i%2==0 else colors.HexColor("#dceef8"))
    ts_tdb.add("BACKGROUND",(1,i),(1,i), colors.HexColor("#fef0e0") if i%2==0 else colors.HexColor("#fde5c8"))
tdb_table.setStyle(ts_tdb)
story.append(tdb_table)

td_src = [[Paragraph("Source: Solmi et al. 2023 (PMID 37957292); Liberona et al. 2024 (PMID 39456983); Candidate biomarkers in psychiatry (PMC10168176)", source_style)]]
td_src_table = Table(td_src, colWidths=[18.5*cm])
td_src_table.setStyle(TableStyle([
    ("BACKGROUND",(0,0),(-1,-1), colors.HexColor("#f0f0f0")),
    ("TOPPADDING",(0,0),(-1,-1),3), ("BOTTOMPADDING",(0,0),(-1,-1),3),
    ("LEFTPADDING",(0,0),(-1,-1),6),
    ("BOX",(0,0),(-1,-1),0.3,MID_GREY),
]))
story.append(td_src_table)
story.append(Spacer(1, 0.4*cm))

# Disorder sections
for d in disorders:
    section_elements = build_disorder_section(
        d["name"], d["good"], d["bad"],
        d["bio_good"], d["bio_bad"],
        d["source"], d["bio_source"], d["emoji"]
    )
    story.extend(section_elements)

# Notes page
story.append(PageBreak())
notes_header = [[Paragraph("  📋  CLINICAL NOTES", section_style)]]
notes_hdr_table = Table(notes_header, colWidths=[18.5*cm])
notes_hdr_table.setStyle(TableStyle([
    ("BACKGROUND",(0,0),(-1,-1),HEADER_BG),
    ("TOPPADDING",(0,0),(-1,-1),7), ("BOTTOMPADDING",(0,0),(-1,-1),7),
    ("LEFTPADDING",(0,0),(-1,-1),6),
]))
story.append(notes_hdr_table)
story.append(Spacer(1, 0.3*cm))
for i in range(22):
    story.append(Paragraph("", item_style))
    story.append(HRFlowable(width="100%", thickness=0.4, color=MID_GREY))
    story.append(Spacer(1, 0.45*cm))

# References
story.append(Spacer(1, 0.4*cm))
ref_header = [[Paragraph("  📚  KEY REFERENCES", section_style)]]
ref_hdr_table = Table(ref_header, colWidths=[18.5*cm])
ref_hdr_table.setStyle(TableStyle([
    ("BACKGROUND",(0,0),(-1,-1),HEADER_BG),
    ("TOPPADDING",(0,0),(-1,-1),6), ("BOTTOMPADDING",(0,0),(-1,-1),6),
    ("LEFTPADDING",(0,0),(-1,-1),6),
]))
story.append(ref_hdr_table)
story.append(Spacer(1, 0.2*cm))

refs = [
    "1. Solmi M, et al. (2023). Umbrella review of candidate predictors of response, remission, recovery, and relapse across mental disorders. Molecular Psychiatry. PMID: 37957292",
    "2. Molstrom IM, et al. (2022). The prognosis of schizophrenia: systematic review & meta-analysis of 20-year follow-up studies. Schizophrenia Research. PMID: 36417817",
    "3. Treuer T & Tohen M. (2010). Predicting the course and outcome of bipolar disorder: a review. European Psychiatry. PMID: 20444581",
    "4. Scott K, et al. (2025). Prediction of Treatment Outcome in Bipolar Disorder. Biological Psychiatry. PMID: 39954922",
    "5. Sharma E, Thennarasu K, Reddy YCJ. (2014). Long-term outcome of OCD in adults: a meta-analysis. J Clin Psychiatry. PMID: 25295427",
    "6. Sharma E & Math SB. (2019). Course and outcome of OCD. Indian J Psychiatry. PMID: 30745676",
    "7. Hazari N, et al. (2016). Predictors of response to serotonin reuptake inhibitors in OCD. Expert Rev Neurother. PMID: 27282021",
    "8. Keyan D, et al. (2024). Predictors of response to trauma-focused psychotherapy for PTSD. Psychological Bulletin. PMID: 38884956",
    "9. Wang J, et al. (2018). Loneliness, social support and outcomes in mental health problems. BMC Psychiatry. PMID: 29843662",
    "10. Liberona A, et al. (2024). BDNF as a predictor of treatment response in schizophrenia & bipolar disorder. Int J Mol Sci. PMID: 39456983",
    "11. Bandelow B, et al. (2016). Biological markers for anxiety disorders, OCD & PTSD - Part I: Neuroimaging & genetics. World J Biol Psychiatry. PMID: 27403679",
    "12. Bandelow B, et al. (2017). Biological markers for anxiety disorders, OCD & PTSD - Part II: Neurochemistry, neurophysiology & neurocognition. World J Biol Psychiatry. PMID: 27419272",
    "13. Candidate biomarkers in psychiatric disorders: state of the field (2023). PMC10168176",
    "14. Sowerby C, et al. (2026). Neuroimaging & neurophysiologic biomarkers for diagnosis & prognosis of depressive disorders, bipolar disorder, anxiety disorders, OCD, PTSD. BMC Psychiatry.",
]
for ref in refs:
    story.append(Paragraph(ref, source_style))
    story.append(Spacer(1, 0.12*cm))

doc.build(story, onFirstPage=add_page_background, onLaterPages=add_page_background)
print("PDF v2 created:", OUTPUT)
Running Command

cd /tmp/workspace/psych-checklist && python build_checklist_v2.py 2>&1

Running Command

ls -lh /tmp/workspace/psych-checklist/

The updated v2 checklist is ready:

Psychiatric Prognostic Checklist v2

PDF Document · PDF


Here is a summary of what was added and the evidence behind it:

Biomarkers Added Per Disorder

🧠 Schizophrenia

FavourableUnfavourable
Higher baseline BDNF (→ antipsychotic response)Low serum BDNF
BDNF Val/Val genotypeElevated striatal functional abnormalities (FSA)
Intact striatal connectivity (SCI) on fMRIReduced caudate volume on MRI
Low CRP / IL-6Elevated CRP / IL-6
Normal ACC and PFC volumesHypofrontality on fMRI
Low polygenic risk scoreElevated homocysteine / low folate/B12
Source: Liberona et al. 2024 (PMID 39456983)

🔄 Bipolar Disorder

Notable: Polygenic risk score for schizophrenia being low is a good prognostic marker for lithium response; abnormal lithium erythrocyte:plasma ratio predicts non-response.

🌧️ MDD

High CRP / IL-6 at baseline specifically predicts poor SSRI response - this is clinically important as it may flag patients who need an anti-inflammatory augmentation strategy rather than standard antidepressants. Elevated anterior cingulate theta activity on EEG (where available) is a good predictor.

🔁 OCD

Elevated CSF/blood 5-HIAA at baseline predicts better clomipramine response - an old but reliable finding. Caudate hyperactivity on fMRI normalises with effective treatment and can serve as a treatment response marker.

⚡ PTSD

Hippocampal volume is the strongest structural biomarker - smaller volume = worse fear extinction capacity. HPA axis pattern is paradoxically reversed (low cortisol / enhanced glucocorticoid suppression) unlike depression.

😰 Anxiety Disorders

Error-related negativity (ERN) on EEG is a specifically validated biomarker for GAD - elevated ERN predicts GAD onset and severity. 5-HTTLPR s/s genotype is associated with anxiety vulnerability.

Important Caveat

Most of these biomarkers are in early-to-intermediate validation stages. The ones routinely available in clinical practice are:
  • Blood tests: CRP, IL-6, BDNF (increasingly available), cortisol, TSH, folate, B12, homocysteine
  • Neuroimaging: Structural MRI (hippocampal/ACC/caudate volumes) in specialist settings
  • EEG: ERN and theta power in neurophysiology labs
  • Genetics: BDNF Val66Met, 5-HTTLPR, polygenic risk scores (research settings)

Network Failure,Behavioral Sign,Patient Subjective Report SN Hijack,"Startle response, dilated pupils, scanning.","""Everything feels like an emergency.""" DMN Hyper-activity,"Vacant stare, slow response to questions.","""I can't turn off the 'What If' movie in my head.""" ECN Decohesion,Repeating a task they just finished.,"""I know it’s clean, but I can’t stop scrubbing.""" NE Overload,"Fidgeting, ""punding,"" rapid-fire speech.","""I feel 'wired' and 'stuck' at the same time.""" This is a clinical questionnaire aimed at assessing dysfunction in the triple network model in psychiatric disorders. Can we check its validity, so that it can further be updated in the clinical interview pdf we had created

Good framing. Let me verify this against the triple network literature before touching the PDF.
Searching PubMed

triple network model salience default mode executive control psychiatric disorders review

Searching PubMed

salience network hyperactivity default mode network rumination executive control dysfunction psychiatric

Searching the Web

triple network model salience network default mode executive control network psychiatric disorders clinical signs symptoms review Menon 2023

Searching PubMed

salience network default mode network frontoparietal network psychopathology Menon

Searching PubMed

norepinephrine overload locus coeruleus punding wired stuck psychiatric hyperarousal

Reading Article

PMID: 36702660

Searching PubMed

locus coeruleus norepinephrine hyperarousal PTSD anxiety wired clinical symptoms review

I have enough evidence to do a thorough critical appraisal. Let me now do that and then update the PDF.Now let me build the updated PDF incorporating both the critical appraisal findings and the corrected/expanded triple network section.
Writing File

~/psych-checklist/build_triple_network_module.py

"""
Triple Network Module - appended to the existing Psychiatric Prognostic Checklist v2 PDF.
Generates a standalone section PDF that covers:
  - Critical appraisal of the original 4-row questionnaire
  - Validated & expanded triple network clinical screening tool
  - Guidance on interpretation
"""
from reportlab.lib.pagesizes import A4
from reportlab.lib import colors
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
from reportlab.lib.units import cm
from reportlab.platypus import (
    SimpleDocTemplate, Paragraph, Spacer, Table, TableStyle,
    HRFlowable, PageBreak
)
from reportlab.lib.enums import TA_LEFT, TA_CENTER, TA_JUSTIFY
from pypdf import PdfWriter, PdfReader

OUTPUT_MODULE = "/tmp/workspace/psych-checklist/triple_network_module.pdf"
OUTPUT_FINAL  = "/tmp/workspace/psych-checklist/Psychiatric_Prognostic_Checklist_v3.pdf"

# ── Colour palette ────────────────────────────────────────────────────────────
DARK_NAVY  = colors.HexColor("#1a2b4a")
TEAL       = colors.HexColor("#007b7f")
MID_GREY   = colors.HexColor("#cccccc")
WHITE      = colors.white

SN_COL     = colors.HexColor("#c0392b")   # red  - Salience Network
SN_LIGHT   = colors.HexColor("#fadbd8")
DMN_COL    = colors.HexColor("#1a5276")   # blue - Default Mode Network
DMN_LIGHT  = colors.HexColor("#d6eaf8")
ECN_COL    = colors.HexColor("#1e8449")   # green - Executive Control Network
ECN_LIGHT  = colors.HexColor("#d5f5e3")
NE_COL     = colors.HexColor("#7d6608")   # amber - NE/LC system
NE_LIGHT   = colors.HexColor("#fef9e7")

APPRAISAL_OK    = colors.HexColor("#d5f5e3")
APPRAISAL_WARN  = colors.HexColor("#fef9e7")
APPRAISAL_ISSUE = colors.HexColor("#fadbd8")

# ── Styles ────────────────────────────────────────────────────────────────────
styles = getSampleStyleSheet()

def S(name, **kw):
    base = kw.pop("parent", styles["Normal"])
    return ParagraphStyle(name, parent=base, **kw)

page_title  = S("PT",  fontSize=18, textColor=WHITE, alignment=TA_CENTER,
                fontName="Helvetica-Bold")
page_sub    = S("PS",  fontSize=9,  textColor=colors.HexColor("#c8d8f0"),
                alignment=TA_CENTER, fontName="Helvetica-Oblique")
sec_hdr     = S("SH",  fontSize=12, textColor=WHITE, fontName="Helvetica-Bold",
                leftIndent=6)
body        = S("BD",  fontSize=8.5, leading=12, fontName="Helvetica")
bold_body   = S("BB",  fontSize=8.5, leading=12, fontName="Helvetica-Bold")
italic_body = S("IB",  fontSize=8.5, leading=12, fontName="Helvetica-Oblique",
                textColor=colors.HexColor("#444444"))
small       = S("SM",  fontSize=7.5, leading=10, fontName="Helvetica",
                textColor=colors.HexColor("#555555"))
small_bold  = S("SMB", fontSize=7.5, leading=10, fontName="Helvetica-Bold")
note        = S("NT",  fontSize=8,   leading=11, fontName="Helvetica",
                textColor=colors.HexColor("#333333"), leftIndent=6)
warn_style  = S("WS",  fontSize=7.5, leading=10, fontName="Helvetica-Oblique",
                textColor=colors.HexColor("#7b3a10"), leftIndent=4)
src_style   = S("SS",  fontSize=7,   leading=10, fontName="Helvetica-Oblique",
                textColor=colors.HexColor("#555555"), leftIndent=4)
network_label = S("NL", fontSize=9, fontName="Helvetica-Bold",
                  textColor=WHITE, alignment=TA_CENTER)
col_hdr     = S("CH",  fontSize=8.5, fontName="Helvetica-Bold",
                alignment=TA_CENTER, textColor=DARK_NAVY)

def checkbox(t): return Paragraph(f"☐  {t}", body)
def check_b(t):  return Paragraph(f"☐  <b>{t}</b>", body)
def bullet(t):   return Paragraph(f"•  {t}", body)
def italic(t):   return Paragraph(f"<i>{t}</i>", italic_body)

def section_bar(text, bg_color):
    t = Table([[Paragraph(f"  {text}", sec_hdr)]], colWidths=[18.5*cm])
    t.setStyle(TableStyle([
        ("BACKGROUND",(0,0),(-1,-1), bg_color),
        ("TOPPADDING",(0,0),(-1,-1),7),
        ("BOTTOMPADDING",(0,0),(-1,-1),7),
        ("LEFTPADDING",(0,0),(-1,-1),6),
        ("BOX",(0,0),(-1,-1),0.5,DARK_NAVY),
    ]))
    return t

def add_bg(canvas, doc):
    canvas.saveState()
    w, h = A4
    canvas.setFillColor(DARK_NAVY)
    canvas.rect(0, h-2.8*cm, w, 2.8*cm, fill=1, stroke=0)
    canvas.setFillColor(TEAL)
    canvas.rect(0, h-3.0*cm, w, 0.22*cm, fill=1, stroke=0)
    canvas.setFillColor(colors.HexColor("#f0f0f0"))
    canvas.rect(0, 0, w, 1.2*cm, fill=1, stroke=0)
    canvas.setFillColor(MID_GREY)
    canvas.rect(0, 1.2*cm, w, 0.05*cm, fill=1, stroke=0)
    canvas.setFont("Helvetica-Oblique", 7)
    canvas.setFillColor(colors.HexColor("#555555"))
    canvas.drawCentredString(w/2, 0.42*cm,
        "Triple Network Clinical Screening Module  |  Research-informed; not validated as a standalone diagnostic instrument")
    canvas.drawRightString(w-1.5*cm, 0.42*cm, f"Page {doc.page}")
    canvas.restoreState()

# ═══════════════════════════════════════════════════════════════════════════════
# CONTENT DATA
# ═══════════════════════════════════════════════════════════════════════════════

# Critical appraisal rows: [network, verdict, verdict_bg, issue, fix]
appraisal_rows = [
    {
        "network": "SN Hijack",
        "original_signs": "Startle response, dilated pupils, scanning",
        "original_report": '"Everything feels like an emergency."',
        "verdict": "✅ VALID - with minor gaps",
        "verdict_bg": APPRAISAL_OK,
        "appraisal": (
            "SN (insula + dACC) hyperactivation is well-evidenced in PTSD, anxiety, and schizophrenia "
            "(Menon 2011; Menon et al. 2023). Startle response and hypervigilant scanning accurately "
            "reflect SN-driven threat-detection bias. Dilated pupils are a valid sympathetic/LC-NE "
            "correlate of SN overactivation. The subjective report is ecologically valid and aligns "
            "with interoceptive alarm signalling.\n"
            "GAP: Does not distinguish SN hyperactivity from pure LC-NE hyperarousal (which overlaps). "
            "Also missing: intrusive re-experiencing (PTSD), and aberrant salience "
            "(schizophrenia - assigning threat to neutral stimuli)."
        ),
        "additions": [
            "Add: misattribution of salience to neutral stimuli (aberrant salience - schizophrenia)",
            "Add: intrusive memories / flashbacks (PTSD context)",
            "Add: tachycardia / palpitations as observable SN-autonomic sign",
            "Add: heightened interoceptive awareness (\"my heart is pounding for no reason\")",
        ]
    },
    {
        "network": "DMN Hyperactivity",
        "original_signs": "Vacant stare, slow response to questions",
        "original_report": '"I can\'t turn off the \'What If\' movie in my head."',
        "verdict": "⚠️ PARTIALLY VALID - sign is inaccurate",
        "verdict_bg": APPRAISAL_WARN,
        "appraisal": (
            "DMN hyperactivity (persistent self-referential, past/future-oriented thought) is robustly "
            "evidenced in depression and anxiety (Buckner et al. 2008; Mulders et al. 2015). "
            "The subjective report is excellent - future-oriented worry / rumination is the cardinal "
            "clinical expression of DMN hyperactivation.\n"
            "PROBLEM: 'Vacant stare, slow response to questions' is more characteristic of "
            "dissociation or psychomotor retardation (depression) than active DMN hyperactivity. "
            "Pure DMN hyperactivity typically presents as mental over-engagement - the patient is "
            "internally busy, not blank. A vacant stare with slow response maps better to "
            "depressive psychomotor slowing or derealization, not DMN ruminative overdrive."
        ),
        "additions": [
            "CORRECT SIGN: Replace 'vacant stare' with 'appears internally preoccupied; interruptible but quickly returns to internal train of thought'",
            "Add: self-referential negative cognition ('everything bad is my fault')",
            "Add: difficulty engaging with present-moment tasks ('I keep drifting off')",
            "Add: autobiographical memory over-retrieval of negative past events",
            "Add: perseverative worry about future scenarios (GAD phenotype)",
            "Keep: slow response to questions - but label it as 'attentional capture by internal narrative'",
        ]
    },
    {
        "network": "ECN Decohesion",
        "original_signs": "Repeating a task they just finished",
        "original_report": '"I know it\'s clean, but I can\'t stop scrubbing."',
        "verdict": "⚠️ PARTIALLY VALID - sign is disorder-specific, not ECN-specific",
        "verdict_bg": APPRAISAL_WARN,
        "appraisal": (
            "ECN (dlPFC + lateral PPC) dysfunction is well-evidenced across multiple disorders - "
            "impaired working memory, cognitive flexibility, and inhibitory control are the core deficits "
            "(Menon 2011). The subjective report maps well to OCD specifically (preserved insight with "
            "compulsive repetition = intact verbal cognition but impaired ECN inhibitory override).\n"
            "PROBLEM: 'Repeating a task they just finished' is highly OCD-specific and will not "
            "sensitively capture ECN decohesion in other contexts (schizophrenia = cognitive "
            "disorganisation; bipolar = executive dysfunction; ADHD = working memory failures). "
            "The term 'decohesion' is also non-standard - 'dysfunction' or 'failure' is preferred."
        ),
        "additions": [
            "BROADEN SIGNS: Add 'difficulty holding two things in mind simultaneously (working memory failure)'",
            "Add: task-switching failure ('gets stuck on one topic in conversation')",
            "Add: inability to inhibit prepotent responses ('acts before thinking')",
            "Add: cognitive rigidity / perseveration (beyond OCD - also schizophrenia, severe depression)",
            "Retain OCD example but label it: 'ECN inhibitory override failure (OCD subtype)'",
            "Add schizophrenia subtype: disorganised speech, thought tangentiality",
        ]
    },
    {
        "network": "NE Overload",
        "original_signs": 'Fidgeting, "punding," rapid-fire speech',
        "original_report": '"I feel \'wired\' and \'stuck\' at the same time."',
        "verdict": "⚠️ CONCEPTUAL ISSUE - NE is not a 'network' in triple network model",
        "verdict_bg": APPRAISAL_ISSUE,
        "appraisal": (
            "CORE PROBLEM: The triple network model (Menon 2011) comprises three cortical resting-state "
            "networks: Salience Network (SN), Default Mode Network (DMN), and Central/Frontal-Parietal "
            "Executive Network (ECN/FPN). Norepinephrine (NE) / locus coeruleus (LC) is a subcortical "
            "neuromodulatory system - not a resting-state network. Including it as a 4th 'network' is "
            "neuroscientifically inaccurate and will confuse clinicians familiar with the literature.\n"
            "HOWEVER: The clinical content is valid. LC-NE hyperactivation IS a biologically distinct "
            "and important clinical state. 'Punding' is a valid dopaminergic (not NE) sign - seen in "
            "stimulant abuse and Parkinson's. Rapid-fire speech + fidgeting + 'wired and stuck' more "
            "accurately reflects combined NE + dopamine dysregulation.\n"
            "RECOMMENDATION: Relabel this row as 'LC-NE Hyperactivation / Neuromodulatory Dysregulation' "
            "and present it as a modifier/subcomponent of SN dysfunction rather than an independent network."
        ),
        "additions": [
            "RELABEL: 'LC-NE Hyperarousal (Neuromodulatory Modifier)' - not a 4th network",
            "Distinguish: NE hyperactivation (hypervigilance, startle, autonomic) from DA dysregulation (punding, compulsive seeking)",
            "Remove 'punding' from NE row - reassign to dopaminergic/reward system dysfunction",
            "Add: pressure of speech, psychomotor agitation (mania subtype)",
            "Add: insomnia despite exhaustion as a specific NE hyperactivation sign",
            "Add: 'I feel like I'm running on adrenaline but can't calm down'",
            "Consider adding a 5th row: Reward/Mesolimbic Dysregulation (anhedonia vs. hyperdopaminergia)",
        ]
    },
]

# ── Validated & expanded network rows ────────────────────────────────────────
network_rows = [
    {
        "network": "SN  Hijack\n(Salience Network\nHyperactivation)",
        "color": SN_COL,
        "light": SN_LIGHT,
        "anchors": "Anterior insula + dACC",
        "disorders": "PTSD, GAD, Panic disorder, Schizophrenia (aberrant salience)",
        "observable_signs": [
            "Exaggerated startle response",
            "Dilated pupils / tachycardia on exam",
            "Hypervigilant environmental scanning",
            "Freezing or flinching to neutral stimuli",
            "Intrusive re-experiencing episodes (PTSD)",
        ],
        "patient_reports": [
            '"Everything feels like an emergency."',
            '"My heart races for no reason."',
            '"I keep thinking that stranger is threatening me."',
            '"I can\'t relax even when I know I\'m safe."',
        ],
        "note": "Distinguish from LC-NE hyperarousal (see modifier row below). In schizophrenia, SN hijack = aberrant salience attribution to neutral stimuli.",
        "source": "Menon 2011 (Trends Cogn Sci); Menon et al. 2023 (Biol Psychiatry PMID 36702660)"
    },
    {
        "network": "DMN  Hyperactivity\n(Default Mode Network\nOverengagement)",
        "color": DMN_COL,
        "light": DMN_LIGHT,
        "anchors": "mPFC + PCC + angular gyrus",
        "disorders": "MDD, GAD, Rumination-predominant OCD, Bipolar depression",
        "observable_signs": [
            "Internally preoccupied; easily returns to internal train of thought when distracted",
            "Slow or delayed response to clinician questions (attentional capture)",
            "Flat affect with preserved verbal fluency about self-related themes",
            "Excessive self-referential narrative in interview",
        ],
        "patient_reports": [
            '"I can\'t turn off the \'What If\' movie in my head."',
            '"I keep replaying what went wrong last year."',
            '"Everything bad always comes back to something I did."',
            '"I drift off during tasks - my mind just goes back to worrying."',
        ],
        "note": "NOTE: A vacant stare with slow responses more accurately reflects psychomotor retardation or dissociation, NOT active DMN hyperactivity. DMN over-engagement = internal busyness, not blankness.",
        "source": "Buckner et al. 2008 (Ann NY Acad Sci); Mulders et al. 2015 (Neurosci Biobehav Rev); PMC10524518"
    },
    {
        "network": "ECN  Decohesion\n(Executive Control Network\nDysfunction)",
        "color": ECN_COL,
        "light": ECN_LIGHT,
        "anchors": "dlPFC + lateral PPC",
        "disorders": "OCD, Schizophrenia, ADHD, Bipolar disorder, Severe MDD",
        "observable_signs": [
            "OCD subtype: Repeating a completed task (inhibitory override failure)",
            "Cognitive subtype: Cannot hold two concepts in mind simultaneously",
            "Rigidity subtype: Gets stuck on one conversational topic; cannot task-switch",
            "Impulsivity subtype: Acts before thinking; poor prepotent response inhibition",
            "Disorganisation subtype: Tangential or disorganised speech (schizophrenia)",
        ],
        "patient_reports": [
            '"I know it\'s clean, but I can\'t stop scrubbing." (OCD)',
            '"I started three things and can\'t finish any of them."',
            '"I keep losing my train of thought mid-sentence."',
            '"I say things before I realise I shouldn\'t."',
        ],
        "note": "The term 'decohesion' is non-standard. Use 'ECN dysfunction' or 'ECN failure'. Subtypes differ by disorder: inhibitory failure (OCD), working memory failure (ADHD/schizophrenia), cognitive rigidity (schizophrenia/severe depression).",
        "source": "Menon 2011 (Trends Cogn Sci); Menon et al. 2023 (PMID 36702660)"
    },
    {
        "network": "LC-NE  Hyperarousal\n(Neuromodulatory Modifier -\nnot a 4th network)",
        "color": NE_COL,
        "light": NE_LIGHT,
        "anchors": "Locus coeruleus → NE release across cortex",
        "disorders": "PTSD, Panic disorder, Mania, Stimulant intoxication/withdrawal",
        "observable_signs": [
            "Motor restlessness / fidgeting",
            "Pressured or rapid-fire speech",
            "Psychomotor agitation (cannot stay seated)",
            "Insomnia despite clear exhaustion",
            "Exaggerated startle (overlaps with SN hijack - distinguish by autonomic prominence)",
        ],
        "patient_reports": [
            '"I feel \'wired\' and \'stuck\' at the same time."',
            '"I\'m exhausted but I can\'t slow down."',
            '"My body is on high alert even when my mind knows it\'s fine."',
            '"I haven\'t slept in days but I\'m not tired."',
        ],
        "note": "⚠️ IMPORTANT: LC-NE is a neuromodulatory system, not a resting-state network. It modulates SN sensitivity and amplifies SN hijack. 'Punding' is a dopaminergic (not NE) sign - reassign to reward/mesolimbic dysfunction if included. Present this row as a modifier of SN, not an independent network.",
        "source": "Aston-Jones & Cohen 2005 (Annu Rev Neurosci); Sara 2009 (Nat Rev Neurosci)"
    },
]

# ═══════════════════════════════════════════════════════════════════════════════
# BUILD
# ═══════════════════════════════════════════════════════════════════════════════
doc = SimpleDocTemplate(OUTPUT_MODULE, pagesize=A4,
    topMargin=3.2*cm, bottomMargin=1.6*cm,
    leftMargin=1.5*cm, rightMargin=1.5*cm,
    title="Triple Network Clinical Screening Module",
    author="Orris AI")

story = []

# ── Title ──
title_block = [
    [Paragraph("Triple Network Clinical Screening Module", page_title)],
    [Paragraph("Critical Appraisal · Validated Expansion · Clinical Interview Tool", page_sub)],
    [Paragraph("Appended to: Psychiatric Prognostic Checklist v2  |  Based on Menon 2011 triple network model", page_sub)],
]
tb = Table(title_block, colWidths=[18.5*cm])
tb.setStyle(TableStyle([
    ("BACKGROUND",(0,0),(-1,-1),DARK_NAVY),
    ("TOPPADDING",(0,0),(-1,-1),4),("BOTTOMPADDING",(0,0),(-1,-1),4),
    ("LEFTPADDING",(0,0),(-1,-1),8),
]))
story.append(tb)
story.append(Spacer(1,0.25*cm))

# ── Background box ──
bg_data = [[Paragraph(
    "<b>TRIPLE NETWORK MODEL (Menon, 2011):</b>  The brain's cognitive architecture can be described through "
    "three large-scale resting-state networks: (1) the <b>Salience Network (SN)</b> - insula + dACC - "
    "detects and filters behaviourally relevant stimuli and acts as the switch between the other two; "
    "(2) the <b>Default Mode Network (DMN)</b> - mPFC + PCC + angular gyrus - active during self-referential, "
    "internally focused cognition; (3) the <b>Executive Control Network (ECN/FPN)</b> - dlPFC + lateral PPC - "
    "governs working memory, cognitive flexibility, and inhibitory control. "
    "Dysfunction in these networks and their dynamic interactions underlies the cognitive and emotional "
    "abnormalities seen across most major psychiatric disorders.",
    note)]]
bg_box = Table(bg_data, colWidths=[18.5*cm])
bg_box.setStyle(TableStyle([
    ("BACKGROUND",(0,0),(-1,-1),colors.HexColor("#eaf4fb")),
    ("BOX",(0,0),(-1,-1),0.5,colors.HexColor("#1a6b8a")),
    ("TOPPADDING",(0,0),(-1,-1),6),("BOTTOMPADDING",(0,0),(-1,-1),6),
    ("LEFTPADDING",(0,0),(-1,-1),8),("RIGHTPADDING",(0,0),(-1,-1),8),
]))
story.append(bg_box)
story.append(Spacer(1,0.3*cm))

# ══════════════════════════════════════════════════════════════════
# SECTION 1: CRITICAL APPRAISAL
# ══════════════════════════════════════════════════════════════════
story.append(section_bar("SECTION 1  ·  Critical Appraisal of the Original Questionnaire", DARK_NAVY))
story.append(Spacer(1,0.2*cm))

for row in appraisal_rows:
    # Network label + verdict header
    hdr_data = [[
        Paragraph(row["network"], S("NHdr", fontSize=10, fontName="Helvetica-Bold",
                                    textColor=WHITE, alignment=TA_CENTER)),
        Paragraph(row["verdict"], S("VHdr", fontSize=9, fontName="Helvetica-Bold",
                                    alignment=TA_CENTER,
                                    textColor=colors.HexColor("#1a3a0a") if "VALID" in row["verdict"] and "PARTIALLY" not in row["verdict"] else colors.HexColor("#7b3a10")))
    ]]
    hdr_t = Table(hdr_data, colWidths=[5*cm, 13.5*cm])
    hdr_t.setStyle(TableStyle([
        ("BACKGROUND",(0,0),(0,0),DARK_NAVY),
        ("BACKGROUND",(1,0),(1,0),row["verdict_bg"]),
        ("TOPPADDING",(0,0),(-1,-1),5),("BOTTOMPADDING",(0,0),(-1,-1),5),
        ("LEFTPADDING",(0,0),(-1,-1),5),
        ("BOX",(0,0),(-1,-1),0.5,MID_GREY),
    ]))
    story.append(hdr_t)

    # Original content row
    orig_data = [[
        Paragraph("<b>Original signs:</b>", small_bold),
        Paragraph(row["original_signs"], small),
        Paragraph("<b>Original report:</b>", small_bold),
        Paragraph(row["original_report"], italic_body),
    ]]
    orig_t = Table(orig_data, colWidths=[3*cm, 5.5*cm, 3*cm, 7*cm])
    orig_t.setStyle(TableStyle([
        ("BACKGROUND",(0,0),(-1,-1),colors.HexColor("#f8f8f8")),
        ("TOPPADDING",(0,0),(-1,-1),3),("BOTTOMPADDING",(0,0),(-1,-1),3),
        ("LEFTPADDING",(0,0),(-1,-1),4),
        ("BOX",(0,0),(-1,-1),0.3,MID_GREY),
        ("VALIGN",(0,0),(-1,-1),"TOP"),
    ]))
    story.append(orig_t)

    # Appraisal text
    appraisal_para = Paragraph(row["appraisal"].replace("\n", "<br/>"), small)
    appr_data = [[appraisal_para]]
    appr_t = Table(appr_data, colWidths=[18.5*cm])
    appr_t.setStyle(TableStyle([
        ("BACKGROUND",(0,0),(-1,-1),colors.HexColor("#fdfdf5")),
        ("TOPPADDING",(0,0),(-1,-1),4),("BOTTOMPADDING",(0,0),(-1,-1),4),
        ("LEFTPADDING",(0,0),(-1,-1),8),("RIGHTPADDING",(0,0),(-1,-1),8),
        ("BOX",(0,0),(-1,-1),0.3,MID_GREY),
    ]))
    story.append(appr_t)

    # Suggested additions
    add_items = [[Paragraph("<b>Suggested additions / corrections:</b>", small_bold)]]
    for a in row["additions"]:
        add_items.append([Paragraph(f"  → {a}", small)])
    add_t = Table(add_items, colWidths=[18.5*cm])
    add_t.setStyle(TableStyle([
        ("BACKGROUND",(0,0),(-1,-1),colors.HexColor("#f0f8ff")),
        ("TOPPADDING",(0,0),(-1,-1),2),("BOTTOMPADDING",(0,0),(-1,-1),2),
        ("LEFTPADDING",(0,0),(-1,-1),8),
        ("BOX",(0,0),(-1,-1),0.3,colors.HexColor("#1a6b8a")),
    ]))
    story.append(add_t)
    story.append(Spacer(1,0.3*cm))

# ══════════════════════════════════════════════════════════════════
# SECTION 2: VALIDATED EXPANDED SCREENING TOOL
# ══════════════════════════════════════════════════════════════════
story.append(PageBreak())
story.append(section_bar("SECTION 2  ·  Validated & Expanded Triple Network Clinical Screening Tool", DARK_NAVY))
story.append(Spacer(1,0.2*cm))

how_use = [[Paragraph(
    "<b>HOW TO USE:</b>  During the clinical interview, tick observable signs (☐) and note matching patient reports. "
    "A consistent pattern within one network domain suggests that network's dysfunction is clinically prominent. "
    "Multiple network involvement is common and diagnostically significant. "
    "This tool screens for network-level dysfunction - it does not replace formal neuropsychological or neuroimaging assessment.",
    note)]]
how_t = Table(how_use, colWidths=[18.5*cm])
how_t.setStyle(TableStyle([
    ("BACKGROUND",(0,0),(-1,-1),colors.HexColor("#fffbe6")),
    ("BOX",(0,0),(-1,-1),0.5,colors.HexColor("#e0c050")),
    ("TOPPADDING",(0,0),(-1,-1),6),("BOTTOMPADDING",(0,0),(-1,-1),6),
    ("LEFTPADDING",(0,0),(-1,-1),8),("RIGHTPADDING",(0,0),(-1,-1),8),
]))
story.append(how_t)
story.append(Spacer(1,0.25*cm))

for net in network_rows:
    # Network header bar
    net_hdr = [[Paragraph(net["network"], S(f"NH{net['color']}", fontSize=11,
                    fontName="Helvetica-Bold", textColor=WHITE, alignment=TA_CENTER))]]
    net_hdr_t = Table(net_hdr, colWidths=[18.5*cm])
    net_hdr_t.setStyle(TableStyle([
        ("BACKGROUND",(0,0),(-1,-1),net["color"]),
        ("TOPPADDING",(0,0),(-1,-1),7),("BOTTOMPADDING",(0,0),(-1,-1),7),
        ("LEFTPADDING",(0,0),(-1,-1),6),
        ("BOX",(0,0),(-1,-1),0.5,DARK_NAVY),
    ]))
    story.append(net_hdr_t)

    # Anchor + disorders row
    meta_data = [[
        Paragraph(f"<b>Brain anchors:</b>  {net['anchors']}", small),
        Paragraph(f"<b>Disorders:</b>  {net['disorders']}", small),
    ]]
    meta_t = Table(meta_data, colWidths=[7*cm, 11.5*cm])
    meta_t.setStyle(TableStyle([
        ("BACKGROUND",(0,0),(-1,-1),net["light"]),
        ("TOPPADDING",(0,0),(-1,-1),4),("BOTTOMPADDING",(0,0),(-1,-1),4),
        ("LEFTPADDING",(0,0),(-1,-1),6),
        ("BOX",(0,0),(-1,-1),0.3,MID_GREY),
    ]))
    story.append(meta_t)

    # Signs + reports columns
    col_hdrs = [[
        Paragraph("☐  OBSERVABLE SIGNS (clinician)", S("CH_L", fontSize=8.5,
            fontName="Helvetica-Bold", textColor=net["color"])),
        Paragraph("💬  PATIENT REPORTS (verbatim prompts)", S("CH_R", fontSize=8.5,
            fontName="Helvetica-Bold", textColor=DARK_NAVY)),
    ]]
    col_hdr_t = Table(col_hdrs, colWidths=[9.25*cm, 9.25*cm])
    col_hdr_t.setStyle(TableStyle([
        ("BACKGROUND",(0,0),(0,0),net["light"]),
        ("BACKGROUND",(1,0),(1,0),colors.HexColor("#f5f5f5")),
        ("TOPPADDING",(0,0),(-1,-1),4),("BOTTOMPADDING",(0,0),(-1,-1),4),
        ("LEFTPADDING",(0,0),(-1,-1),5),
        ("LINEAFTER",(0,0),(0,0),0.5,MID_GREY),
        ("LINEBELOW",(0,0),(-1,-1),0.5,MID_GREY),
    ]))
    story.append(col_hdr_t)

    max_r = max(len(net["observable_signs"]), len(net["patient_reports"]))
    rows_data = []
    for i in range(max_r):
        s = checkbox(net["observable_signs"][i]) if i < len(net["observable_signs"]) else Paragraph("", body)
        r = Paragraph(net["patient_reports"][i], italic_body) if i < len(net["patient_reports"]) else Paragraph("", body)
        rows_data.append([s, r])
    rows_t = Table(rows_data, colWidths=[9.25*cm, 9.25*cm])
    r_ts = TableStyle([
        ("LEFTPADDING",(0,0),(-1,-1),5),("RIGHTPADDING",(0,0),(-1,-1),5),
        ("TOPPADDING",(0,0),(-1,-1),3),("BOTTOMPADDING",(0,0),(-1,-1),3),
        ("VALIGN",(0,0),(-1,-1),"TOP"),
        ("LINEAFTER",(0,0),(0,-1),0.5,MID_GREY),
        ("BOX",(0,0),(-1,-1),0.5,MID_GREY),
    ])
    for i in range(max_r):
        r_ts.add("BACKGROUND",(0,i),(0,i), net["light"] if i%2==0 else colors.HexColor("#fafafa"))
        r_ts.add("BACKGROUND",(1,i),(1,i), colors.HexColor("#f9f9f9") if i%2==0 else WHITE)
    rows_t.setStyle(r_ts)
    story.append(rows_t)

    # Note strip
    note_data = [[Paragraph(f"<i>Note: {net['note']}</i>", warn_style),
                  Paragraph(f"<i>Ref: {net['source']}</i>", src_style)]]
    note_t = Table(note_data, colWidths=[11*cm, 7.5*cm])
    note_t.setStyle(TableStyle([
        ("BACKGROUND",(0,0),(-1,-1),colors.HexColor("#f5f5f5")),
        ("TOPPADDING",(0,0),(-1,-1),3),("BOTTOMPADDING",(0,0),(-1,-1),3),
        ("LEFTPADDING",(0,0),(-1,-1),6),
        ("BOX",(0,0),(-1,-1),0.3,MID_GREY),
        ("VALIGN",(0,0),(-1,-1),"TOP"),
    ]))
    story.append(note_t)
    story.append(Spacer(1,0.3*cm))

# ── Scoring / interpretation box ──
story.append(section_bar("SECTION 3  ·  Interpretation Guide", DARK_NAVY))
story.append(Spacer(1,0.2*cm))

interp_rows = [
    ["Pattern", "Likely Network Failure", "Common Disorder Context"],
    ["≥3 SN signs + emergency subjective report", "SN Hijack (primary)", "PTSD, Panic disorder, GAD, Schizophrenia"],
    ["≥3 DMN signs + ruminative/self-critical reports", "DMN Hyperactivity (primary)", "MDD, GAD, Rumination OCD"],
    ["≥3 ECN signs across subtypes", "ECN Dysfunction (primary)", "OCD, Schizophrenia, ADHD, Bipolar"],
    ["≥2 SN + ≥2 LC-NE signs", "SN Hijack + NE Amplification", "PTSD, Mania, Stimulant use"],
    ["≥2 DMN + ≥2 ECN signs", "DMN-ECN Anti-correlation failure", "MDD with cognitive dysfunction, Bipolar depression"],
    ["All 3 networks + LC-NE signs", "Global network dysregulation", "Severe MDD, Psychosis, Complex PTSD"],
]
interp_t = Table(interp_rows, colWidths=[6*cm, 6*cm, 6.5*cm])
interp_ts = TableStyle([
    ("BACKGROUND",(0,0),(-1,0),DARK_NAVY),
    ("TEXTCOLOR",(0,0),(-1,0),WHITE),
    ("FONTNAME",(0,0),(-1,0),"Helvetica-Bold"),
    ("FONTSIZE",(0,0),(-1,-1),8),
    ("TOPPADDING",(0,0),(-1,-1),4),("BOTTOMPADDING",(0,0),(-1,-1),4),
    ("LEFTPADDING",(0,0),(-1,-1),5),("RIGHTPADDING",(0,0),(-1,-1),5),
    ("GRID",(0,0),(-1,-1),0.3,MID_GREY),
    ("VALIGN",(0,0),(-1,-1),"TOP"),
])
for i in range(1, len(interp_rows)):
    bg = colors.HexColor("#f5f5f5") if i%2==0 else WHITE
    interp_ts.add("BACKGROUND",(0,i),(-1,i),bg)
interp_t.setStyle(interp_ts)
story.append(interp_t)
story.append(Spacer(1,0.2*cm))

# Caveat
caveat = [[Paragraph(
    "⚠️  <b>IMPORTANT LIMITATIONS:</b>  This screening tool maps clinical observations onto network-level constructs "
    "derived from fMRI research. Network dysfunction cannot be confirmed from clinical observation alone - "
    "it requires neuroimaging. This tool is intended to prompt network-informed clinical reasoning and guide "
    "targeted questioning. It has not been validated as a psychometric instrument. "
    "Multiple network involvement is the rule, not the exception, in severe psychiatric illness.",
    warn_style)]]
cav_t = Table(caveat, colWidths=[18.5*cm])
cav_t.setStyle(TableStyle([
    ("BACKGROUND",(0,0),(-1,-1),colors.HexColor("#fdf0e0")),
    ("BOX",(0,0),(-1,-1),0.5,colors.HexColor("#c07030")),
    ("TOPPADDING",(0,0),(-1,-1),5),("BOTTOMPADDING",(0,0),(-1,-1),5),
    ("LEFTPADDING",(0,0),(-1,-1),8),("RIGHTPADDING",(0,0),(-1,-1),8),
]))
story.append(cav_t)
story.append(Spacer(1,0.2*cm))

# References
ref_hdr = [[Paragraph("  📚  REFERENCES", sec_hdr)]]
ref_hdr_t = Table(ref_hdr, colWidths=[18.5*cm])
ref_hdr_t.setStyle(TableStyle([
    ("BACKGROUND",(0,0),(-1,-1),DARK_NAVY),
    ("TOPPADDING",(0,0),(-1,-1),5),("BOTTOMPADDING",(0,0),(-1,-1),5),
    ("LEFTPADDING",(0,0),(-1,-1),6),
]))
story.append(ref_hdr_t)
story.append(Spacer(1,0.1*cm))

refs = [
    "1. Menon V. (2011). Large-scale brain networks and psychopathology: a unifying triple network model. Trends in Cognitive Sciences, 15(10):483-506.",
    "2. Menon V, Palaniyappan L, Supekar K. (2023). Integrative brain network and salience models of psychopathology and cognitive dysfunction in schizophrenia. Biol Psychiatry. PMID: 36702660",
    "3. Buckner RL, Andrews-Hanna JR, Schacter DL. (2008). The brain's default network: anatomy, function, and relevance to disease. Ann NY Acad Sci. 1124:1-38.",
    "4. Mulders PC, et al. (2015). Resting-state functional connectivity in major depressive disorder: a review. Neurosci Biobehav Rev. 56:330-44.",
    "5. Aston-Jones G & Cohen JD. (2005). An integrative theory of locus coeruleus-norepinephrine function: adaptive gain and optimal performance. Annu Rev Neurosci. 28:403-50.",
    "6. Sara SJ. (2009). The locus coeruleus and noradrenergic modulation of cognition. Nat Rev Neurosci. 10(3):211-23.",
]
for ref in refs:
    story.append(Paragraph(ref, src_style))
    story.append(Spacer(1,0.1*cm))

doc.build(story, onFirstPage=add_bg, onLaterPages=add_bg)
print("Module PDF built:", OUTPUT_MODULE)

# ── Merge v2 + module → v3 ──────────────────────────────────────────────────
v2_path = "/tmp/workspace/psych-checklist/Psychiatric_Prognostic_Checklist_v2.pdf"
writer = PdfWriter()
for path in [v2_path, OUTPUT_MODULE]:
    reader = PdfReader(path)
    for page in reader.pages:
        writer.add_page(page)
with open(OUTPUT_FINAL, "wb") as f:
    writer.write(f)
print("Final merged PDF:", OUTPUT_FINAL)
Running Command

cd /tmp/workspace/psych-checklist && python build_triple_network_module.py 2>&1

Running Command

ls -lh /tmp/workspace/psych-checklist/

Your updated PDF (v3) with the triple network module is ready:

Psychiatric Prognostic Checklist v3

PDF Document · PDF

Or just the standalone module if you want to share it separately:

triple network module

PDF Document · PDF


Critical Appraisal Summary

Here is what the literature check found for each row:

✅ SN Hijack - Valid, minor gaps

The core mapping is solid. Startle, scanning, and dilated pupils accurately reflect SN (insula + dACC) hyperactivation and its downstream sympathetic output, well-supported by Menon 2011 and the schizophrenia review (Menon et al. 2023, PMID 36702660). Added: aberrant salience attribution to neutral stimuli (schizophrenia-specific), intrusive re-experiencing (PTSD), tachycardia as an observable sign.

⚠️ DMN Hyperactivity - Partial; the observable sign is wrong

The subjective report ("What If movie") is excellent and ecologically valid. But "vacant stare, slow response" is wrong for DMN hyperactivity - that's psychomotor retardation or dissociation. Active DMN over-engagement presents as internal busyness, not blankness. The patient is cognitively over-occupied, not absent. Corrected sign: "appears internally preoccupied; quickly returns to internal train of thought when interrupted."

⚠️ ECN Decohesion - Partial; too OCD-specific

The OCD example is valid, but "repeating a task just finished" only captures one narrow ECN failure mode. ECN dysfunction manifests differently across disorders - working memory failure in ADHD/schizophrenia, cognitive rigidity in severe depression, impulsivity in mania. Also, "decohesion" is not standard terminology - "ECN dysfunction" is preferred. Added: 4 subtypes (inhibitory, working memory, rigidity, impulsivity/disorganisation).

⚠️ NE Overload - Conceptual error; needs relabelling

This is the most significant issue. LC-NE is not a network - it is a neuromodulatory system. The triple network model is a cortical resting-state network framework (SN, DMN, ECN). Including LC-NE as a "4th network" is neuroscientifically inaccurate. Additionally, punding is a dopaminergic sign (seen in Parkinson's and stimulant use), not NE-mediated. Fix: Relabelled as "LC-NE Hyperarousal (Neuromodulatory Modifier)" - a system that amplifies SN sensitivity rather than a standalone network. Punding removed from this row.

What Was Added to the PDF

The triple network module (appended as new pages to v2) contains:
  1. Section 1 - Critical Appraisal - row-by-row verdict for each original entry with corrections
  2. Section 2 - Validated Expanded Screening Tool - 4 colour-coded network blocks with:
    • Brain region anchors
    • Disorder associations
    • Observable signs (with checkboxes, subtypes labelled)
    • Verbatim patient report prompts
    • Clinical notes with caveats
  3. Section 3 - Interpretation Guide - a pattern-matching table mapping sign clusters to likely network failure and disorder context
  4. A clear limitations caveat that this tool is clinical reasoning support, not a validated psychometric instrument

Compare triple network dysfunction patterns across your 6 psychiatric disorders in a visual summary (e.g. heatmap of which networks are most impaired in each condition) in the original pdf

give me final pdf with clinical prognostic checklist, bio markers (you can remove fmri specific biomakers), and section 2 of triple network screening module, remove section 1 of triple network model

Reading File
Good - I have the structure. I'll now build the clean final PDF from scratch, incorporating all the data with fMRI items stripped and section 1 removed.Your credits are exhausted. You can purchase a top-up or subscribe to a plan to continue using Orris. If you're already subscribed, your daily credits will reset tomorrow.

give me final pdf with clinical prognostic checklist, bio markers (you can remove fmri specific biomakers), and section 2 of triple network screening module, remove section 1 of triple network model

Your credits are exhausted. You can purchase a top-up or subscribe to a plan to continue using Orris. If you're already subscribed, your daily credits will reset tomorrow.

give me final pdf with clinical prognostic checklist, bio markers (you can remove fmri, research grade biomakers), and section 2 of triple network screening module, remove section 1 of triple network model

On it. Building the clean final PDF now.
Writing File

~/psych-checklist/build_final.py

"""
FINAL PDF: Psychiatric Prognostic Checklist
  Part 1 - Clinical prognostic factors (good / bad) per disorder
  Part 2 - Routine-available biomarkers (blood, EEG, genetics only - no fMRI/PET/research-only)
  Part 3 - Triple Network Clinical Screening Tool (Section 2 only)
"""
from reportlab.lib.pagesizes import A4
from reportlab.lib import colors
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
from reportlab.lib.units import cm
from reportlab.platypus import (
    SimpleDocTemplate, Paragraph, Spacer, Table, TableStyle,
    HRFlowable, PageBreak
)
from reportlab.lib.enums import TA_LEFT, TA_CENTER

OUTPUT = "/tmp/workspace/psych-checklist/Psychiatric_Clinical_Tool_FINAL.pdf"

# ── Palette ───────────────────────────────────────────────────────────────────
DARK_NAVY    = colors.HexColor("#1a2b4a")
TEAL         = colors.HexColor("#007b7f")
WHITE        = colors.white
MID_GREY     = colors.HexColor("#cccccc")
GOOD_GREEN   = colors.HexColor("#d4edda")
BAD_RED      = colors.HexColor("#f8d7da")
GOOD_DARK    = colors.HexColor("#155724")
BAD_DARK     = colors.HexColor("#721c24")
HEADER_BG    = colors.HexColor("#2c3e6b")
BIO_GOOD_BG  = colors.HexColor("#d6eef7")
BIO_BAD_BG   = colors.HexColor("#fde8d0")
BIO_GOOD_TXT = colors.HexColor("#1a6b8a")
BIO_BAD_TXT  = colors.HexColor("#7b3a10")
BIO_HDR      = colors.HexColor("#1a6b8a")
SN_COL       = colors.HexColor("#c0392b")
SN_LIGHT     = colors.HexColor("#fadbd8")
DMN_COL      = colors.HexColor("#1a5276")
DMN_LIGHT    = colors.HexColor("#d6eaf8")
ECN_COL      = colors.HexColor("#1e8449")
ECN_LIGHT    = colors.HexColor("#d5f5e3")
NE_COL       = colors.HexColor("#7d6608")
NE_LIGHT     = colors.HexColor("#fef9e7")

# ── Styles ────────────────────────────────────────────────────────────────────
SS = getSampleStyleSheet()
def PS(name, **kw):
    return ParagraphStyle(name, parent=kw.pop("parent", SS["Normal"]), **kw)

title_s    = PS("TS",  fontSize=20, textColor=WHITE, alignment=TA_CENTER, fontName="Helvetica-Bold")
sub_s      = PS("SS2", fontSize=9,  textColor=colors.HexColor("#c8d8f0"), alignment=TA_CENTER, fontName="Helvetica-Oblique")
sec_s      = PS("SHS", fontSize=13, textColor=WHITE, fontName="Helvetica-Bold", leftIndent=6)
sec_s2     = PS("SH2", fontSize=11, textColor=WHITE, fontName="Helvetica-Bold", leftIndent=6)
col_good   = PS("CG",  fontSize=10, textColor=GOOD_DARK, fontName="Helvetica-Bold", alignment=TA_CENTER)
col_bad    = PS("CB",  fontSize=10, textColor=BAD_DARK,  fontName="Helvetica-Bold", alignment=TA_CENTER)
bio_good   = PS("BG",  fontSize=9,  textColor=BIO_GOOD_TXT, fontName="Helvetica-Bold", alignment=TA_CENTER)
bio_bad    = PS("BB2", fontSize=9,  textColor=BIO_BAD_TXT,  fontName="Helvetica-Bold", alignment=TA_CENTER)
item_s     = PS("IS",  fontSize=8.5, fontName="Helvetica", leading=12, leftIndent=4)
bio_s      = PS("BS",  fontSize=8,   fontName="Helvetica", leading=11, leftIndent=4, textColor=colors.HexColor("#1a3a4a"))
src_s      = PS("SRS", fontSize=7,   fontName="Helvetica-Oblique", textColor=colors.HexColor("#555555"), leftIndent=4)
note_s     = PS("NS",  fontSize=8,   fontName="Helvetica", leading=11, leftIndent=6, textColor=colors.HexColor("#333333"))
warn_s     = PS("WS",  fontSize=7.5, fontName="Helvetica-Oblique", textColor=colors.HexColor("#7b3a10"), leftIndent=4, leading=10)
net_lbl    = PS("NL",  fontSize=11,  fontName="Helvetica-Bold", textColor=WHITE, alignment=TA_CENTER)
net_small  = PS("NSM", fontSize=7.5, fontName="Helvetica", textColor=colors.HexColor("#555555"), leading=10)
italic_s   = PS("ITS", fontSize=8.5, fontName="Helvetica-Oblique", textColor=colors.HexColor("#444444"), leading=12)

def cb(t): return Paragraph(f"☐  {t}", item_s)
def di(t): return Paragraph(f"◇  {t}", bio_s)

def sec_bar(text, bg=HEADER_BG):
    t = Table([[Paragraph(f"  {text}", sec_s)]], colWidths=[18.5*cm])
    t.setStyle(TableStyle([
        ("BACKGROUND",(0,0),(-1,-1),bg),
        ("TOPPADDING",(0,0),(-1,-1),7),("BOTTOMPADDING",(0,0),(-1,-1),7),
        ("LEFTPADDING",(0,0),(-1,-1),6),
        ("BOX",(0,0),(-1,-1),0.5,DARK_NAVY),
    ]))
    return t

def page_bg(canvas, doc):
    canvas.saveState()
    w, h = A4
    canvas.setFillColor(DARK_NAVY)
    canvas.rect(0, h-2.8*cm, w, 2.8*cm, fill=1, stroke=0)
    canvas.setFillColor(TEAL)
    canvas.rect(0, h-3.0*cm, w, 0.22*cm, fill=1, stroke=0)
    canvas.setFillColor(colors.HexColor("#f0f0f0"))
    canvas.rect(0, 0, w, 1.2*cm, fill=1, stroke=0)
    canvas.setFillColor(MID_GREY)
    canvas.rect(0, 1.2*cm, w, 0.05*cm, fill=1, stroke=0)
    canvas.setFont("Helvetica-Oblique", 7)
    canvas.setFillColor(colors.HexColor("#555555"))
    canvas.drawCentredString(w/2, 0.42*cm,
        "Psychiatric Clinical Tool (Final)  |  Clinical use only - not a standalone diagnostic instrument")
    canvas.drawRightString(w-1.5*cm, 0.42*cm, f"Page {doc.page}")
    canvas.restoreState()

# ═══════════════════════════════════════════════════════════════════════════════
# DATA
# ═══════════════════════════════════════════════════════════════════════════════

disorders = [
  {
    "name":"SCHIZOPHRENIA","emoji":"🧠",
    "good":[
      "Female sex",
      "Later / acute onset of illness",
      "Predominantly positive symptoms",
      "Good premorbid functioning",
      "Short duration of untreated psychosis (DUP)",
      "Good insight into illness",
      "Strong social support network",
      "Employment / higher education",
      "Early and intensive treatment initiation",
      "Absence of negative symptoms at baseline",
      "No comorbid substance use disorder",
      "Married / stable intimate relationship",
    ],
    "bad":[
      "Male sex",
      "Insidious / early onset",
      "Predominantly negative symptoms",
      "Poor premorbid functioning",
      "Long duration of untreated psychosis",
      "Poor insight / anosognosia",
      "Social isolation / lack of support",
      "Unemployment / low education",
      "Delayed treatment initiation",
      "Prominent cognitive deficits",
      "Comorbid substance use disorder",
      "Family history of schizophrenia",
    ],
    "bio_good":[
      "Higher serum BDNF (→ better antipsychotic response)",
      "BDNF Val/Val genotype",
      "Low CRP / IL-6 at baseline",
      "Low serum homocysteine",
      "Normal serum folate and B12",
      "Low polygenic risk score",
    ],
    "bio_bad":[
      "Low serum BDNF",
      "BDNF Met carrier (Val/Met or Met/Met)",
      "Elevated CRP / IL-6",
      "Elevated homocysteine",
      "Low folate / B12",
      "High polygenic risk score for schizophrenia",
    ],
    "src":"Solmi et al. 2023 (PMID 37957292); Molstrom et al. 2022 (PMID 36417817); Liberona et al. 2024 (PMID 39456983)",
  },
  {
    "name":"BIPOLAR DISORDER","emoji":"🔄",
    "good":[
      "Later age of onset",
      "Fewer prior mood episodes",
      "Good psychosocial functioning at baseline",
      "Long-term symptomatic remission achieved",
      "Good treatment adherence",
      "Strong social / family support",
      "Absence of psychotic features",
      "No comorbid anxiety or substance use",
      "Higher education / employment",
    ],
    "bad":[
      "Early age of onset",
      "High number of prior episodes",
      "Poor interepisode functioning",
      "Rapid cycling course",
      "Psychotic features present",
      "Comorbid ADHD",
      "Comorbid anxiety disorder",
      "Comorbid substance use disorder",
      "Comorbid personality disorder",
      "Cognitive impairment",
      "Poor medication adherence",
    ],
    "bio_good":[
      "Higher serum BDNF (→ better lithium response)",
      "BDNF Val/Val genotype",
      "Low CRP / IL-6",
      "Normal cortisol awakening response",
      "Normal serum lithium level (therapeutic range)",
      "Low polygenic risk score for schizophrenia / MDD",
      "Normal TSH (euthyroid)",
    ],
    "bio_bad":[
      "Low serum BDNF",
      "Elevated CRP / IL-6 / TNF-alpha",
      "Elevated morning cortisol / flattened diurnal cortisol",
      "Elevated homocysteine",
      "Hypothyroidism / subclinical hypothyroidism (TSH elevated)",
      "High polygenic risk score for schizophrenia or MDD",
      "Abnormal lithium erythrocyte:plasma ratio",
    ],
    "src":"Treuer & Tohen 2010 (PMID 20444581); Scott et al. 2025 (PMID 39954922); Liberona et al. 2024 (PMID 39456983)",
  },
  {
    "name":"MAJOR DEPRESSIVE DISORDER (MDD)","emoji":"🌧️",
    "good":[
      "Female sex",
      "Single depressive episode (not recurrent)",
      "Acute onset with identifiable trigger",
      "Mild-to-moderate baseline severity",
      "Early improvement within first 2 weeks of treatment",
      "Good social support",
      "Higher education / employment",
      "Absence of residual symptoms at remission",
      "No childhood maltreatment history",
    ],
    "bad":[
      "Recurrent episode history",
      "Residual depressive symptoms after treatment",
      "Childhood maltreatment / adverse events",
      "Comorbid anxiety disorder",
      "Comorbid physical illness (chronic pain, etc.)",
      "Comorbid personality disorder",
      "High baseline symptom severity",
      "Long duration of untreated depression",
      "Poor social support / social isolation",
      "Negative life events",
      "Suicidality at presentation",
    ],
    "bio_good":[
      "Higher serum BDNF at baseline",
      "BDNF Val/Val genotype",
      "Low CRP / IL-6 (→ better SSRI response)",
      "Normal TSH (euthyroid)",
      "Normal serum folate / B12",
      "Normal homocysteine",
      "Rising BDNF after 2 weeks of antidepressant",
    ],
    "bio_bad":[
      "Low serum BDNF",
      "Elevated CRP / IL-6 / TNF-alpha (inflammatory subtype → poor SSRI response)",
      "Hypothyroidism / subclinical hypothyroidism",
      "Low folate / B12",
      "Elevated homocysteine",
      "High polygenic risk score for MDD",
      "Anterior cingulate theta activity deficit on EEG (if available)",
    ],
    "src":"Solmi et al. 2023 (PMID 37957292); Transl Psychiatry 2019; Liberona et al. 2024 (PMID 39456983)",
  },
  {
    "name":"OBSESSIVE-COMPULSIVE DISORDER (OCD)","emoji":"🔁",
    "good":[
      "Short duration of illness at presentation",
      "Low symptom severity (low Y-BOCS score)",
      "Good insight into obsessions",
      "Later age of onset",
      "Female sex",
      "Early and intensive treatment",
      "Long-term maintenance treatment",
      "Good social support / functional family",
      "Absence of comorbid personality disorder",
      "Pure obsessional or checking subtype",
    ],
    "bad":[
      "Early age of onset",
      "Long illness duration at presentation",
      "High baseline Y-BOCS score",
      "Poor insight / overvalued ideation",
      "Male sex",
      "Hoarding or symmetry symptom subtype",
      "Contamination-washing subtype (for SRI response)",
      "Comorbid schizotypal or borderline PD",
      "Comorbid severe depression",
      "Unemployment / single / unmarried",
      "Family dysfunction / high expressed emotion",
    ],
    "bio_good":[
      "Elevated blood/CSF serotonin metabolite 5-HIAA (→ better clomipramine response)",
      "Normal platelet serotonin transporter binding",
      "Low CRP / inflammatory markers",
      "Absence of SLC6A4 short allele (5-HTTLPR L/L genotype)",
    ],
    "bio_bad":[
      "Low 5-HIAA (serotonin metabolite) - predicts poor SRI response",
      "SLC6A4 short allele (5-HTTLPR s/s or s/L genotype)",
      "DRD4 receptor gene variants (dopamine D4)",
      "Elevated CRP",
      "Low serum BDNF",
    ],
    "src":"Sharma et al. 2014 (PMID 25295427); Sharma & Math 2019 (PMID 30745676); Hazari et al. 2016 (PMID 27282021); Bandelow et al. 2017 (PMID 27419272)",
  },
  {
    "name":"PTSD","emoji":"⚡",
    "good":[
      "Single, adult-onset trauma (no childhood trauma)",
      "Female sex (for treatment response)",
      "Good social support",
      "Higher education",
      "Strong executive control / cognitive function",
      "Early initiation of trauma-focused therapy",
      "Absence of comorbid depression / alcohol use",
      "Single trauma type (not complex/multiple traumas)",
    ],
    "bad":[
      "Male sex",
      "Childhood / early-onset trauma (complex PTSD)",
      "Greater cumulative trauma exposure",
      "Combat trauma history",
      "Poor / absent social support",
      "Comorbid depression",
      "Comorbid alcohol / substance use disorder",
      "Comorbid chronic pain",
      "Comorbid sleep disorder",
      "Older age at treatment",
      "Higher anger dysregulation",
    ],
    "bio_good":[
      "Normal serum cortisol (or slightly elevated - not suppressed)",
      "Normal heart rate variability (HRV) at rest",
      "Normal glucocorticoid receptor sensitivity",
      "Low CRP / inflammatory markers",
      "Low polygenic risk score for PTSD",
    ],
    "bio_bad":[
      "Low baseline cortisol / enhanced dexamethasone suppression (HPA paradox)",
      "Reduced heart rate variability (HRV) - sympathetic hypertonicity",
      "Elevated CRP / IL-6",
      "High polygenic risk score for PTSD",
      "Elevated norepinephrine metabolites (MHPG) in urine/plasma",
    ],
    "src":"Keyan et al. 2024 (PMID 38884956); Solmi et al. 2023 (PMID 37957292); Bandelow et al. 2016/2017 (PMID 27403679, 27419272)",
  },
  {
    "name":"ANXIETY DISORDERS  (Panic · Social Anxiety · GAD)","emoji":"😰",
    "good":[
      "Later / adult onset (not childhood-onset)",
      "Milder baseline symptom severity",
      "Good social support",
      "Absence of comorbid mood disorder",
      "Early treatment initiation",
      "Higher education / employment",
      "Limited avoidance behaviour at presentation",
      "Single anxiety disorder (no comorbid anxiety)",
      "Good treatment adherence",
      "Absence of high neuroticism trait",
    ],
    "bad":[
      "Childhood / early onset",
      "Severe baseline symptoms",
      "Comorbid major depressive disorder",
      "Comorbid personality disorder",
      "Comorbid substance use disorder",
      "Multiple comorbid anxiety disorders",
      "Entrenched avoidance behaviours",
      "High neuroticism trait",
      "Poor social support",
      "Long duration of untreated disorder",
    ],
    "bio_good":[
      "Normal morning cortisol / normal diurnal pattern",
      "Normal HRV at rest",
      "Normal serum oxytocin (social anxiety)",
      "Low CRP / IL-6",
      "5-HTTLPR L/L genotype (lower anxiety vulnerability)",
    ],
    "bio_bad":[
      "Elevated morning cortisol / HPA dysregulation",
      "Reduced HRV (autonomic hyperarousal)",
      "Low serum oxytocin (social anxiety disorder)",
      "Elevated CRP / IL-6",
      "5-HTTLPR s/s genotype (anxiety vulnerability)",
      "Error-related negativity (ERN) elevation on EEG - GAD severity marker",
    ],
    "src":"Solmi et al. 2023 (PMID 37957292); Wang et al. 2018 (PMID 29843662); Bandelow et al. 2017 (PMID 27419272)",
  },
]

transdiag_good = [
    "Female sex",
    "Higher education",
    "Social contacts and employment",
    "Early access to and initiation of treatment",
    "Absence of negative life events",
    "Lower disease-specific symptom burden at baseline",
    "Better quality of life / functioning at baseline",
    "Absence of comorbid mental or physical disorders",
    "Good treatment adherence",
]
transdiag_bad = [
    "Male sex",
    "Younger age at onset",
    "Higher disease-specific symptom burden",
    "Higher depressive or anxiety symptoms (comorbid)",
    "Greater global illness severity",
    "More prior episodes / relapses",
    "Negative life events",
    "Social isolation / unemployment",
    "Comorbid mental or physical disorders",
]
transdiag_bio_good = [
    "Higher serum BDNF (favours Rx response across mood/psychosis)",
    "BDNF Val/Val genotype",
    "Low CRP / IL-6 (low inflammatory burden)",
    "Normal HPA axis cortisol pattern",
    "Normal TSH (euthyroid)",
    "Normal folate / B12 / homocysteine",
]
transdiag_bio_bad = [
    "Low serum BDNF",
    "Elevated CRP / IL-6 / TNF-alpha",
    "HPA axis dysregulation (elevated or suppressed cortisol)",
    "Hypothyroidism",
    "Low folate / B12 / elevated homocysteine",
    "High polygenic risk scores (disorder-specific)",
]

network_rows = [
    {
        "network":"SN  Hijack\n(Salience Network Hyperactivation)",
        "color":SN_COL,"light":SN_LIGHT,
        "anchors":"Anterior insula + dorsal ACC",
        "disorders":"PTSD, GAD, Panic disorder, Schizophrenia (aberrant salience)",
        "signs":[
            "Exaggerated startle response",
            "Dilated pupils / tachycardia on exam",
            "Hypervigilant environmental scanning",
            "Freezing or flinching to neutral stimuli",
            "Intrusive re-experiencing episodes (PTSD context)",
            "Misattribution of threat to neutral stimuli (schizophrenia)",
        ],
        "reports":[
            '"Everything feels like an emergency."',
            '"My heart races for no reason."',
            '"I keep thinking that stranger is threatening me."',
            '"I can\'t relax even when I know I\'m safe."',
        ],
        "note":"Distinguish from LC-NE hyperarousal (see below). In schizophrenia, SN hijack = aberrant salience: neutral stimuli feel threatening or significant.",
        "src":"Menon 2011 (Trends Cogn Sci); Menon et al. 2023 (PMID 36702660)",
    },
    {
        "network":"DMN  Hyperactivity\n(Default Mode Network Overengagement)",
        "color":DMN_COL,"light":DMN_LIGHT,
        "anchors":"mPFC + posterior cingulate cortex + angular gyrus",
        "disorders":"MDD, GAD, Rumination-predominant OCD, Bipolar depression",
        "signs":[
            "Internally preoccupied; quickly returns to internal train of thought when distracted",
            "Delayed response to questions (attentional capture by internal narrative)",
            "Excessive self-referential speech in interview",
            "Flat affect but preserved fluency about self-related worries",
        ],
        "reports":[
            '"I can\'t turn off the \'What If\' movie in my head."',
            '"I keep replaying what went wrong."',
            '"Everything bad always comes back to something I did."',
            '"My mind keeps drifting back to worrying even when I try to focus."',
        ],
        "note":"NOTE: Vacant stare + slow responses = psychomotor retardation or dissociation, NOT DMN hyperactivity. DMN over-engagement is internal busyness, not blankness.",
        "src":"Buckner et al. 2008 (Ann NY Acad Sci); Mulders et al. 2015 (Neurosci Biobehav Rev)",
    },
    {
        "network":"ECN  Dysfunction\n(Executive Control Network Failure)",
        "color":ECN_COL,"light":ECN_LIGHT,
        "anchors":"Dorsolateral PFC + lateral parietal cortex",
        "disorders":"OCD, Schizophrenia, ADHD, Bipolar disorder, Severe MDD",
        "signs":[
            "[OCD] Repeating a task already completed (inhibitory override failure)",
            "[Cognitive] Cannot hold two concepts in mind simultaneously (WM failure)",
            "[Rigidity] Gets stuck on one conversational topic; cannot task-switch",
            "[Impulsivity] Acts before thinking; poor prepotent response inhibition",
            "[Disorganisation] Tangential or disorganised speech (schizophrenia subtype)",
        ],
        "reports":[
            '"I know it\'s clean, but I can\'t stop scrubbing." (OCD)',
            '"I started three things and can\'t finish any of them."',
            '"I keep losing my train of thought mid-sentence."',
            '"I say things before I realise I shouldn\'t."',
        ],
        "note":"Subtypes differ by disorder: inhibitory failure (OCD), working memory failure (ADHD/schizophrenia), cognitive rigidity (schizophrenia/severe MDD), impulsivity (mania/ADHD).",
        "src":"Menon 2011 (Trends Cogn Sci); Menon et al. 2023 (PMID 36702660)",
    },
    {
        "network":"LC-NE  Hyperarousal\n(Neuromodulatory Modifier - not a 4th network)",
        "color":NE_COL,"light":NE_LIGHT,
        "anchors":"Locus coeruleus → norepinephrine release across cortex",
        "disorders":"PTSD, Panic disorder, Mania, Stimulant intoxication / withdrawal",
        "signs":[
            "Motor restlessness / fidgeting / inability to sit still",
            "Pressured or rapid-fire speech",
            "Psychomotor agitation",
            "Insomnia despite clear exhaustion",
            "Exaggerated startle (overlaps SN - distinguish by autonomic prominence)",
        ],
        "reports":[
            '"I feel \'wired\' and \'stuck\' at the same time."',
            '"I\'m exhausted but I can\'t slow down."',
            '"My body is on high alert even when my mind knows it\'s fine."',
            '"I haven\'t slept in days but I\'m not tired."',
        ],
        "note":"LC-NE is a neuromodulatory system, not a resting-state network. It amplifies SN sensitivity. Present as a modifier of SN dysfunction, not a standalone network.",
        "src":"Aston-Jones & Cohen 2005 (Annu Rev Neurosci); Sara 2009 (Nat Rev Neurosci)",
    },
]

interp_rows = [
    ["Pattern observed","Likely network failure","Common disorder context"],
    ["≥3 SN signs + emergency report","SN Hijack (primary)","PTSD, Panic, GAD, Schizophrenia"],
    ["≥3 DMN signs + ruminative reports","DMN Hyperactivity (primary)","MDD, GAD, Rumination OCD"],
    ["≥3 ECN signs across subtypes","ECN Dysfunction (primary)","OCD, Schizophrenia, ADHD, Bipolar"],
    ["≥2 SN + ≥2 LC-NE signs","SN Hijack + NE Amplification","PTSD, Mania, Stimulant use"],
    ["≥2 DMN + ≥2 ECN signs","DMN-ECN failure","MDD with cognitive dysfunction, Bipolar depression"],
    ["All 3 networks + LC-NE","Global network dysregulation","Severe MDD, Psychosis, Complex PTSD"],
]

refs = [
    "1. Solmi M, et al. (2023). Umbrella review of candidate predictors across mental disorders. Mol Psychiatry. PMID 37957292",
    "2. Molstrom IM, et al. (2022). Prognosis of schizophrenia - 20-year meta-analysis. Schizophr Res. PMID 36417817",
    "3. Treuer T & Tohen M. (2010). Predicting course and outcome of bipolar disorder. Eur Psychiatry. PMID 20444581",
    "4. Scott K, et al. (2025). Treatment outcome prediction in bipolar disorder. Biol Psychiatry. PMID 39954922",
    "5. Sharma E, Thennarasu K, Reddy YCJ. (2014). Long-term outcome of OCD - meta-analysis. J Clin Psychiatry. PMID 25295427",
    "6. Sharma E & Math SB. (2019). Course and outcome of OCD. Indian J Psychiatry. PMID 30745676",
    "7. Hazari N, et al. (2016). Predictors of SRI response in OCD. Expert Rev Neurother. PMID 27282021",
    "8. Keyan D, et al. (2024). Predictors of trauma-focused psychotherapy response in PTSD. Psychol Bull. PMID 38884956",
    "9. Wang J, et al. (2018). Loneliness, social support and outcomes in mental health. BMC Psychiatry. PMID 29843662",
    "10. Liberona A, et al. (2024). BDNF as predictor of treatment response in schizophrenia & bipolar. Int J Mol Sci. PMID 39456983",
    "11. Bandelow B, et al. (2016/2017). Biological markers for anxiety disorders, OCD & PTSD - Parts I & II. World J Biol Psychiatry. PMID 27403679, 27419272",
    "12. Menon V. (2011). Large-scale brain networks and psychopathology: triple network model. Trends Cogn Sci. 15(10):483-506",
    "13. Menon V, Palaniyappan L, Supekar K. (2023). Integrative brain network & salience models in schizophrenia. Biol Psychiatry. PMID 36702660",
]

# ═══════════════════════════════════════════════════════════════════════════════
# BUILD
# ═══════════════════════════════════════════════════════════════════════════════
doc = SimpleDocTemplate(OUTPUT, pagesize=A4,
    topMargin=3.2*cm, bottomMargin=1.6*cm,
    leftMargin=1.5*cm, rightMargin=1.5*cm,
    title="Psychiatric Clinical Tool - Final",
    author="Orris AI")

story = []

# ── TITLE ──
tb = Table([
    [Paragraph("Psychiatric Clinical Assessment Tool", title_s)],
    [Paragraph("Prognostic Factors  ·  Routine Biomarkers  ·  Triple Network Screening", sub_s)],
    [Paragraph("Evidence-based  |  Adult Psychiatric Disorders  |  Clinical use only", sub_s)],
], colWidths=[18.5*cm])
tb.setStyle(TableStyle([
    ("BACKGROUND",(0,0),(-1,-1),DARK_NAVY),
    ("TOPPADDING",(0,0),(-1,-1),4),("BOTTOMPADDING",(0,0),(-1,-1),4),
    ("LEFTPADDING",(0,0),(-1,-1),8),
]))
story.append(tb)
story.append(Spacer(1,0.25*cm))

# ── HOW TO USE ──
how = Table([[Paragraph(
    "<b>HOW TO USE:</b>  Tick applicable clinical factors (☐) and note relevant biomarkers (◇) for each patient. "
    "Biomarkers listed are all obtainable from <b>routine blood tests, EEG, or genetics</b> - fMRI and research-only markers have been removed. "
    "The Triple Network tool (Part 3) uses observable signs and patient reports to identify predominant network dysfunction. "
    "This is a clinical aide - it does not replace clinical or diagnostic judgement.",
    note_s)]], colWidths=[18.5*cm])
how.setStyle(TableStyle([
    ("BACKGROUND",(0,0),(-1,-1),colors.HexColor("#fffbe6")),
    ("BOX",(0,0),(-1,-1),0.5,colors.HexColor("#e0c050")),
    ("TOPPADDING",(0,0),(-1,-1),6),("BOTTOMPADDING",(0,0),(-1,-1),6),
    ("LEFTPADDING",(0,0),(-1,-1),8),("RIGHTPADDING",(0,0),(-1,-1),8),
]))
story.append(how)
story.append(Spacer(1,0.3*cm))

# ════════════════════════════════════════════════
# PART 1 + 2: PROGNOSTIC CHECKLIST + BIOMARKERS
# ════════════════════════════════════════════════
part1_hdr = Table([[Paragraph(
    "  PART 1 & 2  ·  Prognostic Factors + Routine Biomarkers", sec_s)]],
    colWidths=[18.5*cm])
part1_hdr.setStyle(TableStyle([
    ("BACKGROUND",(0,0),(-1,-1),colors.HexColor("#1a3a6b")),
    ("TOPPADDING",(0,0),(-1,-1),7),("BOTTOMPADDING",(0,0),(-1,-1),7),
    ("LEFTPADDING",(0,0),(-1,-1),6),
]))
story.append(part1_hdr)
story.append(Spacer(1,0.2*cm))

# ── Transdiagnostic section ──
td_hdr = Table([[Paragraph("  🌐  TRANSDIAGNOSTIC FACTORS  (apply across ALL disorders)", sec_s)]],
               colWidths=[18.5*cm])
td_hdr.setStyle(TableStyle([
    ("BACKGROUND",(0,0),(-1,-1),colors.HexColor("#4a235a")),
    ("TOPPADDING",(0,0),(-1,-1),6),("BOTTOMPADDING",(0,0),(-1,-1),6),
    ("LEFTPADDING",(0,0),(-1,-1),6),
    ("BOX",(0,0),(-1,-1),0.5,colors.HexColor("#2d1440")),
]))
story.append(td_hdr)

# TD clinical cols
tc = Table([[Paragraph("✅  GOOD", col_good), Paragraph("⚠️  POOR", col_bad)]],
           colWidths=[9.25*cm,9.25*cm])
tc.setStyle(TableStyle([
    ("BACKGROUND",(0,0),(0,0),GOOD_GREEN),("BACKGROUND",(1,0),(1,0),BAD_RED),
    ("TOPPADDING",(0,0),(-1,-1),5),("BOTTOMPADDING",(0,0),(-1,-1),5),
    ("LEFTPADDING",(0,0),(-1,-1),4),
]))
story.append(tc)
max_td = max(len(transdiag_good),len(transdiag_bad))
td_data=[]
for i in range(max_td):
    g = cb(transdiag_good[i]) if i<len(transdiag_good) else Paragraph("",item_s)
    b = cb(transdiag_bad[i])  if i<len(transdiag_bad)  else Paragraph("",item_s)
    td_data.append([g,b])
td_t = Table(td_data, colWidths=[9.25*cm,9.25*cm])
ts = TableStyle([
    ("LEFTPADDING",(0,0),(-1,-1),5),("RIGHTPADDING",(0,0),(-1,-1),5),
    ("TOPPADDING",(0,0),(-1,-1),3),("BOTTOMPADDING",(0,0),(-1,-1),3),
    ("VALIGN",(0,0),(-1,-1),"TOP"),
    ("LINEAFTER",(0,0),(0,-1),0.5,MID_GREY),
    ("BOX",(0,0),(-1,-1),0.5,MID_GREY),
])
for i in range(max_td):
    ts.add("BACKGROUND",(0,i),(0,i),colors.HexColor("#f2fbf2") if i%2==0 else colors.HexColor("#e8f8e8"))
    ts.add("BACKGROUND",(1,i),(1,i),colors.HexColor("#fef5f5") if i%2==0 else colors.HexColor("#fdeaea"))
td_t.setStyle(ts)
story.append(td_t)

# TD biomarker
tb2_hdr = Table([[Paragraph("  🔬  TRANSDIAGNOSTIC BIOMARKERS  (routine blood / genetics)",
    PS("TB2H", fontSize=9, fontName="Helvetica-Bold", textColor=WHITE))]],
    colWidths=[18.5*cm])
tb2_hdr.setStyle(TableStyle([
    ("BACKGROUND",(0,0),(-1,-1),BIO_HDR),
    ("TOPPADDING",(0,0),(-1,-1),5),("BOTTOMPADDING",(0,0),(-1,-1),5),
    ("LEFTPADDING",(0,0),(-1,-1),6),
]))
story.append(tb2_hdr)

tb2c = Table([[Paragraph("🔵  FAVOURABLE", bio_good), Paragraph("🔴  UNFAVOURABLE", bio_bad)]],
             colWidths=[9.25*cm,9.25*cm])
tb2c.setStyle(TableStyle([
    ("BACKGROUND",(0,0),(0,0),BIO_GOOD_BG),("BACKGROUND",(1,0),(1,0),BIO_BAD_BG),
    ("TOPPADDING",(0,0),(-1,-1),4),("BOTTOMPADDING",(0,0),(-1,-1),4),
    ("LEFTPADDING",(0,0),(-1,-1),4),
]))
story.append(tb2c)

max_tdb = max(len(transdiag_bio_good),len(transdiag_bio_bad))
tbd=[]
for i in range(max_tdb):
    g = di(transdiag_bio_good[i]) if i<len(transdiag_bio_good) else Paragraph("",bio_s)
    b = di(transdiag_bio_bad[i])  if i<len(transdiag_bio_bad)  else Paragraph("",bio_s)
    tbd.append([g,b])
tbd_t = Table(tbd, colWidths=[9.25*cm,9.25*cm])
ts2 = TableStyle([
    ("LEFTPADDING",(0,0),(-1,-1),5),("RIGHTPADDING",(0,0),(-1,-1),5),
    ("TOPPADDING",(0,0),(-1,-1),3),("BOTTOMPADDING",(0,0),(-1,-1),3),
    ("VALIGN",(0,0),(-1,-1),"TOP"),
    ("LINEAFTER",(0,0),(0,-1),0.5,MID_GREY),
    ("BOX",(0,0),(-1,-1),0.5,MID_GREY),
])
for i in range(max_tdb):
    ts2.add("BACKGROUND",(0,i),(0,i),colors.HexColor("#eaf4fb") if i%2==0 else colors.HexColor("#dceef8"))
    ts2.add("BACKGROUND",(1,i),(1,i),colors.HexColor("#fef0e0") if i%2==0 else colors.HexColor("#fde5c8"))
tbd_t.setStyle(ts2)
story.append(tbd_t)

src_td = Table([[Paragraph("Source: Solmi et al. 2023 (PMID 37957292); Liberona et al. 2024 (PMID 39456983)", src_s)]],
               colWidths=[18.5*cm])
src_td.setStyle(TableStyle([
    ("BACKGROUND",(0,0),(-1,-1),colors.HexColor("#f0f0f0")),
    ("TOPPADDING",(0,0),(-1,-1),3),("BOTTOMPADDING",(0,0),(-1,-1),3),
    ("LEFTPADDING",(0,0),(-1,-1),6),
    ("BOX",(0,0),(-1,-1),0.3,MID_GREY),
]))
story.append(src_td)
story.append(Spacer(1,0.4*cm))

# ── Per-disorder sections ──
def build_disorder(d):
    els=[]
    # Header
    hdr = Table([[Paragraph(f"  {d['emoji']}  {d['name']}", sec_s)]],colWidths=[18.5*cm])
    hdr.setStyle(TableStyle([
        ("BACKGROUND",(0,0),(-1,-1),HEADER_BG),
        ("TOPPADDING",(0,0),(-1,-1),7),("BOTTOMPADDING",(0,0),(-1,-1),7),
        ("LEFTPADDING",(0,0),(-1,-1),6),
        ("BOX",(0,0),(-1,-1),0.5,DARK_NAVY),
    ]))
    els.append(hdr)
    # Clinical col headers
    ch = Table([[Paragraph("✅  GOOD PROGNOSTIC FACTORS",col_good),Paragraph("⚠️  POOR PROGNOSTIC FACTORS",col_bad)]],
               colWidths=[9.25*cm,9.25*cm])
    ch.setStyle(TableStyle([
        ("BACKGROUND",(0,0),(0,0),GOOD_GREEN),("BACKGROUND",(1,0),(1,0),BAD_RED),
        ("TOPPADDING",(0,0),(-1,-1),5),("BOTTOMPADDING",(0,0),(-1,-1),5),
        ("LEFTPADDING",(0,0),(-1,-1),4),
        ("LINEBELOW",(0,0),(-1,-1),0.5,MID_GREY),
    ]))
    els.append(ch)
    # Clinical rows
    ml = max(len(d["good"]),len(d["bad"]))
    rows=[]
    for i in range(ml):
        g = cb(d["good"][i]) if i<len(d["good"]) else Paragraph("",item_s)
        b = cb(d["bad"][i])  if i<len(d["bad"])  else Paragraph("",item_s)
        rows.append([g,b])
    ct = Table(rows,colWidths=[9.25*cm,9.25*cm])
    ts3=TableStyle([
        ("LEFTPADDING",(0,0),(-1,-1),5),("RIGHTPADDING",(0,0),(-1,-1),5),
        ("TOPPADDING",(0,0),(-1,-1),3),("BOTTOMPADDING",(0,0),(-1,-1),3),
        ("VALIGN",(0,0),(-1,-1),"TOP"),
        ("LINEAFTER",(0,0),(0,-1),0.5,MID_GREY),
        ("BOX",(0,0),(-1,-1),0.5,MID_GREY),
    ])
    for i in range(ml):
        ts3.add("BACKGROUND",(0,i),(0,i),colors.HexColor("#f2fbf2") if i%2==0 else colors.HexColor("#e8f8e8"))
        ts3.add("BACKGROUND",(1,i),(1,i),colors.HexColor("#fef5f5") if i%2==0 else colors.HexColor("#fdeaea"))
    ct.setStyle(ts3)
    els.append(ct)
    # Biomarker header
    bh = Table([[Paragraph("  🔬  ROUTINE BIOMARKERS  (blood / EEG / genetics)",
        PS(f"BH{d['name'][:3]}", fontSize=9, fontName="Helvetica-Bold", textColor=WHITE))]],
        colWidths=[18.5*cm])
    bh.setStyle(TableStyle([
        ("BACKGROUND",(0,0),(-1,-1),BIO_HDR),
        ("TOPPADDING",(0,0),(-1,-1),5),("BOTTOMPADDING",(0,0),(-1,-1),5),
        ("LEFTPADDING",(0,0),(-1,-1),6),
    ]))
    els.append(bh)
    bch = Table([[Paragraph("🔵  FAVOURABLE PROFILE",bio_good),Paragraph("🔴  UNFAVOURABLE PROFILE",bio_bad)]],
                colWidths=[9.25*cm,9.25*cm])
    bch.setStyle(TableStyle([
        ("BACKGROUND",(0,0),(0,0),BIO_GOOD_BG),("BACKGROUND",(1,0),(1,0),BIO_BAD_BG),
        ("TOPPADDING",(0,0),(-1,-1),4),("BOTTOMPADDING",(0,0),(-1,-1),4),
        ("LEFTPADDING",(0,0),(-1,-1),4),
        ("LINEBELOW",(0,0),(-1,-1),0.5,MID_GREY),
    ]))
    els.append(bch)
    mb = max(len(d["bio_good"]),len(d["bio_bad"]))
    brows=[]
    for i in range(mb):
        g = di(d["bio_good"][i]) if i<len(d["bio_good"]) else Paragraph("",bio_s)
        b = di(d["bio_bad"][i])  if i<len(d["bio_bad"])  else Paragraph("",bio_s)
        brows.append([g,b])
    bt = Table(brows,colWidths=[9.25*cm,9.25*cm])
    ts4=TableStyle([
        ("LEFTPADDING",(0,0),(-1,-1),5),("RIGHTPADDING",(0,0),(-1,-1),5),
        ("TOPPADDING",(0,0),(-1,-1),3),("BOTTOMPADDING",(0,0),(-1,-1),3),
        ("VALIGN",(0,0),(-1,-1),"TOP"),
        ("LINEAFTER",(0,0),(0,-1),0.5,MID_GREY),
        ("BOX",(0,0),(-1,-1),0.5,MID_GREY),
    ])
    for i in range(mb):
        ts4.add("BACKGROUND",(0,i),(0,i),colors.HexColor("#eaf4fb") if i%2==0 else colors.HexColor("#dceef8"))
        ts4.add("BACKGROUND",(1,i),(1,i),colors.HexColor("#fef0e0") if i%2==0 else colors.HexColor("#fde5c8"))
    bt.setStyle(ts4)
    els.append(bt)
    # Source strip
    st = Table([[Paragraph(f"Source: {d['src']}", src_s)]],colWidths=[18.5*cm])
    st.setStyle(TableStyle([
        ("BACKGROUND",(0,0),(-1,-1),colors.HexColor("#f0f0f0")),
        ("TOPPADDING",(0,0),(-1,-1),3),("BOTTOMPADDING",(0,0),(-1,-1),3),
        ("LEFTPADDING",(0,0),(-1,-1),6),
        ("BOX",(0,0),(-1,-1),0.3,MID_GREY),
    ]))
    els.append(st)
    els.append(Spacer(1,0.35*cm))
    return els

for d in disorders:
    story.extend(build_disorder(d))

# ════════════════════════════════════════════════
# PART 3: TRIPLE NETWORK SCREENING (SECTION 2)
# ════════════════════════════════════════════════
story.append(PageBreak())

part3_hdr = Table([[Paragraph(
    "  PART 3  ·  Triple Network Clinical Screening Tool", sec_s)]],
    colWidths=[18.5*cm])
part3_hdr.setStyle(TableStyle([
    ("BACKGROUND",(0,0),(-1,-1),colors.HexColor("#1a3a6b")),
    ("TOPPADDING",(0,0),(-1,-1),7),("BOTTOMPADDING",(0,0),(-1,-1),7),
    ("LEFTPADDING",(0,0),(-1,-1),6),
]))
story.append(part3_hdr)
story.append(Spacer(1,0.2*cm))

# Background box
bg_box = Table([[Paragraph(
    "<b>TRIPLE NETWORK MODEL (Menon, 2011):</b>  Three large-scale brain networks govern cognition and emotional regulation. "
    "The <b>Salience Network (SN)</b> - insula + dACC - detects and filters threats and switches between the other two networks. "
    "The <b>Default Mode Network (DMN)</b> - mPFC + PCC - drives self-referential, internally focused thought. "
    "The <b>Executive Control Network (ECN)</b> - dlPFC + lateral PPC - governs working memory, cognitive flexibility, and inhibitory control. "
    "Dysfunction in these networks and their interactions underlies cognitive and emotional abnormalities across major psychiatric disorders.",
    note_s)]], colWidths=[18.5*cm])
bg_box.setStyle(TableStyle([
    ("BACKGROUND",(0,0),(-1,-1),colors.HexColor("#eaf4fb")),
    ("BOX",(0,0),(-1,-1),0.5,BIO_HDR),
    ("TOPPADDING",(0,0),(-1,-1),6),("BOTTOMPADDING",(0,0),(-1,-1),6),
    ("LEFTPADDING",(0,0),(-1,-1),8),("RIGHTPADDING",(0,0),(-1,-1),8),
]))
story.append(bg_box)
story.append(Spacer(1,0.2*cm))

how2 = Table([[Paragraph(
    "<b>HOW TO USE:</b>  During the clinical interview, tick (☐) observable signs and note matching patient reports. "
    "A consistent pattern within one domain suggests that network is clinically prominent. "
    "Multiple network involvement is common in severe illness.",
    note_s)]], colWidths=[18.5*cm])
how2.setStyle(TableStyle([
    ("BACKGROUND",(0,0),(-1,-1),colors.HexColor("#fffbe6")),
    ("BOX",(0,0),(-1,-1),0.5,colors.HexColor("#e0c050")),
    ("TOPPADDING",(0,0),(-1,-1),5),("BOTTOMPADDING",(0,0),(-1,-1),5),
    ("LEFTPADDING",(0,0),(-1,-1),8),("RIGHTPADDING",(0,0),(-1,-1),8),
]))
story.append(how2)
story.append(Spacer(1,0.25*cm))

for net in network_rows:
    # Network header
    nh = Table([[Paragraph(net["network"], net_lbl)]],colWidths=[18.5*cm])
    nh.setStyle(TableStyle([
        ("BACKGROUND",(0,0),(-1,-1),net["color"]),
        ("TOPPADDING",(0,0),(-1,-1),7),("BOTTOMPADDING",(0,0),(-1,-1),7),
        ("LEFTPADDING",(0,0),(-1,-1),6),
        ("BOX",(0,0),(-1,-1),0.5,DARK_NAVY),
    ]))
    story.append(nh)
    # Anchors + disorders
    meta = Table([[
        Paragraph(f"<b>Brain anchors:</b>  {net['anchors']}", net_small),
        Paragraph(f"<b>Disorders:</b>  {net['disorders']}", net_small),
    ]], colWidths=[6.5*cm,12*cm])
    meta.setStyle(TableStyle([
        ("BACKGROUND",(0,0),(-1,-1),net["light"]),
        ("TOPPADDING",(0,0),(-1,-1),4),("BOTTOMPADDING",(0,0),(-1,-1),4),
        ("LEFTPADDING",(0,0),(-1,-1),6),
        ("BOX",(0,0),(-1,-1),0.3,MID_GREY),
    ]))
    story.append(meta)
    # Column headers
    nch = Table([[
        Paragraph(f"☐  OBSERVABLE SIGNS (clinician)", PS(f"NSH{net['color']}",
            fontSize=8.5, fontName="Helvetica-Bold", textColor=net["color"])),
        Paragraph("💬  PATIENT REPORTS (verbatim prompts)", PS("NPH",
            fontSize=8.5, fontName="Helvetica-Bold", textColor=DARK_NAVY)),
    ]], colWidths=[9.25*cm,9.25*cm])
    nch.setStyle(TableStyle([
        ("BACKGROUND",(0,0),(0,0),net["light"]),
        ("BACKGROUND",(1,0),(1,0),colors.HexColor("#f5f5f5")),
        ("TOPPADDING",(0,0),(-1,-1),4),("BOTTOMPADDING",(0,0),(-1,-1),4),
        ("LEFTPADDING",(0,0),(-1,-1),5),
        ("LINEAFTER",(0,0),(0,0),0.5,MID_GREY),
        ("LINEBELOW",(0,0),(-1,-1),0.5,MID_GREY),
    ]))
    story.append(nch)
    # Sign/report rows
    maxr = max(len(net["signs"]),len(net["reports"]))
    nrows=[]
    for i in range(maxr):
        s = Paragraph(f"☐  {net['signs'][i]}", item_s) if i<len(net["signs"]) else Paragraph("",item_s)
        r = Paragraph(net["reports"][i], italic_s)       if i<len(net["reports"])else Paragraph("",italic_s)
        nrows.append([s,r])
    nt = Table(nrows,colWidths=[9.25*cm,9.25*cm])
    nts=TableStyle([
        ("LEFTPADDING",(0,0),(-1,-1),5),("RIGHTPADDING",(0,0),(-1,-1),5),
        ("TOPPADDING",(0,0),(-1,-1),3),("BOTTOMPADDING",(0,0),(-1,-1),3),
        ("VALIGN",(0,0),(-1,-1),"TOP"),
        ("LINEAFTER",(0,0),(0,-1),0.5,MID_GREY),
        ("BOX",(0,0),(-1,-1),0.5,MID_GREY),
    ])
    for i in range(maxr):
        nts.add("BACKGROUND",(0,i),(0,i),net["light"] if i%2==0 else colors.HexColor("#fafafa"))
        nts.add("BACKGROUND",(1,i),(1,i),colors.HexColor("#f9f9f9") if i%2==0 else WHITE)
    nt.setStyle(nts)
    story.append(nt)
    # Note strip
    note_row = Table([[
        Paragraph(f"<i>{net['note']}</i>", warn_s),
        Paragraph(f"Ref: {net['src']}", src_s),
    ]], colWidths=[11*cm,7.5*cm])
    note_row.setStyle(TableStyle([
        ("BACKGROUND",(0,0),(-1,-1),colors.HexColor("#f5f5f5")),
        ("TOPPADDING",(0,0),(-1,-1),3),("BOTTOMPADDING",(0,0),(-1,-1),3),
        ("LEFTPADDING",(0,0),(-1,-1),6),
        ("BOX",(0,0),(-1,-1),0.3,MID_GREY),
        ("VALIGN",(0,0),(-1,-1),"TOP"),
    ]))
    story.append(note_row)
    story.append(Spacer(1,0.3*cm))

# ── Interpretation table ──
story.append(sec_bar("INTERPRETATION GUIDE"))
story.append(Spacer(1,0.15*cm))
interp_t = Table(interp_rows, colWidths=[6*cm,6*cm,6.5*cm])
its=TableStyle([
    ("BACKGROUND",(0,0),(-1,0),DARK_NAVY),
    ("TEXTCOLOR",(0,0),(-1,0),WHITE),
    ("FONTNAME",(0,0),(-1,0),"Helvetica-Bold"),
    ("FONTSIZE",(0,0),(-1,-1),8),
    ("TOPPADDING",(0,0),(-1,-1),4),("BOTTOMPADDING",(0,0),(-1,-1),4),
    ("LEFTPADDING",(0,0),(-1,-1),5),("RIGHTPADDING",(0,0),(-1,-1),5),
    ("GRID",(0,0),(-1,-1),0.3,MID_GREY),
    ("VALIGN",(0,0),(-1,-1),"TOP"),
])
for i in range(1,len(interp_rows)):
    its.add("BACKGROUND",(0,i),(-1,i),colors.HexColor("#f5f5f5") if i%2==0 else WHITE)
interp_t.setStyle(its)
story.append(interp_t)
story.append(Spacer(1,0.2*cm))

# Limitations
lim = Table([[Paragraph(
    "⚠️  <b>LIMITATIONS:</b>  Network dysfunction cannot be confirmed from clinical observation alone - neuroimaging is required for definitive characterisation. "
    "This tool prompts network-informed clinical reasoning and focused questioning. It has not been validated as a psychometric instrument. "
    "Multiple network involvement is the rule, not the exception, in severe psychiatric illness.",
    warn_s)]], colWidths=[18.5*cm])
lim.setStyle(TableStyle([
    ("BACKGROUND",(0,0),(-1,-1),colors.HexColor("#fdf0e0")),
    ("BOX",(0,0),(-1,-1),0.5,colors.HexColor("#c07030")),
    ("TOPPADDING",(0,0),(-1,-1),5),("BOTTOMPADDING",(0,0),(-1,-1),5),
    ("LEFTPADDING",(0,0),(-1,-1),8),("RIGHTPADDING",(0,0),(-1,-1),8),
]))
story.append(lim)
story.append(Spacer(1,0.3*cm))

# ── NOTES PAGE ──
story.append(PageBreak())
nh2 = Table([[Paragraph("  📋  CLINICAL NOTES", sec_s)]],colWidths=[18.5*cm])
nh2.setStyle(TableStyle([
    ("BACKGROUND",(0,0),(-1,-1),HEADER_BG),
    ("TOPPADDING",(0,0),(-1,-1),7),("BOTTOMPADDING",(0,0),(-1,-1),7),
    ("LEFTPADDING",(0,0),(-1,-1),6),
]))
story.append(nh2)
story.append(Spacer(1,0.3*cm))
for _ in range(22):
    story.append(Paragraph("", item_s))
    story.append(HRFlowable(width="100%", thickness=0.4, color=MID_GREY))
    story.append(Spacer(1,0.45*cm))

# ── REFERENCES ──
story.append(Spacer(1,0.3*cm))
rh = Table([[Paragraph("  📚  KEY REFERENCES", sec_s)]],colWidths=[18.5*cm])
rh.setStyle(TableStyle([
    ("BACKGROUND",(0,0),(-1,-1),HEADER_BG),
    ("TOPPADDING",(0,0),(-1,-1),6),("BOTTOMPADDING",(0,0),(-1,-1),6),
    ("LEFTPADDING",(0,0),(-1,-1),6),
]))
story.append(rh)
story.append(Spacer(1,0.15*cm))
for r in refs:
    story.append(Paragraph(r, src_s))
    story.append(Spacer(1,0.1*cm))

doc.build(story, onFirstPage=page_bg, onLaterPages=page_bg)
print("DONE:", OUTPUT)
Running Command

cd /tmp/workspace/psych-checklist && python build_final.py 2>&1

Running Command

ls -lh /tmp/workspace/psych-checklist/Psychiatric_Clinical_Tool_FINAL.pdf

Your clean final PDF is ready:

Psychiatric Clinical Tool FINAL

PDF Document · PDF


Here is exactly what is in it:
PART 1 & 2 - Prognostic Factors + Routine Biomarkers
  • Transdiagnostic section (applies across all 6 disorders)
  • 6 disorder-specific sections (Schizophrenia, Bipolar, MDD, OCD, PTSD, Anxiety)
  • Each disorder: two-column Good ✅ / Poor ⚠️ prognostic factors with tick boxes
  • Each disorder: Routine Biomarkers only - blood tests (BDNF, CRP, IL-6, cortisol, TSH, folate/B12, homocysteine, HRV, oxytocin, MHPG), EEG markers (ERN, theta), and genetics (BDNF Val66Met, 5-HTTLPR, polygenic risk score)
  • All fMRI, PET, structural MRI, and research-only biomarkers removed
PART 3 - Triple Network Clinical Screening Tool (Section 2 only)
  • Background box explaining the Menon triple network model
  • 4 colour-coded network blocks: SN (red), DMN (blue), ECN (green), LC-NE modifier (amber)
  • Each block: brain anchors, relevant disorders, observable signs with tick boxes, verbatim patient report prompts
  • Interpretation guide table: sign patterns → likely network → disorder context
  • Limitations caveat
Final pages
  • Blank clinical notes page
  • 13 key references with PMIDs

looks excellent, can we put each disorder on a single page or adjust each disorder on a single page in the pdf

On it - I'll fit each disorder onto its own page by using a compact 3-column layout (good / bad / biomarkers side by side) and tighter spacing.
Reading File
Good - I have everything I need from the previous script. I'll write the new version now with each disorder on its own page using a 3-column layout.
Writing File

~/psych-checklist/build_final_v2.py

"""
FINAL PDF v2 - One disorder per page
Layout per disorder page:
  - Full-width header bar (disorder name)
  - 3-column table: Good Prognostic | Poor Prognostic | Routine Biomarkers
  - Source strip footer
"""
from reportlab.lib.pagesizes import A4
from reportlab.lib import colors
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
from reportlab.lib.units import cm
from reportlab.platypus import (
    SimpleDocTemplate, Paragraph, Spacer, Table, TableStyle,
    HRFlowable, PageBreak, KeepTogether
)
from reportlab.lib.enums import TA_LEFT, TA_CENTER

OUTPUT = "/tmp/workspace/psych-checklist/Psychiatric_Clinical_Tool_FINAL_v2.pdf"

# ── Palette ───────────────────────────────────────────────────────────────────
DARK_NAVY   = colors.HexColor("#1a2b4a")
TEAL        = colors.HexColor("#007b7f")
WHITE       = colors.white
MID_GREY    = colors.HexColor("#cccccc")
LIGHT_GREY  = colors.HexColor("#f5f5f5")
GOOD_GREEN  = colors.HexColor("#d4edda")
BAD_RED     = colors.HexColor("#f8d7da")
GOOD_DARK   = colors.HexColor("#155724")
BAD_DARK    = colors.HexColor("#721c24")
HEADER_BG   = colors.HexColor("#2c3e6b")
BIO_GOOD_BG = colors.HexColor("#d6eef7")
BIO_BAD_BG  = colors.HexColor("#fde8d0")   # kept for transdiag section
BIO_BG      = colors.HexColor("#eaf4fb")
BIO_HDR     = colors.HexColor("#1a6b8a")
BIO_TXT     = colors.HexColor("#1a3a4a")
SN_COL      = colors.HexColor("#c0392b");  SN_LIGHT  = colors.HexColor("#fadbd8")
DMN_COL     = colors.HexColor("#1a5276");  DMN_LIGHT = colors.HexColor("#d6eaf8")
ECN_COL     = colors.HexColor("#1e8449");  ECN_LIGHT = colors.HexColor("#d5f5e3")
NE_COL      = colors.HexColor("#7d6608");  NE_LIGHT  = colors.HexColor("#fef9e7")

# ── Styles ────────────────────────────────────────────────────────────────────
SS = getSampleStyleSheet()
def PS(name, **kw):
    return ParagraphStyle(name, parent=kw.pop("parent", SS["Normal"]), **kw)

title_s  = PS("TS",  fontSize=20, textColor=WHITE, alignment=TA_CENTER, fontName="Helvetica-Bold")
sub_s    = PS("SS2", fontSize=9,  textColor=colors.HexColor("#c8d8f0"),  alignment=TA_CENTER, fontName="Helvetica-Oblique")
sec_s    = PS("SHS", fontSize=13, textColor=WHITE, fontName="Helvetica-Bold", leftIndent=6)
dis_s    = PS("DS",  fontSize=14, textColor=WHITE, fontName="Helvetica-Bold", alignment=TA_CENTER)
col_hdr_g= PS("CHG", fontSize=9,  textColor=GOOD_DARK, fontName="Helvetica-Bold", alignment=TA_CENTER)
col_hdr_b= PS("CHB", fontSize=9,  textColor=BAD_DARK,  fontName="Helvetica-Bold", alignment=TA_CENTER)
col_hdr_bio=PS("CHBio",fontSize=9,textColor=BIO_HDR,   fontName="Helvetica-Bold", alignment=TA_CENTER)
item_s   = PS("IS",  fontSize=8,  fontName="Helvetica", leading=11, leftIndent=3, textColor=colors.HexColor("#222222"))
bio_s    = PS("BS",  fontSize=8,  fontName="Helvetica", leading=11, leftIndent=3, textColor=BIO_TXT)
src_s    = PS("SRS", fontSize=6.5,fontName="Helvetica-Oblique", textColor=colors.HexColor("#555555"), leftIndent=4)
note_s   = PS("NS",  fontSize=8,  fontName="Helvetica", leading=11, leftIndent=6, textColor=colors.HexColor("#333333"))
warn_s   = PS("WS",  fontSize=7.5,fontName="Helvetica-Oblique", textColor=colors.HexColor("#7b3a10"), leftIndent=4, leading=10)
italic_s = PS("ITS", fontSize=8,  fontName="Helvetica-Oblique", textColor=colors.HexColor("#444444"), leading=11)
net_lbl  = PS("NL",  fontSize=11, fontName="Helvetica-Bold", textColor=WHITE, alignment=TA_CENTER)
net_tiny = PS("NT",  fontSize=7,  fontName="Helvetica", textColor=colors.HexColor("#555555"), leading=9)
td_good  = PS("TDG", fontSize=9,  textColor=GOOD_DARK, fontName="Helvetica-Bold", alignment=TA_CENTER)
td_bad   = PS("TDB", fontSize=9,  textColor=BAD_DARK,  fontName="Helvetica-Bold", alignment=TA_CENTER)
td_bio   = PS("TDBIO",fontSize=9, textColor=BIO_HDR,   fontName="Helvetica-Bold", alignment=TA_CENTER)

# Column widths for 3-col layout
CW = 18.5*cm
C1 = 6.1*cm   # Good
C2 = 6.1*cm   # Bad
C3 = 6.3*cm   # Biomarkers

def cb(t):  return Paragraph(f"☐  {t}", item_s)
def di(t):  return Paragraph(f"◇  {t}", bio_s)

def sec_bar(text, bg=HEADER_BG):
    t = Table([[Paragraph(f"  {text}", sec_s)]], colWidths=[CW])
    t.setStyle(TableStyle([
        ("BACKGROUND",(0,0),(-1,-1),bg),
        ("TOPPADDING",(0,0),(-1,-1),7),("BOTTOMPADDING",(0,0),(-1,-1),7),
        ("LEFTPADDING",(0,0),(-1,-1),6),
        ("BOX",(0,0),(-1,-1),0.5,DARK_NAVY),
    ]))
    return t

def page_bg(canvas, doc):
    canvas.saveState()
    w, h = A4
    canvas.setFillColor(DARK_NAVY)
    canvas.rect(0, h-2.8*cm, w, 2.8*cm, fill=1, stroke=0)
    canvas.setFillColor(TEAL)
    canvas.rect(0, h-3.0*cm, w, 0.22*cm, fill=1, stroke=0)
    canvas.setFillColor(colors.HexColor("#f0f0f0"))
    canvas.rect(0, 0, w, 1.2*cm, fill=1, stroke=0)
    canvas.setFillColor(MID_GREY)
    canvas.rect(0, 1.2*cm, w, 0.05*cm, fill=1, stroke=0)
    canvas.setFont("Helvetica-Oblique", 7)
    canvas.setFillColor(colors.HexColor("#555555"))
    canvas.drawCentredString(w/2, 0.42*cm,
        "Psychiatric Clinical Assessment Tool (Final)  |  Clinical use only - not a standalone diagnostic instrument")
    canvas.drawRightString(w-1.5*cm, 0.42*cm, f"Page {doc.page}")
    canvas.restoreState()

# ═══════════════════════════════════════════════════════════════════════════════
# DATA
# ═══════════════════════════════════════════════════════════════════════════════
disorders = [
  {
    "name":"SCHIZOPHRENIA","emoji":"🧠",
    "good":[
      "Female sex",
      "Later / acute onset of illness",
      "Predominantly positive symptoms",
      "Good premorbid functioning",
      "Short duration of untreated psychosis",
      "Good insight into illness",
      "Strong social support network",
      "Employment / higher education",
      "Early and intensive treatment",
      "Absence of negative symptoms",
      "No comorbid substance use",
      "Married / stable relationship",
    ],
    "bad":[
      "Male sex",
      "Insidious / early onset",
      "Predominantly negative symptoms",
      "Poor premorbid functioning",
      "Long duration of untreated psychosis",
      "Poor insight / anosognosia",
      "Social isolation",
      "Unemployment / low education",
      "Delayed treatment initiation",
      "Prominent cognitive deficits",
      "Comorbid substance use disorder",
      "Family history of schizophrenia",
    ],
    "bio":[
      "🔵 Higher serum BDNF (→ antipsychotic response)",
      "🔵 BDNF Val/Val genotype",
      "🔵 Low CRP / IL-6",
      "🔵 Normal folate & B12",
      "🔵 Low homocysteine",
      "🔵 Low polygenic risk score",
      "─────────────────",
      "🔴 Low serum BDNF",
      "🔴 BDNF Met carrier",
      "🔴 Elevated CRP / IL-6",
      "🔴 Elevated homocysteine",
      "🔴 Low folate / B12",
      "🔴 High polygenic risk score",
    ],
    "src":"Solmi et al. 2023 (PMID 37957292); Molstrom et al. 2022 (PMID 36417817); Liberona et al. 2024 (PMID 39456983)",
  },
  {
    "name":"BIPOLAR DISORDER","emoji":"🔄",
    "good":[
      "Later age of onset",
      "Fewer prior mood episodes",
      "Good baseline psychosocial functioning",
      "Long-term symptomatic remission",
      "Good treatment adherence",
      "Strong social / family support",
      "Absence of psychotic features",
      "No comorbid anxiety or substance use",
      "Higher education / employment",
    ],
    "bad":[
      "Early age of onset",
      "High number of prior episodes",
      "Poor interepisode functioning",
      "Rapid cycling course",
      "Psychotic features present",
      "Comorbid ADHD",
      "Comorbid anxiety disorder",
      "Comorbid substance use disorder",
      "Comorbid personality disorder",
      "Cognitive impairment",
      "Poor medication adherence",
    ],
    "bio":[
      "🔵 Higher serum BDNF (→ lithium response)",
      "🔵 BDNF Val/Val genotype",
      "🔵 Low CRP / IL-6",
      "🔵 Normal cortisol awakening response",
      "🔵 Normal TSH (euthyroid)",
      "🔵 Therapeutic serum lithium level",
      "🔵 Low polygenic risk score",
      "─────────────────",
      "🔴 Low serum BDNF",
      "🔴 Elevated CRP / IL-6 / TNF-alpha",
      "🔴 Elevated cortisol / flattened diurnal",
      "🔴 Hypothyroidism (elevated TSH)",
      "🔴 Elevated homocysteine",
      "🔴 High polygenic risk (schiz / MDD)",
    ],
    "src":"Treuer & Tohen 2010 (PMID 20444581); Scott et al. 2025 (PMID 39954922); Liberona et al. 2024 (PMID 39456983)",
  },
  {
    "name":"MAJOR DEPRESSIVE DISORDER (MDD)","emoji":"🌧️",
    "good":[
      "Female sex",
      "Single depressive episode",
      "Acute onset with identifiable trigger",
      "Mild-to-moderate baseline severity",
      "Early improvement in first 2 weeks",
      "Good social support",
      "Higher education / employment",
      "No residual symptoms at remission",
      "No childhood maltreatment",
    ],
    "bad":[
      "Recurrent episode history",
      "Residual depressive symptoms after Rx",
      "Childhood maltreatment",
      "Comorbid anxiety disorder",
      "Comorbid physical illness / chronic pain",
      "Comorbid personality disorder",
      "High baseline symptom severity",
      "Long duration of untreated depression",
      "Poor social support",
      "Negative life events",
      "Suicidality at presentation",
    ],
    "bio":[
      "🔵 Higher serum BDNF",
      "🔵 BDNF Val/Val genotype",
      "🔵 Low CRP / IL-6 (→ better SSRI response)",
      "🔵 Normal TSH (euthyroid)",
      "🔵 Normal folate / B12",
      "🔵 Normal homocysteine",
      "🔵 Rising BDNF after 2 weeks of Rx",
      "─────────────────",
      "🔴 Low serum BDNF",
      "🔴 Elevated CRP / IL-6 (→ poor SSRI response)",
      "🔴 Hypothyroidism",
      "🔴 Low folate / B12",
      "🔴 Elevated homocysteine",
      "🔴 High polygenic risk score",
      "🔴 ACC theta deficit on EEG",
    ],
    "src":"Solmi et al. 2023 (PMID 37957292); Transl Psychiatry 2019; Liberona et al. 2024 (PMID 39456983)",
  },
  {
    "name":"OCD  (Obsessive-Compulsive Disorder)","emoji":"🔁",
    "good":[
      "Short illness duration at presentation",
      "Low symptom severity (low Y-BOCS)",
      "Good insight into obsessions",
      "Later age of onset",
      "Female sex",
      "Early and intensive treatment",
      "Long-term maintenance treatment",
      "Good social / family support",
      "No comorbid personality disorder",
      "Pure obsessional or checking subtype",
    ],
    "bad":[
      "Early age of onset",
      "Long illness duration at presentation",
      "High baseline Y-BOCS score",
      "Poor insight / overvalued ideation",
      "Male sex",
      "Hoarding or symmetry subtype",
      "Contamination-washing subtype (SRI response)",
      "Comorbid schizotypal or borderline PD",
      "Comorbid severe depression",
      "Unemployment / single / unmarried",
      "Family dysfunction / high EE",
    ],
    "bio":[
      "🔵 Elevated blood 5-HIAA (serotonin metabolite)",
      "🔵 Normal platelet 5-HTT binding",
      "🔵 Low CRP",
      "🔵 SLC6A4 L/L genotype (5-HTTLPR)",
      "─────────────────",
      "🔴 Low 5-HIAA (→ poor SRI response)",
      "🔴 SLC6A4 s/s or s/L genotype",
      "🔴 DRD4 receptor gene variants",
      "🔴 Elevated CRP",
      "🔴 Low serum BDNF",
    ],
    "src":"Sharma et al. 2014 (PMID 25295427); Sharma & Math 2019 (PMID 30745676); Hazari et al. 2016 (PMID 27282021); Bandelow et al. 2017 (PMID 27419272)",
  },
  {
    "name":"PTSD","emoji":"⚡",
    "good":[
      "Single, adult-onset trauma",
      "Female sex (for treatment response)",
      "Good social support",
      "Higher education",
      "Strong executive control / cognition",
      "Early initiation of trauma-focused Rx",
      "No comorbid depression / alcohol use",
      "Single trauma type",
    ],
    "bad":[
      "Male sex",
      "Childhood / early-onset trauma (complex PTSD)",
      "Greater cumulative trauma exposure",
      "Combat trauma history",
      "Poor / absent social support",
      "Comorbid depression",
      "Comorbid alcohol / substance use",
      "Comorbid chronic pain",
      "Comorbid sleep disorder",
      "Older age at treatment",
      "Higher anger dysregulation",
    ],
    "bio":[
      "🔵 Normal serum cortisol",
      "🔵 Normal heart rate variability (HRV)",
      "🔵 Normal glucocorticoid receptor sensitivity",
      "🔵 Low CRP / IL-6",
      "🔵 Low polygenic risk score for PTSD",
      "─────────────────",
      "🔴 Low cortisol / enhanced dex suppression",
      "🔴 Reduced HRV (sympathetic hypertonicity)",
      "🔴 Elevated CRP / IL-6",
      "🔴 Elevated urinary MHPG (NE metabolite)",
      "🔴 High polygenic risk score for PTSD",
    ],
    "src":"Keyan et al. 2024 (PMID 38884956); Solmi et al. 2023 (PMID 37957292); Bandelow et al. 2016/2017 (PMID 27403679, 27419272)",
  },
  {
    "name":"ANXIETY DISORDERS  (Panic · Social Anxiety · GAD)","emoji":"😰",
    "good":[
      "Later / adult onset",
      "Milder baseline symptom severity",
      "Good social support",
      "No comorbid mood disorder",
      "Early treatment initiation",
      "Higher education / employment",
      "Limited avoidance at presentation",
      "Single anxiety disorder",
      "Good treatment adherence",
      "Absent high neuroticism trait",
    ],
    "bad":[
      "Childhood / early onset",
      "Severe baseline symptoms",
      "Comorbid major depressive disorder",
      "Comorbid personality disorder",
      "Comorbid substance use disorder",
      "Multiple comorbid anxiety disorders",
      "Entrenched avoidance behaviours",
      "High neuroticism trait",
      "Poor social support",
      "Long duration untreated",
    ],
    "bio":[
      "🔵 Normal morning cortisol / diurnal pattern",
      "🔵 Normal HRV at rest",
      "🔵 Normal serum oxytocin (social anxiety)",
      "🔵 Low CRP / IL-6",
      "🔵 5-HTTLPR L/L genotype",
      "─────────────────",
      "🔴 Elevated morning cortisol / HPA dysreg.",
      "🔴 Reduced HRV (autonomic hyperarousal)",
      "🔴 Low serum oxytocin (social anxiety)",
      "🔴 Elevated CRP / IL-6",
      "🔴 5-HTTLPR s/s genotype",
      "🔴 ERN elevation on EEG (GAD severity)",
    ],
    "src":"Solmi et al. 2023 (PMID 37957292); Wang et al. 2018 (PMID 29843662); Bandelow et al. 2017 (PMID 27419272)",
  },
]

# ── Transdiagnostic data ───────────────────────────────────────────────────────
td_good = [
    "Female sex",
    "Higher education",
    "Social contacts and employment",
    "Early access to treatment",
    "Low baseline symptom burden",
    "Better baseline quality of life",
    "Absence of comorbid mental / physical disorders",
    "Good treatment adherence",
    "Absence of negative life events",
]
td_bad = [
    "Male sex",
    "Younger age at onset",
    "Higher disease-specific symptom burden",
    "Higher depressive / anxiety symptoms (comorbid)",
    "Greater global illness severity",
    "More prior episodes / relapses",
    "Negative life events",
    "Social isolation / unemployment",
    "Comorbid mental or physical disorders",
]
td_bio = [
    "🔵 Higher serum BDNF",
    "🔵 BDNF Val/Val genotype",
    "🔵 Low CRP / IL-6 / TNF-alpha",
    "🔵 Normal HPA axis cortisol",
    "🔵 Normal TSH (euthyroid)",
    "🔵 Normal folate / B12 / homocysteine",
    "─────────────────",
    "🔴 Low serum BDNF",
    "🔴 Elevated CRP / IL-6 / TNF-alpha",
    "🔴 HPA axis dysregulation",
    "🔴 Hypothyroidism",
    "🔴 Low folate / B12 / elevated homocysteine",
    "🔴 High disorder-specific polygenic risk score",
]

# ── Triple network data ────────────────────────────────────────────────────────
network_rows = [
    {
        "network":"SN  Hijack\n(Salience Network Hyperactivation)",
        "color":SN_COL,"light":SN_LIGHT,
        "anchors":"Anterior insula + dorsal ACC",
        "disorders":"PTSD, GAD, Panic, Schizophrenia",
        "signs":[
            "Exaggerated startle response",
            "Dilated pupils / tachycardia on exam",
            "Hypervigilant environmental scanning",
            "Freezing / flinching to neutral stimuli",
            "Intrusive re-experiencing (PTSD)",
            "Misattribution of threat to neutral stimuli",
        ],
        "reports":[
            '"Everything feels like an emergency."',
            '"My heart races for no reason."',
            '"I keep thinking that stranger is threatening me."',
            '"I can\'t relax even when I know I\'m safe."',
        ],
        "note":"In schizophrenia: SN hijack = aberrant salience (neutral stimuli feel significant/threatening). Distinguish from LC-NE hyperarousal by autonomic prominence.",
        "src":"Menon 2011 (Trends Cogn Sci); Menon et al. 2023 (PMID 36702660)",
    },
    {
        "network":"DMN  Hyperactivity\n(Default Mode Network Overengagement)",
        "color":DMN_COL,"light":DMN_LIGHT,
        "anchors":"mPFC + posterior cingulate cortex + angular gyrus",
        "disorders":"MDD, GAD, Rumination OCD, Bipolar depression",
        "signs":[
            "Internally preoccupied; returns quickly to internal train of thought",
            "Delayed response to questions (attentional capture)",
            "Excessive self-referential speech",
            "Flat affect but preserved fluency about self-related worries",
        ],
        "reports":[
            '"I can\'t turn off the \'What If\' movie."',
            '"I keep replaying what went wrong."',
            '"Everything bad comes back to something I did."',
            '"My mind keeps drifting back to worrying."',
        ],
        "note":"Vacant stare + slow response = psychomotor retardation or dissociation, NOT DMN hyperactivity. DMN over-engagement is internal busyness, not blankness.",
        "src":"Buckner et al. 2008; Mulders et al. 2015",
    },
    {
        "network":"ECN  Dysfunction\n(Executive Control Network Failure)",
        "color":ECN_COL,"light":ECN_LIGHT,
        "anchors":"Dorsolateral PFC + lateral parietal cortex",
        "disorders":"OCD, Schizophrenia, ADHD, Bipolar, Severe MDD",
        "signs":[
            "[OCD] Repeating completed task (inhibitory failure)",
            "[WM] Cannot hold two concepts simultaneously",
            "[Rigid] Stuck on one topic; cannot task-switch",
            "[Impulsive] Acts before thinking",
            "[Disorg.] Tangential / disorganised speech",
        ],
        "reports":[
            '"I know it\'s clean but I can\'t stop scrubbing."',
            '"I started 3 things and can\'t finish any."',
            '"I keep losing my train of thought."',
            '"I say things before I realise I shouldn\'t."',
        ],
        "note":"Subtypes: inhibitory failure (OCD), WM failure (ADHD/schizophrenia), rigidity (schizophrenia/MDD), impulsivity (mania/ADHD), disorganisation (schizophrenia).",
        "src":"Menon 2011; Menon et al. 2023 (PMID 36702660)",
    },
    {
        "network":"LC-NE  Hyperarousal\n(Neuromodulatory Modifier - not a 4th network)",
        "color":NE_COL,"light":NE_LIGHT,
        "anchors":"Locus coeruleus → norepinephrine across cortex",
        "disorders":"PTSD, Panic disorder, Mania, Stimulant use",
        "signs":[
            "Motor restlessness / fidgeting",
            "Pressured or rapid-fire speech",
            "Psychomotor agitation",
            "Insomnia despite clear exhaustion",
            "Exaggerated startle (distinguish by autonomic prominence)",
        ],
        "reports":[
            '"I feel \'wired\' and \'stuck\' at the same time."',
            '"I\'m exhausted but I can\'t slow down."',
            '"My body is on high alert."',
            '"I haven\'t slept in days but I\'m not tired."',
        ],
        "note":"LC-NE is a neuromodulatory system, not a resting-state network. It amplifies SN sensitivity. Treat as a modifier of SN dysfunction, not an independent network.",
        "src":"Aston-Jones & Cohen 2005; Sara 2009 (Nat Rev Neurosci)",
    },
]

interp_rows = [
    ["Pattern observed","Likely network failure","Disorder context"],
    ["≥3 SN signs + emergency report","SN Hijack","PTSD, Panic, GAD, Schizophrenia"],
    ["≥3 DMN signs + ruminative reports","DMN Hyperactivity","MDD, GAD, Rumination OCD"],
    ["≥3 ECN signs across subtypes","ECN Dysfunction","OCD, Schizophrenia, ADHD, Bipolar"],
    ["≥2 SN + ≥2 LC-NE signs","SN + NE Amplification","PTSD, Mania, Stimulant use"],
    ["≥2 DMN + ≥2 ECN signs","DMN-ECN failure","MDD with cognitive sx, Bipolar depression"],
    ["All 3 networks + LC-NE","Global dysregulation","Severe MDD, Psychosis, Complex PTSD"],
]

refs = [
    "1. Solmi M et al. (2023). Umbrella review of predictors across mental disorders. Mol Psychiatry. PMID 37957292",
    "2. Molstrom IM et al. (2022). Prognosis of schizophrenia - 20-year meta-analysis. Schizophr Res. PMID 36417817",
    "3. Treuer T & Tohen M. (2010). Course and outcome of bipolar disorder. Eur Psychiatry. PMID 20444581",
    "4. Scott K et al. (2025). Treatment outcome in bipolar disorder. Biol Psychiatry. PMID 39954922",
    "5. Sharma E et al. (2014). Long-term outcome of OCD - meta-analysis. J Clin Psychiatry. PMID 25295427",
    "6. Sharma E & Math SB. (2019). Course and outcome of OCD. Indian J Psychiatry. PMID 30745676",
    "7. Hazari N et al. (2016). Predictors of SRI response in OCD. Expert Rev Neurother. PMID 27282021",
    "8. Keyan D et al. (2024). Predictors of PTSD psychotherapy response. Psychol Bull. PMID 38884956",
    "9. Wang J et al. (2018). Social support and outcomes in mental health. BMC Psychiatry. PMID 29843662",
    "10. Liberona A et al. (2024). BDNF as predictor in schizophrenia & bipolar. Int J Mol Sci. PMID 39456983",
    "11. Bandelow B et al. (2016/2017). Biological markers for anxiety, OCD & PTSD. World J Biol Psychiatry. PMID 27403679, 27419272",
    "12. Menon V. (2011). Triple network model. Trends Cogn Sci. 15(10):483-506",
    "13. Menon V et al. (2023). Brain network models in schizophrenia. Biol Psychiatry. PMID 36702660",
]

# ═══════════════════════════════════════════════════════════════════════════════
# HELPERS
# ═══════════════════════════════════════════════════════════════════════════════
def build_3col_rows(col_a, col_b, col_c, style_a, style_b, style_c, bg_a, bg_b, bg_c):
    """Build a 3-column alternating-row table from three item lists."""
    maxr = max(len(col_a), len(col_b), len(col_c))
    rows = []
    for i in range(maxr):
        a = Paragraph(f"☐  {col_a[i]}", style_a) if i < len(col_a) else Paragraph("", style_a)
        b = Paragraph(f"☐  {col_b[i]}", style_b) if i < len(col_b) else Paragraph("", style_b)
        c = Paragraph(col_c[i], style_c)           if i < len(col_c) else Paragraph("", style_c)
        rows.append([a, b, c])

    t = Table(rows, colWidths=[C1, C2, C3])
    ts = TableStyle([
        ("LEFTPADDING",(0,0),(-1,-1),4),("RIGHTPADDING",(0,0),(-1,-1),4),
        ("TOPPADDING",(0,0),(-1,-1),2.5),("BOTTOMPADDING",(0,0),(-1,-1),2.5),
        ("VALIGN",(0,0),(-1,-1),"TOP"),
        ("LINEAFTER",(0,0),(0,-1),0.4,MID_GREY),
        ("LINEAFTER",(1,0),(1,-1),0.4,MID_GREY),
        ("BOX",(0,0),(-1,-1),0.5,MID_GREY),
    ])
    for i in range(maxr):
        ts.add("BACKGROUND",(0,i),(0,i), colors.HexColor("#f2fbf2") if i%2==0 else colors.HexColor("#e8f8e8"))
        ts.add("BACKGROUND",(1,i),(1,i), colors.HexColor("#fef5f5") if i%2==0 else colors.HexColor("#fdeaea"))
        ts.add("BACKGROUND",(2,i),(2,i), colors.HexColor("#eaf4fb") if i%2==0 else colors.HexColor("#dceef8"))
    t.setStyle(ts)
    return t

def build_disorder_page(d):
    els = []
    # ── Full-width disorder header ──
    hdr = Table([[Paragraph(f"  {d['emoji']}  {d['name']}", dis_s)]], colWidths=[CW])
    hdr.setStyle(TableStyle([
        ("BACKGROUND",(0,0),(-1,-1),HEADER_BG),
        ("TOPPADDING",(0,0),(-1,-1),9),("BOTTOMPADDING",(0,0),(-1,-1),9),
        ("LEFTPADDING",(0,0),(-1,-1),8),
        ("BOX",(0,0),(-1,-1),0.5,DARK_NAVY),
    ]))
    els.append(hdr)

    # ── 3-col header row ──
    ch = Table([[
        Paragraph("✅  GOOD PROGNOSTIC FACTORS", col_hdr_g),
        Paragraph("⚠️  POOR PROGNOSTIC FACTORS", col_hdr_b),
        Paragraph("🔬  ROUTINE BIOMARKERS", col_hdr_bio),
    ]], colWidths=[C1,C2,C3])
    ch.setStyle(TableStyle([
        ("BACKGROUND",(0,0),(0,0),GOOD_GREEN),
        ("BACKGROUND",(1,0),(1,0),BAD_RED),
        ("BACKGROUND",(2,0),(2,0),BIO_GOOD_BG),
        ("TOPPADDING",(0,0),(-1,-1),5),("BOTTOMPADDING",(0,0),(-1,-1),5),
        ("LEFTPADDING",(0,0),(-1,-1),4),
        ("LINEAFTER",(0,0),(0,0),0.4,MID_GREY),
        ("LINEAFTER",(1,0),(1,0),0.4,MID_GREY),
        ("LINEBELOW",(0,0),(-1,-1),0.4,MID_GREY),
    ]))
    els.append(ch)

    # ── 3-col body ──
    els.append(build_3col_rows(
        d["good"], d["bad"], d["bio"],
        item_s, item_s, bio_s,
        None, None, None  # bgs handled inside
    ))

    # ── Source strip ──
    st = Table([[Paragraph(f"Sources: {d['src']}", src_s)]], colWidths=[CW])
    st.setStyle(TableStyle([
        ("BACKGROUND",(0,0),(-1,-1),LIGHT_GREY),
        ("TOPPADDING",(0,0),(-1,-1),3),("BOTTOMPADDING",(0,0),(-1,-1),3),
        ("LEFTPADDING",(0,0),(-1,-1),6),
        ("BOX",(0,0),(-1,-1),0.3,MID_GREY),
    ]))
    els.append(st)
    return els

# ═══════════════════════════════════════════════════════════════════════════════
# BUILD STORY
# ═══════════════════════════════════════════════════════════════════════════════
doc = SimpleDocTemplate(OUTPUT, pagesize=A4,
    topMargin=3.2*cm, bottomMargin=1.6*cm,
    leftMargin=1.5*cm, rightMargin=1.5*cm,
    title="Psychiatric Clinical Tool - Final v2",
    author="Orris AI")

story = []

# ══ COVER / TITLE PAGE ════════════════════════════════════════════════════════
tb = Table([
    [Paragraph("Psychiatric Clinical Assessment Tool", title_s)],
    [Paragraph("Prognostic Factors  ·  Routine Biomarkers  ·  Triple Network Screening", sub_s)],
    [Paragraph("Evidence-based  |  Adult Psychiatric Disorders  |  Clinical use only", sub_s)],
], colWidths=[CW])
tb.setStyle(TableStyle([
    ("BACKGROUND",(0,0),(-1,-1),DARK_NAVY),
    ("TOPPADDING",(0,0),(-1,-1),4),("BOTTOMPADDING",(0,0),(-1,-1),4),
    ("LEFTPADDING",(0,0),(-1,-1),8),
]))
story.append(tb)
story.append(Spacer(1,0.25*cm))

# How to use
how = Table([[Paragraph(
    "<b>HOW TO USE:</b>  Each disorder occupies one page. "
    "Tick (☐) applicable clinical factors across all three columns. "
    "<b>Routine biomarkers</b> (blood, EEG, genetics only - no fMRI/PET) are in the third column. "
    "🔵 = favourable profile; 🔴 = unfavourable profile. "
    "The Triple Network tool (Part 3) screens for predominant network dysfunction using clinical observation and patient reports.",
    note_s)]], colWidths=[CW])
how.setStyle(TableStyle([
    ("BACKGROUND",(0,0),(-1,-1),colors.HexColor("#fffbe6")),
    ("BOX",(0,0),(-1,-1),0.5,colors.HexColor("#e0c050")),
    ("TOPPADDING",(0,0),(-1,-1),6),("BOTTOMPADDING",(0,0),(-1,-1),6),
    ("LEFTPADDING",(0,0),(-1,-1),8),("RIGHTPADDING",(0,0),(-1,-1),8),
]))
story.append(how)
story.append(Spacer(1,0.3*cm))

# ══ TRANSDIAGNOSTIC PAGE ══════════════════════════════════════════════════════
story.append(sec_bar("  🌐  TRANSDIAGNOSTIC FACTORS  (apply across all 6 disorders)",
                     colors.HexColor("#4a235a")))
ch_td = Table([[
    Paragraph("✅  GOOD (across disorders)", td_good),
    Paragraph("⚠️  POOR (across disorders)", td_bad),
    Paragraph("🔬  TRANSDIAGNOSTIC BIOMARKERS", td_bio),
]], colWidths=[C1,C2,C3])
ch_td.setStyle(TableStyle([
    ("BACKGROUND",(0,0),(0,0),GOOD_GREEN),
    ("BACKGROUND",(1,0),(1,0),BAD_RED),
    ("BACKGROUND",(2,0),(2,0),BIO_GOOD_BG),
    ("TOPPADDING",(0,0),(-1,-1),5),("BOTTOMPADDING",(0,0),(-1,-1),5),
    ("LEFTPADDING",(0,0),(-1,-1),4),
    ("LINEAFTER",(0,0),(0,0),0.4,MID_GREY),
    ("LINEAFTER",(1,0),(1,0),0.4,MID_GREY),
    ("LINEBELOW",(0,0),(-1,-1),0.4,MID_GREY),
]))
story.append(ch_td)
story.append(build_3col_rows(td_good, td_bad, td_bio, item_s, item_s, bio_s, None, None, None))
td_src = Table([[Paragraph("Source: Solmi et al. 2023 (PMID 37957292); Liberona et al. 2024 (PMID 39456983)", src_s)]], colWidths=[CW])
td_src.setStyle(TableStyle([
    ("BACKGROUND",(0,0),(-1,-1),LIGHT_GREY),
    ("TOPPADDING",(0,0),(-1,-1),3),("BOTTOMPADDING",(0,0),(-1,-1),3),
    ("LEFTPADDING",(0,0),(-1,-1),6),("BOX",(0,0),(-1,-1),0.3,MID_GREY),
]))
story.append(td_src)

# ══ ONE PAGE PER DISORDER ══════════════════════════════════════════════════════
for d in disorders:
    story.append(PageBreak())
    story.extend(build_disorder_page(d))

# ══ TRIPLE NETWORK SECTION ════════════════════════════════════════════════════
story.append(PageBreak())
part3_hdr = Table([[Paragraph("  PART 3  ·  Triple Network Clinical Screening Tool", sec_s)]], colWidths=[CW])
part3_hdr.setStyle(TableStyle([
    ("BACKGROUND",(0,0),(-1,-1),colors.HexColor("#1a3a6b")),
    ("TOPPADDING",(0,0),(-1,-1),7),("BOTTOMPADDING",(0,0),(-1,-1),7),
    ("LEFTPADDING",(0,0),(-1,-1),6),
]))
story.append(part3_hdr)
story.append(Spacer(1,0.2*cm))

bg_box = Table([[Paragraph(
    "<b>TRIPLE NETWORK MODEL (Menon, 2011):</b>  "
    "<b>Salience Network (SN)</b> - insula + dACC - detects threats and switches between networks. "
    "<b>Default Mode Network (DMN)</b> - mPFC + PCC - drives self-referential thought and rumination. "
    "<b>Executive Control Network (ECN)</b> - dlPFC + lateral PPC - governs working memory, flexibility, and inhibitory control. "
    "Dysfunction in these networks underlies cognitive and emotional symptoms across major psychiatric disorders.",
    note_s)]], colWidths=[CW])
bg_box.setStyle(TableStyle([
    ("BACKGROUND",(0,0),(-1,-1),colors.HexColor("#eaf4fb")),
    ("BOX",(0,0),(-1,-1),0.5,BIO_HDR),
    ("TOPPADDING",(0,0),(-1,-1),6),("BOTTOMPADDING",(0,0),(-1,-1),6),
    ("LEFTPADDING",(0,0),(-1,-1),8),("RIGHTPADDING",(0,0),(-1,-1),8),
]))
story.append(bg_box)
story.append(Spacer(1,0.15*cm))

how2 = Table([[Paragraph(
    "<b>HOW TO USE:</b>  Tick (☐) observable signs and note matching patient reports. "
    "A consistent pattern within one domain suggests that network is clinically prominent. "
    "Multiple network involvement is common in severe illness.",
    note_s)]], colWidths=[CW])
how2.setStyle(TableStyle([
    ("BACKGROUND",(0,0),(-1,-1),colors.HexColor("#fffbe6")),
    ("BOX",(0,0),(-1,-1),0.5,colors.HexColor("#e0c050")),
    ("TOPPADDING",(0,0),(-1,-1),5),("BOTTOMPADDING",(0,0),(-1,-1),5),
    ("LEFTPADDING",(0,0),(-1,-1),8),("RIGHTPADDING",(0,0),(-1,-1),8),
]))
story.append(how2)
story.append(Spacer(1,0.2*cm))

for net in network_rows:
    nh = Table([[Paragraph(net["network"], net_lbl)]], colWidths=[CW])
    nh.setStyle(TableStyle([
        ("BACKGROUND",(0,0),(-1,-1),net["color"]),
        ("TOPPADDING",(0,0),(-1,-1),6),("BOTTOMPADDING",(0,0),(-1,-1),6),
        ("LEFTPADDING",(0,0),(-1,-1),6),
        ("BOX",(0,0),(-1,-1),0.5,DARK_NAVY),
    ]))
    story.append(nh)
    meta = Table([[
        Paragraph(f"<b>Anchors:</b>  {net['anchors']}", net_tiny),
        Paragraph(f"<b>Disorders:</b>  {net['disorders']}", net_tiny),
    ]], colWidths=[6.5*cm, 12*cm])
    meta.setStyle(TableStyle([
        ("BACKGROUND",(0,0),(-1,-1),net["light"]),
        ("TOPPADDING",(0,0),(-1,-1),3),("BOTTOMPADDING",(0,0),(-1,-1),3),
        ("LEFTPADDING",(0,0),(-1,-1),6),
        ("BOX",(0,0),(-1,-1),0.3,MID_GREY),
    ]))
    story.append(meta)
    nch = Table([[
        Paragraph("☐  OBSERVABLE SIGNS",
            PS(f"NSH{str(net['color'])}",fontSize=8.5,fontName="Helvetica-Bold",textColor=net["color"])),
        Paragraph("💬  PATIENT REPORTS (verbatim prompts)",
            PS("NPH",fontSize=8.5,fontName="Helvetica-Bold",textColor=DARK_NAVY)),
    ]], colWidths=[9.25*cm, 9.25*cm])
    nch.setStyle(TableStyle([
        ("BACKGROUND",(0,0),(0,0),net["light"]),
        ("BACKGROUND",(1,0),(1,0),LIGHT_GREY),
        ("TOPPADDING",(0,0),(-1,-1),4),("BOTTOMPADDING",(0,0),(-1,-1),4),
        ("LEFTPADDING",(0,0),(-1,-1),5),
        ("LINEAFTER",(0,0),(0,0),0.5,MID_GREY),
        ("LINEBELOW",(0,0),(-1,-1),0.5,MID_GREY),
    ]))
    story.append(nch)
    maxr = max(len(net["signs"]), len(net["reports"]))
    nrows=[]
    for i in range(maxr):
        s = Paragraph(f"☐  {net['signs'][i]}", item_s)   if i<len(net["signs"])   else Paragraph("",item_s)
        r = Paragraph(net["reports"][i], italic_s)         if i<len(net["reports"]) else Paragraph("",italic_s)
        nrows.append([s,r])
    nt = Table(nrows, colWidths=[9.25*cm,9.25*cm])
    nts = TableStyle([
        ("LEFTPADDING",(0,0),(-1,-1),5),("RIGHTPADDING",(0,0),(-1,-1),5),
        ("TOPPADDING",(0,0),(-1,-1),2.5),("BOTTOMPADDING",(0,0),(-1,-1),2.5),
        ("VALIGN",(0,0),(-1,-1),"TOP"),
        ("LINEAFTER",(0,0),(0,-1),0.5,MID_GREY),
        ("BOX",(0,0),(-1,-1),0.5,MID_GREY),
    ])
    for i in range(maxr):
        nts.add("BACKGROUND",(0,i),(0,i), net["light"] if i%2==0 else colors.HexColor("#fafafa"))
        nts.add("BACKGROUND",(1,i),(1,i), colors.HexColor("#f9f9f9") if i%2==0 else WHITE)
    nt.setStyle(nts)
    story.append(nt)
    note_row = Table([[
        Paragraph(f"<i>{net['note']}</i>", warn_s),
        Paragraph(f"Ref: {net['src']}", src_s),
    ]], colWidths=[11*cm, 7.5*cm])
    note_row.setStyle(TableStyle([
        ("BACKGROUND",(0,0),(-1,-1),LIGHT_GREY),
        ("TOPPADDING",(0,0),(-1,-1),3),("BOTTOMPADDING",(0,0),(-1,-1),3),
        ("LEFTPADDING",(0,0),(-1,-1),6),
        ("BOX",(0,0),(-1,-1),0.3,MID_GREY),
        ("VALIGN",(0,0),(-1,-1),"TOP"),
    ]))
    story.append(note_row)
    story.append(Spacer(1,0.25*cm))

# Interpretation table
story.append(sec_bar("INTERPRETATION GUIDE"))
story.append(Spacer(1,0.12*cm))
it = Table(interp_rows, colWidths=[6*cm,5.5*cm,7*cm])
its = TableStyle([
    ("BACKGROUND",(0,0),(-1,0),DARK_NAVY),
    ("TEXTCOLOR",(0,0),(-1,0),WHITE),
    ("FONTNAME",(0,0),(-1,0),"Helvetica-Bold"),
    ("FONTSIZE",(0,0),(-1,-1),8),
    ("TOPPADDING",(0,0),(-1,-1),4),("BOTTOMPADDING",(0,0),(-1,-1),4),
    ("LEFTPADDING",(0,0),(-1,-1),5),("RIGHTPADDING",(0,0),(-1,-1),5),
    ("GRID",(0,0),(-1,-1),0.3,MID_GREY),
    ("VALIGN",(0,0),(-1,-1),"TOP"),
])
for i in range(1,len(interp_rows)):
    its.add("BACKGROUND",(0,i),(-1,i),LIGHT_GREY if i%2==0 else WHITE)
it.setStyle(its)
story.append(it)
story.append(Spacer(1,0.15*cm))

lim = Table([[Paragraph(
    "⚠️  <b>LIMITATIONS:</b>  Network dysfunction cannot be confirmed from clinical observation alone - neuroimaging is required for definitive characterisation. "
    "This tool prompts network-informed clinical reasoning. It has not been validated as a psychometric instrument. "
    "Multiple network involvement is the rule in severe illness.",
    warn_s)]], colWidths=[CW])
lim.setStyle(TableStyle([
    ("BACKGROUND",(0,0),(-1,-1),colors.HexColor("#fdf0e0")),
    ("BOX",(0,0),(-1,-1),0.5,colors.HexColor("#c07030")),
    ("TOPPADDING",(0,0),(-1,-1),5),("BOTTOMPADDING",(0,0),(-1,-1),5),
    ("LEFTPADDING",(0,0),(-1,-1),8),("RIGHTPADDING",(0,0),(-1,-1),8),
]))
story.append(lim)

# ══ NOTES PAGE ════════════════════════════════════════════════════════════════
story.append(PageBreak())
nh2 = Table([[Paragraph("  📋  CLINICAL NOTES", sec_s)]], colWidths=[CW])
nh2.setStyle(TableStyle([
    ("BACKGROUND",(0,0),(-1,-1),HEADER_BG),
    ("TOPPADDING",(0,0),(-1,-1),7),("BOTTOMPADDING",(0,0),(-1,-1),7),
    ("LEFTPADDING",(0,0),(-1,-1),6),
]))
story.append(nh2)
story.append(Spacer(1,0.3*cm))
for _ in range(22):
    story.append(Paragraph("", item_s))
    story.append(HRFlowable(width="100%", thickness=0.4, color=MID_GREY))
    story.append(Spacer(1,0.45*cm))

# ══ REFERENCES ════════════════════════════════════════════════════════════════
story.append(Spacer(1,0.3*cm))
rh = Table([[Paragraph("  📚  REFERENCES", sec_s)]], colWidths=[CW])
rh.setStyle(TableStyle([
    ("BACKGROUND",(0,0),(-1,-1),HEADER_BG),
    ("TOPPADDING",(0,0),(-1,-1),6),("BOTTOMPADDING",(0,0),(-1,-1),6),
    ("LEFTPADDING",(0,0),(-1,-1),6),
]))
story.append(rh)
story.append(Spacer(1,0.15*cm))
for r in refs:
    story.append(Paragraph(r, src_s))
    story.append(Spacer(1,0.1*cm))

doc.build(story, onFirstPage=page_bg, onLaterPages=page_bg)
print("DONE:", OUTPUT)
Running Command

cd /tmp/workspace/psych-checklist && python build_final_v2.py 2>&1

Variable name collision - td_good, td_bad, td_bio are lists but also used as style names in Paragraph(). Quick fix:
Editing File

~/psych-checklist/build_final_v2.py

Successfully replaced 1 instance(s) of the string in '/tmp/workspace/psych-checklist/build_final_v2.py'
Editing File

~/psych-checklist/build_final_v2.py

Successfully replaced 1 instance(s) of the string in '/tmp/workspace/psych-checklist/build_final_v2.py'
Running Command

cd /tmp/workspace/psych-checklist && python build_final_v2.py 2>&1

Running Command

ls -lh /tmp/workspace/psych-checklist/Psychiatric_Clinical_Tool_FINAL_v2.pdf

Here is your updated final PDF:

Psychiatric Clinical Tool FINAL v2

PDF Document · PDF


What changed:
  • Each disorder now has its own dedicated page with a 3-column layout:
    • Column 1: ✅ Good prognostic factors (green)
    • Column 2: ⚠️ Poor prognostic factors (red)
    • Column 3: 🔬 Routine biomarkers (blue) - 🔵 favourable / 🔴 unfavourable clearly marked within the column
  • The biomarker column uses a separator line (─────) to divide favourable from unfavourable entries, keeping everything on one page without needing a sub-header row
  • Font size is slightly tighter (8pt) to fit all content per disorder on a single A4 page
  • All other sections unchanged: transdiagnostic page, triple network screening tool, interpretation guide, clinical notes page, and references
This is a shared conversation. Sign in to Orris to start your own chat.