```ts
/**
 * Counts distinct routes from top-left to bottom-right in a grid,
 * avoiding blocked cells.
 *
 * @param rows - Number of rows in the grid, must be at least 1.
 * @param cols - Number of columns in the grid, must be at least 1.
 * @param blocked - List of blocked cell coordinates [row, col].
 * @returns Number of distinct routes.
 */
export function countTrails(rows: number, cols: number, blocked: number[][]): number {
    if (!Number.isInteger(rows) || rows < 1) throw new Error('Rows must be a whole number >= 1');
    if (!Number.isInteger(cols) || cols < 1) throw new Error('Columns must be a whole number >= 1');

    const blockedSet = new Set(blocked.map(([r, c]) => `${r},${c}`));

    if (blockedSet.has('0,0') || blockedSet.has(`${rows - 1},${cols - 1}`)) {
        throw new Error('Rope across entrance or exit cell');
    }

    const dp: number[][] = Array.from({ length: rows }, () => Array(cols).fill(0));

    for (let r = 0; r < rows; r++) {
        for (let c = 0; c < cols; c++) {
            if (blockedSet.has(`${r},${c}`)) continue;
            if (r === 0 && c === 0) dp[r][c] = 1;
            else {
                dp[r][c] += (r > 0 ? dp[r - 1][c] : 0) + (c > 0 ? dp[r][c - 1] : 0);
            }
        }
    }

    return dp[rows - 1][cols - 1];
}
```