@striae-org/striae 5.3.2 → 5.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (112) hide show
  1. package/app/components/auth/auth.module.css +531 -0
  2. package/app/components/auth/mfa-enrollment.tsx +132 -79
  3. package/app/components/auth/mfa-totp-enrollment.tsx +231 -0
  4. package/app/components/auth/mfa-verification.tsx +162 -33
  5. package/app/components/{sidebar/cases/cases-modal.tsx → navbar/case-modals/all-cases-modal.tsx} +4 -4
  6. package/app/components/navbar/case-modals/archive-case-modal.tsx +9 -10
  7. package/app/components/navbar/case-modals/case-modal-shared.module.css +88 -0
  8. package/app/components/navbar/case-modals/delete-case-modal.tsx +9 -10
  9. package/app/components/navbar/case-modals/export-case-modal.tsx +9 -10
  10. package/app/components/navbar/case-modals/export-confirmations-modal.tsx +9 -10
  11. package/app/components/navbar/case-modals/open-case-modal.tsx +4 -4
  12. package/app/components/navbar/case-modals/rename-case-modal.tsx +9 -10
  13. package/app/components/navbar/navbar.tsx +1 -1
  14. package/app/components/sidebar/files/delete-files-modal.tsx +3 -3
  15. package/app/components/sidebar/files/files-modal.module.css +29 -0
  16. package/app/components/sidebar/notes/{class-details-fields.tsx → class-details/class-details-fields.tsx} +1 -1
  17. package/app/components/sidebar/notes/{class-details-modal.tsx → class-details/class-details-modal.tsx} +1 -1
  18. package/app/components/sidebar/notes/{class-details-sections.tsx → class-details/class-details-sections.tsx} +1 -1
  19. package/app/components/sidebar/notes/notes-editor-form.tsx +2 -2
  20. package/app/components/sidebar/notes/notes-editor-modal.tsx +6 -6
  21. package/app/components/sidebar/notes/notes.module.css +52 -0
  22. package/app/components/toolbar/toolbar-color-selector.tsx +8 -8
  23. package/app/components/toolbar/toolbar.module.css +181 -2
  24. package/app/components/user/delete-account.tsx +7 -7
  25. package/app/components/user/inactivity-warning.tsx +6 -6
  26. package/app/components/user/manage-profile.tsx +18 -1
  27. package/app/components/user/mfa-enrolled-factors.tsx +117 -0
  28. package/app/components/user/mfa-phone-update.tsx +8 -4
  29. package/app/components/user/mfa-totp-section.tsx +446 -0
  30. package/app/components/user/user.module.css +665 -0
  31. package/app/routes/striae/striae.tsx +1 -1
  32. package/app/services/audit/audit.service.ts +1 -1
  33. package/app/services/audit/builders/audit-event-builders-user-security.ts +4 -2
  34. package/app/services/firebase/errors.ts +2 -0
  35. package/app/utils/auth/mfa.ts +35 -1
  36. package/package.json +23 -28
  37. package/scripts/deploy-all.sh +166 -0
  38. package/scripts/deploy-config/modules/env-utils.sh +322 -0
  39. package/scripts/deploy-config/modules/keys.sh +404 -0
  40. package/scripts/deploy-config/modules/prompt.sh +375 -0
  41. package/scripts/deploy-config/modules/scaffolding.sh +310 -0
  42. package/scripts/deploy-config/modules/validation.sh +354 -0
  43. package/scripts/deploy-config.sh +236 -0
  44. package/scripts/deploy-pages-secrets.sh +231 -0
  45. package/scripts/deploy-pages.sh +34 -0
  46. package/scripts/deploy-primershear-emails.sh +167 -0
  47. package/scripts/deploy-worker-secrets.sh +385 -0
  48. package/scripts/dev.cjs +23 -0
  49. package/scripts/enable-totp-mfa.mjs +57 -0
  50. package/scripts/install-workers.sh +87 -0
  51. package/scripts/run-eslint.cjs +43 -0
  52. package/scripts/unenroll-totp-mfa.mjs +82 -0
  53. package/scripts/update-compatibility-dates.cjs +124 -0
  54. package/scripts/update-markdown-versions.cjs +43 -0
  55. package/workers/audit-worker/.editorconfig +12 -0
  56. package/workers/audit-worker/.prettierrc +6 -0
  57. package/workers/audit-worker/package.json +1 -1
  58. package/workers/audit-worker/wrangler.jsonc.example +1 -1
  59. package/workers/data-worker/.editorconfig +12 -0
  60. package/workers/data-worker/.prettierrc +6 -0
  61. package/workers/data-worker/package.json +1 -1
  62. package/workers/data-worker/wrangler.jsonc.example +1 -1
  63. package/workers/image-worker/.editorconfig +12 -0
  64. package/workers/image-worker/.prettierrc +6 -0
  65. package/workers/image-worker/package.json +1 -1
  66. package/workers/image-worker/wrangler.jsonc.example +1 -1
  67. package/workers/pdf-worker/.editorconfig +12 -0
  68. package/workers/pdf-worker/.prettierrc +6 -0
  69. package/workers/pdf-worker/package.json +1 -1
  70. package/workers/pdf-worker/wrangler.jsonc.example +1 -1
  71. package/workers/user-worker/.editorconfig +12 -0
  72. package/workers/user-worker/.prettierrc +6 -0
  73. package/workers/user-worker/package.json +1 -1
  74. package/workers/user-worker/wrangler.jsonc.example +1 -1
  75. package/wrangler.toml.example +1 -1
  76. package/app/components/auth/mfa-enrollment.module.css +0 -276
  77. package/app/components/auth/mfa-verification.module.css +0 -259
  78. package/app/components/navbar/case-modals/archive-case-modal.module.css +0 -34
  79. package/app/components/navbar/case-modals/delete-case-modal.module.css +0 -9
  80. package/app/components/navbar/case-modals/export-case-modal.module.css +0 -27
  81. package/app/components/navbar/case-modals/export-confirmations-modal.module.css +0 -24
  82. package/app/components/navbar/case-modals/open-case-modal.module.css +0 -82
  83. package/app/components/navbar/case-modals/rename-case-modal.module.css +0 -9
  84. package/app/components/sidebar/files/delete-files-modal.module.css +0 -26
  85. package/app/components/sidebar/notes/notes-editor-modal.module.css +0 -49
  86. package/app/components/toolbar/toolbar-color-selector.module.css +0 -171
  87. package/app/components/user/delete-account.module.css +0 -277
  88. package/app/components/user/inactivity-warning.module.css +0 -148
  89. package/app/components/user/manage-profile.module.css +0 -192
  90. package/app/routes/auth/login.module.css +0 -523
  91. package/app/routes/auth/login.tsx +0 -705
  92. package/workers/audit-worker/worker-configuration.d.ts +0 -7448
  93. package/workers/data-worker/worker-configuration.d.ts +0 -7448
  94. package/workers/image-worker/worker-configuration.d.ts +0 -7448
  95. package/workers/pdf-worker/worker-configuration.d.ts +0 -7447
  96. package/workers/user-worker/worker-configuration.d.ts +0 -7450
  97. /package/app/components/{sidebar → navbar}/case-import/case-import.module.css +0 -0
  98. /package/app/components/{sidebar → navbar}/case-import/case-import.tsx +0 -0
  99. /package/app/components/{sidebar → navbar}/case-import/components/CasePreviewSection.tsx +0 -0
  100. /package/app/components/{sidebar → navbar}/case-import/components/ConfirmationDialog.tsx +0 -0
  101. /package/app/components/{sidebar → navbar}/case-import/components/ConfirmationPreviewSection.tsx +0 -0
  102. /package/app/components/{sidebar → navbar}/case-import/components/ExistingCaseSection.tsx +0 -0
  103. /package/app/components/{sidebar → navbar}/case-import/components/FileSelector.tsx +0 -0
  104. /package/app/components/{sidebar → navbar}/case-import/components/ProgressSection.tsx +0 -0
  105. /package/app/components/{sidebar → navbar}/case-import/hooks/useFilePreview.ts +0 -0
  106. /package/app/components/{sidebar → navbar}/case-import/hooks/useImportExecution.ts +0 -0
  107. /package/app/components/{sidebar → navbar}/case-import/hooks/useImportState.ts +0 -0
  108. /package/app/components/{sidebar → navbar}/case-import/index.ts +0 -0
  109. /package/app/components/{sidebar → navbar}/case-import/utils/file-validation.ts +0 -0
  110. /package/app/components/{sidebar/cases/cases-modal.module.css → navbar/case-modals/all-cases-modal.module.css} +0 -0
  111. /package/app/components/sidebar/notes/{class-details-shared.ts → class-details/class-details-shared.ts} +0 -0
  112. /package/app/components/sidebar/notes/{use-class-details-state.ts → class-details/use-class-details-state.ts} +0 -0
