diff --git a/.clang-format b/.clang-format
new file mode 100644
index 0000000..047604a
--- /dev/null
+++ b/.clang-format
@@ -0,0 +1,7 @@
+BasedOnStyle: LLVM
+UseTab: Always
+TabWidth: 4
+IndentWidth: 4
+ColumnLimit: 100
+AllowShortIfStatementsOnASingleLine: true
+AllowShortLoopsOnASingleLine: true
diff --git a/.clang-tidy b/.clang-tidy
new file mode 100644
index 0000000..92c3c8e
--- /dev/null
+++ b/.clang-tidy
@@ -0,0 +1,12 @@
+Checks: >
+ clang-diagnostic-*,
+ clang-analyzer-*,
+ bugprone-*,
+ -bugprone-easily-swappable-parameters,
+ performance-*,
+ readability-*,
+ -readability-magic-numbers,
+ -readability-function-cognitive-complexity,
+ -readability-identifier-length,
+ -readability-braces-around-statements
+HeaderFilterRegex: '^(src|include)/.*'
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
new file mode 100644
index 0000000..a705607
--- /dev/null
+++ b/.github/workflows/ci.yml
@@ -0,0 +1,36 @@
+name: C CI
+
+on:
+ push:
+ pull_request:
+ branches: [ "main" ]
+
+jobs:
+ build-and-test:
+ runs-on: ubuntu-latest
+
+ steps:
+ - uses: actions/checkout@v4
+
+ - name: Install dependencies
+ run: |
+ sudo apt-get update
+ sudo apt-get install -y build-essential clang libcmocka-dev
+ sudo apt-get install -y build-essential clang clang-format clang-tidy libcmocka-dev bear
+
+ - name: Build
+ run: make CC=gcc
+
+ - name: Run tests
+ run: make test
+
+ - name: Check code formatting
+ run: |
+ find src/ -name '*.c' -o -name '*.h' | xargs clang-format --dry-run -Werror
+
+ - name: Generate compile_commands.json
+ run: bear -- make
+
+ - name: Static analysis
+ run: |
+ find src/ -name '*.c' | xargs clang-tidy --quiet
diff --git a/Makefile b/Makefile
index e69de29..04c7649 100644
--- a/Makefile
+++ b/Makefile
@@ -0,0 +1,62 @@
+CC = gcc
+CFLAGS = -std=c11 -Wall -Wextra -O2 -D_POSIX_C_SOURCE=199309L \
+ -Iinclude -Ithird_party/stb -fopenmp
+CFLAGS_FAST = $(CFLAGS) -O3 -march=native
+LDFLAGS = -lm -fopenmp
+TARGET = conv
+VPATH = src src/lib benchmarks
+
+SRCS = main.c conv.c cli.c filters.c
+OBJS = $(SRCS:.c=.o)
+
+BENCH_DIR = benchmarks
+BENCH_BIN = conv_bench
+
+LIB_OBJS = conv.o cli.o filters.o
+TEST_SRC = tests/test_conv.c
+TEST_BIN = test_runner
+
+all: $(TARGET)
+
+$(TARGET): $(OBJS)
+ $(CC) $(CFLAGS) $^ -o $@ $(LDFLAGS)
+
+bench_conv.o: src/lib/conv.c
+ $(CC) $(CFLAGS_FAST) -c $< -o $@
+
+bench_filters.o: src/lib/filters.c
+ $(CC) $(CFLAGS_FAST) -c $< -o $@
+
+$(BENCH_DIR)/bench_main.o: $(BENCH_DIR)/bench_main.c
+ $(CC) $(CFLAGS_FAST) -c $< -o $@
+
+$(BENCH_DIR)/benchmark.o: $(BENCH_DIR)/benchmark.c
+ $(CC) $(CFLAGS_FAST) -c $< -o $@
+
+$(BENCH_BIN): $(BENCH_DIR)/bench_main.o $(BENCH_DIR)/benchmark.o bench_conv.o bench_filters.o
+ $(CC) $(CFLAGS_FAST) $^ -o $@ $(LDFLAGS)
+
+bench: $(BENCH_BIN)
+ mkdir -p results
+ ./$(BENCH_BIN) > results/bench.csv
+ @echo "Done → results/bench.csv"
+
+%.o: %.c
+ $(CC) $(CFLAGS) -c $< -o $@
+
+test: $(TEST_BIN)
+ ./$(TEST_BIN)
+
+$(TEST_BIN): $(LIB_OBJS) $(TEST_SRC)
+ $(CC) -std=c11 -Wall -Wextra -O0 -g -Iinclude -Ithird_party/stb -fopenmp \
+ $^ -o $@ -lm -lcmocka
+
+results:
+ mkdir -p results
+
+clean:
+ rm -f $(TARGET) $(BENCH_BIN) $(OBJS) $(BENCH_DIR)/bench_main.o \
+ $(BENCH_DIR)/benchmark.o bench_conv.o bench_filters.o $(TEST_BIN)
+ rm -rf results
+
+.PHONY: all test bench clean results
diff --git a/README.md b/README.md
index e69de29..acf48fa 100644
--- a/README.md
+++ b/README.md
@@ -0,0 +1,35 @@
+# Image Convolution
+
+Simple CLI tool for applying convolution filters to images.
+Supports sequential and parallel execution via OpenMP.
+
+## Build & Run
+
+```bash
+make # build
+make test # run tests
+make bench # run benchmarks → results/bench.csv
+```
+
+## Usage
+
+```bash
+./conv