diff --git a/cfStatementParser.js b/cfStatementParser.js
new file mode 100644
index 0000000..dd7b838
--- /dev/null
+++ b/cfStatementParser.js
@@ -0,0 +1,115 @@
+/**
+ * Server-side helper: parse a Codeforces problem page HTML string into
+ * structured, plain-text fields safe to return to the React frontend.
+ *
+ * Returns null when the page does not contain a `.problem-statement` element
+ * (e.g. the problem/contest does not exist or the URL was wrong).
+ */
+
+import * as cheerio from 'cheerio';
+
+/**
+ * Replace block-level children and
tags with newlines, then return the
+ * trimmed text content of an element. This preserves paragraph breaks without
+ * leaking any HTML tags to the caller.
+ *
+ * @param {import('cheerio').CheerioAPI} $ - cheerio root
+ * @param {import('cheerio').Cheerio} el - element to extract text from
+ * @returns {string}
+ */
+function blockText($, el) {
+ const clone = $(el).clone();
+ clone.find('br').replaceWith('\n');
+ clone.find('p').each((_, p) => {
+ $(p).prepend('\n').append('\n');
+ });
+ return clone
+ .text()
+ .replace(/\r\n|\r/g, '\n')
+ .replace(/\n{3,}/g, '\n\n')
+ .trim();
+}
+
+/**
+ * Parse a raw Codeforces problem page HTML string.
+ *
+ * @param {string} html - full HTML of the Codeforces problemset/problem page
+ * @returns {{ title: string, timeLimit: string, memoryLimit: string,
+ * statement: string, inputSpecification: string,
+ * outputSpecification: string,
+ * samples: Array<{ input: string, output: string }> } | null}
+ */
+export function parseCFProblemStatement(html) {
+ const $ = cheerio.load(html);
+
+ const stmtEl = $('.problem-statement').first();
+ if (!stmtEl.length) return null;
+
+ const header = stmtEl.find('.header').first();
+
+ // ---- title ----------------------------------------------------------------
+ const title = header.find('.title').first().text().trim();
+
+ // ---- limits ---------------------------------------------------------------
+ // The limit divs look like:
+ //
You are given two integers $a$ and $b$.
+Print their sum.
+ +The first line contains two integers $a$ and $b$ ($1 <= a, b <= 10^9$).
+Print a single integer — the sum of $a$ and $b$.
+3 4+
7+
1000000000 1000000000+
2000000000+
In the first example the answer is 7.
+First paragraph.
+Second paragraph.
+Third paragraph.
+ +First line is $n$.
+Second line contains $n$ integers.
+Print the answer.
+3 +1 2 3+
6+