@@ -0,0 +1,124 @@
1
+ const fs = require('fs');
2
+ const path = require('path');
3
+
4
+ const DATE_PATTERN = /^\d{4}-\d{2}-\d{2}$/;
5
+
6
+ function getCurrentDate() {
7
+ const now = new Date();
8
+ const year = now.getFullYear();
9
+ const month = String(now.getMonth() + 1).padStart(2, '0');
10
+ const day = String(now.getDate()).padStart(2, '0');
11
+ return `${year}-${month}-${day}`;
12
+ }
13
+
14
+ function replaceTomlCompatibilityDate(content, date) {
15
+ return content.replace(
16
+ /(compatibility_date\s*=\s*")\d{4}-\d{2}-\d{2}(")/,
17
+ `$1${date}$2`
18
+ );
19
+ }
20
+
21
+ function replaceJsoncCompatibilityDate(content, date) {
22
+ return content.replace(
23
+ /("compatibility_date"\s*:\s*")\d{4}-\d{2}-\d{2}(",?)/,
24
+ `$1${date}$2`
25
+ );
26
+ }
27
+
28
+ function updateFile(filePath, date, replacer) {
29
+ if (!fs.existsSync(filePath)) {
30
+ return { filePath, status: 'missing' };
31
+ }
32
+
33
+ const original = fs.readFileSync(filePath, 'utf8');
34
+ const updated = replacer(original, date);
35
+
36
+ if (original === updated) {
37
+ return { filePath, status: 'unchanged' };
38
+ }
39
+
40
+ fs.writeFileSync(filePath, updated, 'utf8');
41
+ return { filePath, status: 'updated' };
42
+ }
43
+
44
+ function updateCompatibilityDates(date = getCurrentDate()) {
45
+ if (!DATE_PATTERN.test(date)) {
46
+ throw new Error(`Invalid date format: ${date}. Use YYYY-MM-DD.`);
47
+ }
48
+
49
+ const rootDir = path.resolve(__dirname, '..');
50
+ const workersDir = path.join(rootDir, 'workers');
51
+
52
+ const results = [];
53
+
54
+ results.push(
55
+ updateFile(
56
+ path.join(rootDir, 'wrangler.toml'),
57
+ date,
58
+ replaceTomlCompatibilityDate
59
+ )
60
+ );
61
+
62
+ results.push(
63
+ updateFile(
64
+ path.join(rootDir, 'wrangler.toml.example'),
65
+ date,
66
+ replaceTomlCompatibilityDate
67
+ )
68
+ );
69
+
70
+ if (fs.existsSync(workersDir)) {
71
+ const workerDirs = fs
72
+ .readdirSync(workersDir, { withFileTypes: true })
73
+ .filter((entry) => entry.isDirectory())
74
+ .map((entry) => entry.name);
75
+
76
+ for (const workerDir of workerDirs) {
77
+ const workerPath = path.join(workersDir, workerDir);
78
+ results.push(
79
+ updateFile(
80
+ path.join(workerPath, 'wrangler.jsonc.example'),
81
+ date,
82
+ replaceJsoncCompatibilityDate
83
+ )
84
+ );
85
+ results.push(
86
+ updateFile(
87
+ path.join(workerPath, 'wrangler.jsonc'),
88
+ date,
89
+ replaceJsoncCompatibilityDate
90
+ )
91
+ );
92
+ }
93
+ }
94
+
95
+ const updatedCount = results.filter((result) => result.status === 'updated').length;
96
+ const unchangedCount = results.filter((result) => result.status === 'unchanged').length;
97
+ const missingCount = results.filter((result) => result.status === 'missing').length;
98
+
99
+ console.log(`Updated compatibility dates to ${date}`);
100
+ console.log(`- Updated: ${updatedCount}`);
101
+ console.log(`- Unchanged: ${unchangedCount}`);
102
+ console.log(`- Missing: ${missingCount}`);
103
+
104
+ for (const result of results) {
105
+ if (result.status !== 'updated') {
106
+ console.log(` ${result.status.toUpperCase()}: ${path.relative(rootDir, result.filePath)}`);
107
+ }
108
+ }
109
+
110
+ return results;
111
+ }
112
+
113
+ if (require.main === module) {
114
+ const dateArg = process.argv[2] || getCurrentDate();
115
+
116
+ try {
117
+ updateCompatibilityDates(dateArg);
118
+ } catch (error) {
119
+ console.error(error.message);
120
+ process.exit(1);
121
+ }
122
+ }
123
+
124
+ module.exports = { updateCompatibilityDates };
@@ -0,0 +1,43 @@
1
+ const fs = require('fs');
2
+ const path = require('path');
3
+ const packageJson = require('../package.json');
4
+
5
+ const markdownFiles = [
6
+ '.github/SECURITY.md',
7
+ // Add other markdown files that need version updates
8
+ ];
9
+
10
+ function updateMarkdownVersions() {
11
+ console.log(`📝 Updating markdown files with version ${packageJson.version}...`);
12
+
13
+ markdownFiles.forEach(filePath => {
14
+ const fullPath = path.join(__dirname, '..', filePath);
15
+
16
+ if (!fs.existsSync(fullPath)) {
17
+ console.log(`⚠️ Skipping ${filePath} (file not found)`);
18
+ return;
19
+ }
20
+
21
+ try {
22
+ let content = fs.readFileSync(fullPath, 'utf8');
23
+
24
+ // Replace version placeholders
25
+ content = content.replace(/{{VERSION}}/g, packageJson.version);
26
+ content = content.replace(/v\d+\.\d+\.\d+(-\w+)?/g, `v${packageJson.version}`);
27
+
28
+ fs.writeFileSync(fullPath, content);
29
+ console.log(`✅ Updated ${filePath}`);
30
+ } catch (error) {
31
+ console.error(`❌ Error updating ${filePath}:`, error.message);
32
+ }
33
+ });
34
+
35
+ console.log('🎉 Markdown version update complete!');
36
+ }
37
+
38
+ // Run if called directly
39
+ if (require.main === module) {
40
+ updateMarkdownVersions();
41
+ }
42
+
43
+ module.exports = { updateMarkdownVersions };
@@ -0,0 +1,12 @@
1
+ # http://editorconfig.org
2
+ root = true
3
+
4
+ [*]
5
+ indent_style = tab
6
+ end_of_line = lf
7
+ charset = utf-8
8
+ trim_trailing_whitespace = true
9
+ insert_final_newline = true
10
+
11
+ [*.yml]
12
+ indent_style = space
@@ -0,0 +1,6 @@
1
+ {
2
+ "printWidth": 140,
3
+ "singleQuote": true,
4
+ "semi": true,
5
+ "useTabs": true
6
+ }
@@ -9,7 +9,7 @@
9
9
  },
