diff --git a/apps/portal-shell/src/app/components/footer/footer.spec.ts b/apps/portal-shell/src/app/components/footer/footer.spec.ts
new file mode 100644
index 0000000..1080fe5
--- /dev/null
+++ b/apps/portal-shell/src/app/components/footer/footer.spec.ts
@@ -0,0 +1,35 @@
+import { TestBed } from '@angular/core/testing';
+import { provideRouter } from '@angular/router';
+import { Footer } from './footer';
+
+describe('Footer', () => {
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ imports: [Footer],
+ providers: [provideRouter([])],
+ }).compileComponents();
+ });
+
+ it('exposes a footer landmark', async () => {
+ const fixture = TestBed.createComponent(Footer);
+ await fixture.whenStable();
+ const root = fixture.nativeElement as HTMLElement;
+ expect(root.querySelector('footer[aria-label="Page footer"]')).not.toBeNull();
+ });
+
+ it('shows the APF copyright with the current year', async () => {
+ const fixture = TestBed.createComponent(Footer);
+ await fixture.whenStable();
+ const text = (fixture.nativeElement as HTMLElement).textContent ?? '';
+ expect(text).toContain('APF France handicap');
+ expect(text).toContain(String(new Date().getFullYear()));
+ });
+
+ it('renders both accessibility statement links (EN + FR)', async () => {
+ const fixture = TestBed.createComponent(Footer);
+ await fixture.whenStable();
+ const root = fixture.nativeElement as HTMLElement;
+ expect(root.querySelector('a[href="/accessibility"]')).not.toBeNull();
+ expect(root.querySelector('a[href="/accessibilite"]')).not.toBeNull();
+ });
+});
diff --git a/apps/portal-shell/src/app/components/footer/footer.ts b/apps/portal-shell/src/app/components/footer/footer.ts
new file mode 100644
index 0000000..5c06715
--- /dev/null
+++ b/apps/portal-shell/src/app/components/footer/footer.ts
@@ -0,0 +1,25 @@
+import { ChangeDetectionStrategy, Component } from '@angular/core';
+import { RouterLink } from '@angular/router';
+
+/**
+ * Thin full-width footer pinned to the bottom of the shell.
+ *
+ * v1 ships:
+ * - APF France handicap copyright with the current year.
+ * - Accessibility statement links in both languages (RGAA-mandated;
+ * ADR-0016). Both stay visible until the language selector lands,
+ * after which we drop the one matching the current locale.
+ *
+ * The footer is reserved for cross-cutting page furniture — once we
+ * have them, the language toggle and a dev-only version badge land
+ * here. Real product navigation stays in the sidebar.
+ */
+@Component({
+ selector: 'app-footer',
+ imports: [RouterLink],
+ templateUrl: './footer.html',
+ changeDetection: ChangeDetectionStrategy.OnPush,
+})
+export class Footer {
+ protected readonly year = new Date().getFullYear();
+}
diff --git a/apps/portal-shell/src/app/components/header/header.html b/apps/portal-shell/src/app/components/header/header.html
index aaba46d..9ef243d 100644
--- a/apps/portal-shell/src/app/components/header/header.html
+++ b/apps/portal-shell/src/app/components/header/header.html
@@ -1,5 +1,5 @@