diff --git a/deno.lock b/deno.lock index 9c672388..b5c5c60e 100644 --- a/deno.lock +++ b/deno.lock @@ -29,34 +29,37 @@ "jsr:@std/path@^1.1.6": "1.1.6", "jsr:@std/testing@^1.0.21": "1.0.21", "jsr:@tajpouria/cors@^1.2.1": "1.2.1", - "npm:@eslint/js@^10.0.1": "10.0.1_eslint@10.11.0__jiti@2.7.0_jiti@2.7.0", + "npm:@eslint/js@^10.0.1": "10.0.1_eslint@10.11.0_jiti@2.7.0", "npm:@fontsource-variable/fraunces@5.3.0": "5.3.0", "npm:@fontsource-variable/jetbrains-mono@5.3.0": "5.3.0", + "npm:@fontsource-variable/libre-franklin@5.3.0": "5.3.0", + "npm:@fontsource-variable/newsreader@5.3.0": "5.3.0", "npm:@fontsource-variable/saira@5.3.0": "5.3.0", "npm:@fontsource-variable/space-grotesk@5.3.0": "5.3.0", "npm:@fontsource/instrument-serif@5.3.0": "5.3.0", + "npm:@fontsource/manufacturing-consent@5.3.0": "5.3.0", "npm:@lingui/cli@*": "6.8.0", "npm:@lingui/cli@6.8.0": "6.8.0", "npm:@lingui/conf@6.8.0": "6.8.0", "npm:@lingui/core@6.8.0": "6.8.0_@babel+core@7.29.7", "npm:@lingui/format-po@6.8.0": "6.8.0", "npm:@lingui/react@6.8.0": "6.8.0_react@19.3.0_@babel+core@7.29.7", - "npm:@lingui/swc-plugin@6.7.0": "6.7.0_@lingui+core@6.8.0__@babel+core@7.29.7_@babel+core@7.29.7", - "npm:@lingui/vite-plugin@6.8.0": "6.8.0_@babel+core@7.29.7_vite@8.3.1__@types+node@26.6.3__jiti@2.7.0_@types+node@26.6.3_jiti@2.7.0", + "npm:@lingui/swc-plugin@6.7.0": "6.7.0_@lingui+core@6.8.0_@babel+core@7.29.7", + "npm:@lingui/vite-plugin@6.8.0": "6.8.0_@babel+core@7.29.7_vite@8.3.1_@types+node@26.6.3_jiti@2.7.0", "npm:@resvg/resvg-wasm@2.6.2": "2.6.2", "npm:@resvg/resvg-wasm@^2.6.2": "2.6.2", "npm:@testing-library/dom@^10.4.2": "10.4.2", - "npm:@testing-library/jest-dom@^7.0.1": "7.0.1_@testing-library+dom@10.4.2_vitest@5.0.2__@types+node@26.6.3__@vitest+coverage-v8@5.0.2__jsdom@30.1.1__vite@8.3.1___@types+node@26.6.3___jiti@2.7.0_@vitest+coverage-v8@5.0.2__vitest@5.0.2__@types+node@26.6.3__jsdom@30.1.1__vite@8.3.1___@types+node@26.6.3___jiti@2.7.0", - "npm:@testing-library/react@^16.3.3": "16.3.3_@testing-library+dom@10.4.2_@types+react@19.3.0_@types+react-dom@19.3.0__@types+react@19.3.0_react@19.3.0_react-dom@19.3.0__react@19.3.0", + "npm:@testing-library/jest-dom@^7.0.1": "7.0.1_@testing-library+dom@10.4.2_vitest@5.0.2_@vitest+coverage-v8@5.0.2", + "npm:@testing-library/react@^16.3.3": "16.3.3_@testing-library+dom@10.4.2_@types+react@19.3.0_@types+react-dom@19.3.0_react@19.3.0_react-dom@19.3.0", "npm:@testing-library/user-event@^14.6.7": "14.6.7_@testing-library+dom@10.4.2", "npm:@types/node@^26.6.3": "26.6.3", "npm:@types/react-dom@^19.3.0": "19.3.0_@types+react@19.3.0", "npm:@types/react@^19.3.0": "19.3.0", - "npm:@vitejs/plugin-react-swc@^4.3.3": "4.3.3_vite@8.3.1__@types+node@26.6.3__jiti@2.7.0_@types+node@26.6.3_jiti@2.7.0", - "npm:@vitest/coverage-v8@^5.0.2": "5.0.2_vitest@5.0.2_@types+node@26.6.3_jsdom@30.1.1_vite@8.3.1__@types+node@26.6.3__jiti@2.7.0", + "npm:@vitejs/plugin-react-swc@^4.3.3": "4.3.3_vite@8.3.1_@types+node@26.6.3_jiti@2.7.0", + "npm:@vitest/coverage-v8@^5.0.2": "5.0.2_vitest@5.0.2_@types+node@26.6.3_jsdom@30.1.1_vite@8.3.1", "npm:emojibase-data@17.0.0": "17.0.0_emojibase@17.0.0", - "npm:eslint-plugin-react-hooks@^7.1.1": "7.1.1_eslint@10.11.0__jiti@2.7.0_jiti@2.7.0", - "npm:eslint-plugin-react-refresh@~0.5.7": "0.5.7_eslint@10.11.0__jiti@2.7.0_jiti@2.7.0", + "npm:eslint-plugin-react-hooks@^7.1.1": "7.1.1_eslint@10.11.0_jiti@2.7.0", + "npm:eslint-plugin-react-refresh@~0.5.7": "0.5.7_eslint@10.11.0_jiti@2.7.0", "npm:eslint@^10.11.0": "10.11.0_jiti@2.7.0", "npm:frimousse@0.4": "0.4.0_react@19.3.0_typescript@6.0.3", "npm:globals@^17.12.0": "17.12.0", @@ -68,15 +71,15 @@ "npm:react-dom@^19.3.0": "19.3.0_react@19.3.0", "npm:react-hook-form@^7.89.0": "7.89.0_@types+react@19.3.0_react@19.3.0", "npm:react-markdown@^10.1.0": "10.1.0_@types+react@19.3.0_react@19.3.0", - "npm:react-router@^8.4.0": "8.4.0_react@19.3.0_react-dom@19.3.0__react@19.3.0", + "npm:react-router@^8.4.0": "8.4.0_react@19.3.0_react-dom@19.3.0", "npm:react@^19.3.0": "19.3.0", "npm:remark-gfm@^4.0.1": "4.0.1", - "npm:typescript-eslint@^8.70.1": "8.70.1_eslint@10.11.0__jiti@2.7.0_typescript@6.0.3_jiti@2.7.0", + "npm:typescript-eslint@^8.70.1": "8.70.1_eslint@10.11.0_typescript@6.0.3_jiti@2.7.0", "npm:typescript@~6.0.3": "6.0.3", "npm:vite@*": "8.3.1_@types+node@26.6.3_jiti@2.7.0", "npm:vite@^8.3.1": "8.3.1_@types+node@26.6.3_jiti@2.7.0", - "npm:vitest@*": "5.0.2_@types+node@26.6.3_@vitest+coverage-v8@5.0.2_jsdom@30.1.1_vite@8.3.1__@types+node@26.6.3__jiti@2.7.0", - "npm:vitest@^5.0.2": "5.0.2_@types+node@26.6.3_@vitest+coverage-v8@5.0.2_jsdom@30.1.1_vite@8.3.1__@types+node@26.6.3__jiti@2.7.0" + "npm:vitest@*": "5.0.2_@types+node@26.6.3_@vitest+coverage-v8@5.0.2_jsdom@30.1.1_vite@8.3.1", + "npm:vitest@^5.0.2": "5.0.2_@types+node@26.6.3_@vitest+coverage-v8@5.0.2_jsdom@30.1.1_vite@8.3.1" }, "jsr": { "@db/sqlite@0.13.0": { @@ -382,14 +385,14 @@ "@csstools/color-helpers@6.1.2": { "integrity": "sha512-grhRy3OKmniaAEKXMjua5z/EODX0MSqBGjunw8+j/3HQjOnahs2AGhvEOIYVUWcU6ScApbhLhVrQTX8XqrMrow==" }, - "@csstools/css-calc@3.4.1_@csstools+css-parser-algorithms@4.0.1__@csstools+css-tokenizer@4.0.2_@csstools+css-tokenizer@4.0.2": { + "@csstools/css-calc@3.4.1_@csstools+css-parser-algorithms@4.0.1_@csstools+css-tokenizer@4.0.2": { "integrity": "sha512-EtC7SoN1j6J4E4DCwg5QgbO5TGxgxIA1RXqe+W+qUM+BUcezx9wT+/tiQ/WO2yCX4i5X+Cuf9ciJ22aP4UEwWw==", "dependencies": [ "@csstools/css-parser-algorithms", "@csstools/css-tokenizer" ] }, - "@csstools/css-color-parser@4.2.4_@csstools+css-parser-algorithms@4.0.1__@csstools+css-tokenizer@4.0.2_@csstools+css-tokenizer@4.0.2": { + "@csstools/css-color-parser@4.2.4_@csstools+css-parser-algorithms@4.0.1_@csstools+css-tokenizer@4.0.2": { "integrity": "sha512-DyefytAZ735mX4Dq/WcDAXFtXhaEFvme0ZS9tVEBAc2whxUthXr0R0L2rmEPm59SrMBkGrFzQviBXMs/UtnABQ==", "dependencies": [ "@csstools/color-helpers", @@ -416,7 +419,7 @@ "@csstools/css-tokenizer@4.0.2": { "integrity": "sha512-OoKoR0f76dCY666JlcbhmVTs2drYj1GUXZTYTcbUgJjh9Nv41aFfZ21bPQTERm5+L5cBDo466NltB2lplS5GBw==" }, - "@eslint-community/eslint-utils@4.9.1_eslint@10.11.0__jiti@2.7.0_jiti@2.7.0": { + "@eslint-community/eslint-utils@4.9.1_eslint@10.11.0_jiti@2.7.0": { "integrity": "sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==", "dependencies": [ "eslint", @@ -446,7 +449,7 @@ "@types/json-schema" ] }, - "@eslint/js@10.0.1_eslint@10.11.0__jiti@2.7.0_jiti@2.7.0": { + "@eslint/js@10.0.1_eslint@10.11.0_jiti@2.7.0": { "integrity": "sha512-zeR9k5pd4gxjZ0abRoIaxdc7I3nDktoXZk2qOv9gCNWx3mVwEn32VRhyLaRsDiJjTs0xq/T8mfPtyuXu7GWBcA==", "dependencies": [ "eslint" @@ -474,6 +477,12 @@ "@fontsource-variable/jetbrains-mono@5.3.0": { "integrity": "sha512-F32xpS2NsGYoQi2ADSkKTgpJj7ozajsGgDJ8woTnqjmIB+dxDIqImjl4pXZVEExu8UFZ2ndhmX18EBS/hdz3Lw==" }, + "@fontsource-variable/libre-franklin@5.3.0": { + "integrity": "sha512-PEQemCKVRtkLZJNpfvR4wG7x87c8iG05XJuEaX59ogMcAQpdtCZz/NXZUrXLJyvyx3/HD70W4LUppWLBdEdU2g==" + }, + "@fontsource-variable/newsreader@5.3.0": { + "integrity": "sha512-rrzYi43qMpbzwuFtf9OkWH8sxAPVPcQQQEwXpPtwaKYeJ8yVg5aLs5kawmo1f2Q1t1M38TLmEKCkGVDsYwgdFw==" + }, "@fontsource-variable/saira@5.3.0": { "integrity": "sha512-C1rm7n1OuEx4Se/SAB/5EeUuCviGFFnYTr5vxuQQm2YIOfgbQWx/P6qqhdXlVCBRm/1UzNWCtDK5jNbVmO6W2w==" }, @@ -483,6 +492,9 @@ "@fontsource/instrument-serif@5.3.0": { "integrity": "sha512-mDiaIg0u67sYV59fie92Wz4sM8UiVlbL7fLxnFPCKkX15DMASEnQTREbaP5S5/3DCcsoAOcQ0sV9E4AeY4QqYQ==" }, + "@fontsource/manufacturing-consent@5.3.0": { + "integrity": "sha512-p/omJ13hFgV6+THlT370nyHjlMkrZWkKPiMSZHROcyFoylVvazo19Rp9KJhwudWqcbkClgzjEryvC330y4Pr6Q==" + }, "@humanfs/core@0.19.2": { "integrity": "sha512-UhXNm+CFMWcbChXywFwkmhqjs3PRCmcSa/hfBgLIb7oQ5HNb1wS0icWsGtSAUNgefHeI+eBrA8I1fxmbHsGdvA==", "dependencies": [ @@ -647,14 +659,14 @@ "use-sync-external-store" ] }, - "@lingui/swc-plugin@6.7.0_@lingui+core@6.8.0__@babel+core@7.29.7_@babel+core@7.29.7": { + "@lingui/swc-plugin@6.7.0_@lingui+core@6.8.0_@babel+core@7.29.7": { "integrity": "sha512-TRLHfeQT/q9JXy0cBGOtbfcEWJFF1szjPCVM4AT1HdZyfGj+dQ0r8hHq+08c4iXZLzlsYilzw+bZJ3TahG5g5w==", "dependencies": [ "@lingui/conf", "@lingui/core" ] }, - "@lingui/vite-plugin@6.8.0_@babel+core@7.29.7_vite@8.3.1__@types+node@26.6.3__jiti@2.7.0_@types+node@26.6.3_jiti@2.7.0": { + "@lingui/vite-plugin@6.8.0_@babel+core@7.29.7_vite@8.3.1_@types+node@26.6.3_jiti@2.7.0": { "integrity": "sha512-pyOGwY8llaXzBBDrbZpAV5a0z7ekz0zJxU+EGXxyqL/gLneqHgLhPCv8uNB+MyIGwrw0PC9CMtCpHVlS3oWguA==", "dependencies": [ "@babel/core", @@ -869,7 +881,7 @@ "pretty-format@27.5.1" ] }, - "@testing-library/jest-dom@7.0.1_@testing-library+dom@10.4.2_vitest@5.0.2__@types+node@26.6.3__@vitest+coverage-v8@5.0.2__jsdom@30.1.1__vite@8.3.1___@types+node@26.6.3___jiti@2.7.0_@vitest+coverage-v8@5.0.2__vitest@5.0.2__@types+node@26.6.3__jsdom@30.1.1__vite@8.3.1___@types+node@26.6.3___jiti@2.7.0": { + "@testing-library/jest-dom@7.0.1_@testing-library+dom@10.4.2_vitest@5.0.2_@vitest+coverage-v8@5.0.2": { "integrity": "sha512-oMDTC3oA+6CXSO2JZnvOI7CA6oVub6kij5ggk9ohwye5slmkwxYDXcPOVxgMw/RQlticjtO0C1RZkR97HgrWMw==", "dependencies": [ "@adobe/css-tools", @@ -885,7 +897,7 @@ "vitest" ] }, - "@testing-library/react@16.3.3_@testing-library+dom@10.4.2_@types+react@19.3.0_@types+react-dom@19.3.0__@types+react@19.3.0_react@19.3.0_react-dom@19.3.0__react@19.3.0": { + "@testing-library/react@16.3.3_@testing-library+dom@10.4.2_@types+react@19.3.0_@types+react-dom@19.3.0_react@19.3.0_react-dom@19.3.0": { "integrity": "sha512-Uo193NgQbPMz6lrrhtRQQFcMC6Re/ELLFbbuVL30WDlZxlpZf9/lMHTAVxPRLw1q1iu9OJmR1c2BLiENRstdBg==", "dependencies": [ "@babel/runtime", @@ -1003,7 +1015,7 @@ "@types/yargs-parser" ] }, - "@typescript-eslint/eslint-plugin@8.70.1_@typescript-eslint+parser@8.70.1__eslint@10.11.0___jiti@2.7.0__typescript@6.0.3_eslint@10.11.0__jiti@2.7.0_typescript@6.0.3_jiti@2.7.0": { + "@typescript-eslint/eslint-plugin@8.70.1_@typescript-eslint+parser@8.70.1_eslint@10.11.0_typescript@6.0.3_jiti@2.7.0": { "integrity": "sha512-nDNrUQ/4ruSNYbu749TRY7cfrzPtoLHEXSNBI8aaNY32LlZCajixqRf3FqcKC4p5Cam4VOHYx/t+i5+nKXvrqA==", "dependencies": [ "@eslint-community/regexpp", @@ -1019,7 +1031,7 @@ "typescript" ] }, - "@typescript-eslint/parser@8.70.1_eslint@10.11.0__jiti@2.7.0_typescript@6.0.3": { + "@typescript-eslint/parser@8.70.1_eslint@10.11.0_typescript@6.0.3": { "integrity": "sha512-nO974WLllwhSFWQXnMLj6nDGa8f0khKEz1JzpPJ1u7Vm/4X1X6ZHajpoknU4bb41vJyMB0HHVyS2GqdhWfIXZw==", "dependencies": [ "@typescript-eslint/scope-manager", @@ -1053,7 +1065,7 @@ "typescript" ] }, - "@typescript-eslint/type-utils@8.70.1_eslint@10.11.0__jiti@2.7.0_typescript@6.0.3_jiti@2.7.0": { + "@typescript-eslint/type-utils@8.70.1_eslint@10.11.0_typescript@6.0.3_jiti@2.7.0": { "integrity": "sha512-7zKTnyvaVWqzLZHPFQtX1hVHqgkMC+WebPWakNCSyrQVbIP1AM0L0TlBZtACldIRb6PptI8Odk+jyZ5kP3B1VA==", "dependencies": [ "@typescript-eslint/types", @@ -1083,7 +1095,7 @@ "typescript" ] }, - "@typescript-eslint/utils@8.70.1_eslint@10.11.0__jiti@2.7.0_typescript@6.0.3_jiti@2.7.0": { + "@typescript-eslint/utils@8.70.1_eslint@10.11.0_typescript@6.0.3_jiti@2.7.0": { "integrity": "sha512-Esgul8MsnKnRLdYU2Eb2cRV9bS5HJYtKj1ByJnOzzG2M58DGdSUQ1jUuILxipqcpB2h9WLrbD5GijIWUjX/Tqw==", "dependencies": [ "@eslint-community/eslint-utils", @@ -1105,7 +1117,7 @@ "integrity": "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==", "deprecated": true }, - "@vitejs/plugin-react-swc@4.3.3_vite@8.3.1__@types+node@26.6.3__jiti@2.7.0_@types+node@26.6.3_jiti@2.7.0": { + "@vitejs/plugin-react-swc@4.3.3_vite@8.3.1_@types+node@26.6.3_jiti@2.7.0": { "integrity": "sha512-bti8ZAcvz4Lh6/e4Uk2k3aa1TiUXbbMsahuqOHvd3MveFTkKDZOA6wQVkpj7J/+tepX/wGfe+lsGh/t24HTXMQ==", "dependencies": [ "@rolldown/pluginutils", @@ -1113,7 +1125,7 @@ "vite" ] }, - "@vitest/coverage-v8@5.0.2_vitest@5.0.2_@types+node@26.6.3_jsdom@30.1.1_vite@8.3.1__@types+node@26.6.3__jiti@2.7.0": { + "@vitest/coverage-v8@5.0.2_vitest@5.0.2_@types+node@26.6.3_jsdom@30.1.1_vite@8.3.1": { "integrity": "sha512-3ffHBEi8DOOBLwIGBhOBZbRfYFYWjMUuxZicONdhuFEFEG5AVsMOSrVeuROskqnqpbOmEJwxM2eTVZ9N3a1t+A==", "dependencies": [ "@bcoe/v8-coverage", @@ -1136,7 +1148,7 @@ "@vitest/istanbul-lib-coverage" ] }, - "@vitest/mocker@5.0.2_vite@8.3.1__@types+node@26.6.3__jiti@2.7.0_@types+node@26.6.3_jiti@2.7.0": { + "@vitest/mocker@5.0.2_vite@8.3.1_@types+node@26.6.3_jiti@2.7.0": { "integrity": "sha512-Z5FS00Q1SJHkB35xATsmWGdQ5WA1/0MV3CDjqyv7GavHv1OfOj145MNfHOlHk7QLes21dKFDHr8EO2zvL+9WGA==", "dependencies": [ "@jridgewell/trace-mapping", @@ -1428,7 +1440,7 @@ "escape-string-regexp@5.0.0": { "integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==" }, - "eslint-plugin-react-hooks@7.1.1_eslint@10.11.0__jiti@2.7.0_jiti@2.7.0": { + "eslint-plugin-react-hooks@7.1.1_eslint@10.11.0_jiti@2.7.0": { "integrity": "sha512-f2I7Gw6JbvCexzIInuSbZpfdQ44D7iqdWX01FKLvrPgqxoE7oMj8clOfto8U6vYiz4yd5oKu39rRSVOe1zRu0g==", "dependencies": [ "@babel/core", @@ -1439,7 +1451,7 @@ "zod-validation-error" ] }, - "eslint-plugin-react-refresh@0.5.7_eslint@10.11.0__jiti@2.7.0_jiti@2.7.0": { + "eslint-plugin-react-refresh@0.5.7_eslint@10.11.0_jiti@2.7.0": { "integrity": "sha512-XhJSzLljuYD4UjNuFGJu2v7aD3sqNVK12w5/DCUfrNfHWegZo2+TsavNx8MuxMwEquuFAzNbNfXKU0WoZ+YUIg==", "dependencies": [ "eslint" @@ -2569,7 +2581,7 @@ "vfile" ] }, - "react-router@8.4.0_react@19.3.0_react-dom@19.3.0__react@19.3.0": { + "react-router@8.4.0_react@19.3.0_react-dom@19.3.0": { "integrity": "sha512-JtydAkBvU9UTEe5qNC+POhu+ZBNM7rlKY2IegwXc7Idj7xfRG16x5RZrw3mju+XlqBxfvb2ky9P3jUp9WyWC1g==", "dependencies": [ "@remix-run/route-pattern", @@ -2834,7 +2846,7 @@ "prelude-ls" ] }, - "typescript-eslint@8.70.1_eslint@10.11.0__jiti@2.7.0_typescript@6.0.3_jiti@2.7.0": { + "typescript-eslint@8.70.1_eslint@10.11.0_typescript@6.0.3_jiti@2.7.0": { "integrity": "sha512-AcWG7KDjZ2THNXsgwttMaGmzVi0VFRlFYfqFHYQRbDpF3owuYbuiL8c7UUrd2k8s3PoSfIQrWfrGXfcElrWLYA==", "dependencies": [ "@typescript-eslint/eslint-plugin", @@ -2955,7 +2967,7 @@ ], "bin": true }, - "vitest@5.0.2_@types+node@26.6.3_@vitest+coverage-v8@5.0.2_jsdom@30.1.1_vite@8.3.1__@types+node@26.6.3__jiti@2.7.0": { + "vitest@5.0.2_@types+node@26.6.3_@vitest+coverage-v8@5.0.2_jsdom@30.1.1_vite@8.3.1": { "integrity": "sha512-7MQrx9pDv5aHiUcovIb/70Ys3tgtkUVgCtledvKdCmEO+/1Dicq5ZqoSxOW034m03oqC+oHOKui2dM6qtMLoJg==", "dependencies": [ "@types/chai", @@ -3071,9 +3083,12 @@ "npm:@eslint/js@^10.0.1", "npm:@fontsource-variable/fraunces@5.3.0", "npm:@fontsource-variable/jetbrains-mono@5.3.0", + "npm:@fontsource-variable/libre-franklin@5.3.0", + "npm:@fontsource-variable/newsreader@5.3.0", "npm:@fontsource-variable/saira@5.3.0", "npm:@fontsource-variable/space-grotesk@5.3.0", "npm:@fontsource/instrument-serif@5.3.0", + "npm:@fontsource/manufacturing-consent@5.3.0", "npm:@lingui/cli@6.8.0", "npm:@lingui/conf@6.8.0", "npm:@lingui/core@6.8.0", diff --git a/package.json b/package.json index 0af7038f..8b31d99d 100644 --- a/package.json +++ b/package.json @@ -14,7 +14,10 @@ "@fontsource-variable/jetbrains-mono": "5.3.0", "@fontsource-variable/saira": "5.3.0", "@fontsource-variable/space-grotesk": "5.3.0", + "@fontsource-variable/libre-franklin": "5.3.0", + "@fontsource-variable/newsreader": "5.3.0", "@fontsource/instrument-serif": "5.3.0", + "@fontsource/manufacturing-consent": "5.3.0", "@lingui/cli": "6.8.0", "@lingui/conf": "6.8.0", "@lingui/core": "6.8.0", diff --git a/src/fonts.css b/src/fonts.css index 4b39fee5..193d0b6a 100644 --- a/src/fonts.css +++ b/src/fonts.css @@ -62,7 +62,8 @@ unicode-range: U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD; } -/* nyt */ +/* nyt: the headlines' serif, the text's (opsz as well as wght), the + interface's sans, and the nameplate's blackletter */ @font-face { font-family: 'Instrument Serif'; font-style: normal; @@ -99,6 +100,96 @@ format('woff2'); unicode-range: U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD; } +@font-face { + font-family: 'Newsreader Variable'; + font-style: normal; + font-display: swap; + font-weight: 200 800; + src: url(@fontsource-variable/newsreader/files/newsreader-latin-ext-opsz-normal.woff2) + format('woff2-variations'); + unicode-range: U+0100-02BA,U+02BD-02C5,U+02C7-02CC,U+02CE-02D7,U+02DD-02FF,U+0304,U+0308,U+0329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF; +} +@font-face { + font-family: 'Newsreader Variable'; + font-style: normal; + font-display: swap; + font-weight: 200 800; + src: url(@fontsource-variable/newsreader/files/newsreader-latin-opsz-normal.woff2) + format('woff2-variations'); + unicode-range: U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD; +} +@font-face { + font-family: 'Newsreader Variable'; + font-style: italic; + font-display: swap; + font-weight: 200 800; + src: url(@fontsource-variable/newsreader/files/newsreader-latin-ext-opsz-italic.woff2) + format('woff2-variations'); + unicode-range: U+0100-02BA,U+02BD-02C5,U+02C7-02CC,U+02CE-02D7,U+02DD-02FF,U+0304,U+0308,U+0329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF; +} +@font-face { + font-family: 'Newsreader Variable'; + font-style: italic; + font-display: swap; + font-weight: 200 800; + src: url(@fontsource-variable/newsreader/files/newsreader-latin-opsz-italic.woff2) + format('woff2-variations'); + unicode-range: U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD; +} +@font-face { + font-family: 'Libre Franklin Variable'; + font-style: normal; + font-display: swap; + font-weight: 100 900; + src: url(@fontsource-variable/libre-franklin/files/libre-franklin-latin-ext-wght-normal.woff2) + format('woff2-variations'); + unicode-range: U+0100-02BA,U+02BD-02C5,U+02C7-02CC,U+02CE-02D7,U+02DD-02FF,U+0304,U+0308,U+0329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF; +} +@font-face { + font-family: 'Libre Franklin Variable'; + font-style: normal; + font-display: swap; + font-weight: 100 900; + src: url(@fontsource-variable/libre-franklin/files/libre-franklin-latin-wght-normal.woff2) + format('woff2-variations'); + unicode-range: U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD; +} +@font-face { + font-family: 'Libre Franklin Variable'; + font-style: italic; + font-display: swap; + font-weight: 100 900; + src: url(@fontsource-variable/libre-franklin/files/libre-franklin-latin-ext-wght-italic.woff2) + format('woff2-variations'); + unicode-range: U+0100-02BA,U+02BD-02C5,U+02C7-02CC,U+02CE-02D7,U+02DD-02FF,U+0304,U+0308,U+0329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF; +} +@font-face { + font-family: 'Libre Franklin Variable'; + font-style: italic; + font-display: swap; + font-weight: 100 900; + src: url(@fontsource-variable/libre-franklin/files/libre-franklin-latin-wght-italic.woff2) + format('woff2-variations'); + unicode-range: U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD; +} +@font-face { + font-family: 'Manufacturing Consent'; + font-style: normal; + font-display: swap; + font-weight: 400; + src: url(@fontsource/manufacturing-consent/files/manufacturing-consent-latin-ext-400-normal.woff2) + format('woff2'); + unicode-range: U+0100-02BA,U+02BD-02C5,U+02C7-02CC,U+02CE-02D7,U+02DD-02FF,U+0304,U+0308,U+0329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF; +} +@font-face { + font-family: 'Manufacturing Consent'; + font-style: normal; + font-display: swap; + font-weight: 400; + src: url(@fontsource/manufacturing-consent/files/manufacturing-consent-latin-400-normal.woff2) + format('woff2'); + unicode-range: U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD; +} /* kanagawa */ @font-face { diff --git a/src/themes/nyt.css b/src/themes/nyt.css index a2e4d022..b8dca882 100644 --- a/src/themes/nyt.css +++ b/src/themes/nyt.css @@ -1,7 +1,9 @@ /* ══════════════════════════════════════════════════════════════════════ "Old Grey Lady" theme — New York Times editorial styling. - Serif typography, paper-and-ink palette, hairline rules, sharp corners, - restrained NYT-blue links, inverted ink-on-paper primary buttons. + Four faces, each with one job: a blackletter nameplate, a condensed display + serif for headlines, a news serif for prose, and a Franklin gothic for + everything the interface says. + Paper-and-ink palette, ink rules over hairlines, square pictures. Light scheme → newsprint (white page, black ink). Dark scheme → NYT night mode (near-black page, warm-grey ink). @@ -9,33 +11,26 @@ /* ── Palette (light-dark: see tokens.css) ─────────────────────────── */ :root[data-style="nyt"] { - /* Square corners everywhere App.css uses the radius scale. */ + /* Pictures, cards and sheets are square; a control keeps the 3px of a + newspaper site's buttons (see "Controls" below). */ --radius-xs: 0; --radius-sm: 0; --radius-md: 0; --radius-lg: 0; --radius-xl: 0; --radius-full: 0; + --nyt-control-radius: 3px; /* Hairlines, not strokes — controls match the rules between stories. */ --border-width: 1px; - /* Instrument Serif is a condensed display face: at the shared scale its - small sizes read a step smaller than other themes', so lift the low end. */ - --text-2xs: 0.78rem; - --text-xs: 0.86rem; - --text-sm: 0.94rem; - --text-md: 1rem; - --text-base: 1.06rem; - --text-lg: 1.2rem; - --color-text: light-dark(#121212, #e6e6e6); --color-text-secondary: light-dark(#5a5a5a, #ababab); - --color-text-muted: light-dark(#767676, #8a8a8a); + --color-text-muted: light-dark(#727272, #8a8a8a); --color-on-accent: light-dark(#ffffff, #121212); --color-bg: light-dark(#ffffff, #121212); --color-surface: light-dark(#ffffff, #1e1e1e); - --color-border: light-dark(#e2e2e2, #3a3a3a); + --color-border: light-dark(#dfdfdf, #3a3a3a); --color-border-subtle: light-dark(rgba(0, 0, 0, 0.08), rgba(255, 255, 255, 0.12)); --color-accent: light-dark(#121212, #ececec); --color-accent-hover: light-dark(#000000, #ffffff); @@ -48,41 +43,115 @@ --color-success-bg: light-dark(#e6f4ea, #122a14); --color-success-hover: light-dark(#136a2c, #95d699); --color-overlay: light-dark(rgba(0, 0, 0, 0.4), rgba(0, 0, 0, 0.6)); - --color-header-user-bg: light-dark(rgba(0, 0, 0, 0.05), rgba(255, 255, 255, 0.06)); - --color-header-user-bg-hover: light-dark(rgba(0, 0, 0, 0.09), rgba(255, 255, 255, 0.12)); - --color-option-bg: light-dark(#f2f2f2, #181818); - --color-option-border: light-dark(#e2e2e2, #3a3a3a); + /* The header's controls sit on the page; only hovering one tints it. */ + --color-header-user-bg: transparent; + --color-header-user-bg-hover: light-dark(rgba(0, 0, 0, 0.06), rgba(255, 255, 255, 0.1)); + --color-option-bg: light-dark(#f4f4f4, #181818); + --color-option-border: light-dark(#dfdfdf, #3a3a3a); + + /* The masthead is printed on the page, in both schemes. */ + --header-bg: var(--color-bg); + + /* Section tabs read as a paper's section list: sentence case, no pill. */ + --tab-text-transform: none; + --tab-tracking: 0; } /* ══════════════════════════════════════════════════════════════════════ - Structural overrides + Typography ══════════════════════════════════════════════════════════════════════ */ -/* ── Typography — one serif everywhere ───────────────────────────── */ +/* What is read is serif, and it is the default; what is operated is sans + (--font-ui, the list below). */ [data-style="nyt"] { - --font-body: "Instrument Serif", Georgia, "Times New Roman", serif; + --font-body: "Newsreader Variable", Georgia, "Times New Roman", serif; --font-display: "Instrument Serif", Georgia, "Times New Roman", serif; + --font-ui: "Libre Franklin Variable", "Franklin Gothic Medium", "Helvetica Neue", Arial, + sans-serif; + --font-nameplate: "Manufacturing Consent", "Old English Text MT", var(--font-display); font-family: var(--font-body); font-weight: 400; line-height: 1.5; - /* Single weight, no small caps: never let the browser fake either — a - smeared faux bold or spindly faux small caps give the typeface away. - Hierarchy comes from size, italics and capitals instead. */ - font-synthesis: none; /* Hairline icons, drawn in ink like the rules. */ --nav-icon-stroke: 1.25; } -/* This theme squares everything off — opt the FAB into a square too (it keeps - its animations; only the corners change). */ -[data-style="nyt"] .dump-fab, -[data-style="nyt"] .chat-fab { - --fab-radius: 0; +/* ── The interface's sans — controls, navigation, labels, datelines ── */ +[data-style="nyt"] :is( + button, + input, + select, + label, + .app-header-nav, + .app-header-status, + .feed-sort, + .profile-tabs, + .search-tabs, + .playlist-sort-tabs, + .category-switcher-menu, + .user-menu-dropdown, + .user-list-popover, + .dump-card-meta, + .dump-card-vote, + .playlist-card-meta, + .journal-card-meta, + .category-link, + .dump-op, + .dump-actions, + .dump-edited-label, + .rich-content-badge, + .rich-content-url, + .comment-section-title, + .related-section-title, + .comment-meta, + .comment-actions, + .comment-form-actions, + .notification-time, + .notifications-unread-pill, + .notification-badge, + .page-group-label, + .form-page-eyebrow, + .form-hint, + .form-field, + .profile-role-chip, + .profile-invited-by, + .profile-email-display, + .profile-section-header, + .profile-appearance-row, + .playlist-badge, + .dump-card-private-badge, + .avatar-initials, + .thumb-placeholder-initials, + .feed-end, + .empty-state, + .tooltip, + .dump-steps, + .auth-card-footer, + .fdz__hint, + .fdz__limit, + .fdz__file-meta, + .chat-message-meta, + .chat-day-sep, + .chat-status, + .chat-typing, + .chat-reply-bar, + .chat-reply-ref, + .audio-player-time, + .global-player-position, + .global-player-status, + .global-player-subtitle +) { + font-family: var(--font-ui); } -/* ── Headlines — display serif ───────────────────────────────────── */ +/* A note is written in the face it will be read in. */ +[data-style="nyt"] textarea { + font-family: var(--font-body); + font-size: var(--text-base); +} + +/* ── Headlines ───────────────────────────────────────────────────── */ [data-style="nyt"] h1, -[data-style="nyt"] .app-header-brand, [data-style="nyt"] .dump-title, [data-style="nyt"] .dump-card-title, [data-style="nyt"] .playlist-card-title, @@ -91,35 +160,69 @@ [data-style="nyt"] .form-page-title, [data-style="nyt"] .auth-card-title, [data-style="nyt"] .page-title, -[data-style="nyt"] .dump-create-title { +[data-style="nyt"] .profile-username, +[data-style="nyt"] .dump-create-title, +[data-style="nyt"] .rich-content-title, +[data-style="nyt"] .modal-title { font-family: var(--font-display); - font-weight: 400; /* Instrument Serif is single-weight; size carries it */ + /* Instrument Serif has one weight: size carries the hierarchy, and the + browser is kept from smearing a bold out of it. */ + font-weight: 400; + font-synthesis: none; letter-spacing: 0; - line-height: 1.14; + line-height: 1.12; } -/* A confident broadsheet type scale */ -[data-style="nyt"] .app-header-brand { - font-size: 1.45rem; - letter-spacing: 0; -} +/* A broadsheet's scale. The face is condensed and runs small, so each step + is set larger than its rem reads. */ [data-style="nyt"] .dump-title { - font-size: 2.1rem; + font-size: clamp(2.2rem, 1.3rem + 3.4vw, 3.2rem); + line-height: 1.04; } [data-style="nyt"] .playlist-detail-title { - font-size: 1.9rem; + font-size: clamp(2rem, 1.3rem + 2.6vw, 2.75rem); + line-height: 1.06; +} +[data-style="nyt"] .page-title, +[data-style="nyt"] .profile-username, +[data-style="nyt"] .form-page-title, +[data-style="nyt"] .auth-card-title { + font-size: 2.5rem; + line-height: 1.05; +} +/* The base title breaks anywhere, which lets its text be squeezed under the + width of a word: here a word stays whole unless it is wider than the line. */ +[data-style="nyt"] .page-title { + overflow-wrap: break-word; } [data-style="nyt"] .dump-card-title, [data-style="nyt"] .playlist-card-title { - font-size: 1.2rem; - line-height: 1.2; + font-size: 1.5rem; + line-height: 1.12; +} +[data-style="nyt"] .rich-content-title { + font-size: 1.3rem; + line-height: 1.15; +} +[data-style="nyt"] .modal-title { + font-size: 1.5rem; } [data-style="nyt"] .journal-card-title { - font-size: 1.15rem; - line-height: 1.18; + font-size: 1.35rem; + line-height: 1.1; } [data-style="nyt"] .journal-card--feature .journal-card-title { - font-size: 1.6rem; + font-size: 2.2rem; + line-height: 1.04; +} +/* The base scale sets these three smaller than a card's title; in this face + they need the same lift as the rest. */ +[data-style="nyt"] .journal-card--square.journal-card--image .journal-card-title, +[data-style="nyt"] .journal-card--text .journal-card-title { + font-size: 1.25rem; +} +[data-style="nyt"] .journal-card--quote .journal-card-title { + font-size: 1.1rem; } /* Headline links underline on hover (ink stays ink). */ @@ -128,7 +231,8 @@ [data-style="nyt"] .journal-card-title:hover { color: var(--color-text); text-decoration: underline; - text-underline-offset: 2px; + text-decoration-thickness: 1px; + text-underline-offset: 3px; } /* …except over a photo, where the headline is white on a dark scrim: ink there goes black-on-black in the light scheme. */ @@ -150,28 +254,264 @@ text-decoration: none; } -/* ── Kickers — section labels & badges in spaced capitals ───────── */ -[data-style="nyt"] .comment-section-title, -[data-style="nyt"] .related-section-title, -[data-style="nyt"] .form-page-eyebrow, -[data-style="nyt"] .playlist-badge, -[data-style="nyt"] .dump-card-private-badge, -[data-style="nyt"] .page-group-label, -[data-style="nyt"] .follow-btn { - text-transform: uppercase; - letter-spacing: 0.08em; +/* ── Summaries — the grey paragraph under a headline ─────────────── */ +[data-style="nyt"] .dump-card-comment { + color: var(--color-text-secondary); + font-size: var(--text-base); + line-height: 1.4; + margin-top: 0.2rem; } -/* ── Datelines — the serif's real italic, in muted ink ────────────── */ +[data-style="nyt"] .rich-content-description { + font-size: var(--text-md); + line-height: 1.4; +} + +/* ── Section labels — heavy sans capitals under an ink rule ──────── */ +[data-style="nyt"] .comment-section-title, +[data-style="nyt"] .related-section-title, +[data-style="nyt"] .page-group-label, +[data-style="nyt"] .form-page-eyebrow { + color: var(--color-text); + font-size: var(--text-2xs); + font-weight: 700; + text-transform: uppercase; + letter-spacing: 0.06em; +} +[data-style="nyt"] .page-group-label { + border-top: 1px solid var(--color-text); + padding-top: 0.5rem; +} +/* These two sections already open on a rule: it turns to ink and the label + moves up under it. */ +[data-style="nyt"] .comment-section, +[data-style="nyt"] .related-section { + border-top-color: var(--color-text); + padding-top: 0.5rem; +} +/* The label's own trailing hairline would double the rule above it. */ +[data-style="nyt"] .page-group-label::after { + content: none; +} +[data-style="nyt"] .profile-section-header { + border-bottom: none; + border-top: 1px solid var(--color-text); + padding: 0.5rem 0 0; +} +[data-style="nyt"] .profile-section-title { + font-family: var(--font-ui); + font-size: var(--text-2xs); + font-weight: 700; + text-transform: uppercase; + letter-spacing: 0.06em; +} + +/* ── Kickers — a story's categories, and the small badges ────────── */ +[data-style="nyt"] .category-link, +[data-style="nyt"] .playlist-badge, +[data-style="nyt"] .dump-card-private-badge, +[data-style="nyt"] .profile-role-chip, +[data-style="nyt"] .rich-content-badge { + font-size: 0.66rem; + font-weight: 700; + text-transform: uppercase; + letter-spacing: 0.06em; +} +[data-style="nyt"] .category-link { + padding: 0; + background: none; + color: var(--color-text); +} +[data-style="nyt"] .category-link:hover { + background: none; + text-decoration: underline; + text-underline-offset: 2px; +} +[data-style="nyt"] .dump-card-categories { + gap: 0.15rem 0.75rem; +} + +/* ── Datelines and bylines — small sans, the name in ink ─────────── */ [data-style="nyt"] .dump-card-date, +[data-style="nyt"] .dump-card-comment-count, +[data-style="nyt"] .playlist-card-meta, [data-style="nyt"] .journal-card-meta, [data-style="nyt"] .comment-time, [data-style="nyt"] .notification-time, [data-style="nyt"] .dump-edited-label { - text-transform: none; - letter-spacing: 0; - font-style: italic; + font-size: 0.72rem; + font-style: normal; + letter-spacing: 0.01em; } +[data-style="nyt"] .dump-op { + opacity: 1; + color: var(--color-text-muted); + font-size: var(--text-xs); + margin-top: 0.6rem; +} +[data-style="nyt"] .dump-op-link, +[data-style="nyt"] .comment-author { + color: var(--color-text); + font-size: var(--text-xs); + font-weight: 700; +} +[data-style="nyt"] .dump-actions a { + font-size: var(--text-xs); + font-weight: 500; +} +[data-style="nyt"] .vote-btn { + font-size: var(--text-xs); + font-weight: 600; + font-variant-numeric: tabular-nums; +} + +/* ══════════════════════════════════════════════════════════════════════ + Masthead + ══════════════════════════════════════════════════════════════════════ */ + +/* The nameplate: the site's name in blackletter, its emoji beside it at the + size of a printer's mark, the two on one baseline. Centring them put the + emoji against the middle of the name's line, which is above the middle of + its letters. The face has one weight; the base rule's bold would be faked. */ +[data-style="nyt"] .app-header-brand { + display: inline-flex; + align-items: baseline; + gap: 0.15em; + font-size: 1.25rem; + font-weight: 400; + letter-spacing: 0; + line-height: 1; + padding-block: 0.7rem 0.6rem; + white-space: nowrap; +} +[data-style="nyt"] .app-header-brand-name { + font-family: var(--font-nameplate); + font-size: 1.8rem; + /* An emoji hangs a quarter of its height under the baseline, so on a shared + baseline its middle is a little under the middle of the lowercase: the + name comes down to meet it. */ + translate: 0 0.04em; +} + +[data-style="nyt"] .app-header { + border-bottom: 1px solid var(--color-text); +} + +/* Where there is room the nameplate takes the middle of a row of its own, + the buttons beside it, and what the page puts in the header (a feed's + sections, the open search field) runs underneath between a hairline and a + double rule, as a front page's section list does. A page that puts nothing + there keeps the one row and a single rule. */ +@media (min-width: 860px) { + [data-style="nyt"] .app-header--has-center { + grid-template-columns: 1fr auto 1fr; + column-gap: 1rem; + row-gap: 0; + } + [data-style="nyt"] .app-header-brand { + grid-area: 1 / 2; + justify-self: center; + font-size: 1.5rem; + padding-block: 1.1rem 0.9rem; + } + [data-style="nyt"] .app-header-brand-name { + font-size: 2.9rem; + } + [data-style="nyt"] .app-header--has-center .app-header-nav { + grid-area: 1 / 3; + } + [data-style="nyt"] .app-header-center { + grid-area: 2 / 1 / auto / -1; + border-top: 1px solid var(--color-border); + padding-block: 0.3rem; + } + [data-style="nyt"] .app-header-center:not(:has(.header-center-slot, .search-bar--expanded)) { + display: none; + } + [data-style="nyt"] .app-header:has(.header-center-slot, .search-bar--expanded) { + border-bottom: 3px double var(--color-text); + } +} + +[data-style="nyt"] .header-search-below { + border-bottom: 1px solid var(--color-border); +} + +/* The header's links and buttons: small sans, the weight of a section list. */ +[data-style="nyt"] .app-header-nav button, +[data-style="nyt"] .app-header-user { + font-size: var(--text-xs); + font-weight: 600; +} +[data-style="nyt"] .app-header-nav button:not(.btn-primary) { + border-color: transparent; +} +[data-style="nyt"] .app-header--guest .app-header-nav > button:last-child { + border-color: var(--color-text); +} + +/* ── Section tabs — a word, underlined in ink when it is the open one ── */ +[data-style="nyt"] .feed-sort { + gap: 0.25rem; +} +[data-style="nyt"] .feed-sort-btn { + border: none; + border-bottom: 2px solid transparent; + border-radius: 0; + background: none; + color: var(--color-text-secondary); + padding: 0.3rem 0.1rem 0.2rem; + margin: 0 0.5rem; + font-size: var(--text-xs); + font-weight: 500; +} +[data-style="nyt"] .feed-sort-btn:hover { + background: none; + color: var(--color-text); +} +[data-style="nyt"] .feed-sort-btn.active { + background: none; + color: var(--color-text); + font-weight: 700; + border-bottom-color: var(--color-text); +} + +/* ══════════════════════════════════════════════════════════════════════ + Controls + ══════════════════════════════════════════════════════════════════════ */ + +[data-style="nyt"] :is( + .btn, + .btn-primary, + .btn-sm, + .vote-btn, + .follow-btn, + .invite-btn, + .btn-add-playlist, + .new-item-toggle, + .app-header-nav button, + .app-header-user, + input, + select, + textarea +) { + border-radius: var(--nyt-control-radius); +} +[data-style="nyt"] :is(.btn, .btn-primary, .btn-sm, .follow-btn, .invite-btn) { + font-weight: 600; + letter-spacing: 0.01em; +} + +/* This theme squares everything off — opt the FAB into a square too (it keeps + its animations; only the corners change). */ +[data-style="nyt"] .dump-fab, +[data-style="nyt"] .chat-fab { + --fab-radius: 0; +} + +/* ══════════════════════════════════════════════════════════════════════ + Structural overrides + ══════════════════════════════════════════════════════════════════════ */ /* ── Lead note — a standfirst with an editorial drop cap ─────────── */ [data-style="nyt"] .dump-comment { @@ -179,24 +519,26 @@ padding: 0; margin: 0.35rem 0 0; opacity: 1; - color: var(--color-text-secondary); + color: var(--color-text); font-size: 1.2rem; +} +[data-style="nyt"] .dump-comment, +[data-style="nyt"] .dump-comment p { line-height: 1.6; } [data-style="nyt"] .dump-comment p:first-of-type::first-letter { float: left; font-family: var(--font-display); font-weight: 400; - font-size: 3.4rem; + font-size: 4.4rem; line-height: 0.78; - padding: 0.12em 0.14em 0 0; + padding: 0.08em 0.1em 0 0; color: var(--color-text); } -/* ── Generous editorial leading for the lead note; comments stay tight ── */ -[data-style="nyt"] .dump-comment, -[data-style="nyt"] .dump-comment p { - line-height: 1.7; +/* Comments are read too: serif, a step over the labels around them. */ +[data-style="nyt"] .comment-body { + font-size: var(--text-base); } /* Tight, ruled comment thread — small padding, snug leading, no extra gaps. */ @@ -266,27 +608,29 @@ } /* Journal view: a section-front grid of stories divided by hairline rules, - not boxed cards. Close the gaps and rule each cell on its top & left edge so - the rules read as continuous row + column delimiters (à la nytimes.com). - The outermost rules are masked off (below) so there's no frame around the - block — CSS grid can't target a first column or a last row, hence the mask. */ + not boxed cards. Every cell draws a rule on all four of its sides, as a + 1px shadow lying in a 1px gap: two neighbours' shadows fall on the same + pixels and read as one rule, and a cell beside an empty track (the last + row, when the stories don't fill it) still closes on its own. A rule on the + top and left edges only left such a cell open on the right, and the row + above it unruled over the empty tracks. The rules around the outside of the + block are masked off (below), so there is no frame. */ /* How far a photo holds off the rules. It sits on the feed rather than on the grid because the break between two grids is cut to the same measure, and a property declared on the grid wouldn't reach it. */ [data-style="nyt"] .journal-feed { --journal-art-inset: 0.55rem; + --journal-rule: 0 0 0 1px var(--color-border); } [data-style="nyt"] .journal-grid { - gap: 0; + gap: 1px; position: relative; } [data-style="nyt"] .journal-card { background: transparent; border: none; - border-top: 1px solid var(--color-border); - border-left: 1px solid var(--color-border); - box-shadow: none; + box-shadow: var(--journal-rule); } /* Rules run to the edges of their cell, but a photo holds off them by @@ -298,14 +642,14 @@ One page-coloured frame, laid over the grid's own edge and as thick as that inset, cuts all of them back to the art at once. It takes the outer rules - (the first row's top, the first column's left) with it, which is the same - frameless block the two strips it replaces were after. One frame per grid - means one per sub-block, so every block reads frameless, not just the feed. */ + with it, which sit one pixel outside the grid, hence the negative inset. + One frame per grid means one per sub-block, so every block reads frameless, + not just the feed. */ [data-style="nyt"] .journal-grid::before { content: ""; position: absolute; - inset: 0; - border: var(--journal-art-inset) solid var(--color-bg); + inset: -1px; + border: calc(var(--journal-art-inset) + 1px) solid var(--color-bg); z-index: 1; pointer-events: none; } @@ -327,9 +671,8 @@ margin: var(--journal-break-space) var(--journal-art-inset); } [data-style="nyt"] .journal-card:hover { - border-color: var(--color-border); transform: none; - box-shadow: none; + box-shadow: var(--journal-rule); } /* Inset content from the rules so the grid breathes — photos get a margin @@ -346,15 +689,6 @@ padding: var(--journal-quote-pad); } -/* ── App header — crisp masthead rule ────────────────────────────── */ -[data-style="nyt"] .app-header { - border-bottom: 2px solid var(--color-text); -} - -[data-style="nyt"] .header-search-below { - border-bottom: 1px solid var(--color-border); -} - /* ── Primary button — inverted ink-on-paper (auto-flips per scheme) ─ */ [data-style="nyt"] .btn-primary { background: var(--color-text); @@ -420,27 +754,6 @@ notification badge, brand-provider cues). Everything else is ink & paper. ══════════════════════════════════════════════════════════════════════ */ -/* Feed sort — section-front tabs: an underline, not a filled chip. */ -[data-style="nyt"] .feed-sort-btn { - border: none; - border-bottom: 2px solid transparent; - background: none; - color: var(--color-text-muted); - padding: 0.2rem 0.1rem; - margin: 0 0.3rem; - font-weight: 400; - letter-spacing: 0.1em; /* condensed caps need more air than the default */ -} -[data-style="nyt"] .feed-sort-btn:hover { - background: none; - color: var(--color-text); -} -[data-style="nyt"] .feed-sort-btn.active { - background: none; - color: var(--color-text); - border-bottom-color: var(--color-text); -} - /* Square off the remaining pills & chips. */ [data-style="nyt"] .playlist-badge, [data-style="nyt"] .dump-card-private-badge,