{"spec_id":"genome-track-multi","library":"pygal","language":"python","code":"\"\"\" anyplot.ai\ngenome-track-multi: Genome Track Viewer\nLibrary: pygal 3.1.0 | Python 3.13.13\nQuality: 85/100 | Updated: 2026-06-02\n\"\"\"\n\nimport importlib\nimport os\nimport re\nimport sys\n\nimport numpy as np\n\n\n# Script filename matches library name; use importlib to avoid circular import\n_script_dir = sys.path[0]\nsys.path.remove(_script_dir)\npygal = importlib.import_module(\"pygal\")\nStyle = importlib.import_module(\"pygal.style\").Style\ncairosvg = importlib.import_module(\"cairosvg\")\nsys.path.insert(0, _script_dir)\n\nnp.random.seed(42)\n\n# Theme tokens\nTHEME = os.getenv(\"ANYPLOT_THEME\", \"light\")\nPAGE_BG = \"#FAF8F1\" if THEME == \"light\" else \"#1A1A17\"\nELEVATED_BG = \"#FFFDF6\" if THEME == \"light\" else \"#242420\"\nINK = \"#1A1A17\" if THEME == \"light\" else \"#F0EFE8\"\nINK_SOFT = \"#4A4A44\" if THEME == \"light\" else \"#B8B7B0\"\nINK_MUTED = \"#6B6A63\" if THEME == \"light\" else \"#A8A79F\"\n\n# Imprint palette — data series\nGENE_CLR = \"#4467A3\"  # blue — gene exons (manual SVG, semantic: reference)\nCOV_CLR = \"#009E73\"  # brand green — coverage (first Imprint series)\nCOV_STROKE = \"#007A59\"  # darker green for fill edge\nSNP_CLR = \"#AE3030\"  # matte red — SNPs (semantic: mutation)\nINDEL_CLR = \"#C475FD\"  # lavender — indels\nPROM_CLR = \"#BD8233\"  # ochre — promoters\nENH_CLR = \"#2ABCCD\"  # cyan — enhancers\nCTCF_CLR = \"#954477\"  # rose — CTCF binding sites\nTRACK_ACCENTS = [GENE_CLR, COV_CLR, SNP_CLR, ENH_CLR]\n\n# === Genomic data: BRCA1 gene region on chromosome 17 ===\nchrom = \"chr17\"\nregion_start = 41_150_000\nregion_end = 41_310_000\nregion_length = region_end - region_start\n\n# BRCA1 exons — 23 exons, minus strand (~hg38 coordinates)\nexons = [\n    (41_196_312, 41_197_819),\n    (41_199_659, 41_199_720),\n    (41_203_079, 41_203_134),\n    (41_209_068, 41_209_152),\n    (41_215_349, 41_215_390),\n    (41_219_624, 41_219_712),\n    (41_222_944, 41_223_255),\n    (41_226_347, 41_226_538),\n    (41_228_504, 41_228_592),\n    (41_234_415, 41_234_592),\n    (41_238_000, 41_238_200),\n    (41_242_961, 41_243_049),\n    (41_246_877, 41_246_956),\n    (41_249_260, 41_249_338),\n    (41_251_791, 41_251_897),\n    (41_256_139, 41_256_278),\n    (41_258_473, 41_258_535),\n    (41_267_742, 41_267_796),\n    (41_270_711, 41_270_795),\n    (41_273_218, 41_273_341),\n    (41_276_033, 41_276_132),\n    (41_277_186, 41_277_468),\n    (41_277_500, 41_279_374),\n]\n\nn_cov = 500\ncov_pos = np.linspace(region_start, region_end, n_cov)\ncov_base = np.random.poisson(25, n_cov).astype(float)\nfor es, ee in exons:\n    mask = (cov_pos >= es) & (cov_pos <= ee)\n    cov_base[mask] += np.random.poisson(55, mask.sum())\ncov_vals = np.convolve(cov_base, np.ones(5) / 5, mode=\"same\")\n\nvariants = [\n    {\"pos\": 41_197_000, \"type\": \"SNP\", \"quality\": 92},\n    {\"pos\": 41_199_700, \"type\": \"SNP\", \"quality\": 85},\n    {\"pos\": 41_203_100, \"type\": \"indel\", \"quality\": 62},\n    {\"pos\": 41_209_100, \"type\": \"SNP\", \"quality\": 78},\n    {\"pos\": 41_215_370, \"type\": \"SNP\", \"quality\": 95},\n    {\"pos\": 41_219_660, \"type\": \"SNP\", \"quality\": 88},\n    {\"pos\": 41_223_100, \"type\": \"indel\", \"quality\": 55},\n    {\"pos\": 41_234_480, \"type\": \"SNP\", \"quality\": 91},\n    {\"pos\": 41_243_000, \"type\": \"SNP\", \"quality\": 75},\n    {\"pos\": 41_249_300, \"type\": \"SNP\", \"quality\": 82},\n    {\"pos\": 41_256_200, \"type\": \"SNP\", \"quality\": 97},\n    {\"pos\": 41_267_770, \"type\": \"indel\", \"quality\": 68},\n    {\"pos\": 41_277_200, \"type\": \"SNP\", \"quality\": 89},\n]\n\nregulatory = [\n    {\"start\": 41_150_000, \"end\": 41_153_000, \"type\": \"Promoter\"},\n    {\"start\": 41_170_000, \"end\": 41_175_000, \"type\": \"Enhancer\"},\n    {\"start\": 41_190_000, \"end\": 41_196_000, \"type\": \"Promoter\"},\n    {\"start\": 41_225_000, \"end\": 41_229_000, \"type\": \"Enhancer\"},\n    {\"start\": 41_260_000, \"end\": 41_264_000, \"type\": \"Enhancer\"},\n    {\"start\": 41_300_000, \"end\": 41_304_000, \"type\": \"CTCF\"},\n]\n\n# === Layout: 3200 × 1800 composite SVG ===\nWIDTH = 3200\nHEIGHT = 1800\nMARGIN_LEFT = 240\nMARGIN_RIGHT = 80\nMARGIN_TOP = 140\nMARGIN_BOTTOM = 120\nPLOT_W = WIDTH - MARGIN_LEFT - MARGIN_RIGHT  # 2880\nPLOT_H = HEIGHT - MARGIN_TOP - MARGIN_BOTTOM  # 1540\nN_TRACKS = 4\nTRACK_GAP = 20\nTRACK_H = (PLOT_H - (N_TRACKS - 1) * TRACK_GAP) / N_TRACKS  # 370.0\n\nPYGAL_PAD = 1 / 52  # pygal internal margin fraction per side\n\nnorm_pos = (cov_pos - region_start) / region_length\nnorm_variants = [(v[\"pos\"] - region_start) / region_length for v in variants]\n\n# === Coverage chart: pygal.XY fill + hermite interpolation ===\ncov_style = Style(\n    background=\"transparent\",\n    plot_background=\"transparent\",\n    foreground=INK,\n    foreground_strong=INK,\n    foreground_subtle=\"transparent\",\n    colors=(COV_CLR,),\n    font_family=\"sans-serif\",\n    tooltip_font_size=18,\n)\ncov_chart = pygal.XY(\n    width=int(PLOT_W),\n    height=int(TRACK_H),\n    style=cov_style,\n    fill=True,\n    show_legend=False,\n    show_x_labels=False,\n    show_y_labels=False,\n    show_x_guides=False,\n    show_y_guides=False,\n    margin=0,\n    interpolate=\"hermite\",\n    dots_size=0,\n    stroke_style={\"width\": 2, \"color\": COV_STROKE},\n    range=(0, float(cov_vals.max() * 1.05)),\n)\ncov_xy = [\n    {\"value\": (float(nx), float(v)), \"label\": f\"Depth: {v:.0f}x at {p / 1e6:.3f} Mb\"}\n    for nx, p, v in zip(norm_pos, cov_pos, cov_vals, strict=True)\n]\ncov_chart.add(\"Read Depth\", cov_xy)\ncov_svg_raw = cov_chart.render(is_unicode=True)\n\n# === Variant chart: pygal.XY scatter ===\nvar_style = Style(\n    background=\"transparent\",\n    plot_background=\"transparent\",\n    foreground=INK,\n    foreground_strong=INK,\n    foreground_subtle=\"transparent\",\n    colors=(SNP_CLR, INDEL_CLR),\n    font_family=\"sans-serif\",\n    tooltip_font_size=18,\n)\nvar_chart = pygal.XY(\n    width=int(PLOT_W),\n    height=int(TRACK_H),\n    style=var_style,\n    show_legend=False,\n    show_x_labels=False,\n    show_y_labels=False,\n    show_x_guides=False,\n    show_y_guides=False,\n    margin=0,\n    dots_size=10,\n    stroke=False,\n    range=(40, 105),\n    x_range=(0, 1),\n)\nsnp_series = [\n    {\"value\": (float(nv), float(v[\"quality\"])), \"label\": f\"SNP at {v['pos']:,} (Q={v['quality']})\"}\n    for v, nv in zip(variants, norm_variants, strict=True)\n    if v[\"type\"] == \"SNP\"\n]\nindel_series = [\n    {\"value\": (float(nv), float(v[\"quality\"])), \"label\": f\"Indel at {v['pos']:,} (Q={v['quality']})\"}\n    for v, nv in zip(variants, norm_variants, strict=True)\n    if v[\"type\"] == \"indel\"\n]\nvar_chart.add(\"SNP\", snp_series)\nvar_chart.add(\"Indel\", indel_series)\nvar_svg_raw = var_chart.render(is_unicode=True)\n\n# === Regulatory chart: pygal.Histogram for interval bars ===\nreg_style = Style(\n    background=\"transparent\",\n    plot_background=\"transparent\",\n    foreground=INK,\n    foreground_strong=INK,\n    foreground_subtle=\"transparent\",\n    colors=(PROM_CLR, ENH_CLR, CTCF_CLR),\n    font_family=\"sans-serif\",\n    tooltip_font_size=18,\n)\nreg_chart = pygal.Histogram(\n    width=int(PLOT_W),\n    height=int(TRACK_H),\n    style=reg_style,\n    show_legend=False,\n    show_x_labels=False,\n    show_y_labels=False,\n    show_x_guides=False,\n    show_y_guides=False,\n    margin=0,\n    range=(0, 1.5),\n)\npromoters = [\n    (1.0, (r[\"start\"] - region_start) / region_length, (r[\"end\"] - region_start) / region_length)\n    for r in regulatory\n    if r[\"type\"] == \"Promoter\"\n]\nenhancers = [\n    (1.0, (r[\"start\"] - region_start) / region_length, (r[\"end\"] - region_start) / region_length)\n    for r in regulatory\n    if r[\"type\"] == \"Enhancer\"\n]\nctcf_els = [\n    (1.0, (r[\"start\"] - region_start) / region_length, (r[\"end\"] - region_start) / region_length)\n    for r in regulatory\n    if r[\"type\"] == \"CTCF\"\n]\n# Anchor bars at x=0 and x=1 to lock the histogram x-range\npromoters.extend([(0, 0.0, 0.001), (0, 0.999, 1.0)])\nreg_chart.add(\"Promoter\", promoters)\nreg_chart.add(\"Enhancer\", enhancers)\nreg_chart.add(\"CTCF\", ctcf_els)\nreg_svg_raw = reg_chart.render(is_unicode=True)\n\n# Precomputed viewBox padding for embedding pygal SVGs\npad_x = PLOT_W * PYGAL_PAD\npad_y = TRACK_H * PYGAL_PAD\nvb_w = PLOT_W - 2 * pad_x\nvb_h = TRACK_H - 2 * pad_y\n\n\ndef embed_pygal_svg(raw_svg, track_y):\n    svg = re.sub(r\"<\\?xml[^?]*\\?>\\s*\", \"\", raw_svg)\n    svg = re.sub(r\"<!DOCTYPE[^>]*>\\s*\", \"\", svg)\n    svg_id = re.search(r'id=\"([^\"]+)\"', svg).group(1)\n    return re.sub(\n        r\"<svg[^>]*>\",\n        f'<svg id=\"{svg_id}\" class=\"pygal-chart\" '\n        f'x=\"{MARGIN_LEFT}\" y=\"{track_y:.0f}\" '\n        f'width=\"{PLOT_W}\" height=\"{TRACK_H:.0f}\" '\n        f'viewBox=\"{pad_x:.2f} {pad_y:.2f} {vb_w:.2f} {vb_h:.2f}\">',\n        svg,\n        count=1,\n    )\n\n\n# === Build composite SVG ===\nparts = []\nparts.append(\n    f'<svg xmlns=\"http://www.w3.org/2000/svg\" '\n    f'xmlns:xlink=\"http://www.w3.org/1999/xlink\" '\n    f'width=\"{WIDTH}\" height=\"{HEIGHT}\" viewBox=\"0 0 {WIDTH} {HEIGHT}\">'\n)\nparts.append(f'<rect width=\"{WIDTH}\" height=\"{HEIGHT}\" fill=\"{PAGE_BG}\"/>')\n\n# Clip path constrains track label text to the plot area\nparts.append(\n    f\"<defs>\"\n    f'<clipPath id=\"plotArea\">'\n    f'<rect x=\"{MARGIN_LEFT}\" y=\"{MARGIN_TOP}\" width=\"{PLOT_W}\" height=\"{PLOT_H}\"/>'\n    f\"</clipPath>\"\n    f'<linearGradient id=\"hdr\" x1=\"0\" y1=\"0\" x2=\"1\" y2=\"0\">'\n    f'<stop offset=\"0%\" stop-color=\"{GENE_CLR}\" stop-opacity=\"0\"/>'\n    f'<stop offset=\"25%\" stop-color=\"{GENE_CLR}\" stop-opacity=\"0.8\"/>'\n    f'<stop offset=\"55%\" stop-color=\"{COV_CLR}\" stop-opacity=\"0.8\"/>'\n    f'<stop offset=\"80%\" stop-color=\"{SNP_CLR}\" stop-opacity=\"0.8\"/>'\n    f'<stop offset=\"100%\" stop-color=\"{SNP_CLR}\" stop-opacity=\"0\"/>'\n    f\"</linearGradient>\"\n    f\"</defs>\"\n)\n\n# Title area\ntitle = \"BRCA1 Gene Region (chr17) · genome-track-multi · python · pygal · anyplot.ai\"\ntitle_fs = max(44, round(66 * 67 / len(title)))\nparts.append(\n    f'<text x=\"{WIDTH / 2}\" y=\"68\" font-family=\"sans-serif\" font-size=\"{title_fs}\" '\n    f'fill=\"{INK}\" text-anchor=\"middle\" font-weight=\"bold\">{title}</text>'\n)\nparts.append(\n    f'<text x=\"{WIDTH / 2}\" y=\"100\" font-family=\"sans-serif\" font-size=\"26\" '\n    f'fill=\"{INK_MUTED}\" text-anchor=\"middle\" font-style=\"italic\">'\n    f\"Breast Cancer Susceptibility Gene 1 — 23 exons, ~83 kb</text>\"\n)\nparts.append(f'<line x1=\"{WIDTH * 0.15}\" y1=\"116\" x2=\"{WIDTH * 0.85}\" y2=\"116\" stroke=\"url(#hdr)\" stroke-width=\"3\"/>')\n\n# Track backgrounds, accent strips, labels\ntrack_names = [\"Genes\", \"Coverage\", \"Variants\", \"Regulatory\"]\ntrack_bgrounds = [PAGE_BG, ELEVATED_BG, PAGE_BG, ELEVATED_BG]\nfor i in range(N_TRACKS):\n    ty = MARGIN_TOP + i * (TRACK_H + TRACK_GAP)\n    parts.append(\n        f'<rect x=\"{MARGIN_LEFT}\" y=\"{ty:.0f}\" width=\"{PLOT_W}\" height=\"{TRACK_H:.0f}\" fill=\"{track_bgrounds[i]}\"/>'\n    )\n    parts.append(f'<rect x=\"{MARGIN_LEFT}\" y=\"{ty:.0f}\" width=\"5\" height=\"{TRACK_H:.0f}\" fill=\"{TRACK_ACCENTS[i]}\"/>')\n    parts.append(\n        f'<text x=\"{MARGIN_LEFT - 14}\" y=\"{ty + TRACK_H / 2 + 9:.0f}\" '\n        f'font-family=\"sans-serif\" font-size=\"25\" fill=\"{INK_SOFT}\" '\n        f'text-anchor=\"end\" font-weight=\"bold\">{track_names[i]}</text>'\n    )\n\n# Track separators\nfor i in range(1, N_TRACKS):\n    sy = MARGIN_TOP + i * (TRACK_H + TRACK_GAP) - TRACK_GAP / 2\n    parts.append(\n        f'<line x1=\"{MARGIN_LEFT}\" y1=\"{sy:.0f}\" '\n        f'x2=\"{MARGIN_LEFT + PLOT_W}\" y2=\"{sy:.0f}\" '\n        f'stroke=\"{INK_MUTED}\" stroke-width=\"1\" stroke-opacity=\"0.35\"/>'\n    )\n\n# Vertical position grid lines (shared across all tracks)\ntick_interval = 20_000\ntick_start = ((region_start // tick_interval) + 1) * tick_interval\nfor tick_pos in range(tick_start, region_end, tick_interval):\n    tx = MARGIN_LEFT + (tick_pos - region_start) / region_length * PLOT_W\n    parts.append(\n        f'<line x1=\"{tx:.1f}\" y1=\"{MARGIN_TOP}\" x2=\"{tx:.1f}\" '\n        f'y2=\"{MARGIN_TOP + PLOT_H}\" stroke=\"{INK_MUTED}\" '\n        f'stroke-width=\"0.8\" stroke-dasharray=\"4,4\" stroke-opacity=\"0.3\"/>'\n    )\n\n# --- Track 1: Gene annotations (manual SVG — BRCA1, minus strand) ---\ngene_ty = MARGIN_TOP\ngene_cy = gene_ty + TRACK_H / 2\ngene_x1 = MARGIN_LEFT + (exons[0][0] - region_start) / region_length * PLOT_W\ngene_x2 = MARGIN_LEFT + (exons[-1][1] - region_start) / region_length * PLOT_W\n\nparts.append(\n    f'<line x1=\"{gene_x1:.1f}\" y1=\"{gene_cy:.1f}\" x2=\"{gene_x2:.1f}\" '\n    f'y2=\"{gene_cy:.1f}\" stroke=\"{GENE_CLR}\" stroke-width=\"2.5\"/>'\n)\n\nexon_h = TRACK_H * 0.46\nfor es, ee in exons:\n    ex1 = MARGIN_LEFT + (es - region_start) / region_length * PLOT_W\n    ex2 = MARGIN_LEFT + (ee - region_start) / region_length * PLOT_W\n    ew = max(ex2 - ex1, 7)\n    parts.append(\n        f'<rect x=\"{ex1:.1f}\" y=\"{gene_cy - exon_h / 2:.1f}\" width=\"{ew:.1f}\" '\n        f'height=\"{exon_h:.1f}\" fill=\"{GENE_CLR}\" rx=\"2\">'\n        f\"<title>Exon: {es:,}–{ee:,}</title></rect>\"\n    )\n\n# Minus-strand chevrons (left-pointing) at intron positions\nfor j in range(1, 13):\n    ax = gene_x2 - j * PLOT_W / 14\n    if ax > gene_x1 + 20:\n        gpos = region_start + (ax - MARGIN_LEFT) / PLOT_W * region_length\n        if not any(es <= gpos <= ee for es, ee in exons):\n            parts.append(\n                f'<path d=\"M{ax + 8:.1f},{gene_cy + 6:.1f} '\n                f\"L{ax - 8:.1f},{gene_cy:.1f} \"\n                f'L{ax + 8:.1f},{gene_cy - 6:.1f}\" fill=\"none\" '\n                f'stroke=\"{GENE_CLR}\" stroke-width=\"2\"/>'\n            )\n\nparts.append(\n    f'<text x=\"{(gene_x1 + gene_x2) / 2:.1f}\" '\n    f'y=\"{gene_cy - exon_h / 2 - 12:.1f}\" '\n    f'font-family=\"sans-serif\" font-size=\"24\" fill=\"{GENE_CLR}\" '\n    f'text-anchor=\"middle\" font-style=\"italic\" font-weight=\"600\">BRCA1</text>'\n)\nparts.append(\n    f'<text x=\"{gene_x2 + 22:.1f}\" y=\"{gene_cy + 8:.1f}\" '\n    f'font-family=\"sans-serif\" font-size=\"20\" fill=\"{GENE_CLR}\" '\n    f'font-weight=\"bold\">(−)</text>'\n)\n\n# --- Track 2: Coverage (embedded pygal.XY) ---\ncov_ty = MARGIN_TOP + 1 * (TRACK_H + TRACK_GAP)\nparts.append(embed_pygal_svg(cov_svg_raw, cov_ty))\n\nmax_cov = float(cov_vals.max())\ncov_range_max = max_cov * 1.05\nfor tick_val in [0, int(max_cov)]:\n    frac = tick_val / cov_range_max\n    tick_y = cov_ty + TRACK_H * (1 - frac)\n    parts.append(\n        f'<text x=\"{MARGIN_LEFT - 8}\" y=\"{tick_y + 6:.1f}\" '\n        f'font-family=\"sans-serif\" font-size=\"19\" fill=\"{INK_SOFT}\" '\n        f'text-anchor=\"end\" font-weight=\"500\">{tick_val}x</text>'\n    )\n\n# --- Track 3: Variants (embedded pygal.XY scatter) ---\nvar_ty = MARGIN_TOP + 2 * (TRACK_H + TRACK_GAP)\nparts.append(embed_pygal_svg(var_svg_raw, var_ty))\n\nvleg_x = MARGIN_LEFT + PLOT_W - 250\nvleg_y = var_ty + 26\nparts.append(f'<circle cx=\"{vleg_x}\" cy=\"{vleg_y}\" r=\"7\" fill=\"{SNP_CLR}\"/>')\nparts.append(\n    f'<text x=\"{vleg_x + 14}\" y=\"{vleg_y + 6}\" font-family=\"sans-serif\" font-size=\"19\" fill=\"{INK_SOFT}\">SNP</text>'\n)\nparts.append(f'<rect x=\"{vleg_x + 72}\" y=\"{vleg_y - 8}\" width=\"14\" height=\"14\" fill=\"{INDEL_CLR}\" rx=\"2\"/>')\nparts.append(\n    f'<text x=\"{vleg_x + 92}\" y=\"{vleg_y + 6}\" font-family=\"sans-serif\" font-size=\"19\" fill=\"{INK_SOFT}\">Indel</text>'\n)\n\n# --- Track 4: Regulatory (embedded pygal.Histogram) ---\nreg_ty = MARGIN_TOP + 3 * (TRACK_H + TRACK_GAP)\nparts.append(embed_pygal_svg(reg_svg_raw, reg_ty))\n\nparts.append('<g clip-path=\"url(#plotArea)\">')\nfor reg in regulatory:\n    rx1 = MARGIN_LEFT + (reg[\"start\"] - region_start) / region_length * PLOT_W\n    rx2 = MARGIN_LEFT + (reg[\"end\"] - region_start) / region_length * PLOT_W\n    mid_x = (rx1 + rx2) / 2\n    # Right-align label when the element center is within 60px of the plot left edge\n    # to avoid the label being clipped by the plotArea clip-path\n    if mid_x - MARGIN_LEFT < 60:\n        label_x, anchor = rx2 + 4, \"start\"\n    else:\n        label_x, anchor = mid_x, \"middle\"\n    parts.append(\n        f'<text x=\"{label_x:.1f}\" y=\"{reg_ty + 30:.1f}\" '\n        f'font-family=\"sans-serif\" font-size=\"17\" fill=\"{INK_MUTED}\" '\n        f'text-anchor=\"{anchor}\">{reg[\"type\"]}</text>'\n    )\nparts.append(\"</g>\")\n\nrlx = MARGIN_LEFT + PLOT_W - 410\nrly = reg_ty + TRACK_H - 28\nfor j, (rtype, rclr) in enumerate({\"Promoter\": PROM_CLR, \"Enhancer\": ENH_CLR, \"CTCF\": CTCF_CLR}.items()):\n    lx = rlx + j * 138\n    parts.append(f'<rect x=\"{lx}\" y=\"{rly - 8}\" width=\"14\" height=\"14\" fill=\"{rclr}\" rx=\"2\"/>')\n    parts.append(\n        f'<text x=\"{lx + 20}\" y=\"{rly + 6}\" font-family=\"sans-serif\" font-size=\"18\" fill=\"{INK_SOFT}\">{rtype}</text>'\n    )\n\n# X-axis ticks and labels\nxay = MARGIN_TOP + PLOT_H + 8\nfor tick_pos in range(tick_start, region_end, tick_interval):\n    tx = MARGIN_LEFT + (tick_pos - region_start) / region_length * PLOT_W\n    parts.append(\n        f'<line x1=\"{tx:.1f}\" y1=\"{xay}\" x2=\"{tx:.1f}\" y2=\"{xay + 10}\" stroke=\"{INK_SOFT}\" stroke-width=\"1.5\"/>'\n    )\n    parts.append(\n        f'<text x=\"{tx:.1f}\" y=\"{xay + 33}\" font-family=\"sans-serif\" '\n        f'font-size=\"19\" fill=\"{INK_SOFT}\" text-anchor=\"middle\">'\n        f\"{tick_pos / 1_000_000:.2f} Mb</text>\"\n    )\n\nparts.append(\n    f'<text x=\"{MARGIN_LEFT + PLOT_W / 2}\" y=\"{xay + 73}\" '\n    f'font-family=\"sans-serif\" font-size=\"24\" fill=\"{INK}\" '\n    f'text-anchor=\"middle\" font-weight=\"500\">Genomic Position ({chrom})</text>'\n)\n\nparts.append(\"</svg>\")\nsvg_output = \"\\n\".join(parts)\n\n# Save PNG — cairosvg renders at the declared 3200×1800 SVG dimensions\ncairosvg.svg2png(\n    bytestring=svg_output.encode(\"utf-8\"), write_to=f\"plot-{THEME}.png\", output_width=WIDTH, output_height=HEIGHT\n)\n\n# Save HTML with pygal interactive tooltips\nhtml_content = f\"\"\"<!DOCTYPE html>\n<html>\n<head>\n    <meta charset=\"utf-8\">\n    <title>genome-track-multi - pygal</title>\n    <style>\n        body {{ margin: 0; display: flex; justify-content: center; align-items: center;\n               min-height: 100vh; background: {PAGE_BG}; }}\n        .chart {{ max-width: 100%; height: auto; }}\n    </style>\n</head>\n<body>\n    <figure class=\"chart\">\n        {svg_output}\n    </figure>\n</body>\n</html>\n\"\"\"\n\nwith open(f\"plot-{THEME}.html\", \"w\", encoding=\"utf-8\") as fout:\n    fout.write(html_content)\n"}