diff --git a/src/app/shared/ui/pagination/pagination.component.html b/src/app/shared/ui/pagination/pagination.component.html
new file mode 100644
index 0000000..af7d941
--- /dev/null
+++ b/src/app/shared/ui/pagination/pagination.component.html
@@ -0,0 +1,37 @@
+
diff --git a/src/app/shared/ui/pagination/pagination.component.scss b/src/app/shared/ui/pagination/pagination.component.scss
new file mode 100644
index 0000000..c4b99b3
--- /dev/null
+++ b/src/app/shared/ui/pagination/pagination.component.scss
@@ -0,0 +1,48 @@
+.app-pagination {
+ display: flex;
+ align-items: center;
+ gap: var(--space-xs, 0.25rem);
+}
+
+.app-pagination__page {
+ min-width: 32px;
+ height: 32px;
+ padding: 0 var(--space-xs, 0.25rem);
+ border: 1px solid transparent;
+ border-radius: var(--radius-md, 6px);
+ background: transparent;
+ color: var(--text-primary, #1f322d);
+ font-size: 0.8125rem;
+ cursor: pointer;
+ transition: background-color var(--transition-fast, 120ms ease),
+ border-color var(--transition-fast, 120ms ease);
+
+ &:hover {
+ background: var(--bg-secondary, #f4f4f5);
+ }
+
+ &:focus-visible {
+ outline: 2px solid var(--primary-color, #2f6e5d);
+ outline-offset: 2px;
+ }
+}
+
+.app-pagination__page--active {
+ background: var(--primary-color, #2f6e5d);
+ color: var(--bg-primary, #fff);
+
+ &:hover {
+ background: var(--primary-color, #2f6e5d);
+ }
+}
+
+.app-pagination__ellipsis {
+ padding: 0 var(--space-xs, 0.25rem);
+ color: var(--text-secondary, #6b7280);
+}
+
+@media (prefers-reduced-motion: reduce) {
+ .app-pagination__page {
+ transition: none;
+ }
+}
diff --git a/src/app/shared/ui/pagination/pagination.component.ts b/src/app/shared/ui/pagination/pagination.component.ts
new file mode 100644
index 0000000..97b9485
--- /dev/null
+++ b/src/app/shared/ui/pagination/pagination.component.ts
@@ -0,0 +1,58 @@
+import { ChangeDetectionStrategy, Component, computed, input, output } from '@angular/core';
+import { ButtonComponent } from '../button/button.component';
+
+const SIBLING_COUNT = 1;
+const ELLIPSIS = '…' as const;
+
+@Component({
+ selector: 'app-pagination',
+ standalone: true,
+ imports: [ButtonComponent],
+ templateUrl: './pagination.component.html',
+ styleUrl: './pagination.component.scss',
+ changeDetection: ChangeDetectionStrategy.OnPush
+})
+export class PaginationComponent {
+ readonly currentPage = input.required();
+ readonly totalPages = input.required();
+ readonly previousLabel = input('Previous');
+ readonly nextLabel = input('Next');
+
+ readonly pageChange = output();
+
+ protected readonly pages = computed<(number | typeof ELLIPSIS)[]>(() => {
+ const total = this.totalPages();
+ const current = this.currentPage();
+ const result: (number | typeof ELLIPSIS)[] = [];
+
+ const rangeStart = Math.max(2, current - SIBLING_COUNT);
+ const rangeEnd = Math.min(total - 1, current + SIBLING_COUNT);
+
+ result.push(1);
+ if (rangeStart > 2) {
+ result.push(ELLIPSIS);
+ }
+ for (let page = rangeStart; page <= rangeEnd; page++) {
+ result.push(page);
+ }
+ if (rangeEnd < total - 1) {
+ result.push(ELLIPSIS);
+ }
+ if (total > 1) {
+ result.push(total);
+ }
+
+ return result;
+ });
+
+ protected goTo(page: number): void {
+ if (page === this.currentPage() || page < 1 || page > this.totalPages()) {
+ return;
+ }
+ this.pageChange.emit(page);
+ }
+
+ protected isEllipsis(value: number | typeof ELLIPSIS): value is typeof ELLIPSIS {
+ return value === ELLIPSIS;
+ }
+}