10
10
  "devDependencies": {
11
11
  "@cloudflare/puppeteer": "^1.0.6",
12
- "wrangler": "^4.77.0"
12
+ "wrangler": "^4.78.0"
13
13
  },
14
14
  "overrides": {
15
15
  "undici": "7.24.1",
@@ -7,7 +7,7 @@
7
7
  "name": "AUDIT_WORKER_NAME",
8
8
  "account_id": "ACCOUNT_ID",
9
9
  "main": "src/audit-worker.ts",
10
- "compatibility_date": "2026-03-29",
10
+ "compatibility_date": "2026-03-31",
11
11
  "compatibility_flags": [
12
12
  "nodejs_compat"
13
13
  ],
@@ -0,0 +1,12 @@
1
+ # http://editorconfig.org
2
+ root = true
3
+
4
+ [*]
5
+ indent_style = tab
6
+ end_of_line = lf
7
+ charset = utf-8
8
+ trim_trailing_whitespace = true
9
+ insert_final_newline = true
10
+
11
+ [*.yml]
12
+ indent_style = space
@@ -0,0 +1,6 @@
1
+ {
2
+ "printWidth": 140,
3
+ "singleQuote": true,
4
+ "semi": true,
5
+ "useTabs": true
6
+ }
@@ -9,7 +9,7 @@
9
9
  },
10
10
  "devDependencies": {
11
11
  "@cloudflare/puppeteer": "^1.0.6",
12
- "wrangler": "^4.77.0"
12
+ "wrangler": "^4.78.0"
13
13
  },
14
14
  "overrides": {
15
15
  "undici": "7.24.1",
@@ -5,7 +5,7 @@
5
5
  "name": "DATA_WORKER_NAME",
6
6
  "account_id": "ACCOUNT_ID",
7
7
  "main": "src/data-worker.ts",
8
- "compatibility_date": "2026-03-29",
8
+ "compatibility_date": "2026-03-31",
9
9
  "compatibility_flags": [
10
10
  "nodejs_compat"
11
11
  ],
@@ -0,0 +1,12 @@
1
+ # http://editorconfig.org
2
+ root = true
3
+
4
+ [*]
5
+ indent_style = tab
6
+ end_of_line = lf
7
+ charset = utf-8
8
+ trim_trailing_whitespace = true
9
+ insert_final_newline = true
10
+
11
+ [*.yml]
12
+ indent_style = space
@@ -0,0 +1,6 @@
1
+ {
2
+ "printWidth": 140,
3
+ "singleQuote": true,
4
+ "semi": true,
5
+ "useTabs": true
6
+ }
@@ -9,7 +9,7 @@
9
9
  },
10
10
  "devDependencies": {
11
11
  "@cloudflare/puppeteer": "^1.0.6",
12
- "wrangler": "^4.77.0"
12
+ "wrangler": "^4.78.0"
13
13
  },
14
14
  "overrides": {
15
15
  "undici": "7.24.1",
@@ -2,7 +2,7 @@
2
2
  "name": "IMAGES_WORKER_NAME",
3
3
  "account_id": "ACCOUNT_ID",
4
4
  "main": "src/image-worker.ts",
5
- "compatibility_date": "2026-03-29",
5
+ "compatibility_date": "2026-03-31",
6
6
  "compatibility_flags": [
7
7
  "nodejs_compat"
8
8
  ],
@@ -0,0 +1,12 @@
1
+ # http://editorconfig.org
2
+ root = true
3
+
4
+ [*]
5
+ indent_style = tab
6
+ end_of_line = lf
7
+ charset = utf-8
8
+ trim_trailing_whitespace = true
9
+ insert_final_newline = true
10
+
11
+ [*.yml]
12
+ indent_style = space
@@ -0,0 +1,6 @@
1
+ {
2
+ "printWidth": 140,
3
+ "singleQuote": true,
4
+ "semi": true,
5
+ "useTabs": true
6
+ }
@@ -10,7 +10,7 @@
10
10
  },
11
11
  "devDependencies": {
12
12
  "@cloudflare/puppeteer": "^1.0.6",
13
- "wrangler": "^4.77.0"
13
+ "wrangler": "^4.78.0"
14
14
  },
15
15
  "overrides": {
16
16
  "undici": "7.24.1",
@@ -2,7 +2,7 @@
2
2
  "name": "PDF_WORKER_NAME",
3
3
  "account_id": "ACCOUNT_ID",
4
4
  "main": "src/pdf-worker.ts",
5
- "compatibility_date": "2026-03-29",
5
+ "compatibility_date": "2026-03-31",
6
6
  "compatibility_flags": [
7
7
  "nodejs_compat"
8
8
  ],
@@ -0,0 +1,12 @@
1
+ # http://editorconfig.org
2
+ root = true
3
+
4
+ [*]
5
+ indent_style = tab
6
+ end_of_line = lf
7
+ charset = utf-8
8
+ trim_trailing_whitespace = true
9
+ insert_final_newline = true
10
+
11
+ [*.yml]
12
+ indent_style = space
@@ -0,0 +1,6 @@
1
+ {
2
+ "printWidth": 140,
3
+ "singleQuote": true,
4
+ "semi": true,
5
+ "useTabs": true
6
+ }
@@ -9,7 +9,7 @@
9
9
  },
10
10
  "devDependencies": {
11
11
  "@cloudflare/puppeteer": "^1.0.6",
12
- "wrangler": "^4.77.0"
12
+ "wrangler": "^4.78.0"
13
13
  },
14
14
  "overrides": {
15
15
  "undici": "7.24.1",
@@ -2,7 +2,7 @@
2
2
  "name": "USER_WORKER_NAME",
3
3
  "account_id": "ACCOUNT_ID",
4
4
  "main": "src/user-worker.ts",
5
- "compatibility_date": "2026-03-29",
5
+ "compatibility_date": "2026-03-31",
6
6
  "compatibility_flags": [
7
7
  "nodejs_compat"
8
8
  ],
@@ -1,6 +1,6 @@
1
1
  #:schema node_modules/wrangler/config-schema.json
2
2
  name = "PAGES_PROJECT_NAME"
3
- compatibility_date = "2026-03-29"
3
+ compatibility_date = "2026-03-31"
4
4
  compatibility_flags = ["nodejs_compat"]
5
5
  pages_build_output_dir = "./build/client"
6
6
 
@@ -1,276 +0,0 @@
1
- .overlay {
2
- position: fixed;
3
- top: 0;
4
- left: 0;
5
- right: 0;
6
- bottom: 0;
7
- background-color: rgba(0, 0, 0, 0.75);
8
- display: flex;
9
- align-items: center;
10
- justify-content: center;
11
- z-index: 1000;
12
- padding: 1rem;
13
- cursor: default;
14
- }
15
-
16
- .modal {
17
- background: white;
18
- border-radius: 12px;
19
- padding: 2rem;
20
- max-width: 500px;
21
- width: 100%;
22
- max-height: 90vh;
23
- overflow-y: auto;
24
- box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);
25
- cursor: default;
26
- }
27
-
28
- .header {
29
- text-align: center;
30
- margin-bottom: 2rem;
31
- }
32
-
33
- .header h2 {
34
- color: #333;
35
- margin: 0 0 1rem 0;
36
- font-size: 1.5rem;
37
- font-weight: 600;
38
- }
39
-
40
- .header p {
41
- color: #666;
42
- margin: 0;
43
- line-height: 1.5;
44
- }
45
-
46
- .content {
47
- margin-bottom: 2rem;
48
- }
49
-
50
- .phoneStep,
51
- .codeStep {
52
- text-align: center;
53
- }
54
-
55
- .phoneStep h3,
56
- .codeStep h3 {
57
- color: #333;
58
- margin: 0 0 1rem 0;
59
- font-size: 1.2rem;
60
- font-weight: 500;
61
- }
62
-
63
- .input {
64
- width: 100%;
65
- padding: 1rem;
66
- border: 2px solid #e1e5e9;
67
- border-radius: 8px;
68
- font-size: 1rem;
69
- margin-bottom: 1rem;
70
- transition: border-color 0.2s ease;
71
- box-sizing: border-box;
72
- }
73
-
74
- .input:focus {
75
- outline: none;
76
- border-color: #4285f4;
77
- box-shadow: 0 0 0 3px rgba(66, 133, 244, 0.1);
78
- }
79
-
80
- .input:disabled {
81
- background-color: #f5f5f5;
82
- cursor: not-allowed;
83
- }
84
-
85
- .note {
86
- color: #666;
87
- font-size: 0.9rem;
88
- margin: 0 0 1.5rem 0;
89
- line-height: 1.4;
90
- }
91
-
92
- .buttonGroup {
93
- display: flex;
94
- flex-direction: column;
95
- gap: 0.75rem;
96
- }
97
-
98
- .primaryButton {
99
- background-color: #4285f4;
100
- color: white;
101
- border: none;
102
- padding: 1rem 2rem;
103
- border-radius: 8px;
104
- font-size: 1rem;
105
- font-weight: 500;
106
- cursor: pointer;
107
- transition: background-color 0.2s ease;
108
- width: 100%;
109
- box-sizing: border-box;
110
- }
111
-
112
- .primaryButton:hover:not(:disabled) {
113
- background-color: #3367d6;
114
- }
115
-
116
- .primaryButton:disabled {
117
- background-color: #ccc;
118
- cursor: not-allowed;
119
- }
120
-
121
- .secondaryButton {
122
- background-color: transparent;
123
- color: #4285f4;
124
- border: 2px solid #4285f4;
125
- padding: 0.75rem 1.5rem;
126
- border-radius: 8px;
127
- font-size: 0.9rem;
128
- font-weight: 500;
129
- cursor: pointer;
130
- transition: all 0.2s ease;
131
- width: 100%;
132
- box-sizing: border-box;
133
- }
134
-
135
- .secondaryButton:hover:not(:disabled) {
136
- background-color: #4285f4;
137
- color: white;
138
- }
139
-
140
- .secondaryButton:disabled {
141
- border-color: #ccc;
142
- color: #ccc;
143
- cursor: not-allowed;
144
- }
145
-
146
- .resendTimer {
147
- color: #999;
148
- font-size: 0.9rem;
149
- margin: 0;
150
- text-align: center;
151
- }
152
-
153
- .errorMessage {
154
- background: linear-gradient(
155
- 135deg,
156
- color-mix(in lab, var(--error) 12%, transparent),
157
- color-mix(in lab, var(--error) 8%, transparent)
158
- );
159
- border: 1px solid color-mix(in lab, var(--error) 30%, transparent);
160
- border-left: 4px solid var(--error);
161
- color: color-mix(in lab, var(--error) 90%, var(--black));
162
- padding: var(--spaceL);
163
- border-radius: var(--spaceS);
164
- font-size: 0.9rem;
165
- margin-bottom: 1rem;
166
- text-align: left;
167
- line-height: 1.4;
168
- position: relative;
169
- overflow: hidden;
170
- box-shadow: 0 4px 16px color-mix(in lab, var(--text) 10%, transparent);
171
- animation: slideInError var(--durationM) var(--bezierFastoutSlowin);
172
- }
173
-
174
- .errorMessage::before {
175
- content: "";
176
- position: absolute;
177
- top: 0;
178
- left: 0;
179
- right: 0;
180
- height: 2px;
181
- background: linear-gradient(
182
- 90deg,
183
- var(--error),
184
- color-mix(in lab, var(--error) 60%, transparent)
185
- );
186
- animation: shimmer 2s ease-in-out infinite;
187
- }
188
-
189
- .errorMessage:empty {
190
- display: none;
191
- }
192
-
193
- .footer {
194
- border-top: 1px solid #e1e5e9;
195
- padding-top: 1.5rem;
196
- text-align: center;
197
- }
198
-
199
- .skipButton {
200
- background-color: transparent;
201
- color: #666;
202
- border: none;
203
- padding: 0.75rem 1.5rem;
204
- border-radius: 8px;
205
- font-size: 0.9rem;
206
- cursor: pointer;
207
- transition: color 0.2s ease;
208
- }
209
-
210
- .skipButton:hover:not(:disabled) {
211
- color: #333;
212
- text-decoration: underline;
213
- }
214
-
215
- .skipButton:disabled {
216
- color: #ccc;
217
- cursor: not-allowed;
218
- }
219
-
220
- .signOutContainer {
221
- margin-top: var(--spaceL);
222
- padding-top: var(--spaceL);
223
- border-top: 1px solid var(--borderLight);
224
- text-align: center;
225
- }
226
-
227
- .signOutText {
228
- color: var(--textLight);
229
- font-size: var(--fontSizeSmall);
230
- margin-bottom: var(--spaceM);
231
- }
232
-
233
- /* Animations */
234
- @keyframes slideInError {
235
- from {
236
- opacity: 0;
237
- transform: translateY(calc(-1 * var(--spaceM))) scaleY(0.8);
238
- max-height: 0;
239
- padding-top: 0;
240
- padding-bottom: 0;
241
- margin-top: 0;
242
- margin-bottom: 0;
243
- }
244
- to {
245
- opacity: 1;
246
- transform: translateY(0) scaleY(1);
247
- max-height: 200px;
248
- padding-top: var(--spaceL);
249
- padding-bottom: var(--spaceL);
250
- margin-top: 0;
251
- margin-bottom: 1rem;
252
- }
253
- }
254
-
255
- @keyframes shimmer {
256
- 0%,
257
- 100% {
258
- opacity: 0.6;
259
- transform: translateX(-100%);
260
- }
261
- 50% {
262
- opacity: 1;
263
- transform: translateX(100%);
264
- }
265
- }
266
-
267
- /* Reduce motion for accessibility */
268
- @media (prefers-reduced-motion: reduce) {
269
- .errorMessage {
270
- animation: none;
271
- }
272
-
273
- .errorMessage::before {
274
- animation: none;
275
- }
276
- }