Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0ab08131d6
|
||
|
|
b0cf2d7119 | ||
|
|
3e05458944 | ||
|
|
4d7955fb48 | ||
|
|
81d1f21236 | ||
|
|
aa406e623d | ||
|
|
5d6d4af8bd | ||
|
|
d944cb0afe | ||
|
|
9d6e0b982e | ||
|
|
167277b037 | ||
|
|
db8d90f579 | ||
|
|
16a72caa00 | ||
|
|
95c5f229f5 | ||
|
|
55adb79705 | ||
|
|
e97f0ea9b4 | ||
|
|
ff80ee516f | ||
|
|
6a5e54e130 | ||
|
|
ebc99c0f36 | ||
|
|
0eb3fcc3f6 | ||
|
|
1a56cd7603 | ||
|
|
7cdf209032
|
||
|
|
eecb582665
|
||
|
|
61b2bd0f1e
|
||
|
|
a635a5f55c | ||
|
|
63806f106c | ||
|
|
f41af1966e
|
||
|
|
7a6cab29db
|
||
|
|
b102a061fe
|
||
|
|
356282f61c
|
||
|
|
ce17728e0b
|
||
|
|
704c8d27c9
|
||
|
|
d20372fe33
|
||
|
|
e874b26bb9
|
||
|
|
08612d97e6
|
||
|
|
f98576f3ae
|
||
|
|
537f9864a0
|
||
|
|
59911eb929
|
||
|
|
8e64d32209
|
||
|
|
b5bc95bc2b
|
||
|
|
a699d467f1
|
||
|
|
8192ea71c4 | ||
|
|
8646792d46
|
||
|
|
0957c06a18 | ||
|
|
6f5ac3c0d2
|
||
|
|
b20c253383
|
||
|
|
76a035424d
|
||
|
|
34d5394b3c
|
||
|
|
bc399b43b6
|
||
|
|
051fb7c108
|
||
|
|
4079156776
|
||
|
|
4ed01d7d28
|
||
|
|
c720917627 | ||
|
|
2ce8c849c0 | ||
|
|
32112edbde
|
||
|
|
f28bf9ad06 | ||
|
|
1e1aafa27a
|
||
|
|
cd097d0e76
|
||
|
|
ea2f1eaa05
|
||
|
|
60c534d4a2
|
||
|
|
147b55c586 | ||
|
|
6e0ff0c596 | ||
|
|
21b0ad7461 | ||
|
|
6db91105da | ||
|
|
cc8e861b33 | ||
|
|
d127309a13 | ||
|
|
b592524204 | ||
|
|
5c89f10e9f
|
||
|
|
cfd6e630a3
|
||
|
|
ac43ae3669
|
||
|
|
5554489d70
|
||
|
|
ae6ab88e19
|
||
|
|
a0cbcc7e71
|
||
|
|
679731d3e2
|
||
|
|
96d3c138f6
|
||
|
|
6648d1d7d5
|
||
|
|
9c391c8c05
|
||
|
|
77b83f5d21
|
||
|
|
8c9c581cc0
|
||
|
|
46090e9159
|
||
|
|
fd8e12f925
|
||
|
|
b73e92a8a4
|
||
|
|
a3f7f90fdc
|
||
|
|
a39caf1a34
|
||
|
|
38c1f78d4f
|
||
|
|
5cbe055c34
|
||
|
|
2f208aa6e4
|
||
|
|
c6202d2ac6
|
||
|
|
a3f666c974
|
||
|
|
4a7045a1cb
|
||
|
|
a048d395f7
|
||
|
|
9398321fac
|
||
|
|
d348174f95
|
||
|
|
d0d8222e40
|
||
|
|
ed2bda5c02
|
||
|
|
96d829accb
|
||
|
|
67951fa031
|
||
|
|
c98a51795d
|
||
|
|
95d78c22eb
|
||
|
|
05f5c59721
|
||
|
|
a498dc59d7
|
||
|
|
4a2087d4e3
|
||
|
|
5e75ace58d
|
||
|
|
562109f207
|
||
|
|
f5c715f5c0
|
||
|
|
496744471a
|
||
|
|
4675809911
|
||
|
|
f64c535971
|
||
|
|
79e9e08464
|
||
|
|
7ef8b3a585
|
||
|
|
1be3ad0410
|
||
|
|
36f82a9a76
|
||
|
|
0db10a04d8
|
||
|
|
3003a68e39
|
||
|
|
266137ff5f
|
||
|
|
cd5a3c6868
|
||
|
|
785c2ccaf7
|
||
|
|
6c4ac504da
|
||
|
|
26db9e76e1
|
||
|
|
796bc223af
|
||
|
|
626f1b7f37
|
||
|
|
1eeb5a6820
|
||
|
|
5f4fbd5e05
|
||
|
|
1bc0224f76
|
||
|
|
ce5e53c71f
|
||
|
|
615412c880
|
||
|
|
c2b49e66d9
|
||
|
|
ef13d4a6e6
|
||
|
|
1ec59b0c07
|
||
|
|
e1023b5d50
|
||
|
|
4bbd58b361
|
||
|
|
5a66284bb5
|
||
|
|
0b276e7b32
|
||
|
|
e51f2133ef
|
||
|
|
4b23bff789
|
||
|
|
e44b95db2e
|
||
|
|
bb4cee97a8
|
||
|
|
826d5e8c7d
|
||
|
|
9678fc9a3c
|
||
|
|
e4e6f15e9a
|
||
|
|
bfed3bee15
|
||
|
|
96edc6e538
|
||
|
|
33e78ff8a5
|
||
|
|
f3d3562cec
|
||
|
|
d8a0016dba
|
No files matched your search
@@ -38,6 +38,7 @@ temp/
|
||||
# Editor and IDE state
|
||||
.idea/
|
||||
.vscode/
|
||||
.frontmatter/
|
||||
*.code-workspace
|
||||
|
||||
# Local AI-agent state
|
||||
@@ -47,11 +48,16 @@ temp/
|
||||
.cursor/
|
||||
.continue/
|
||||
.windsurf/
|
||||
/.playwright-mcp/
|
||||
|
||||
# Local visual-comparison captures
|
||||
/live-*.png
|
||||
/live-*-snapshot.md
|
||||
|
||||
# Operating-system metadata
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
Desktop.ini
|
||||
|
||||
# Read-only wget mirror used as migration source material
|
||||
/www.azinstitute4autism.com/
|
||||
# Private portal collection, mappings and ciphertext; never a public asset source.
|
||||
/www/private/employee-portal/
|
||||
@@ -1,34 +1,43 @@
|
||||
# AGENTS.md
|
||||
|
||||
## Instruction precedence
|
||||
|
||||
This file is the authoritative project policy for all editors and AI agents.
|
||||
`MIGRATION_BRIEF.md` supplies detailed implementation guidance and must be read
|
||||
with this file. If the two files appear to conflict, follow `AGENTS.md` and
|
||||
update the conflicting documentation before continuing.
|
||||
|
||||
The `migration-foundation` branch is a reference and recovery point only. It is
|
||||
not the visual or architectural source of truth. Reuse its nonvisual tooling or
|
||||
content only after verifying it against the live public site.
|
||||
|
||||
## Project
|
||||
|
||||
This repository contains a mirrored copy of the Arizona Institute for Autism website.
|
||||
|
||||
Raw mirror source:
|
||||
|
||||
```txt
|
||||
./www.azinstitute4autism.com
|
||||
```
|
||||
|
||||
New Astro site target:
|
||||
|
||||
```txt
|
||||
./www
|
||||
```
|
||||
|
||||
The mirror was created with:
|
||||
The live public website is the single source of truth:
|
||||
|
||||
```sh
|
||||
wget --mirror --page-requisites --adjust-extension --convert-links --no-parent https://www.azinstitute4autism.com
|
||||
```txt
|
||||
https://www.azinstitute4autism.com
|
||||
```
|
||||
|
||||
Do not modify, move, rename, or delete `./www.azinstitute4autism.com`.
|
||||
|
||||
Treat the mirror as read-only raw source material.
|
||||
Verify content, design, URLs, metadata, assets, and responsive behavior against
|
||||
the live public site before treating them as authoritative.
|
||||
|
||||
## Primary goal
|
||||
|
||||
Create a clean, maintainable Astro + TypeScript site in `./www` that reproduces the current public site as faithfully as practical while replacing HubSpot-generated structure with a future-friendly static site architecture.
|
||||
Create a clean, maintainable Astro + TypeScript site in `./www` that faithfully
|
||||
reproduces the current public site's content, visual design, page structure, and
|
||||
responsive behavior while replacing HubSpot-generated implementation details
|
||||
with a future-friendly static site architecture.
|
||||
|
||||
This is a fidelity-first migration, not a redesign. The live public site is the
|
||||
source of truth for both content and presentation. Maintainability must improve
|
||||
the implementation without materially changing what visitors see.
|
||||
|
||||
The new site should be suitable for:
|
||||
|
||||
@@ -149,13 +158,31 @@ Do not invent large Arabic or Spanish translations unless source content is avai
|
||||
|
||||
## Design rules
|
||||
|
||||
The new design should be mostly faithful to the current site while becoming more maintainable.
|
||||
The new site must remain visually faithful to the live public site. Do not
|
||||
reinterpret, modernize, simplify, or genericize the design unless a specific
|
||||
change is required for accessibility or reliable responsive behavior.
|
||||
|
||||
Preserve each source page's:
|
||||
|
||||
- section order and visible content
|
||||
- typography, font weights, and text hierarchy
|
||||
- colors, backgrounds, and decorative treatments
|
||||
- container widths, spacing, alignment, and proportions
|
||||
- imagery, crops, icons, borders, radii, and shadows
|
||||
- header, navigation, footer, calls to action, and forms
|
||||
- desktop and mobile responsive behavior
|
||||
|
||||
Reuse exact source assets and locally available fonts wherever practical.
|
||||
Do not replace distinctive source sections with generic cards, generic heroes,
|
||||
or a new site-wide layout merely to make component reuse easier.
|
||||
|
||||
Use plain CSS with design tokens.
|
||||
|
||||
Create reusable Astro components for repeated layout sections.
|
||||
|
||||
Do not create a messy page-by-page clone of HubSpot output.
|
||||
Component boundaries must follow genuinely repeated source patterns. Distinctive
|
||||
pages and sections may retain page-specific markup and CSS when abstraction
|
||||
would reduce fidelity. Do not copy HubSpot's generated DOM or CSS wholesale.
|
||||
|
||||
Favor:
|
||||
|
||||
@@ -163,10 +190,30 @@ Favor:
|
||||
- reusable components
|
||||
- organized CSS
|
||||
- clear data files
|
||||
- consistent spacing
|
||||
- source-faithful spacing
|
||||
- accessible markup
|
||||
- predictable file structure
|
||||
|
||||
### Visual validation
|
||||
|
||||
Before implementing a page family or shared component:
|
||||
|
||||
1. Inspect the live page and its HTML, CSS, assets, and responsive behavior.
|
||||
2. Resolve any discrepancy in favor of the live public site.
|
||||
3. Record the source structure and important visual details.
|
||||
4. Identify which patterns are truly shared and which are page-specific.
|
||||
|
||||
After implementation, compare the Astro output with the live public site at
|
||||
representative desktop and mobile viewport widths. Check the full page, not
|
||||
only the header or first viewport. Fix material differences before treating the
|
||||
page as complete.
|
||||
|
||||
Document intentional visual deviations and their reasons in:
|
||||
|
||||
```txt
|
||||
reports/migration-summary.md
|
||||
```
|
||||
|
||||
## URL and SEO rules
|
||||
|
||||
Preserve existing public URL paths whenever possible.
|
||||
@@ -294,9 +341,10 @@ reports/migration-summary.md
|
||||
|
||||
Before large filesystem changes:
|
||||
|
||||
1. Inspect the mirror.
|
||||
2. Summarize the implementation plan.
|
||||
3. Proceed methodically.
|
||||
1. Inspect the live public site.
|
||||
2. Inspect the relevant source HTML, CSS, and assets in detail.
|
||||
3. Summarize the implementation and visual-validation plan.
|
||||
4. Proceed methodically.
|
||||
|
||||
At the end of a task, summarize:
|
||||
|
||||
@@ -307,4 +355,7 @@ At the end of a task, summarize:
|
||||
- audit results
|
||||
- unresolved issues
|
||||
|
||||
Prioritize a clean, maintainable foundation over a pixel-perfect but messy clone.
|
||||
Prioritize visual fidelity and content correctness while implementing them with
|
||||
clean, maintainable Astro code. Do not trade away visible source design for a
|
||||
generic foundation, and do not copy HubSpot-generated implementation merely to
|
||||
obtain fidelity.
|
||||
@@ -23,6 +23,7 @@ workspace even when Codex cannot discover its Git metadata.
|
||||
The profile:
|
||||
|
||||
- never pauses for approval
|
||||
- automatically permits configured Playwright and OpenAI documentation MCP tools
|
||||
- limits filesystem access to minimal runtime files plus this project
|
||||
- allows writes in this project
|
||||
- keeps `.codex` and `www.azinstitute4autism.com` read-only
|
||||
@@ -42,6 +43,27 @@ The profile is stored outside the workspace at:
|
||||
Do not add `--sandbox`; legacy sandbox flags override the custom permission
|
||||
profile. Do not use `--dangerously-bypass-approvals-and-sandbox`.
|
||||
|
||||
The launcher also applies these session overrides:
|
||||
|
||||
```txt
|
||||
approval_policy="never"
|
||||
mcp_servers.playwright.default_tools_approval_mode="approve"
|
||||
mcp_servers.playwright.tool_timeout_sec=300
|
||||
mcp_servers.openaiDeveloperDocs.default_tools_approval_mode="approve"
|
||||
mcp_servers.openaiDeveloperDocs.tool_timeout_sec=300
|
||||
```
|
||||
|
||||
The server-level `approve` settings pre-approve MCP tools without writing
|
||||
incompatible per-tool approval tables into the profile. These settings allow
|
||||
MCP tools to run without approval prompts and give
|
||||
long-running browser operations up to five minutes. An MCP server may still
|
||||
fail or time out. MCP elicitations that inherently require user input are
|
||||
rejected rather than shown as unattended prompts.
|
||||
|
||||
Do not persist an individual MCP tool approval when prompted by an older or
|
||||
misconfigured session. Codex CLI 0.133.0 may write a per-tool table that the
|
||||
profile-v2 parser rejects on the next launch.
|
||||
|
||||
## Verification
|
||||
|
||||
The nested setup smoke test confirmed that the project is readable and
|
||||
@@ -60,6 +82,35 @@ sandboxed command-line tools may fail HTTPS certificate verification. Do not
|
||||
disable certificate verification for package installation or sensitive
|
||||
retrievals; use live web search or review the retrieval from the host instead.
|
||||
|
||||
## Optional local browser environment
|
||||
|
||||
The repository now provides a Linux-only `browser` development shell, separate
|
||||
from the default content-editing shell. In a normal host terminal, from the
|
||||
repository root:
|
||||
|
||||
```sh
|
||||
nix --extra-experimental-features "nix-command flakes" develop .#browser
|
||||
```
|
||||
|
||||
It supplies a lockfile-matched Playwright driver, Chromium, headless shell and
|
||||
font configuration. New processes launched inside this shell inherit
|
||||
`PLAYWRIGHT_MODULE` and `PLAYWRIGHT_BROWSERS_PATH`; existing sessions do not.
|
||||
For a guarded real-page smoke test, run from `www`:
|
||||
|
||||
```sh
|
||||
TMPDIR=/tmp npm run build
|
||||
TMPDIR=/tmp node tools/playwright-smoke.mjs
|
||||
```
|
||||
|
||||
Preview and browser run in the same process environment, avoiding dependence on
|
||||
remote MCP transport or cross-execution loopback access. All non-loopback and
|
||||
non-GET/HEAD browser requests are blocked. Results/screenshots stay in `/tmp`.
|
||||
The tool does not grant access to files or sockets denied by the autonomous
|
||||
profile. If Nix realization, Chromium startup or loopback access is denied, keep
|
||||
that result as an environment limitation and use the normal host terminal;
|
||||
do not weaken TLS, identity, signing or sandbox configuration. No global MCP
|
||||
configuration or launcher permissions were changed for this setup.
|
||||
|
||||
## Git limitation
|
||||
|
||||
Codex always protects `.git` recursively in its `workspace-write` sandbox.
|
||||
@@ -79,3 +130,9 @@ Check the startup banner before assigning work. It should report:
|
||||
approval: never
|
||||
sandbox: workspace-write ... (network access enabled)
|
||||
```
|
||||
|
||||
Then check the MCP configuration:
|
||||
|
||||
```txt
|
||||
/mcp verbose
|
||||
```
|
||||
@@ -36,19 +36,24 @@ For the migration task:
|
||||
|
||||
1. Read `AGENTS.md`.
|
||||
2. Read `MIGRATION_BRIEF.md`.
|
||||
3. Inspect the local mirror.
|
||||
4. Provide a concise implementation plan.
|
||||
5. Scaffold or update the Astro project in `./www`.
|
||||
6. Build extraction, crawl, audit, sitemap, and redirect tools.
|
||||
7. Extract content, media, metadata, navigation, and forms.
|
||||
8. Refactor into reusable Astro components.
|
||||
9. Configure Astro content collections and Front Matter CMS.
|
||||
10. Add English, Arabic, and Spanish structure.
|
||||
11. Add RTL CSS for Arabic.
|
||||
12. Add SEO, sitemap, robots, redirects, and reports.
|
||||
13. Run build and audits.
|
||||
14. Fix practical errors.
|
||||
15. Document incomplete items in `reports/migration-summary.md`.
|
||||
3. Inspect the live public site as the single source of truth.
|
||||
4. Inspect the local mirror as a convenient extraction aid and identify
|
||||
relevant discrepancies.
|
||||
5. Inspect the relevant live HTML, CSS, assets, and responsive behavior.
|
||||
6. Provide a concise implementation and visual-validation plan.
|
||||
7. Scaffold or update the Astro project in `./www`.
|
||||
8. Build extraction, crawl, audit, sitemap, and redirect tools.
|
||||
9. Extract content, media, metadata, navigation, and forms.
|
||||
10. Refactor genuinely repeated patterns into reusable Astro components.
|
||||
11. Compare rendered pages with the live site at desktop and mobile widths.
|
||||
12. Configure Astro content collections and Front Matter CMS.
|
||||
13. Add English, Arabic, and Spanish structure.
|
||||
14. Add RTL CSS for Arabic.
|
||||
15. Add SEO, sitemap, robots, redirects, and reports.
|
||||
16. Run build and audits.
|
||||
17. Fix practical errors.
|
||||
18. Document incomplete items and intentional visual deviations in
|
||||
`reports/migration-summary.md`.
|
||||
|
||||
## Important Claude-specific behavior
|
||||
|
||||
@@ -60,6 +65,15 @@ When asked to begin migration, first read the relevant project files, then work
|
||||
|
||||
Treat `./www.azinstitute4autism.com` as read-only source material.
|
||||
|
||||
Treat the live public site as the single content and visual source of truth.
|
||||
Use the mirror only as a convenient local extraction and inspection aid. When
|
||||
the live site and mirror disagree, follow the live site and document material
|
||||
discrepancies. This migration is not a redesign. Do not substitute a generic
|
||||
visual system for the live design.
|
||||
|
||||
Treat the `migration-foundation` branch as reference-only. Do not use it as the
|
||||
visual baseline.
|
||||
|
||||
Do not delete or overwrite raw source assets.
|
||||
|
||||
Do not remove files unless you are certain they are generated output in `./www`.
|
||||
|
||||
@@ -1,14 +1,21 @@
|
||||
# MIGRATION_BRIEF.md
|
||||
|
||||
## Authority and interpretation
|
||||
|
||||
Read and follow `AGENTS.md` first. It is the authoritative project policy; this
|
||||
brief provides detailed implementation guidance. If any wording here appears
|
||||
to conflict with `AGENTS.md`, follow `AGENTS.md` and correct this brief before
|
||||
continuing.
|
||||
|
||||
This is a fidelity-first migration, not a redesign. The live public site is the
|
||||
single content and visual source of truth. The `migration-foundation` branch
|
||||
may be consulted for nonvisual tooling and recovered content, but it must not
|
||||
be used as the visual baseline.
|
||||
|
||||
## Task
|
||||
|
||||
Migrate the public Arizona Institute for Autism website from a mirrored HubSpot-generated static copy into a clean, maintainable Astro + TypeScript site.
|
||||
|
||||
Raw mirrored source:
|
||||
|
||||
```txt
|
||||
./www.azinstitute4autism.com
|
||||
```
|
||||
Migrate the public Arizona Institute for Autism website into a clean,
|
||||
maintainable Astro + TypeScript site.
|
||||
|
||||
New Astro project target:
|
||||
|
||||
@@ -16,23 +23,17 @@ New Astro project target:
|
||||
./www
|
||||
```
|
||||
|
||||
The raw mirror was created with:
|
||||
|
||||
```sh
|
||||
wget --mirror --page-requisites --adjust-extension --convert-links --no-parent https://www.azinstitute4autism.com
|
||||
```
|
||||
|
||||
There is no HubSpot export, no HubSpot CLI access, and no HubSpot API access.
|
||||
|
||||
Use the local mirror as the primary source.
|
||||
|
||||
If internet access is available, also crawl the live public website to catch missing URLs or assets:
|
||||
Use the live public website as the single source of truth:
|
||||
|
||||
```txt
|
||||
https://www.azinstitute4autism.com
|
||||
```
|
||||
|
||||
The live crawl is only for comparison and gap-filling. The local mirror remains the source of truth.
|
||||
Verify content, design, URLs, metadata, assets, and responsive behavior against
|
||||
the live site. If the live site is temporarily unavailable, work that depends
|
||||
on its authority must remain explicitly unverified.
|
||||
|
||||
## High-level goal
|
||||
|
||||
@@ -49,7 +50,7 @@ Create a new Astro site in `./www` that is:
|
||||
- SEO-conscious
|
||||
- accessible
|
||||
- maintainable
|
||||
- mostly faithful to the current design
|
||||
- faithful to the current design, layout, and responsive behavior
|
||||
- free of unnecessary HubSpot-generated structure
|
||||
- prepared for future development and publishing
|
||||
|
||||
@@ -57,10 +58,6 @@ Create a new Astro site in `./www` that is:
|
||||
|
||||
Do not modify or delete:
|
||||
|
||||
```txt
|
||||
./www.azinstitute4autism.com
|
||||
```
|
||||
|
||||
Do not depend on:
|
||||
|
||||
- HubSpot CLI
|
||||
@@ -238,11 +235,20 @@ Do not invent extensive translations.
|
||||
|
||||
### Design direction
|
||||
|
||||
The new site should be more maintainable while remaining mostly faithful to the current design.
|
||||
The new site must faithfully reproduce the live public site's visible design,
|
||||
layout, content hierarchy, and responsive behavior. Do not redesign,
|
||||
reinterpret, modernize, simplify, or genericize source pages.
|
||||
|
||||
Do not create a messy clone of HubSpot output.
|
||||
Preserve typography, colors, backgrounds, spacing, proportions, imagery,
|
||||
section order, decorative treatments, navigation, calls to action, forms, and
|
||||
page-specific distinctions. Reuse exact source assets and fonts wherever
|
||||
practical.
|
||||
|
||||
Refactor repeated sections into reusable components.
|
||||
Do not copy HubSpot-generated DOM or CSS wholesale.
|
||||
|
||||
Refactor genuinely repeated sections into reusable components. Do not force
|
||||
distinctive source sections into generic components when doing so changes their
|
||||
appearance or structure.
|
||||
|
||||
Use semantic markup.
|
||||
|
||||
@@ -250,6 +256,21 @@ Use plain CSS with design tokens.
|
||||
|
||||
Do not use Tailwind.
|
||||
|
||||
### Fidelity workflow
|
||||
|
||||
For each page family or shared component:
|
||||
|
||||
1. Inspect the live page, HTML, CSS, assets, and responsive behavior.
|
||||
2. Resolve discrepancies in favor of the live public site.
|
||||
3. Record important visual details and identify genuine shared patterns.
|
||||
4. Implement the page or component in maintainable Astro code.
|
||||
5. Compare the complete rendered page with the live public site at representative
|
||||
desktop and mobile viewport widths.
|
||||
6. Fix material visual differences before marking it complete.
|
||||
|
||||
Document intentional visual deviations and their reasons in
|
||||
`reports/migration-summary.md`.
|
||||
|
||||
## Required target structure
|
||||
|
||||
Create this project structure in `./www`:
|
||||
@@ -362,15 +383,11 @@ www/
|
||||
rtl.css
|
||||
|
||||
tools/
|
||||
extract-site.mjs
|
||||
crawl-live-site.mjs
|
||||
audit-links.mjs
|
||||
generate-redirects.mjs
|
||||
generate-sitemap.mjs
|
||||
|
||||
original/
|
||||
README.md
|
||||
|
||||
reports/
|
||||
url-inventory.csv
|
||||
asset-inventory.csv
|
||||
@@ -383,17 +400,10 @@ www/
|
||||
migration-summary.md
|
||||
```
|
||||
|
||||
The `original/README.md` file should explain that the raw source mirror lives outside the Astro project at:
|
||||
|
||||
```txt
|
||||
../www.azinstitute4autism.com
|
||||
```
|
||||
|
||||
Do not duplicate the entire raw mirror inside `./www` unless there is a specific reason.
|
||||
|
||||
## Initial inspection
|
||||
|
||||
Before creating or modifying files, inspect the mirror.
|
||||
Before creating or modifying files, inspect the live public site and current
|
||||
project sources.
|
||||
|
||||
Look for:
|
||||
|
||||
@@ -643,7 +653,9 @@ src/styles/rtl.css
|
||||
src/styles/global.css
|
||||
```
|
||||
|
||||
Infer the current site’s visual language from the mirrored CSS and HTML.
|
||||
Extract the current site's visual language from the live public site. Tokens
|
||||
must encode source values; they must not introduce a replacement visual
|
||||
system.
|
||||
|
||||
Use design tokens such as:
|
||||
|
||||
@@ -790,7 +802,7 @@ It should support:
|
||||
- hreflang links where translation relationships are known
|
||||
- JSON-LD/schema when extracted
|
||||
|
||||
Preserve metadata from the mirror wherever possible.
|
||||
Preserve metadata from the live site wherever possible.
|
||||
|
||||
Generate:
|
||||
|
||||
@@ -843,49 +855,27 @@ src/pages/es/[...slug].astro
|
||||
|
||||
Implement routing in a clean, maintainable way.
|
||||
|
||||
## Extraction scripts
|
||||
## Migration utility scripts
|
||||
|
||||
Create:
|
||||
|
||||
```txt
|
||||
tools/extract-site.mjs
|
||||
tools/crawl-live-site.mjs
|
||||
tools/audit-links.mjs
|
||||
tools/generate-redirects.mjs
|
||||
tools/generate-sitemap.mjs
|
||||
```
|
||||
|
||||
### `extract-site.mjs`
|
||||
|
||||
This script should:
|
||||
|
||||
- scan the mirror
|
||||
- identify HTML files
|
||||
- infer URL paths
|
||||
- identify page type
|
||||
- identify blog posts
|
||||
- identify listing pages
|
||||
- extract title/meta/H1/body/main content
|
||||
- extract canonical and Open Graph metadata
|
||||
- extract schema/JSON-LD where present
|
||||
- convert blog posts to Markdown where feasible
|
||||
- create page Markdown where useful
|
||||
- copy/normalize assets
|
||||
- rewrite local asset references
|
||||
- create initial reports
|
||||
|
||||
The script does not need to be perfect, but it should be useful and documented.
|
||||
|
||||
### `crawl-live-site.mjs`
|
||||
|
||||
This script should:
|
||||
|
||||
- attempt to crawl the live site if internet is available
|
||||
- discover URLs from sitemap and internal links
|
||||
- compare discovered URLs against the mirror
|
||||
- compare discovered URLs against the current site source and generated output
|
||||
- document missing URLs/assets
|
||||
- avoid aggressive crawling
|
||||
- respect the scope of `www.azinstitute4autism.com`
|
||||
- respect the live-site scope
|
||||
|
||||
### `audit-links.mjs`
|
||||
|
||||
@@ -1096,7 +1086,6 @@ Add scripts similar to:
|
||||
"dev": "astro dev",
|
||||
"build": "astro check && astro build",
|
||||
"preview": "astro preview",
|
||||
"extract": "node tools/extract-site.mjs",
|
||||
"crawl": "node tools/crawl-live-site.mjs",
|
||||
"audit:links": "node tools/audit-links.mjs",
|
||||
"generate:sitemap": "node tools/generate-sitemap.mjs",
|
||||
@@ -1118,7 +1107,6 @@ www/README.md
|
||||
It should explain:
|
||||
|
||||
- what the project is
|
||||
- where the raw mirror lives
|
||||
- how the migration was performed
|
||||
- how to install dependencies
|
||||
- how to run local dev
|
||||
@@ -1168,6 +1156,8 @@ The migration is successful when:
|
||||
- `./www` contains a working Astro project
|
||||
- `npm run build` succeeds or unresolved build issues are clearly documented
|
||||
- main public pages render locally
|
||||
- main public pages have been compared with the live site at desktop and mobile
|
||||
widths and have no undocumented material visual differences
|
||||
- blog posts are migrated into file-based Markdown where feasible
|
||||
- Markdown files work with Front Matter CMS
|
||||
- MDX is available but not overused
|
||||
@@ -1191,18 +1181,22 @@ Proceed methodically:
|
||||
1. Read `AGENTS.md`.
|
||||
2. Read `CLAUDE.md`, if using Claude Code.
|
||||
3. Read this `MIGRATION_BRIEF.md`.
|
||||
4. Inspect `./www.azinstitute4autism.com`.
|
||||
5. Summarize a concise implementation plan.
|
||||
6. Scaffold Astro in `./www`.
|
||||
7. Build extraction/reporting scripts.
|
||||
8. Extract pages, blog posts, assets, metadata, navigation, and forms.
|
||||
9. Refactor repeated sections into Astro components.
|
||||
10. Configure content collections and Front Matter CMS.
|
||||
11. Add multilingual structure.
|
||||
12. Add SEO, sitemap, robots, and redirects.
|
||||
13. Add reports.
|
||||
14. Run build and audits.
|
||||
15. Fix errors.
|
||||
16. Summarize what changed and what still needs manual review.
|
||||
4. Inspect the live public site.
|
||||
5. Inspect the relevant live public site pages and identify discrepancies.
|
||||
6. Summarize a concise implementation plan.
|
||||
7. Scaffold Astro in `./www`.
|
||||
8. Build extraction/reporting scripts.
|
||||
9. Extract pages, blog posts, assets, metadata, navigation, and forms.
|
||||
10. Refactor repeated sections into Astro components.
|
||||
11. Configure content collections and Front Matter CMS.
|
||||
12. Add multilingual structure.
|
||||
13. Add SEO, sitemap, robots, and redirects.
|
||||
14. Add reports.
|
||||
15. Run build and audits.
|
||||
16. Fix errors.
|
||||
17. Summarize what changed and what still needs manual review.
|
||||
|
||||
Prioritize a clean, maintainable foundation over a messy pixel-perfect clone.
|
||||
Prioritize visual fidelity and content correctness while implementing them with
|
||||
clean, maintainable Astro code. Do not trade away visible source design for a
|
||||
generic foundation, and do not copy HubSpot-generated implementation merely to
|
||||
obtain fidelity.
|
||||
@@ -9,7 +9,12 @@ if [[ "${1:-}" == "exec" ]]; then
|
||||
fi
|
||||
|
||||
exec codex \
|
||||
--profile-v2 azi4a2-autonomous \
|
||||
--profile azi4a2-autonomous \
|
||||
--strict-config \
|
||||
--config 'approval_policy="never"' \
|
||||
--config 'mcp_servers.playwright.default_tools_approval_mode="approve"' \
|
||||
--config 'mcp_servers.playwright.tool_timeout_sec=300' \
|
||||
--config 'mcp_servers.openaiDeveloperDocs.default_tools_approval_mode="approve"' \
|
||||
--config 'mcp_servers.openaiDeveloperDocs.tool_timeout_sec=300' \
|
||||
--cd "$repo_root" \
|
||||
"$@"
|
||||
@@ -2,16 +2,18 @@
|
||||
"nodes": {
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1728018373,
|
||||
"narHash": "sha256-NOiTvBbRLIOe5F6RbHaAh6++BNjsb149fGZd1T4+KBg=",
|
||||
"rev": "bc947f541ae55e999ffdb4013441347d83b00feb",
|
||||
"revCount": 688563,
|
||||
"type": "tarball",
|
||||
"url": "https://api.flakehub.com/f/pinned/NixOS/nixpkgs/0.1.688563%2Brev-bc947f541ae55e999ffdb4013441347d83b00feb/01925b66-3b70-71bd-b277-924a6c208ba7/source.tar.gz"
|
||||
"lastModified": 1785090369,
|
||||
"narHash": "sha256-m0pDuRJG7EDo9ri+4Ksu83VsI+PlxNC9lNBfydejce4=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "624af665418d3c65d544145b4d34ad696439570e",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"type": "tarball",
|
||||
"url": "https://flakehub.com/f/NixOS/nixpkgs/0.1.%2A.tar.gz"
|
||||
"owner": "NixOS",
|
||||
"ref": "nixos-unstable",
|
||||
"repo": "nixpkgs",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"root": {
|
||||
|
||||
@@ -1,25 +1,63 @@
|
||||
{
|
||||
description = "A Nix-flake-based Node.js development environment";
|
||||
description = "Arizona Institute for Autism Astro development environment";
|
||||
|
||||
inputs.nixpkgs.url = "https://flakehub.com/f/NixOS/nixpkgs/0.1.*.tar.gz";
|
||||
inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
|
||||
|
||||
outputs = { self, nixpkgs }:
|
||||
outputs =
|
||||
{ nixpkgs, ... }:
|
||||
let
|
||||
supportedSystems = [ "x86_64-linux" "aarch64-linux" "x86_64-darwin" "aarch64-darwin" ];
|
||||
forEachSupportedSystem = f: nixpkgs.lib.genAttrs supportedSystems (system: f {
|
||||
pkgs = import nixpkgs { inherit system; overlays = [ self.overlays.default ]; };
|
||||
});
|
||||
supportedSystems = [
|
||||
"x86_64-linux"
|
||||
"aarch64-linux"
|
||||
"aarch64-darwin"
|
||||
];
|
||||
|
||||
forAllSystems =
|
||||
function: nixpkgs.lib.genAttrs supportedSystems (system: function nixpkgs.legacyPackages.${system});
|
||||
in
|
||||
{
|
||||
overlays.default = final: prev: rec {
|
||||
nodejs = prev.nodejs;
|
||||
yarn = (prev.yarn.override { inherit nodejs; });
|
||||
devShells = forAllSystems (
|
||||
pkgs:
|
||||
let
|
||||
developmentPackages = with pkgs; [
|
||||
nodejs_24
|
||||
git
|
||||
curl
|
||||
jq
|
||||
ripgrep
|
||||
fd
|
||||
python3
|
||||
];
|
||||
in
|
||||
{
|
||||
default = pkgs.mkShell { packages = developmentPackages; };
|
||||
}
|
||||
// pkgs.lib.optionalAttrs pkgs.stdenv.hostPlatform.isLinux {
|
||||
# Keep driver and browser revisions paired through the existing lockfile.
|
||||
# Opt-in so ordinary content editing does not require browser downloads.
|
||||
browser = pkgs.mkShell {
|
||||
packages = developmentPackages ++ [ pkgs.playwright-driver ];
|
||||
PLAYWRIGHT_MODULE = "${pkgs.playwright-driver}/index.mjs";
|
||||
PLAYWRIGHT_BROWSERS_PATH = pkgs.playwright-driver.selectBrowsers {
|
||||
withFirefox = false;
|
||||
withWebkit = false;
|
||||
withFfmpeg = false;
|
||||
};
|
||||
PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD = "1";
|
||||
# Nix supplies browser libraries via patched runtime paths, not apt.
|
||||
PLAYWRIGHT_SKIP_VALIDATE_HOST_REQUIREMENTS = "true";
|
||||
FONTCONFIG_FILE = pkgs.makeFontsConf {
|
||||
fontDirectories = with pkgs; [
|
||||
dejavu_fonts
|
||||
liberation_ttf
|
||||
noto-fonts
|
||||
];
|
||||
};
|
||||
ASTRO_TELEMETRY_DISABLED = "1";
|
||||
};
|
||||
}
|
||||
);
|
||||
|
||||
devShells = forEachSupportedSystem ({ pkgs }: {
|
||||
default = pkgs.mkShell {
|
||||
packages = with pkgs; [ node2nix nodejs nodePackages.pnpm yarn ];
|
||||
};
|
||||
});
|
||||
formatter = forAllSystems (pkgs: pkgs.nixfmt);
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
PUBLIC_AIA_API_BASE=https://api.azinstitute4autism.com
|
||||
PUBLIC_COUNTERS_MODE=off
|
||||
PUBLIC_COUNTERS_LIVE_APPROVED=false
|
||||
PUBLIC_COUNTERS_MOCK_RESULT=success
|
||||
# Optional override only: absent/empty means live on exactly 16 approved routes.
|
||||
# PUBLIC_AIA_FORMS_MODE=off
|
||||
# PUBLIC_AIA_FORMS_MODE=mock
|
||||
PUBLIC_AIA_FORMS_MOCK_RESULT=success
|
||||
# Explicit, local-only Form A meeting preview; requires PUBLIC_AIA_FORMS_MODE=mock.
|
||||
PUBLIC_AIA_FORM_A_PREVIEW=false
|
||||
PUBLIC_ALLOW_INDEXING=false
|
||||
|
||||
# Employee portal ciphertext is an opt-in local compatibility preview only.
|
||||
# Never supply a passphrase through environment variables or build arguments.
|
||||
AIA_PORTAL_LOCAL_PREVIEW_APPROVED=false
|
||||
# AIA_PORTAL_CIPHERTEXT_FILE=/owner-controlled/private/ciphertext.json
|
||||
@@ -0,0 +1,8 @@
|
||||
node_modules/
|
||||
dist/
|
||||
.astro/
|
||||
.env
|
||||
.env.*
|
||||
!.env.example
|
||||
reports/live-crawl.json
|
||||
reports/*.png
|
||||
@@ -0,0 +1,648 @@
|
||||
# Arizona Institute for Autism Astro Site
|
||||
|
||||
Static Astro + TypeScript migration of the public Arizona Institute for Autism
|
||||
website.
|
||||
|
||||
## Quick Start
|
||||
|
||||
Run commands from this directory:
|
||||
|
||||
```sh
|
||||
cd www
|
||||
npm install
|
||||
npm run dev
|
||||
```
|
||||
|
||||
Astro prints the local development URL, normally:
|
||||
|
||||
```txt
|
||||
http://localhost:4321
|
||||
```
|
||||
|
||||
The development server watches source files and refreshes the browser after
|
||||
edits. Stop it with `Ctrl+C`.
|
||||
|
||||
## Environment Setup
|
||||
|
||||
Requirements:
|
||||
|
||||
- Node.js 22.12 or newer (see `package.json`); the Nix shell supplies Node.js 24
|
||||
- npm
|
||||
|
||||
This repository also has a Nix development shell at the repository root:
|
||||
|
||||
```sh
|
||||
cd ..
|
||||
nix --extra-experimental-features "nix-command flakes" develop
|
||||
cd www
|
||||
npm install
|
||||
```
|
||||
|
||||
### Local Playwright environment (Linux)
|
||||
|
||||
From the repository root, enter the opt-in browser shell:
|
||||
|
||||
```sh
|
||||
nix --extra-experimental-features "nix-command flakes" develop .#browser
|
||||
cd www
|
||||
TMPDIR=/tmp npm run build
|
||||
TMPDIR=/tmp node tools/playwright-smoke.mjs
|
||||
```
|
||||
|
||||
The existing `flake.lock` pairs the Nix Playwright driver and Chromium/headless
|
||||
shell. `PLAYWRIGHT_MODULE` points directly to that driver; `PLAYWRIGHT_BROWSERS_PATH`
|
||||
selects its matching browsers. Fontconfig supplies Latin/Arabic fallback fonts.
|
||||
The ordinary development shell and npm dependencies remain unchanged. This
|
||||
browser shell is offered on Linux; the existing Darwin development shell is
|
||||
unchanged. Do not run `npx playwright install` or mix an unrelated npm Playwright
|
||||
version with these browser revisions.
|
||||
|
||||
The smoke test starts Astro preview and Chromium in the **same execution** on an
|
||||
ephemeral `127.0.0.1` port. It blocks external and non-GET/HEAD requests, loads the
|
||||
real Spanish privacy and Library pages, verifies staging robots/title/H1, captures
|
||||
two full-page screenshots in a new `/tmp/aia-playwright-smoke-*` directory, and
|
||||
closes browser/preview. It does not submit forms or exercise production APIs.
|
||||
Success establishes browser transport only, not Phase 8 visual or human acceptance.
|
||||
|
||||
Use the [Phase 8 guarded browser procedure](reports/phase-8-host-browser-verification-2026-10-14.md)
|
||||
for full review. Entering the Nix shell does not repair an already-running remote
|
||||
MCP transport or relax sandbox permissions. If the autonomous environment cannot
|
||||
access the Nix store, trusted certificates, sockets or browser runtime files, run
|
||||
the same smoke test from the host terminal. Do not disable TLS verification or
|
||||
bypass the sandbox. The Nix environment needs host validation before browser
|
||||
availability can be claimed.
|
||||
|
||||
Copy the example environment file when testing the like/view counter or staging
|
||||
SEO behavior:
|
||||
|
||||
```sh
|
||||
cp .env.example .env
|
||||
```
|
||||
|
||||
The default values are:
|
||||
|
||||
```txt
|
||||
PUBLIC_AIA_API_BASE=https://api.azinstitute4autism.com
|
||||
PUBLIC_ALLOW_INDEXING=false
|
||||
```
|
||||
|
||||
The site still renders when the AIA API is unavailable. Migration and staging
|
||||
builds emit `noindex,nofollow` by default. `PUBLIC_ALLOW_INDEXING=true` enables
|
||||
production publication policy, including per-page noindex exceptions. Exercise
|
||||
that mode locally with `PUBLIC_ALLOW_INDEXING=true npm run build`; this does
|
||||
not deploy anything or change environment files. Run `npm run build` afterward
|
||||
to restore staging output. Only explicitly authorized production deployments
|
||||
should publish indexing-enabled artifacts.
|
||||
|
||||
## View And Build
|
||||
|
||||
### Development Server
|
||||
|
||||
Use this while editing:
|
||||
|
||||
```sh
|
||||
npm run dev
|
||||
```
|
||||
|
||||
Useful development URLs include:
|
||||
|
||||
```txt
|
||||
/
|
||||
/aba-therapy
|
||||
/autism-evaluations
|
||||
/learner-social-club
|
||||
/client-consultation
|
||||
/library
|
||||
/es
|
||||
/es/library
|
||||
/ar/library
|
||||
```
|
||||
|
||||
### Static Build
|
||||
|
||||
Generate the static site with staging indexing protection:
|
||||
|
||||
```sh
|
||||
npm run build
|
||||
```
|
||||
|
||||
The autonomous Codex sandbox denies `/etc/hosts`, so its Node runtime cannot
|
||||
resolve `localhost`. Use this equivalent validation command only inside that
|
||||
sandbox:
|
||||
|
||||
```sh
|
||||
npm run build:sandbox
|
||||
```
|
||||
|
||||
Build output is written to:
|
||||
|
||||
```txt
|
||||
dist/
|
||||
```
|
||||
|
||||
### Preview Production Output
|
||||
|
||||
After a successful build:
|
||||
|
||||
```sh
|
||||
npm run preview
|
||||
```
|
||||
|
||||
Astro prints the preview URL, normally `http://localhost:4321`.
|
||||
|
||||
### Validate Before Committing
|
||||
|
||||
```sh
|
||||
npm run build
|
||||
npm run audit:links
|
||||
npm run audit:images
|
||||
npm run audit:blog
|
||||
```
|
||||
|
||||
The link audit reads `dist/` after a successful build. If rendered output is
|
||||
unavailable, it audits generated source routes, relative and root-relative
|
||||
Markdown links, and public assets instead. It writes:
|
||||
|
||||
```txt
|
||||
reports/broken-links.md
|
||||
```
|
||||
|
||||
## Editing Content
|
||||
|
||||
Normal content editing happens in Markdown:
|
||||
|
||||
```txt
|
||||
src/content/pages/en
|
||||
src/content/pages/es
|
||||
src/content/pages/ar
|
||||
src/content/blog/en
|
||||
src/content/blog/es
|
||||
src/content/blog/ar
|
||||
src/content/authors/en
|
||||
src/content/authors/es
|
||||
src/content/authors/ar
|
||||
```
|
||||
|
||||
Page and post frontmatter is validated by `src/content.config.ts`.
|
||||
|
||||
Example:
|
||||
|
||||
```md
|
||||
---
|
||||
title: "Example Article"
|
||||
description: "Short search and social description."
|
||||
slug: "example-article"
|
||||
canonical: "https://www.azinstitute4autism.com/library/example-article"
|
||||
lang: "en"
|
||||
translationKey: "example-article"
|
||||
featuredImage: "/assets/images/example.webp"
|
||||
alt: "Descriptive image alt text"
|
||||
date: "2026-06-07"
|
||||
author: "rula-diab"
|
||||
category: "Library"
|
||||
tags:
|
||||
- ABA Therapy
|
||||
draft: false
|
||||
---
|
||||
|
||||
Article content goes here.
|
||||
```
|
||||
|
||||
Set `draft: true` while preparing unpublished content.
|
||||
|
||||
### Display Headings and Social Metadata
|
||||
|
||||
Pages and articles may supply an optional plain-text `displayH1` in frontmatter:
|
||||
|
||||
```yaml
|
||||
title: "The existing SEO title"
|
||||
displayH1: "The source-supported visible heading"
|
||||
```
|
||||
|
||||
The override changes only the visible H1. It does not change the document or
|
||||
social title, article cards, navigation labels, or related-post text. Empty,
|
||||
whitespace-only and non-string overrides fail content validation. Without an
|
||||
override, articles keep `title`, regular pages keep their existing service/page
|
||||
heading mappings and title-derived fallback, and homepage/Library components
|
||||
keep their existing headings. Homepage heading markup stays intact unless a
|
||||
plain-text override is explicitly supplied. Library template SEO titles remain
|
||||
unchanged. Front Matter CMS exposes the optional field for pages and blog posts.
|
||||
|
||||
Existing featured images and page banner fallbacks supply `og:image` and
|
||||
`twitter:image`. Relative paths resolve against the public origin in
|
||||
`src/data/site.json`, never a preview host or an external canonical. Existing
|
||||
`alt` text supplies both image-alt metadata tags only when it describes the
|
||||
selected featured image; no descriptions are invented for banner fallbacks.
|
||||
Pages without a selected image omit image tags and retain the `summary` card;
|
||||
image pages retain `summary_large_image`.
|
||||
|
||||
`og:locale` is omitted: the content languages `en`, `es` and `ar` do not establish
|
||||
the regional `language_TERRITORY` values required by the
|
||||
[Open Graph protocol](https://ogp.me/). Regional targeting remains a human
|
||||
localization decision. HTML language/direction and hreflang are unchanged.
|
||||
|
||||
Run `npm run test:seo` for isolated real-Astro builds covering heading defaults,
|
||||
overrides, schema rejection, image metadata and shared publication invariants
|
||||
in staging and indexing modes. Fixtures never modify the working content or
|
||||
the project's `dist/`. See the dated Phase 4A section of
|
||||
[`reports/migration-summary.md`](reports/migration-summary.md) for source evidence.
|
||||
|
||||
### Publication, Canonicals, and Sitemap
|
||||
|
||||
`src/utils/publication-policy.ts` owns route eligibility, normalized public
|
||||
URLs, canonical validation, robots values, and sitemap eligibility.
|
||||
`src/utils/publication.ts` loads the Astro collections into that manifest.
|
||||
`src/pages/[...slug].astro` generates every content route from eligible entries,
|
||||
including home and Library indexes; their existing presentation components are
|
||||
under `src/components/pages/`. Shared SEO and the generated sitemap use that
|
||||
same manifest. The route and source-link audits also reuse the policy.
|
||||
|
||||
- `draft: true` means unpublished: no generated HTML route in either build mode.
|
||||
The Arabic homepage `/ar` remains a draft; published Arabic Library routes
|
||||
remain available. Navigation does not link to the unpublished homepage.
|
||||
- `noindex: true` keeps a published route accessible, emits `noindex,follow`
|
||||
in indexing-enabled builds, and excludes it from the sitemap. It defaults
|
||||
to `false`. Staging always overrides this with `noindex,nofollow`.
|
||||
- An omitted `canonical` gets the normalized public URL from `site.json` and
|
||||
its local route. A supplied local canonical must match that route. Invalid
|
||||
canonicals, duplicate canonical targets, and route collisions fail validation.
|
||||
- Verified syndicated articles use their existing `canonical` field for the
|
||||
external publisher URL. External-canonical routes are excluded from the
|
||||
sitemap even when they are not marked noindex. Do not use canonical values
|
||||
to create local routes or sitemap locations.
|
||||
- `src/pages/sitemap.xml.ts` generates `dist/sitemap.xml` on every build.
|
||||
Staging emits an empty sitemap and does not advertise it in `robots.txt`.
|
||||
Indexing-enabled builds include only eligible, indexable, self-canonical
|
||||
local routes. There is no hand-maintained `public/sitemap.xml`.
|
||||
- `npm run generate:sitemap` is a compatibility command that runs the Astro
|
||||
build; it no longer writes a separate public sitemap from frontmatter strings.
|
||||
|
||||
The verified noindex exceptions are `/schedule-consultation`, `/employee-portal`,
|
||||
`/library/community-highlight-meet-rula-diab`, and
|
||||
`/library/new-aia-scottsdale-office`. The last two also retain their verified
|
||||
external canonicals. The dated evidence is in `reports/migration-summary.md`.
|
||||
|
||||
Run `npm run test:publication` for policy tests and isolated builds of the
|
||||
actual application in staging and indexing-enabled modes. Fixtures test draft
|
||||
content, draft home/Library indexes, missing and external canonicals, and
|
||||
generated head/sitemap consistency without modifying the working site's output.
|
||||
The policy is TypeScript; Node audit/test commands use `--experimental-strip-types`
|
||||
for compatibility with the declared Node 22.12 minimum. Dependencies are unchanged.
|
||||
|
||||
After publication changes, build and run `npm run audit:routes -- --offline`
|
||||
before `npm run audit:routes -- --check`. This creates a newly dated local
|
||||
reconciliation using saved production observations, preserving earlier evidence.
|
||||
Saved rendered metadata is specific to its build mode: after restoring the
|
||||
normal staging build, reconcile that output again before checking it.
|
||||
|
||||
### Links And Buttons
|
||||
|
||||
Use normal Markdown links for links within prose:
|
||||
|
||||
```md
|
||||
Read the [ABA therapy guide](/aba-therapy) for more information.
|
||||
```
|
||||
|
||||
Prose links render as the live site's orange inline links. Do not place an
|
||||
ordinary Markdown link on its own line merely to make it look like a button.
|
||||
|
||||
Use `.mdx` and the `Button` component only for a source-verified call to action:
|
||||
|
||||
```mdx
|
||||
import Button from '../../../components/Button.astro';
|
||||
|
||||
<Button href="/client-consultation">Request an Appointment</Button>
|
||||
<Button href="/services" variant="outlined">Explore Services</Button>
|
||||
<Button href="https://example.com" target="_blank">External Action</Button>
|
||||
```
|
||||
|
||||
Available `Button` variants are `filled` (default), `outlined`, and `light`.
|
||||
Available sizes are `default` and `small`. A `_blank` target automatically adds
|
||||
`rel="noopener noreferrer"`.
|
||||
|
||||
Normal Markdown links remain preferred for ordinary page and article content.
|
||||
|
||||
### Stable Heading Links
|
||||
|
||||
Astro automatically generates an ID for each Markdown heading. In `.md` files,
|
||||
when a section needs a stable, editorially controlled fragment link, add an
|
||||
explicit ID with Sätteri heading-attribute syntax:
|
||||
|
||||
```md
|
||||
## Accepted Insurance Carriers {#accepted-insurance-heading}
|
||||
```
|
||||
|
||||
Link to it with the resulting fragment URL:
|
||||
|
||||
```md
|
||||
[View accepted carriers](/insurance#accepted-insurance-heading)
|
||||
```
|
||||
|
||||
Keep explicit IDs unique within the page, lowercase, and hyphenated. Treat a
|
||||
published ID as permanent so inbound links do not break when heading text is
|
||||
edited. The installed MDX parser rejects this shorthand, independently of
|
||||
directive parsing. In `.mdx` files, use an explicit HTML heading instead:
|
||||
|
||||
```mdx
|
||||
<h2 id="accepted-insurance-heading">Accepted Insurance Carriers</h2>
|
||||
```
|
||||
|
||||
### Markdown Directives
|
||||
|
||||
Sätteri directive parsing is disabled. The content corpus has no intentional
|
||||
container (`:::name`), leaf (`::name`), or text (`:name`) directives, and no
|
||||
directive transformation plugins are registered. Enabling parsing silently
|
||||
removed literal text such as `:1` in `1:1`, `:00` in times, and `:Plan` after
|
||||
an adjacent colon. Heading attributes remain enabled.
|
||||
|
||||
Run `npm run test:markdown` after changing the Markdown/MDX configuration. It
|
||||
builds temporary fixtures and the affected content through the actual Astro
|
||||
configuration, checking ratios, punctuation, times, URLs, code, and explicit
|
||||
heading IDs. It makes no production requests and leaves no fixture routes in
|
||||
the site. Any future directive feature needs compatible rendering and must
|
||||
preserve these literal-content checks.
|
||||
|
||||
### Front Matter CMS
|
||||
|
||||
The project includes `frontmatter.json` for the Front Matter CMS VS Code
|
||||
extension. Open the `www` folder as the editor workspace so its content folders
|
||||
and media paths resolve correctly.
|
||||
|
||||
### Edit The Homepage
|
||||
|
||||
The English and Spanish homepages are driven from the structured `home:`
|
||||
frontmatter block in:
|
||||
|
||||
```txt
|
||||
src/content/pages/en/index.md
|
||||
src/content/pages/es/index.md
|
||||
```
|
||||
|
||||
`HomePage.astro` renders those named sections in order. Use the section names
|
||||
as the editing surface:
|
||||
|
||||
```txt
|
||||
hero
|
||||
servicesIntro
|
||||
benefits
|
||||
skills
|
||||
insurance
|
||||
esa
|
||||
hsaFsa (optional; English only where published)
|
||||
financialHelp
|
||||
process
|
||||
director
|
||||
testimonials
|
||||
logos (optional)
|
||||
```
|
||||
|
||||
Keep homepage copy in those named blocks so the page remains easy to scan in
|
||||
Front Matter CMS and VS Code. `src/content.config.ts` validates the structure.
|
||||
`hsaFsa` uses `heading`, `body`, `image`, and `imageAlt`; omit the whole block
|
||||
where no section is published. It renders between ESA and financial help.
|
||||
`logos.items` holds `file`/`alt` pairs after testimonials. Process steps accept
|
||||
an optional `href`; use an existing intake route and section ID in the same
|
||||
language. Steps without a destination remain plain content.
|
||||
|
||||
### Edit The Team Page
|
||||
|
||||
The English Team page uses the validated `team:` frontmatter block in
|
||||
`src/content/pages/en/team.md`. That block is the single source for the intro,
|
||||
ordered clinical groups, ABA care team, member names/roles/images, and careers
|
||||
content. The Markdown body contains no second roster. Update the structured
|
||||
frontmatter rather than hardcoding people in `TeamPage.astro`; image filenames
|
||||
refer to `public/assets/images`.
|
||||
|
||||
### Images And Downloads
|
||||
|
||||
Store content-referenced assets in:
|
||||
|
||||
```txt
|
||||
public/assets/images
|
||||
public/assets/downloads
|
||||
public/assets/media
|
||||
```
|
||||
|
||||
Reference public assets from Markdown using root-relative paths:
|
||||
|
||||
```md
|
||||

|
||||
```
|
||||
|
||||
Use `src/assets/images` for images imported directly by Astro components and
|
||||
processed by Astro.
|
||||
|
||||
### Components And Styling
|
||||
|
||||
Reusable page sections are in:
|
||||
|
||||
```txt
|
||||
src/components
|
||||
src/layouts
|
||||
```
|
||||
|
||||
Global styles and design tokens are in:
|
||||
|
||||
```txt
|
||||
src/styles/variables.css
|
||||
src/styles/global.css
|
||||
src/styles/rtl.css
|
||||
```
|
||||
|
||||
Navigation, services, redirects, and site details are in:
|
||||
|
||||
```txt
|
||||
src/data
|
||||
```
|
||||
|
||||
## Adding Content
|
||||
|
||||
### Add An English Blog Post
|
||||
|
||||
1. Create `src/content/blog/en/post-slug.md`.
|
||||
2. Add valid frontmatter and article Markdown.
|
||||
3. Place referenced images in `public/assets/images`.
|
||||
4. Run `npm run dev` and view `/library/post-slug`.
|
||||
5. Run the production validation commands before committing.
|
||||
|
||||
### Add A Translation
|
||||
|
||||
Use the same nonempty `translationKey` across genuine translated entries in
|
||||
the **same collection**. The key is an explicit equivalence declaration, not a
|
||||
URL or an entry reference. Slugs may differ; similar slugs alone never create a
|
||||
relationship. Do not change keys to manufacture translation pairs.
|
||||
|
||||
```txt
|
||||
src/content/blog/en/post-slug.md
|
||||
src/content/blog/es/post-slug.md
|
||||
src/content/blog/ar/post-slug.md
|
||||
```
|
||||
|
||||
The URLs become:
|
||||
|
||||
```txt
|
||||
/library/post-slug
|
||||
/es/library/post-slug
|
||||
/ar/library/post-slug
|
||||
```
|
||||
|
||||
Arabic routes automatically render with `lang="ar"` and `dir="rtl"`.
|
||||
|
||||
The shared publication manifest supplies both the language switcher and SEO
|
||||
alternates. A translation set needs at least two eligible members. Drafts,
|
||||
unpublished placeholders, noindex pages, and external-canonical pages do not
|
||||
participate. Mark placeholders `draft: true`; `/ar` remains unpublished.
|
||||
Author records have no generated routes yet and cannot participate.
|
||||
|
||||
One member per language per collection/key is allowed, including draft members;
|
||||
duplicates and blank/whitespace-padded keys fail validation. A lone key is valid
|
||||
and emits no alternate: it does not assert that a particular missing language
|
||||
exists. Dangling graph references, wrong-family targets, ineligible targets,
|
||||
and nonreciprocal links fail validation. The rendered-output regression checks
|
||||
also fail if an alternate target has no generated HTML file.
|
||||
|
||||
Indexing-enabled builds emit the same absolute hreflang links on every member,
|
||||
including its own language. `x-default` points to the eligible English member
|
||||
only; sets without one omit it. Staging suppresses hreflang while retaining the
|
||||
eligible language menu and global `noindex,nofollow`. Unpaired/excluded pages
|
||||
retain their current-language label without an empty interactive menu.
|
||||
|
||||
Run `npm run test:publication`, `npm run build`, and
|
||||
`PUBLIC_ALLOW_INDEXING=true npm run build` to validate both modes. Finish with
|
||||
`npm run build` to restore staging. See `reports/migration-summary.md` for
|
||||
Phase 3B's exact route sets, evidence, and route-audit refresh commands.
|
||||
|
||||
Do not publish unreviewed machine translations. Do not invent translations;
|
||||
substantive translation changes require human language review.
|
||||
|
||||
### Edit A Blog FAQ
|
||||
|
||||
Posts with live-style FAQ sections use `.mdx` and import:
|
||||
|
||||
```mdx
|
||||
import FAQAccordion from '../../../components/FAQAccordion.astro';
|
||||
```
|
||||
|
||||
Edit the component's `label`, `heading`, and `items` data in the post. Keep
|
||||
`answer` and `answerHtml` consistent. JSON-LD derives its answer text from the
|
||||
actual visible `answerHtml` when supplied, otherwise from the rendered `answer`
|
||||
fallback. Shared FAQ markup, styling, interaction, and schema generation remain
|
||||
in `src/components/FAQAccordion.astro`, using `src/utils/structured-data.ts`.
|
||||
|
||||
The standalone FAQ page passes its rendered Markdown/MDX slot as `sourceHtml`
|
||||
to the same component. Its server schema describes the direct H3 questions and
|
||||
following answer nodes that the existing browser code turns into accordions.
|
||||
Runtime-only sources without server content emit no invented FAQ schema. Do
|
||||
not mix `items` with `sourceSelector`. Duplicate questions, multiple schema
|
||||
producing accordions on one page, empty answers and conflicting canonical
|
||||
props fail validation; consolidate questions into one accordion per page.
|
||||
|
||||
### Article and FAQ Structured Data
|
||||
|
||||
Article JSON-LD uses the existing SEO `title`, description, featured image,
|
||||
language, `date`, optional `updatedDate`, and the exact locale/slug match in the
|
||||
authors collection. Dates use calendar-day precision; article/card bylines
|
||||
format that day in UTC to avoid shifting it on the build host. Missing
|
||||
modification dates are omitted. Missing or ambiguous author references fail validation.
|
||||
No author archive URL or publisher is invented. `displayH1` does not change
|
||||
the schema headline. No new frontmatter fields are required.
|
||||
|
||||
BlogPosting and FAQPage emission reuse the manifest's `sitemapEligible` record
|
||||
gate: drafts, noindex records and external-canonical records are excluded.
|
||||
This gate is independent of the build's global indexing switch, so staging
|
||||
can exercise schema while retaining its global noindex, empty sitemap and
|
||||
absent hreflang. The existing site organization block remains unchanged.
|
||||
|
||||
These schemas describe the rendered local records; they do not certify that
|
||||
older article dates or FAQ answers have been reconciled with production.
|
||||
Phase 6C drift observations and validation results are recorded in
|
||||
`reports/migration-summary.md`. Run `npm run test:seo` and
|
||||
`npm run test:publication`, and validate both build modes before publication.
|
||||
|
||||
## Reports And Utilities
|
||||
|
||||
For the current migration checkpoint, next Codex phase, ratified decisions,
|
||||
and outstanding cutover blockers, begin with the
|
||||
**[living session handoff](reports/SESSION_HANDOFF.md)**. Update that short
|
||||
handoff at each accepted milestone transition; detailed historical evidence
|
||||
remains in `reports/migration-summary.md`.
|
||||
|
||||
Migration reports are stored in `reports/`.
|
||||
|
||||
Useful commands:
|
||||
|
||||
```sh
|
||||
npm run crawl:live
|
||||
npm run generate:sitemap
|
||||
npm run generate:redirects
|
||||
npm run audit:links
|
||||
```
|
||||
|
||||
The live crawl is optional and may fail in restricted network environments.
|
||||
|
||||
## Forms
|
||||
|
||||
As of the owner decision on 2026-10-30, `npm run dev` and `npm run build`
|
||||
use live Jotform Form A on exactly 16 approved placements: 10 immediate frames
|
||||
and six deferred bilingual service drawers. No Form A flags are required.
|
||||
Public development-host forms are owner-authorized. This does not authorize cutover.
|
||||
|
||||
Use `PUBLIC_AIA_FORMS_MODE=off npm run build` for an offline artifact, or
|
||||
`PUBLIC_AIA_FORMS_MODE=mock npm run dev` for synthetic fixtures. Missing/empty
|
||||
mode means live; obsolete `isolated-test` and other invalid values produce an
|
||||
explicit configuration error. Remove old approval/environment variables from
|
||||
local settings. Indexing is independent of forms; counters remain off by default.
|
||||
|
||||
See the [current Form A guide](integrations/jotform/README.md) for the exact
|
||||
registry, safeguards, rollback and owner acceptance requirements. Form C remains
|
||||
inactive. Employee-portal private input retains its own separate gates.
|
||||
|
||||
Verify matching output with
|
||||
`node tools/phase10-artifact-verify.mjs --mode=staging --forms=live`;
|
||||
use `--mode=indexing` for indexing output and `--forms=off` or `--forms=mock`
|
||||
for those explicit builds. No actual submissions run in automated tests.
|
||||
|
||||
### Google Ads consultation-form conversion handoff
|
||||
|
||||
The Google Ads conversion ID, label, and privacy-gated Astro integration
|
||||
instructions are in
|
||||
[`reports/google-ads-consultation-conversion.md`](reports/google-ads-consultation-conversion.md).
|
||||
This is documentation only: **do not install Google tags or conversion snippets**
|
||||
on health-service pages or turn on enhanced conversions until AIA's
|
||||
privacy/HIPAA and advertising-policy review explicitly approves the complete
|
||||
data flow. Any future measurement needs separately approved success and privacy semantics; the former API-success premise is historical and no tracking is active.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### `astro: command not found`
|
||||
|
||||
Dependencies are not installed:
|
||||
|
||||
```sh
|
||||
npm install
|
||||
```
|
||||
|
||||
### npm TLS or certificate errors
|
||||
|
||||
Do not disable npm certificate verification. Run `npm install` from a normal
|
||||
host terminal or trusted development environment with working CA certificates.
|
||||
|
||||
### Link audit says build output is unavailable
|
||||
|
||||
Build first:
|
||||
|
||||
```sh
|
||||
npm run build
|
||||
npm run audit:links
|
||||
npm run audit:images
|
||||
npm run audit:blog
|
||||
```
|
||||
|
||||
### A content page is missing
|
||||
|
||||
Check:
|
||||
|
||||
- its Markdown file is in the correct language/content folder
|
||||
- `draft` is `false`
|
||||
- `slug` is unique within that language and content type
|
||||
- frontmatter satisfies `src/content.config.ts`
|
||||
@@ -0,0 +1,21 @@
|
||||
import { defineConfig } from 'astro/config';
|
||||
import mdx from '@astrojs/mdx';
|
||||
import { satteri } from '@astrojs/markdown-satteri';
|
||||
import site from './src/data/site.json' with { type: 'json' };
|
||||
|
||||
export default defineConfig({
|
||||
site: site.url,
|
||||
trailingSlash: 'never',
|
||||
compressHTML: true,
|
||||
integrations: [mdx()],
|
||||
markdown: {
|
||||
processor: satteri({
|
||||
features: {
|
||||
headingAttributes: true,
|
||||
// No directive consumers: parsing them drops literal ratios and times.
|
||||
directive: false
|
||||
}
|
||||
}),
|
||||
shikiConfig: { theme: 'github-light' }
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,61 @@
|
||||
{
|
||||
"$schema": "https://frontmatter.codes/frontmatter.schema.json",
|
||||
"frontMatter.framework.id": "astro",
|
||||
"frontMatter.content.publicFolder": "public",
|
||||
"frontMatter.content.pageFolders": [
|
||||
{ "title": "Pages", "path": "[[workspace]]/www/src/content/pages" },
|
||||
{ "title": "Blog", "path": "[[workspace]]/www/src/content/blog" },
|
||||
{ "title": "Authors", "path": "[[workspace]]/www/src/content/authors" }
|
||||
],
|
||||
"frontMatter.taxonomy.contentTypes": [
|
||||
{
|
||||
"name": "page",
|
||||
"pageBundle": false,
|
||||
"fields": [
|
||||
{ "title": "Title", "name": "title", "type": "string", "required": true },
|
||||
{ "title": "Display H1", "name": "displayH1", "type": "string" },
|
||||
{ "title": "Description", "name": "description", "type": "string" },
|
||||
{ "title": "Slug", "name": "slug", "type": "string", "required": true },
|
||||
{ "title": "Language", "name": "lang", "type": "choice", "choices": ["en", "ar", "es"] },
|
||||
{ "title": "Canonical", "name": "canonical", "type": "string" },
|
||||
{ "title": "Featured image", "name": "featuredImage", "type": "image" },
|
||||
{ "title": "Alt text", "name": "alt", "type": "string" },
|
||||
{ "title": "Translation key", "name": "translationKey", "type": "string" },
|
||||
{ "title": "Draft", "name": "draft", "type": "boolean", "default": false }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "blog",
|
||||
"pageBundle": false,
|
||||
"fields": [
|
||||
{ "title": "Title", "name": "title", "type": "string", "required": true },
|
||||
{ "title": "Display H1", "name": "displayH1", "type": "string" },
|
||||
{ "title": "Description", "name": "description", "type": "string" },
|
||||
{ "title": "Slug", "name": "slug", "type": "string", "required": true },
|
||||
{ "title": "Date", "name": "date", "type": "datetime", "required": true },
|
||||
{ "title": "Author", "name": "author", "type": "string", "default": "rula-diab" },
|
||||
{ "title": "Category", "name": "category", "type": "string" },
|
||||
{ "title": "Tags", "name": "tags", "type": "tags" },
|
||||
{ "title": "Language", "name": "lang", "type": "choice", "choices": ["en", "ar", "es"] },
|
||||
{ "title": "Featured image", "name": "featuredImage", "type": "image" },
|
||||
{ "title": "Alt text", "name": "alt", "type": "string" },
|
||||
{ "title": "Canonical", "name": "canonical", "type": "string" },
|
||||
{ "title": "Translation key", "name": "translationKey", "type": "string" },
|
||||
{ "title": "Draft", "name": "draft", "type": "boolean", "default": false }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "author",
|
||||
"pageBundle": false,
|
||||
"fields": [
|
||||
{ "title": "Name", "name": "name", "type": "string", "required": true },
|
||||
{ "title": "Display name (published heading/byline)", "name": "displayName", "type": "string" },
|
||||
{ "title": "Slug", "name": "slug", "type": "string", "required": true },
|
||||
{ "title": "Description", "name": "description", "type": "string" },
|
||||
{ "title": "Avatar", "name": "avatar", "type": "image" },
|
||||
{ "title": "Language", "name": "lang", "type": "choice", "choices": ["en", "ar", "es"] },
|
||||
{ "title": "Translation key", "name": "translationKey", "type": "string" }
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,144 @@
|
||||
# Employee portal compatibility and private migration
|
||||
|
||||
Recorded 2026-10-30. Browser-side shared-code decryption is approved only for
|
||||
local compatibility development. It is not authentication, individual revocation,
|
||||
a secure session or a HIPAA compliance determination. Resources need separate
|
||||
server authorization before cutover.
|
||||
|
||||
## Source and private collection
|
||||
|
||||
Read-only legacy footer source:
|
||||
`/home/alice/projects/wrk/aia/local-archives/aia-employee-portal-hubspot-footer-source.txt`.
|
||||
The original stays outside Git as rollback evidence. No access code was available
|
||||
to the agent. The exact resource count and real content compatibility remain
|
||||
unverified. Do not infer the earlier approximate 43 links as a measured count.
|
||||
|
||||
From `www`, in the owner's private interactive terminal:
|
||||
|
||||
```sh
|
||||
node tools/employee-portal-migrate.mjs collect \
|
||||
/home/alice/projects/wrk/aia/local-archives/aia-employee-portal-hubspot-footer-source.txt
|
||||
```
|
||||
|
||||
Enter each legitimate view code at its hidden prompt. Never place codes in
|
||||
arguments, environment variables, shell history or reports. The utility checks
|
||||
both streams, keeps plaintext in memory, and collects only referenced `/hubfs/`
|
||||
resources from the verified source origin. Other origins require explicit
|
||||
review/code allowlisting; redirects are bounded and never inherit credentials.
|
||||
No Jotform, Google or other vendor forms are fetched.
|
||||
|
||||
Private output is `private/employee-portal/`: directories 0700, manifests and
|
||||
binary resources 0600. This exact subtree is gitignored and outside Astro's
|
||||
public directory. Filenames are SHA-256 hashes of source URLs, independently
|
||||
verified by content hashes. The manifest retains original references, labels,
|
||||
view membership, MIME, bytes, status and delivery/classification decisions.
|
||||
All new resources default to UNRESOLVED. Publicly reachable does not mean public
|
||||
approved. Resuming verifies an existing file's content hash; mismatches are not
|
||||
overwritten. HTML error documents, executable responses, invalid known signatures,
|
||||
redirects outside approved destinations and resources over 50 MiB are rejected.
|
||||
The first pass allows only the verified site origin. If a legitimate `/hubfs/`
|
||||
response redirects to a HubSpot CDN, inspect that exact HTTPS origin first, then
|
||||
add its origin to the private manifest's `approvedOrigins` list and rerun. Never
|
||||
approve a redirect based only on a failed download or a broadly shared vendor
|
||||
domain. Failures remain private review items. The utility prints counts only.
|
||||
|
||||
Before every artifact transfer, verify that only `dist` is copied and that it
|
||||
contains no private manifest, document or real ciphertext unless that ciphertext
|
||||
has separately been authorized for the local preview. Never archive the whole
|
||||
working directory as a deployable artifact.
|
||||
|
||||
## Approved rewriting
|
||||
|
||||
The owner classifies records as PUBLIC-APPROVED, STAFF-INTERNAL,
|
||||
MANAGEMENT-INTERNAL or UNRESOLVED in the private manifest. Set `deliveryPath`,
|
||||
`rewriteApproved`, `destinationImplemented`, `destinationVerified`, and (for
|
||||
internal documents) `destinationAccessControlled` only with actual evidence.
|
||||
The utility does not create those destinations or infer their approval.
|
||||
|
||||
```sh
|
||||
node tools/employee-portal-migrate.mjs rewrite \
|
||||
/home/alice/projects/wrk/aia/local-archives/aia-employee-portal-hubspot-footer-source.txt
|
||||
```
|
||||
|
||||
It reauthenticates both views, changes approved quoted href/src attributes only,
|
||||
preserves unrelated HTML/labels, and re-encrypts with fresh salts and IVs. It
|
||||
verifies round-trip decryption and distinct codes before writing new ciphertext
|
||||
with exclusive filenames. The original source is untouched. CSS/srcset mappings
|
||||
require separate review and are rejected rather than silently rewritten.
|
||||
Unresolved links retain their original destination as a recorded dependency,
|
||||
not a claim of verified destination health. A changed manifest alone never
|
||||
changes links in the encrypted stream.
|
||||
|
||||
## Local preview and content safety
|
||||
|
||||
Use a private ciphertext file generated by `collect` or `rewrite`:
|
||||
|
||||
```sh
|
||||
AIA_PORTAL_LOCAL_PREVIEW_APPROVED=true \
|
||||
AIA_PORTAL_CIPHERTEXT_FILE=/absolute/private/ciphertext-TIMESTAMP.json \
|
||||
PUBLIC_AIA_FORMS_MODE=off PUBLIC_ALLOW_INDEXING=false \
|
||||
npm run dev -- --host 127.0.0.1
|
||||
```
|
||||
|
||||
The 2026-10-30 revision removes dependence on the retired Form A environment role. Form mode is independent; `off` above is optional for local testing and cannot grant portal approval. The page defaults to locked/unavailable without its own explicit approval and valid private input. Indexing
|
||||
builds cannot include it. The codes never go to a server, storage or URL. No
|
||||
native form submits; Enter and Unlock use local WebCrypto. Only the matching
|
||||
stream is displayed; refresh or leaving the page locks it again.
|
||||
|
||||
The passive DOM renderer preserves headings, lists, text, tables and safe
|
||||
HTTPS/mailto/tel links, with no referrer on links. It rejects scripts, inline
|
||||
handlers, styles, media, embedded forms and unknown tags. Real portal markup
|
||||
has not been decrypted or reviewed; any removed behavior must be reviewed with
|
||||
the owner before claiming fidelity. It must not silently become arbitrary
|
||||
script execution. No real encrypted stream or plaintext is in Git.
|
||||
|
||||
### Scoped presentation correction, 2026-10-30
|
||||
|
||||
The renderer now maps only `hs-button`, `content-button`, or
|
||||
`portal-resource-button` anchor tokens, or exact inline AIA orange background
|
||||
colors (`#ffbd59` / `#ff914d`), to the local `portal-resource-button` class.
|
||||
It does not copy the original style or arbitrary classes. These are narrow
|
||||
compatibility candidates, not a claim that encrypted real views have been
|
||||
inspected. HTTPS/mailto/tel links retain the existing safe URL checks; contact
|
||||
links receive a local dark-orange accent for readable contrast. Table scope
|
||||
and bounded numeric row/column spans are preserved. All other executable or
|
||||
unapproved attributes remain removed.
|
||||
|
||||
The follow-up styling correction recognizes the live portal's `Daily Lineup
|
||||
Report` and `Monthly Lineup Report` link labels, even when their original
|
||||
HubSpot classes or inline fills are absent. It maps only those links to the
|
||||
existing orange resource-button style; arbitrary anchors remain ordinary links.
|
||||
Both unlocked views now use the site's orange accent for list-item markers.
|
||||
The source classes and styles still do not reach the passive DOM.
|
||||
|
||||
### Staff resources and Management Templates, 2026-11-01
|
||||
|
||||
The owner observed that the non-management (general-staff) portal still lost
|
||||
HubSpot's resource-button presentation. After **sanitizing** the decrypted DOM,
|
||||
a headed `Management Templates` section identifies the general-staff resource
|
||||
view. Valid HTTP(S) resource links in that view receive only the local
|
||||
`portal-resource-button` class; `mailto:` and `tel:` retain contact-link
|
||||
styling. Neither destination validation nor removed legacy classes/styles,
|
||||
scripts, media or HTML attributes are relaxed.
|
||||
|
||||
The `Management Templates` unordered list receives a local
|
||||
`portal-template-grid` class: two columns at desktop widths (768px+),
|
||||
one column on smaller screens. Semantic list order and keyboard tab order
|
||||
are retained, with responsive full-width buttons. This is a presentation
|
||||
change only: it does not infer or approve employee resource access.
|
||||
Synthetic staff/management browser fixtures check the distinction and
|
||||
responsive columns. The actual decrypted staff source remains private;
|
||||
owner should confirm layout and links in the local preview before merging.
|
||||
|
||||
Scoped typography, spacing, lists, table rules and keyboard focus apply to both
|
||||
unlocked streams. Synthetic staff/management fixtures exercise these mappings
|
||||
at 320/390/768/1440px. Owner must inspect the real views privately to confirm
|
||||
legacy button recognition and source fidelity; if a different class is used,
|
||||
provide sanitized class semantics rather than decrypted content or credentials.
|
||||
|
||||
Before owner acceptance: validate two real view codes privately, inspect every
|
||||
resource/category, responsive directories, focus, link semantics and sanitization
|
||||
impact. Check wrong-code feedback, keyboard unlock, no-JS explanation and refresh.
|
||||
Production must use a separately reviewed runtime ciphertext delivery mechanism;
|
||||
this batch intentionally provides only a gated local build input. Browser-delivered
|
||||
ciphertext is publicly downloadable by anyone able to reach that preview.
|
||||
@@ -0,0 +1,144 @@
|
||||
# AIA Form A Jotform styling and conditional-button guide
|
||||
|
||||
This directory contains the accepted CSS backup and integration guidance for AIA Form A. [`aia-form-a.css`](./aia-form-a.css) is intended for Jotform Classic Forms' **Form Designer → Styles → Inject Custom CSS** under owner-controlled configuration review. The existing repository CSS is the accepted backup; do not regenerate it from Astro styles.
|
||||
|
||||
The CSS translates the existing AIA form treatment: Rubik typography, blue labels and text, `#fcf9f5` inputs with `#edeef3` borders, the `#ffbd59` filled button, the `#ff914d` hover/focus border, four-pixel corners, visible validation errors, and strong keyboard focus. The Astro page supplies the heading, introduction, privacy-policy link, and surrounding card. The stylesheet therefore removes the Jotform surface rather than adding another header, footer, or nested card.
|
||||
|
||||
The selectors target Jotform Classic Forms identifiers such as `#form`, `.form-all`, `.form-line`, `.form-label`, `.form-sub-label`, `.form-required`, `.form-textbox`, `.form-dropdown`, `.form-textarea`, `.form-error-message`, and `.form-submit-button`. Jotform's final generated DOM must be checked in the account before relying on pixel-level parity. The stylesheet does not hide required indicators, error messages, honeypots, or focus outlines, and it does not request fonts or other external assets.
|
||||
|
||||
The current Builder theme reconciliation adds narrowly scoped `!important` declarations to the outer surface, labels, inputs, and submit-button treatments that Jotform's generated theme otherwise overrides. Error, required-indicator, and focus rules remain visible and are deliberately not reset. The account owner must still confirm the final generated DOM and rendered form after any Jotform theme or field change.
|
||||
|
||||
## Installation and comparison
|
||||
|
||||
1. Open the approved Form A in AIA's HIPAA-enabled Jotform account. Do not change its fields or settings from this repository workflow.
|
||||
2. Preserve the approved account configuration; account changes and site cutover require their separate authorization.
|
||||
3. Paste the contents of `aia-form-a.css` into Jotform's custom CSS area.
|
||||
4. Compare the Jotform preview with the Astro Form A mock at 390px and 1440px. Check the form surface, label rhythm, input height, button fill, outlined hover/focus state, error state, and mobile reflow.
|
||||
5. Check both English and Spanish configurations. Confirm translated labels, required indicators, validation messages, confirmation copy, and the accessible iframe title supplied by the parent Astro integration.
|
||||
6. Test the form in a real embedded iframe and on the direct hosted-form fallback. Confirm the iframe has a practical mobile height, does not create horizontal overflow, and does not trap keyboard focus.
|
||||
|
||||
The optional Astro meeting preview requires `PUBLIC_AIA_FORMS_MODE=mock` and `PUBLIC_AIA_FORM_A_PREVIEW=true` in a non-indexing build. It does not load Jotform and is not a live submission test.
|
||||
|
||||
## Current Form A operation — superseding decision, 2026-10-30
|
||||
|
||||
The owner authorizes real Form A on the publicly reachable development host
|
||||
`https://aia.web3app.dev/`. Normal development and static builds use **live** Form A
|
||||
without environment flags. This intentionally supersedes the former isolated-test
|
||||
and seven-route production approval gates. It is not site-cutover authorization.
|
||||
|
||||
From `www/`:
|
||||
|
||||
```sh
|
||||
npm run dev
|
||||
npm run build
|
||||
# Optional offline/restricted artifact:
|
||||
PUBLIC_AIA_FORMS_MODE=off npm run build
|
||||
# Optional synthetic fixtures:
|
||||
PUBLIC_AIA_FORMS_MODE=mock npm run dev
|
||||
```
|
||||
|
||||
`PUBLIC_AIA_FORMS_MODE` is optional; missing/empty means `live`. The only accepted
|
||||
values are `live`, `off`, and `mock`. Remove obsolete `isolated-test` settings from
|
||||
local environment files; invalid explicit values stop the build with actionable
|
||||
guidance. The former live/isolated/consultation/production approvals and form
|
||||
environment role are removed, with no replacement gates. `PUBLIC_ALLOW_INDEXING`
|
||||
controls publication independently: both staging and indexing builds use the same
|
||||
16 placements. Explicit off/mock builds contain no real Form A iframe.
|
||||
|
||||
One typed registry in `src/utils/form-integration.ts` fixes every route, locale
|
||||
and context. No visitor query, referrer, arbitrary URL or option preselection is
|
||||
forwarded. The only origin/ID is `https://form.jotform.com/262946078314059`.
|
||||
Every URL has exactly `formContext` and `language` parameters:
|
||||
|
||||
| Context | Routes | Loading |
|
||||
|---|---|---|
|
||||
| contact | `/contact` (en), `/es/contact` (es) | Immediate, 2 |
|
||||
| landing | `/lp/aba-therapy`, `/lp/aba-insurance`, `/lp/aba-ahcccs`, `/lp/aba-private-pay`, `/lp/early-intervention` (en) | Immediate, 5 |
|
||||
| consultation | `/client-consultation` (en), `/es/client-consultation` (es), `/schedule-consultation` (en) | Immediate, 3 |
|
||||
| consultation | `/aba-therapy`, `/autism-evaluations`, `/learner-social-club` (en), and their `/es/` counterparts (es) | Deferred, 6 |
|
||||
|
||||
The context is presentation-only. Physical Form B is retired. Form C and
|
||||
`/referrals` remain inactive; other routes cannot acquire Form A by setting a mode.
|
||||
The typed future Academy service copy does not publish another page.
|
||||
|
||||
Live service drawers open at four seconds OR 50% scroll, canceling the unused
|
||||
trigger. Dismissal or verified completion sets the existing shared 30-day
|
||||
suppression timestamp. The existing service CTA overrides automatic suppression;
|
||||
without JavaScript it remains a normal consultation-page link. Jotform loads only
|
||||
when the drawer opens. Off mode never automatically opens an inactive drawer.
|
||||
Mock mode retains the existing synthetic fixture workflow.
|
||||
|
||||
A verified `submission-completed` message must match the exact approved origin,
|
||||
actual activated/open iframe window, approved form ID and action. It displays a
|
||||
localized acknowledgment, waits five seconds, then uses the existing slide-out.
|
||||
Duplicate messages cannot restart the countdown. Manual close/navigation cancels
|
||||
it; suppression remains. Deliberate reopening after completion creates a fresh
|
||||
iframe. Provider-envelope acceptance still requires the owner-controlled real
|
||||
submission check; synthetic browser messages are not that acceptance.
|
||||
|
||||
The shared iframe preserves fixed responsive heights, accessible EN/ES titles,
|
||||
`referrerpolicy="no-referrer"`, and
|
||||
`sandbox="allow-forms allow-scripts allow-same-origin"`. No popup, top-navigation,
|
||||
camera, microphone, payment or geolocation permissions are added. The hosted
|
||||
fallback is outside the iframe and hidden without layout space until a 15-second
|
||||
missing-load timeout. Late load hides it; disposal cancels timers. A localized
|
||||
`noscript` fallback remains. Load does not prove application health or submission.
|
||||
No helper script, cross-origin DOM inspection, arbitrary messaging or submission
|
||||
forwarding is introduced. The official resize handler remains unimplemented and
|
||||
conditionally acceptable only after its separate security/browser review.
|
||||
|
||||
Owner-reported direct EN/ES synthetic acceptance and sampled embedded acceptance
|
||||
(English contact, Spanish contact and one ABA landing page) remain recorded as
|
||||
owner evidence. This change does not independently verify Jotform or resolve its
|
||||
Spanish consultation dropdown discrepancy, runtime telemetry, fluent-language,
|
||||
screen-reader, final HTTPS-host or operational recipient review.
|
||||
|
||||
For form rollback, explicitly set `PUBLIC_AIA_FORMS_MODE=off`, rebuild, verify
|
||||
`--forms=off`, and use the separately authorized artifact-switch procedure.
|
||||
Removing that override restores live, so removing settings is **not** rollback.
|
||||
Do not change the Jotform account as part of an Astro rollback.
|
||||
|
||||
Artifact checks (build matching output first):
|
||||
|
||||
```sh
|
||||
node tools/phase10-artifact-verify.mjs --mode=staging --forms=live
|
||||
# For PUBLIC_ALLOW_INDEXING=true output:
|
||||
node tools/phase10-artifact-verify.mjs --mode=indexing --forms=live
|
||||
# For explicit off or mock output, select the matching --forms value.
|
||||
```
|
||||
|
||||
Employee-portal ciphertext remains separately gated by its own approval, private
|
||||
input and non-indexing preview requirement. Form A modes cannot authorize it.
|
||||
|
||||
## Form context and button labels
|
||||
|
||||
The current Form A design uses a hidden `formContext` value with fixed, approved values such as `contact` and `landing`. Jotform does not provide a native conditional rule that changes one Submit button's accessible label safely in every context. If owner-approved context-specific labels are required, use multiple native Jotform Submit buttons and Jotform's conditional visibility rules:
|
||||
|
||||
- Add one native Submit button for each approved context/language label.
|
||||
- Show exactly one intended Submit button for the current, validated `formContext`.
|
||||
- Keep every button a real Jotform Submit control; do not use CSS `::before`/`::after` text as a label.
|
||||
- Verify that every visible variant submits through the same approved form and preserves the honeypot and validation behavior.
|
||||
- Make the no-context and invalid-context states fail closed: no ambiguous extra button and no unintended submission path.
|
||||
- Verify keyboard order, focus visibility, screen-reader names, English/Spanish labels, and mobile layout for every variant.
|
||||
|
||||
All five landing routes use Form A's fixed `landing` context. The nine consultation
|
||||
placements use its fixed `consultation` context. Jotform owns native Submit labels
|
||||
and conditions. Do not add an external Astro button that attempts to submit a
|
||||
cross-origin iframe.
|
||||
|
||||
## Verification checklist
|
||||
|
||||
- [x] Direct English/Spanish Form A language handling, conditional email requirement, synthetic submission and native thank-you were owner-verified; this is not embedded-iframe acceptance.
|
||||
- [x] Direct staff-notification/autoresponder delivery, generic no-answer/no-PDF content, fixed Reply-To, recipient access/retention, and absent webhooks/integrations were owner-verified for Form A.
|
||||
- [ ] Exact remaining Form A consultation question properties, choices, conditional routing and availability, plus future Form C configuration, are approved.
|
||||
- [ ] English and Spanish embedded-iframe labels, validation, confirmation, and button variants are reviewed by fluent-language owners.
|
||||
- [ ] The hidden honeypot remains present, effective, and excluded from user-facing visual changes.
|
||||
- [ ] Exactly one native Submit button is visible for every valid context.
|
||||
- [ ] Invalid or missing context does not expose an unintended submission path.
|
||||
- [ ] No PHI-bearing answer, PDF, recipient, redirect, webhook, or HTTP POST forwarding is inherited without privacy/security approval.
|
||||
- [x] Owner-reported embedded synthetic submission and confirmation passed on English contact, Spanish contact and one ABA landing placement; record the exact ABA path in the owner test log before release.
|
||||
- [ ] All sixteen placements have final responsive, keyboard, screen-reader and fluent-language acceptance, including timeout/fallback and error expansion.
|
||||
- [ ] Jotform runtime requests, telemetry and cross-origin failure behavior are reviewed with privacy/security owners.
|
||||
- [ ] No Jotform, Contabo, analytics, advertising, or production API request is made by off/mock builds.
|
||||
|
||||
Existing owner-verification scopes remain recorded in the contract/checklist. Outstanding consultation configuration acceptance, runtime/privacy, accessibility, fluent-language and site-release gates remain open. Default-live Form A is owner-authorized; this guide does not authorize Jotform account changes or site deployment.
|
||||
@@ -0,0 +1,252 @@
|
||||
/* ==========================================================
|
||||
AIA FORM A — JOTFORM CUSTOM CSS
|
||||
Form ID: 262946078314059
|
||||
Maximum form width: 640px
|
||||
========================================================== */
|
||||
|
||||
/* Document and language selector */
|
||||
|
||||
html.supernova,
|
||||
html.supernova body,
|
||||
body.supernova {
|
||||
background: #fff !important;
|
||||
background-image: none !important;
|
||||
}
|
||||
|
||||
/* Avoid horizontal overflow, including on the thank-you page */
|
||||
body {
|
||||
margin: 0 !important;
|
||||
}
|
||||
|
||||
/* Confirmation page styling */
|
||||
body.thankyou,
|
||||
body.thankyou #stage {
|
||||
background: #fff !important;
|
||||
background-image: none !important;
|
||||
}
|
||||
|
||||
.jfLangDd.jfLangDd--match-form {
|
||||
padding: 12px 0 !important;
|
||||
}
|
||||
|
||||
/* Form container */
|
||||
|
||||
form[id="262946078314059"] {
|
||||
width: 100% !important;
|
||||
max-width: 640px !important;
|
||||
margin: 8px auto !important;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.form-all {
|
||||
width: 100% !important;
|
||||
max-width: 100% !important;
|
||||
margin: 0 !important;
|
||||
padding: 0 !important;
|
||||
background: #fff !important;
|
||||
border: 0 !important;
|
||||
box-shadow: none !important;
|
||||
color: #254080;
|
||||
font-family: Rubik, Arial, sans-serif;
|
||||
}
|
||||
|
||||
.form-all .form-section {
|
||||
margin: 0 !important;
|
||||
padding: 0 !important;
|
||||
}
|
||||
|
||||
/* Field layout */
|
||||
|
||||
.form-all .form-line {
|
||||
margin: 0 !important;
|
||||
padding: 0 0 21px !important;
|
||||
background: transparent !important;
|
||||
border: 0 !important;
|
||||
}
|
||||
|
||||
.form-all .form-input-wide {
|
||||
margin-top: 0 !important;
|
||||
padding-top: 0 !important;
|
||||
}
|
||||
|
||||
/* Labels and sublabels */
|
||||
|
||||
.form-all .form-label,
|
||||
.form-all .form-label-left,
|
||||
.form-all .form-label-right,
|
||||
.form-all .form-label-top {
|
||||
color: #254080 !important;
|
||||
font-size: .875rem;
|
||||
font-weight: 700;
|
||||
line-height: 1.35 !important;
|
||||
margin: 0 0 6px !important;
|
||||
padding: 0 !important;
|
||||
}
|
||||
|
||||
.form-all .form-sub-label {
|
||||
color: #254080;
|
||||
font-size: .75rem;
|
||||
line-height: 1.3 !important;
|
||||
margin: 4px 0 0 !important;
|
||||
padding: 0 !important;
|
||||
}
|
||||
|
||||
.form-all .form-sub-label-container {
|
||||
margin-top: 0 !important;
|
||||
margin-bottom: 0 !important;
|
||||
}
|
||||
|
||||
.form-all .form-required {
|
||||
color: #a21c28 !important;
|
||||
margin-left: .2rem;
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
/* Inputs */
|
||||
|
||||
.form-all input.form-textbox,
|
||||
.form-all select.form-dropdown,
|
||||
.form-all textarea.form-textarea {
|
||||
background: #fcf9f5 !important;
|
||||
background-image: none !important;
|
||||
border: 1px solid #edeef3 !important;
|
||||
border-radius: 4px !important;
|
||||
box-shadow: none !important;
|
||||
box-sizing: border-box;
|
||||
color: #254080 !important;
|
||||
font-family: inherit;
|
||||
font-size: 1rem;
|
||||
line-height: 1.4;
|
||||
min-height: 2.75rem;
|
||||
padding: .55rem .7rem;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
/* Preserve two-column name fields */
|
||||
|
||||
.form-all .form-sub-label-container input.form-textbox {
|
||||
width: 100% !important;
|
||||
max-width: 100% !important;
|
||||
}
|
||||
|
||||
/* Full-width phone and email fields */
|
||||
|
||||
.form-all
|
||||
.form-line:is(
|
||||
[data-type="control_phone"],
|
||||
[data-type="control_email"]
|
||||
)
|
||||
:is(.form-input, .form-input-wide, input.form-textbox) {
|
||||
width: 100% !important;
|
||||
max-width: 100% !important;
|
||||
}
|
||||
|
||||
/* Message field */
|
||||
|
||||
.form-all textarea.form-textarea {
|
||||
width: 100% !important;
|
||||
min-height: 5.5rem !important;
|
||||
resize: vertical;
|
||||
}
|
||||
|
||||
/* Radio buttons and checkboxes */
|
||||
|
||||
.form-all .form-radio-item,
|
||||
.form-all .form-checkbox-item {
|
||||
color: #254080;
|
||||
line-height: 1.4;
|
||||
margin: 0 .75rem .2rem 0 !important;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.form-all .form-radio,
|
||||
.form-all .form-checkbox {
|
||||
accent-color: #254080;
|
||||
}
|
||||
|
||||
/* Submit button and wrapper */
|
||||
|
||||
.form-all .jf-form-buttons,
|
||||
.form-all .form-buttons-wrapper {
|
||||
box-sizing: border-box;
|
||||
width: 100% !important;
|
||||
max-width: 100% !important;
|
||||
margin: 0 !important;
|
||||
padding: 0 !important;
|
||||
background: transparent !important;
|
||||
border: 0 !important;
|
||||
}
|
||||
|
||||
.form-all .form-submit-button,
|
||||
.form-all button[type="submit"] {
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
min-height: 3rem;
|
||||
padding: .65rem 1.2rem;
|
||||
background: #ffbd59 !important;
|
||||
background-image: none !important;
|
||||
border: 2px solid #ffbd59 !important;
|
||||
border-radius: 4px !important;
|
||||
box-shadow: none !important;
|
||||
color: #254080 !important;
|
||||
cursor: pointer;
|
||||
font-family: inherit;
|
||||
font-size: 1rem;
|
||||
font-weight: 400;
|
||||
line-height: 1.4;
|
||||
text-shadow: none !important;
|
||||
transition: background .3s linear, border-color .3s linear;
|
||||
}
|
||||
|
||||
.form-all .form-submit-button:hover,
|
||||
.form-all button[type="submit"]:hover {
|
||||
background: transparent !important;
|
||||
border-color: #ff914d !important;
|
||||
color: #254080 !important;
|
||||
}
|
||||
|
||||
.form-all .form-submit-button:disabled,
|
||||
.form-all button[type="submit"]:disabled {
|
||||
cursor: not-allowed;
|
||||
opacity: .65;
|
||||
}
|
||||
|
||||
/* Keyboard focus */
|
||||
|
||||
.form-all input.form-textbox:focus-visible,
|
||||
.form-all select.form-dropdown:focus-visible,
|
||||
.form-all textarea.form-textarea:focus-visible,
|
||||
.form-all input[type="radio"]:focus-visible,
|
||||
.form-all input[type="checkbox"]:focus-visible,
|
||||
.form-all .form-submit-button:focus-visible {
|
||||
outline: 3px solid #115bbd !important;
|
||||
outline-offset: -3px;
|
||||
}
|
||||
|
||||
/* Validation */
|
||||
|
||||
.form-all .form-line-error input.form-textbox,
|
||||
.form-all .form-line-error select.form-dropdown,
|
||||
.form-all .form-line-error textarea.form-textarea,
|
||||
.form-all input.form-validation-error {
|
||||
border-color: #a21c28 !important;
|
||||
}
|
||||
|
||||
.form-all .form-error-message,
|
||||
.form-all .form-line-error .form-error-message {
|
||||
color: #a21c28 !important;
|
||||
font-size: .8rem;
|
||||
font-weight: 700;
|
||||
line-height: 1.4;
|
||||
margin: .3rem 0 0 !important;
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
/* Accessibility: reduced motion */
|
||||
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
.form-all .form-submit-button,
|
||||
.form-all button[type="submit"] {
|
||||
transition: none;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
# Inactive SOPS/age preparation, 2026-10-30
|
||||
|
||||
The owner selected SOPS, age and sops-nix. This directory is a template, not a
|
||||
host configuration. The development flake remains unchanged. No real recipient,
|
||||
key, encrypted secret or server user is supplied. No module is deployed.
|
||||
|
||||
Official references inspected for this pass:
|
||||
- [SOPS documentation](https://getsops.io/docs/)
|
||||
- [sops-nix installation and secret permissions](https://github.com/Mic92/sops-nix)
|
||||
|
||||
SOPS encrypts configuration at rest. sops-nix can supply decrypted values at
|
||||
runtime under `/run/secrets` with explicit owners and modes. This does not make
|
||||
browser ciphertext or publicly served documents private.
|
||||
|
||||
## Owner setup before activation
|
||||
|
||||
1. Supply the actual NixOS topology, host flake, deploy user, persisted key
|
||||
location, backup owner, HTTPS termination and resource-service identity.
|
||||
2. Pin a reviewed `github:Mic92/sops-nix` input in that host flake; import
|
||||
`inputs.sops-nix.nixosModules.sops` and the reviewed template. Do not add the
|
||||
input to the Astro development flake just to evaluate a deployment sketch.
|
||||
3. The owner generates independent developer/recovery and VPS age identities in
|
||||
private terminals. Record only public recipients in the reviewed SOPS
|
||||
creation rule. Use distinct recipients, never a placeholder recipient. Keep
|
||||
the VPS private identity on persistent root-controlled storage (0600), outside
|
||||
`/nix/store`, Git, the public static root and CI artifacts.
|
||||
4. Encrypt the approved credential material using SOPS with both recipients.
|
||||
Review recipient access, least privilege and an offline recovery copy. SOPS
|
||||
encrypted files may enter a deployment repository only after owner approval.
|
||||
5. Set `aia.portalSecrets` options explicitly, then evaluate the complete host
|
||||
configuration and test decryption on authorized nonproduction infrastructure.
|
||||
Runtime consumers use the generated `.path`, not Nix evaluation of plaintext.
|
||||
6. Decide which service reloads after rotation. Rotate application credentials
|
||||
as well as SOPS recipients when revoking access: rewrapping alone does not
|
||||
invalidate an already copied password. Test recovery and key restoration.
|
||||
|
||||
## Separate staff and management resource authorization
|
||||
|
||||
Keep original documents outside Astro `public`, `dist`, release archives and
|
||||
CDN roots. Proposed delivery namespaces are `/staff-resources/` and
|
||||
`/management-resources/`; neither is implemented or considered working here.
|
||||
Use nginx `auth_request` to an approved authorization service or an equivalent
|
||||
server-enforced policy with individually revocable identities and staff and
|
||||
management roles. Staff credentials must not authorize management resources;
|
||||
management access to staff resources must be an explicit policy. Backend file
|
||||
locations must be internal, inaccessible through static aliases or alternate
|
||||
paths. Never trust a browser-provided role header.
|
||||
|
||||
A separate shared Basic Auth credential file per namespace can be considered
|
||||
only as an explicitly approved weaker interim model, not identity management or
|
||||
automatic role inheritance. The template prepares separate secret inputs without
|
||||
selecting that access model or installing an identity provider.
|
||||
|
||||
Use HTTPS, `Cache-Control: private, no-store`, no shared/CDN caching, and
|
||||
`Referrer-Policy: no-referrer` for protected delivery. Avoid credentials and
|
||||
sensitive resource paths in access/error logs; review monitoring and backups.
|
||||
Authorization failures must fail closed, including at proxy/cache boundaries.
|
||||
Test direct requests, alternate encodings, role crossover and rollback.
|
||||
|
||||
## Distinct boundaries and release checks
|
||||
|
||||
- SOPS: deployment secrets at rest and controlled runtime access.
|
||||
- Portal HTML: shared-code browser decryption, offline-guessable access friction.
|
||||
- Documents: require server authorization even if URLs are encrypted in the UI.
|
||||
|
||||
The compatibility preview cannot grant production portal access. Before cutover,
|
||||
classify every document, implement and verify its delivery destination, rewrite
|
||||
and re-encrypt links, test two-view separation, rotate shared codes privately,
|
||||
check the public artifact for private files, and obtain owner/security approval.
|
||||
Back up encrypted configuration and documents separately from keys; rehearse
|
||||
restore and rollback. Revoking a static release cannot retract ciphertext or
|
||||
files already downloaded by visitors. Site cutover is still unauthorized.
|
||||
@@ -0,0 +1,26 @@
|
||||
# INACTIVE TEMPLATE. Not imported by the development flake or any deployed host.
|
||||
# Requires an owner-reviewed sops-nix NixOS module import in the host flake.
|
||||
{ config, lib, ... }:
|
||||
let cfg = config.aia.portalSecrets;
|
||||
in {
|
||||
options.aia.portalSecrets = {
|
||||
enable = lib.mkEnableOption "owner-reviewed portal deployment secrets";
|
||||
encryptedFile = lib.mkOption { type = lib.types.path; description = "SOPS-encrypted YAML only; never a plaintext file"; };
|
||||
ageKeyFile = lib.mkOption { type = lib.types.str; description = "Private persisted host age key path outside the Nix store"; };
|
||||
readerUser = lib.mkOption { type = lib.types.str; description = "Approved resource authorization service user"; };
|
||||
};
|
||||
config = lib.mkIf cfg.enable {
|
||||
assertions = [{ assertion = lib.hasPrefix "/" cfg.ageKeyFile && !(lib.hasPrefix "/nix/store/" cfg.ageKeyFile);
|
||||
message = "The host age identity must be an absolute runtime path outside the Nix store."; }];
|
||||
sops.defaultSopsFile = cfg.encryptedFile;
|
||||
sops.age.keyFile = cfg.ageKeyFile;
|
||||
sops.age.generateKey = false;
|
||||
sops.age.sshKeyPaths = [];
|
||||
sops.gnupg.sshKeyPaths = [];
|
||||
sops.secrets."portal/staff-credentials" = { owner = cfg.readerUser; mode = "0400"; };
|
||||
sops.secrets."portal/management-credentials" = { owner = cfg.readerUser; mode = "0400"; };
|
||||
# Consumers reference config.sops.secrets.<name>.path at runtime.
|
||||
# Do not use builtins.readFile on a decrypted secret, environment.etc.text,
|
||||
# PUBLIC_* values, or Astro/Vite imports to transport secrets.
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
{
|
||||
"name": "arizona-institute-for-autism",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"engines": {
|
||||
"node": ">=22.12.0"
|
||||
},
|
||||
"scripts": {
|
||||
"dev": "astro dev",
|
||||
"check": "astro check",
|
||||
"build": "astro check && astro build",
|
||||
"build:sandbox": "NODE_OPTIONS=--require=./tools/localhost-dns.cjs astro check && NODE_OPTIONS=--require=./tools/localhost-dns.cjs astro build",
|
||||
"preview": "astro preview",
|
||||
"extract:blog-footers": "node tools/extract-blog-footers.mjs",
|
||||
"crawl:live": "node tools/crawl-live-site.mjs",
|
||||
"audit:links": "node --experimental-strip-types tools/audit-links.mjs",
|
||||
"audit:images": "node tools/audit-page-imagery.mjs",
|
||||
"audit:blog": "node tools/audit-blog-content.mjs",
|
||||
"audit:routes": "node --experimental-strip-types tools/audit-routes.mjs",
|
||||
"test:routes": "node --experimental-strip-types --test tools/audit-routes.test.mjs",
|
||||
"test:markdown": "node --test tools/markdown.test.mjs",
|
||||
"test:publication": "node --experimental-strip-types --test tools/publication.test.mjs",
|
||||
"test:seo": "node --experimental-strip-types --test tools/seo.test.mjs",
|
||||
"test:library": "node --experimental-strip-types --test tools/library.test.mjs",
|
||||
"test:campaign": "node --test tools/campaign.test.mjs",
|
||||
"verify:campaign": "node tools/campaign-browser.mjs",
|
||||
"generate:sitemap": "node tools/generate-sitemap.mjs",
|
||||
"generate:redirects": "node tools/generate-redirects.mjs"
|
||||
},
|
||||
"dependencies": {
|
||||
"@astrojs/markdown-satteri": "^0.3.5",
|
||||
"@astrojs/mdx": "^7.0.5",
|
||||
"astro": "^7.1.5",
|
||||
"cheerio": "^1.1.0",
|
||||
"fast-glob": "^3.3.3",
|
||||
"gray-matter": "^4.0.3",
|
||||
"swiper": "^12.2.0",
|
||||
"turndown": "^7.2.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@astrojs/check": "^0.9.10",
|
||||
"typescript": "^6.0.3"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
Copyright 2017 The Playfair Display Project Authors (https://github.com/clauseggers/Playfair-Display), with Reserved Font Name "Playfair Display"
|
||||
|
||||
This Font Software is licensed under the SIL Open Font License, Version 1.1.
|
||||
This license is copied below, and is also available with a FAQ at:
|
||||
http://scripts.sil.org/OFL
|
||||
|
||||
|
||||
-----------------------------------------------------------
|
||||
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
|
||||
-----------------------------------------------------------
|
||||
|
||||
PREAMBLE
|
||||
The goals of the Open Font License (OFL) are to stimulate worldwide
|
||||
development of collaborative font projects, to support the font creation
|
||||
efforts of academic and linguistic communities, and to provide a free and
|
||||
open framework in which fonts may be shared and improved in partnership
|
||||
with others.
|
||||
|
||||
The OFL allows the licensed fonts to be used, studied, modified and
|
||||
redistributed freely as long as they are not sold by themselves. The
|
||||
fonts, including any derivative works, can be bundled, embedded,
|
||||
redistributed and/or sold with any software provided that any reserved
|
||||
names are not used by derivative works. The fonts and derivatives,
|
||||
however, cannot be released under any other type of license. The
|
||||
requirement for fonts to remain under this license does not apply
|
||||
to any document created using the fonts or their derivatives.
|
||||
|
||||
DEFINITIONS
|
||||
"Font Software" refers to the set of files released by the Copyright
|
||||
Holder(s) under this license and clearly marked as such. This may
|
||||
include source files, build scripts and documentation.
|
||||
|
||||
"Reserved Font Name" refers to any names specified as such after the
|
||||
copyright statement(s).
|
||||
|
||||
"Original Version" refers to the collection of Font Software components as
|
||||
distributed by the Copyright Holder(s).
|
||||
|
||||
"Modified Version" refers to any derivative made by adding to, deleting,
|
||||
or substituting -- in part or in whole -- any of the components of the
|
||||
Original Version, by changing formats or by porting the Font Software to a
|
||||
new environment.
|
||||
|
||||
"Author" refers to any designer, engineer, programmer, technical
|
||||
writer or other person who contributed to the Font Software.
|
||||
|
||||
PERMISSION & CONDITIONS
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of the Font Software, to use, study, copy, merge, embed, modify,
|
||||
redistribute, and sell modified and unmodified copies of the Font
|
||||
Software, subject to the following conditions:
|
||||
|
||||
1) Neither the Font Software nor any of its individual components,
|
||||
in Original or Modified Versions, may be sold by itself.
|
||||
|
||||
2) Original or Modified Versions of the Font Software may be bundled,
|
||||
redistributed and/or sold with any software, provided that each copy
|
||||
contains the above copyright notice and this license. These can be
|
||||
included either as stand-alone text files, human-readable headers or
|
||||
in the appropriate machine-readable metadata fields within text or
|
||||
binary files as long as those fields can be easily viewed by the user.
|
||||
|
||||
3) No Modified Version of the Font Software may use the Reserved Font
|
||||
Name(s) unless explicit written permission is granted by the corresponding
|
||||
Copyright Holder. This restriction only applies to the primary font name as
|
||||
presented to the users.
|
||||
|
||||
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
|
||||
Software shall not be used to promote, endorse or advertise any
|
||||
Modified Version, except to acknowledge the contribution(s) of the
|
||||
Copyright Holder(s) and the Author(s) or with their explicit written
|
||||
permission.
|
||||
|
||||
5) The Font Software, modified or unmodified, in part or in whole,
|
||||
must be distributed entirely under this license, and must not be
|
||||
distributed under any other license. The requirement for fonts to
|
||||
remain under this license does not apply to any document created
|
||||
using the Font Software.
|
||||
|
||||
TERMINATION
|
||||
This license becomes null and void if any of the above conditions are
|
||||
not met.
|
||||
|
||||
DISCLAIMER
|
||||
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
|
||||
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
|
||||
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
|
||||
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
|
||||
OTHER DEALINGS IN THE FONT SOFTWARE.
|
||||
@@ -0,0 +1,93 @@
|
||||
Copyright 2015 The Rubik Project Authors (https://github.com/googlefonts/rubik)
|
||||
|
||||
This Font Software is licensed under the SIL Open Font License, Version 1.1.
|
||||
This license is copied below, and is also available with a FAQ at:
|
||||
https://scripts.sil.org/OFL
|
||||
|
||||
|
||||
-----------------------------------------------------------
|
||||
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
|
||||
-----------------------------------------------------------
|
||||
|
||||
PREAMBLE
|
||||
The goals of the Open Font License (OFL) are to stimulate worldwide
|
||||
development of collaborative font projects, to support the font creation
|
||||
efforts of academic and linguistic communities, and to provide a free and
|
||||
open framework in which fonts may be shared and improved in partnership
|
||||
with others.
|
||||
|
||||
The OFL allows the licensed fonts to be used, studied, modified and
|
||||
redistributed freely as long as they are not sold by themselves. The
|
||||
fonts, including any derivative works, can be bundled, embedded,
|
||||
redistributed and/or sold with any software provided that any reserved
|
||||
names are not used by derivative works. The fonts and derivatives,
|
||||
however, cannot be released under any other type of license. The
|
||||
requirement for fonts to remain under this license does not apply
|
||||
to any document created using the fonts or their derivatives.
|
||||
|
||||
DEFINITIONS
|
||||
"Font Software" refers to the set of files released by the Copyright
|
||||
Holder(s) under this license and clearly marked as such. This may
|
||||
include source files, build scripts and documentation.
|
||||
|
||||
"Reserved Font Name" refers to any names specified as such after the
|
||||
copyright statement(s).
|
||||
|
||||
"Original Version" refers to the collection of Font Software components as
|
||||
distributed by the Copyright Holder(s).
|
||||
|
||||
"Modified Version" refers to any derivative made by adding to, deleting,
|
||||
or substituting -- in part or in whole -- any of the components of the
|
||||
Original Version, by changing formats or by porting the Font Software to a
|
||||
new environment.
|
||||
|
||||
"Author" refers to any designer, engineer, programmer, technical
|
||||
writer or other person who contributed to the Font Software.
|
||||
|
||||
PERMISSION & CONDITIONS
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of the Font Software, to use, study, copy, merge, embed, modify,
|
||||
redistribute, and sell modified and unmodified copies of the Font
|
||||
Software, subject to the following conditions:
|
||||
|
||||
1) Neither the Font Software nor any of its individual components,
|
||||
in Original or Modified Versions, may be sold by itself.
|
||||
|
||||
2) Original or Modified Versions of the Font Software may be bundled,
|
||||
redistributed and/or sold with any software, provided that each copy
|
||||
contains the above copyright notice and this license. These can be
|
||||
included either as stand-alone text files, human-readable headers or
|
||||
in the appropriate machine-readable metadata fields within text or
|
||||
binary files as long as those fields can be easily viewed by the user.
|
||||
|
||||
3) No Modified Version of the Font Software may use the Reserved Font
|
||||
Name(s) unless explicit written permission is granted by the corresponding
|
||||
Copyright Holder. This restriction only applies to the primary font name as
|
||||
presented to the users.
|
||||
|
||||
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
|
||||
Software shall not be used to promote, endorse or advertise any
|
||||
Modified Version, except to acknowledge the contribution(s) of the
|
||||
Copyright Holder(s) and the Author(s) or with their explicit written
|
||||
permission.
|
||||
|
||||
5) The Font Software, modified or unmodified, in part or in whole,
|
||||
must be distributed entirely under this license, and must not be
|
||||
distributed under any other license. The requirement for fonts to
|
||||
remain under this license does not apply to any document created
|
||||
using the Font Software.
|
||||
|
||||
TERMINATION
|
||||
This license becomes null and void if any of the above conditions are
|
||||
not met.
|
||||
|
||||
DISCLAIMER
|
||||
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
|
||||
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
|
||||
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
|
||||
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
|
||||
OTHER DEALINGS IN THE FONT SOFTWARE.
|
||||
|
After Width: | Height: | Size: 44 KiB |
|
After Width: | Height: | Size: 119 KiB |
|
After Width: | Height: | Size: 56 KiB |
|
After Width: | Height: | Size: 163 KiB |
|
After Width: | Height: | Size: 33 KiB |
|
After Width: | Height: | Size: 84 KiB |
|
After Width: | Height: | Size: 35 KiB |
|
After Width: | Height: | Size: 93 KiB |
|
After Width: | Height: | Size: 33 KiB |
|
After Width: | Height: | Size: 77 KiB |
|
After Width: | Height: | Size: 55 KiB |
|
After Width: | Height: | Size: 135 KiB |
|
After Width: | Height: | Size: 38 KiB |
|
After Width: | Height: | Size: 93 KiB |
|
After Width: | Height: | Size: 37 KiB |
|
After Width: | Height: | Size: 94 KiB |
@@ -0,0 +1 @@
|
||||
<svg viewBox="113.85996 134.2596 679.2 315.6" xmlns="http://www.w3.org/2000/svg" id="Layer_1" style="max-height: 500px" width="679.2" height="315.6"><defs><style>.cls-1,.cls-2,.cls-3,.cls-4,.cls-5,.cls-6{stroke-width:0px;}.cls-2{fill:#27417f;}.cls-3{fill:#554f62;}.cls-4{fill:#254080;}.cls-5{fill:#aea990;}.cls-6{fill:#f2b85b;}</style></defs><path d="m176.47,162.76l22.18,57.09c.85,2.24,1.83,3.76,2.95,4.54,1.12.79,2.17,1.21,3.14,1.27v1.82c-1.46-.12-3.17-.2-5.14-.22-1.97-.03-3.95-.05-5.95-.05-2.54,0-4.91.01-7.09.05-2.18.03-3.94.11-5.28.22v-1.82c3.09-.12,5.05-.65,5.87-1.59.82-.94.62-2.95-.59-6.04l-16.36-44.17,1.46-1.64-15.09,39.45c-1.09,2.73-1.74,5-1.95,6.82-.22,1.82-.06,3.24.45,4.27.52,1.03,1.41,1.76,2.68,2.18,1.27.43,2.85.67,4.73.73v1.82c-1.82-.12-3.65-.2-5.5-.22-1.85-.03-3.59-.05-5.23-.05-1.46,0-2.74.01-3.86.05-1.12.03-2.14.11-3.05.22v-1.82c1.27-.3,2.55-1.06,3.82-2.27,1.27-1.21,2.48-3.27,3.63-6.18l21.09-54.45h3.09Zm9.54,38.54v1.82h-27.08l.91-1.82h26.18Z" class="cls-2"/><path d="m235.82,179.3c1.76,0,3.26.37,4.5,1.09,1.24.73,2.18,1.65,2.82,2.77.64,1.12.96,2.38.96,3.77,0,1.76-.53,3.26-1.59,4.5-1.06,1.24-2.47,1.86-4.23,1.86-1.51,0-2.8-.46-3.86-1.36-1.06-.91-1.59-2.15-1.59-3.73,0-1.33.35-2.47,1.04-3.41.7-.94,1.5-1.68,2.41-2.22-.6-.49-1.33-.7-2.18-.64-1.64.06-3.15.59-4.55,1.59-1.39,1-2.58,2.27-3.54,3.82-.97,1.54-1.73,3.19-2.27,4.95-.54,1.76-.82,3.4-.82,4.91v20.91c0,2.97.82,4.96,2.46,5.95,1.63,1,3.91,1.5,6.81,1.5v1.91c-1.39-.06-3.38-.14-5.95-.22-2.58-.09-5.28-.14-8.14-.14-2.18,0-4.36.05-6.54.14-2.18.09-3.85.16-5,.22v-1.91c2.37,0,4.04-.45,5.05-1.36,1-.91,1.5-2.64,1.5-5.18v-28.54c0-2.73-.46-4.74-1.36-6.04-.91-1.3-2.64-1.95-5.18-1.95v-1.91c1.94.18,3.82.27,5.64.27,2.06,0,3.99-.11,5.77-.32,1.78-.22,3.44-.53,4.95-.96v10.46c.73-1.76,1.7-3.45,2.91-5.09,1.21-1.63,2.66-2.98,4.36-4.04,1.7-1.06,3.58-1.59,5.64-1.59Z" class="cls-2"/><path d="m264,179.57v39.45c0,2.54.5,4.27,1.5,5.18,1,.91,2.68,1.36,5.05,1.36v1.91c-1.03-.06-2.62-.14-4.77-.22-2.16-.09-4.35-.14-6.59-.14s-4.38.05-6.59.14c-2.21.09-3.86.16-4.96.22v-1.91c2.37,0,4.04-.45,5.05-1.36,1-.91,1.5-2.64,1.5-5.18v-28.54c0-2.73-.46-4.74-1.36-6.04-.91-1.3-2.64-1.95-5.18-1.95v-1.91c1.94.18,3.82.27,5.64.27,2.06,0,3.99-.11,5.77-.32,1.78-.22,3.44-.53,4.95-.96Zm-5.45-21.91c1.88,0,3.45.59,4.73,1.78,1.27,1.18,1.91,2.65,1.91,4.41s-.64,3.23-1.91,4.41c-1.27,1.18-2.85,1.77-4.73,1.77s-3.45-.59-4.73-1.77c-1.27-1.18-1.91-2.65-1.91-4.41s.64-3.23,1.91-4.41c1.27-1.18,2.85-1.78,4.73-1.78Z" class="cls-2"/><path d="m312.54,180.12l-25.09,45.99h8.18c2.42,0,4.58-.49,6.45-1.46,1.88-.97,3.42-2.57,4.64-4.81,1.21-2.24,1.94-5.21,2.18-8.91h2.09c0,4.06.03,7.45.09,10.18s.15,4.97.27,6.73c-2.72-.12-5.54-.22-8.45-.27-2.91-.06-5.79-.09-8.63-.09-3.09,0-6.23.05-9.41.14-3.18.09-6.23.2-9.14.32l25.09-45.99h-8.36c-2.6,0-4.85.5-6.73,1.5-1.88,1-3.35,2.61-4.41,4.82-1.06,2.21-1.74,5.17-2.05,8.86h-2c0-4.12-.03-7.51-.09-10.18-.06-2.66-.15-4.91-.27-6.73,2.67.12,5.45.22,8.36.27,2.91.06,5.82.09,8.73.09s6.14-.05,9.32-.14c3.18-.09,6.26-.2,9.23-.32Z" class="cls-2"/><path d="m340.81,179.3c4.06,0,7.66.85,10.82,2.55,3.15,1.7,5.66,4.36,7.55,8,1.88,3.64,2.81,8.39,2.81,14.27s-.94,10.62-2.81,14.23c-1.88,3.61-4.4,6.24-7.55,7.91-3.15,1.67-6.76,2.5-10.82,2.5s-7.6-.83-10.82-2.5c-3.21-1.67-5.74-4.3-7.59-7.91-1.85-3.61-2.77-8.35-2.77-14.23s.92-10.63,2.77-14.27c1.85-3.64,4.38-6.3,7.59-8,3.21-1.7,6.82-2.55,10.82-2.55Zm0,1.82c-3.15,0-5.73,1.8-7.73,5.41-2,3.61-3,9.47-3,17.59s1,13.95,3,17.5c2,3.55,4.58,5.32,7.73,5.32s5.65-1.77,7.68-5.32c2.03-3.55,3.05-9.38,3.05-17.5s-1.02-13.98-3.05-17.59c-2.03-3.61-4.59-5.41-7.68-5.41Z" class="cls-2"/><path d="m400.07,179.3c2.42,0,4.41.32,5.95.96,1.55.64,2.83,1.5,3.86,2.59,1.09,1.15,1.86,2.64,2.32,4.46.45,1.82.68,4.27.68,7.36v24.36c0,2.54.5,4.27,1.5,5.18,1,.91,2.68,1.36,5.05,1.36v1.91c-1.03-.06-2.64-.14-4.82-.22-2.18-.09-4.33-.14-6.45-.14s-4.11.05-6.14.14c-2.03.09-3.56.16-4.59.22v-1.91c2.06,0,3.51-.45,4.37-1.36.85-.91,1.27-2.64,1.27-5.18v-26.9c0-1.76-.15-3.35-.45-4.77-.31-1.42-.94-2.58-1.91-3.45-.97-.88-2.43-1.32-4.37-1.32-2.24,0-4.23.64-5.95,1.91-1.73,1.27-3.09,3.02-4.09,5.23-1,2.21-1.5,4.68-1.5,7.41v21.91c0,2.54.44,4.27,1.32,5.18.88.91,2.32,1.36,4.32,1.36v1.91c-.97-.06-2.44-.14-4.41-.22-1.97-.09-4.01-.14-6.14-.14s-4.21.05-6.45.14c-2.24.09-3.91.16-5,.22v-1.91c2.37,0,4.04-.45,5.05-1.36,1-.91,1.5-2.64,1.5-5.18v-28.54c0-2.73-.46-4.74-1.36-6.04-.91-1.3-2.64-1.95-5.18-1.95v-1.91c1.94.18,3.82.27,5.64.27,2.06,0,3.99-.11,5.77-.32,1.78-.22,3.44-.53,4.95-.96v9.91c1.57-3.76,3.74-6.39,6.5-7.91,2.75-1.51,5.68-2.27,8.77-2.27Z" class="cls-2"/><path d="m436.06,228.11c-2.54,0-4.73-.49-6.54-1.46-1.82-.97-3.18-2.3-4.09-4-.91-1.69-1.36-3.63-1.36-5.82,0-2.67.6-4.83,1.82-6.5,1.21-1.67,2.77-3.02,4.69-4.04,1.91-1.03,3.94-1.89,6.09-2.59,2.15-.7,4.18-1.36,6.09-2,1.91-.64,3.48-1.38,4.73-2.22,1.24-.85,1.86-1.97,1.86-3.36v-6.18c0-2.18-.35-3.93-1.05-5.23-.7-1.3-1.63-2.22-2.81-2.77-1.19-.55-2.59-.82-4.23-.82-1.4,0-2.88.2-4.46.59-1.57.39-2.81,1.17-3.72,2.32,1.33.37,2.44,1.08,3.32,2.14.88,1.06,1.32,2.38,1.32,3.95,0,1.7-.56,3.03-1.68,4s-2.53,1.45-4.Line truncated
|
||||
|
After Width: | Height: | Size: 33 KiB |
@@ -0,0 +1,4 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 160 160" fill="none" stroke="#E5AE56" stroke-width="2">
|
||||
<path d="m67 13 2-10h22l2 10 13 5 9-6 16 16-6 9 5 13 10 2v22l-10 2-5 13 6 9-16 16-9-6-13 5-2 10H69l-2-10-13-5-9 6-16-16 6-9-5-13-10-2V52l10-2 5-13-6-9 16-16 9 6Z" transform="translate(0 17)"/>
|
||||
<circle cx="80" cy="80" r="26"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 346 B |
|
After Width: | Height: | Size: 109 KiB |
|
After Width: | Height: | Size: 28 KiB |
|
After Width: | Height: | Size: 20 KiB |
|
After Width: | Height: | Size: 31 KiB |
|
After Width: | Height: | Size: 37 KiB |
|
After Width: | Height: | Size: 13 KiB |
|
After Width: | Height: | Size: 25 KiB |
|
After Width: | Height: | Size: 25 KiB |
|
After Width: | Height: | Size: 22 KiB |
|
After Width: | Height: | Size: 109 KiB |
|
After Width: | Height: | Size: 22 KiB |
|
After Width: | Height: | Size: 30 KiB |
|
After Width: | Height: | Size: 27 KiB |
|
After Width: | Height: | Size: 22 KiB |
|
After Width: | Height: | Size: 17 KiB |
|
After Width: | Height: | Size: 63 KiB |
|
After Width: | Height: | Size: 29 KiB |
|
After Width: | Height: | Size: 34 KiB |
|
After Width: | Height: | Size: 84 KiB |
|
After Width: | Height: | Size: 71 KiB |
|
After Width: | Height: | Size: 35 KiB |
|
After Width: | Height: | Size: 5.2 KiB |
|
After Width: | Height: | Size: 38 KiB |
@@ -0,0 +1 @@
|
||||
<svg viewBox="113.85996 134.2596 679.2 315.6" xmlns="http://www.w3.org/2000/svg" id="Layer_1" style="max-height: 500px" width="679.2" height="315.6"><defs><style>.cls-1,.cls-2,.cls-3,.cls-4,.cls-5,.cls-6{stroke-width:0px;}.cls-2{fill:#27417f;}.cls-3{fill:#554f62;}.cls-4{fill:#254080;}.cls-5{fill:#aea990;}.cls-6{fill:#f2b85b;}</style></defs><path d="m176.47,162.76l22.18,57.09c.85,2.24,1.83,3.76,2.95,4.54,1.12.79,2.17,1.21,3.14,1.27v1.82c-1.46-.12-3.17-.2-5.14-.22-1.97-.03-3.95-.05-5.95-.05-2.54,0-4.91.01-7.09.05-2.18.03-3.94.11-5.28.22v-1.82c3.09-.12,5.05-.65,5.87-1.59.82-.94.62-2.95-.59-6.04l-16.36-44.17,1.46-1.64-15.09,39.45c-1.09,2.73-1.74,5-1.95,6.82-.22,1.82-.06,3.24.45,4.27.52,1.03,1.41,1.76,2.68,2.18,1.27.43,2.85.67,4.73.73v1.82c-1.82-.12-3.65-.2-5.5-.22-1.85-.03-3.59-.05-5.23-.05-1.46,0-2.74.01-3.86.05-1.12.03-2.14.11-3.05.22v-1.82c1.27-.3,2.55-1.06,3.82-2.27,1.27-1.21,2.48-3.27,3.63-6.18l21.09-54.45h3.09Zm9.54,38.54v1.82h-27.08l.91-1.82h26.18Z" class="cls-2"/><path d="m235.82,179.3c1.76,0,3.26.37,4.5,1.09,1.24.73,2.18,1.65,2.82,2.77.64,1.12.96,2.38.96,3.77,0,1.76-.53,3.26-1.59,4.5-1.06,1.24-2.47,1.86-4.23,1.86-1.51,0-2.8-.46-3.86-1.36-1.06-.91-1.59-2.15-1.59-3.73,0-1.33.35-2.47,1.04-3.41.7-.94,1.5-1.68,2.41-2.22-.6-.49-1.33-.7-2.18-.64-1.64.06-3.15.59-4.55,1.59-1.39,1-2.58,2.27-3.54,3.82-.97,1.54-1.73,3.19-2.27,4.95-.54,1.76-.82,3.4-.82,4.91v20.91c0,2.97.82,4.96,2.46,5.95,1.63,1,3.91,1.5,6.81,1.5v1.91c-1.39-.06-3.38-.14-5.95-.22-2.58-.09-5.28-.14-8.14-.14-2.18,0-4.36.05-6.54.14-2.18.09-3.85.16-5,.22v-1.91c2.37,0,4.04-.45,5.05-1.36,1-.91,1.5-2.64,1.5-5.18v-28.54c0-2.73-.46-4.74-1.36-6.04-.91-1.3-2.64-1.95-5.18-1.95v-1.91c1.94.18,3.82.27,5.64.27,2.06,0,3.99-.11,5.77-.32,1.78-.22,3.44-.53,4.95-.96v10.46c.73-1.76,1.7-3.45,2.91-5.09,1.21-1.63,2.66-2.98,4.36-4.04,1.7-1.06,3.58-1.59,5.64-1.59Z" class="cls-2"/><path d="m264,179.57v39.45c0,2.54.5,4.27,1.5,5.18,1,.91,2.68,1.36,5.05,1.36v1.91c-1.03-.06-2.62-.14-4.77-.22-2.16-.09-4.35-.14-6.59-.14s-4.38.05-6.59.14c-2.21.09-3.86.16-4.96.22v-1.91c2.37,0,4.04-.45,5.05-1.36,1-.91,1.5-2.64,1.5-5.18v-28.54c0-2.73-.46-4.74-1.36-6.04-.91-1.3-2.64-1.95-5.18-1.95v-1.91c1.94.18,3.82.27,5.64.27,2.06,0,3.99-.11,5.77-.32,1.78-.22,3.44-.53,4.95-.96Zm-5.45-21.91c1.88,0,3.45.59,4.73,1.78,1.27,1.18,1.91,2.65,1.91,4.41s-.64,3.23-1.91,4.41c-1.27,1.18-2.85,1.77-4.73,1.77s-3.45-.59-4.73-1.77c-1.27-1.18-1.91-2.65-1.91-4.41s.64-3.23,1.91-4.41c1.27-1.18,2.85-1.78,4.73-1.78Z" class="cls-2"/><path d="m312.54,180.12l-25.09,45.99h8.18c2.42,0,4.58-.49,6.45-1.46,1.88-.97,3.42-2.57,4.64-4.81,1.21-2.24,1.94-5.21,2.18-8.91h2.09c0,4.06.03,7.45.09,10.18s.15,4.97.27,6.73c-2.72-.12-5.54-.22-8.45-.27-2.91-.06-5.79-.09-8.63-.09-3.09,0-6.23.05-9.41.14-3.18.09-6.23.2-9.14.32l25.09-45.99h-8.36c-2.6,0-4.85.5-6.73,1.5-1.88,1-3.35,2.61-4.41,4.82-1.06,2.21-1.74,5.17-2.05,8.86h-2c0-4.12-.03-7.51-.09-10.18-.06-2.66-.15-4.91-.27-6.73,2.67.12,5.45.22,8.36.27,2.91.06,5.82.09,8.73.09s6.14-.05,9.32-.14c3.18-.09,6.26-.2,9.23-.32Z" class="cls-2"/><path d="m340.81,179.3c4.06,0,7.66.85,10.82,2.55,3.15,1.7,5.66,4.36,7.55,8,1.88,3.64,2.81,8.39,2.81,14.27s-.94,10.62-2.81,14.23c-1.88,3.61-4.4,6.24-7.55,7.91-3.15,1.67-6.76,2.5-10.82,2.5s-7.6-.83-10.82-2.5c-3.21-1.67-5.74-4.3-7.59-7.91-1.85-3.61-2.77-8.35-2.77-14.23s.92-10.63,2.77-14.27c1.85-3.64,4.38-6.3,7.59-8,3.21-1.7,6.82-2.55,10.82-2.55Zm0,1.82c-3.15,0-5.73,1.8-7.73,5.41-2,3.61-3,9.47-3,17.59s1,13.95,3,17.5c2,3.55,4.58,5.32,7.73,5.32s5.65-1.77,7.68-5.32c2.03-3.55,3.05-9.38,3.05-17.5s-1.02-13.98-3.05-17.59c-2.03-3.61-4.59-5.41-7.68-5.41Z" class="cls-2"/><path d="m400.07,179.3c2.42,0,4.41.32,5.95.96,1.55.64,2.83,1.5,3.86,2.59,1.09,1.15,1.86,2.64,2.32,4.46.45,1.82.68,4.27.68,7.36v24.36c0,2.54.5,4.27,1.5,5.18,1,.91,2.68,1.36,5.05,1.36v1.91c-1.03-.06-2.64-.14-4.82-.22-2.18-.09-4.33-.14-6.45-.14s-4.11.05-6.14.14c-2.03.09-3.56.16-4.59.22v-1.91c2.06,0,3.51-.45,4.37-1.36.85-.91,1.27-2.64,1.27-5.18v-26.9c0-1.76-.15-3.35-.45-4.77-.31-1.42-.94-2.58-1.91-3.45-.97-.88-2.43-1.32-4.37-1.32-2.24,0-4.23.64-5.95,1.91-1.73,1.27-3.09,3.02-4.09,5.23-1,2.21-1.5,4.68-1.5,7.41v21.91c0,2.54.44,4.27,1.32,5.18.88.91,2.32,1.36,4.32,1.36v1.91c-.97-.06-2.44-.14-4.41-.22-1.97-.09-4.01-.14-6.14-.14s-4.21.05-6.45.14c-2.24.09-3.91.16-5,.22v-1.91c2.37,0,4.04-.45,5.05-1.36,1-.91,1.5-2.64,1.5-5.18v-28.54c0-2.73-.46-4.74-1.36-6.04-.91-1.3-2.64-1.95-5.18-1.95v-1.91c1.94.18,3.82.27,5.64.27,2.06,0,3.99-.11,5.77-.32,1.78-.22,3.44-.53,4.95-.96v9.91c1.57-3.76,3.74-6.39,6.5-7.91,2.75-1.51,5.68-2.27,8.77-2.27Z" class="cls-2"/><path d="m436.06,228.11c-2.54,0-4.73-.49-6.54-1.46-1.82-.97-3.18-2.3-4.09-4-.91-1.69-1.36-3.63-1.36-5.82,0-2.67.6-4.83,1.82-6.5,1.21-1.67,2.77-3.02,4.69-4.04,1.91-1.03,3.94-1.89,6.09-2.59,2.15-.7,4.18-1.36,6.09-2,1.91-.64,3.48-1.38,4.73-2.22,1.24-.85,1.86-1.97,1.86-3.36v-6.18c0-2.18-.35-3.93-1.05-5.23-.7-1.3-1.63-2.22-2.81-2.77-1.19-.55-2.59-.82-4.23-.82-1.4,0-2.88.2-4.46.59-1.57.39-2.81,1.17-3.72,2.32,1.33.37,2.44,1.08,3.32,2.14.88,1.06,1.32,2.38,1.32,3.95,0,1.7-.56,3.03-1.68,4s-2.53,1.45-4.Line truncated
|
||||
|
After Width: | Height: | Size: 33 KiB |
|
After Width: | Height: | Size: 42 KiB |
@@ -0,0 +1,24 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="800" height="450" viewBox="0 0 800 450">
|
||||
<title>نموذج المقدمة والسلوك والنتيجة اللاحقة</title>
|
||||
<image href="data:image/webp;base64,UklGRqqoAABXRUJQVlA4IJ6oAACQlgKdASogA8IBPpE+mkoloyIhp7FMkLASCWNu0atuo+8/hc72vn/l2SXST+W89GyhV23khTWeu5Lez2L9Q/u9qvK/yf+36EPIvir8//Fet3/n8DPgP/B5wD5X/g9aH9Z/3nsM/1Do4+aDzdPT1/dvUe/yv+49cz1oPRC6aH+3ecpnGX+1/1/ox+efxn+78Qfzf7Z/ef379zfiF/e8zfwf+r5qfcx+V/kfa1/gf+r/OeUv67/R/9f1DvzL+o/671YOSviA8P+9P+r9g722+2/9n/MflT8TH6H/t9Kf4b/X/+X3Af1x/4fsj/4/Hp/J/9f2CP6F/i//P93X1D/7P/2/4/pd+wf/n7h/9B/wX/l/zXt5///3a/vR///dw/dT//oDY+ZlmGz0aurLMNno0wobo7evPt0FDdHbsSKEbpWYbPRpfGdegq7sgIyQddLHyeE1E+dff2k5GMjfvtugobortGfJ+m0JAPReL3Cux8zLMLsk66IAKK3r6kForL4yMq7h8RGPM2N6HxQd+3O676CToBfiaMPUQXSd9fYRTv8nj/+pLeptrihK71mSB/pvDFxxkwEZdYletgvowIer5iRsG3C5HGHv9kbQAvWJqr0ocqUft/OfBN1Ap91BX9KoXPfB3/8BU5k2BXVrQCtNKGAU1FkQZYXiwSnxCAtWk7mBm4QqD4hww0rYerXIjRw/BLsLDZ9KmRB6GN9SHTUWWwGP23X541M360l1EiWdWFrUonM8FeUUHTm0QGGqDYoWh9fPX8Oh5KD4uEEthsveAgqwXPG7A60+Xzt2JXPcojUWGyIHoT5u+X/t2rwO0oH3fBIro1nWuwwCOwdRK9Cg2c2qeBCbrKOSHvexq5fz/wLUCL/PQTiLq34v9qDAeAxKFmpny7QDHycQo4zz89R/hss3SR7/+KM/17eCKnRVzsIJsezet7HUXglrcbVrUohbYEDQv0uPRly6m6W2YQ/HblOm6Xc9vIMKGsXSJgQOv/iiYmq7hetYde5gF2ZiF6fd50xrebMJstfkB664BPxw7ehmYO12uRS35iX5npdM5i7Ga9h3LkITUuKIxOJHpdZMqthzz5YCllzGGl1Z1kDMN08j8/9wdMuoHbnjwnfcqovTEi75p+kUKhPYxdwcLXLZOIag6UWTUVcoGAZbIeZlzUgKxSxPa9AaSDZO8MhqYTQSVI8B7EJ6j3M2NXyRX3j8n5Qn9LBB0xwQHfXnwTh6WWWEQ5Z3j0FWygnqC4N09zETtCulsn8OPa5xm/s4n5Mng54fLL0CxzhlGOFaDKZLHxD20iaqbKKF60BQF/1RrpgxxRQeo6hsrduqLQdOW4abv6spEQTeFSj3p4XbLWuuI05Jh7CUnwajLoiqiPUs8omnWmdLHdQ7AKyd+vGVBu4YGstAJF/bFAl+gxNYI3GkEqu4HkSKcoVwg6HMXk7kvy2uvM1OV0N7mbhSbH/XTDokarECyQmH2m0jJ4EzQjvKi+YoJpeZTIZhqYOntNxOEqNWujN15qh7Desf61WLwAoz8MY4Lz19PbF7ZGNFD1Xl/JqE5O7/uhv+KJpsbJszLK53AF76z/dRknxsFQzikbRkpOQTxZoPczG+9FtGO6bEKuYTZb++t4JFj+H4DPPUbwSPlMobbbTcYlDIt3HzNYF+Hbv89rDXAxNUzdsSdPFAY4DRzvCtd+U1oBM70YtcyOrd4t9vX5KzQjMIp5rr8bgNNFc76lxIcif/mDuVwExUSlAoEtgg75zD+aNy/u/dpIi06DtwjB3Em0d6kTdOsgkQ/UHXsD3QlPpT//JvWt+iuL6Ou5YSI6mnzgJ6Sgtc+q3Ittv0MO6zbf09HoQhM/5DCLNyE4lKM75sc/Xn2MrdZds8kfSi1VWayPK3z8+9I8adQUdqQga6ZGtrAaSmjDGR6OFv17WMIRPL+3b80oJ0GPz946ICFR4INi9Iz0XuHu0GLHDreJAspUtSPPosi35CPd8lD2RBWewm+7hvLJPdnPddP0zxMLyA8iBjyKVHdEZIaxxmXK88OI1x4ZWGNPs8rEKr1bmfkrP/G9wdbNqlK7eRgQ0skIR4o9lp6XYJUhiPfFRqLQUtrQKeNRMi+9uXKrhRBDc12eQ32itzsuOEzq24zn1RqXGphrMGpUgJO9AY/9U7vUyjXIs8D1VzbomF7VjXRUlk77QnMXlO9FFB8WmaQtYfXq7+lEj8bt/ZakDXK4vhs2B+Ab2ZGHP6M+4efj/yIOcH9uzluP5Jda5Afi5VAQsy2iF4If+aJGz22I3bGs9uKYjs5VjCOdOhFY5+658k/dUmW8ylI93vibK3Naj2QyyUBSFBYKPcwKI7p+SJZUDbeYTHuAORWyiO+0BgsQS/bKtWNfNaM/vccEDOvE9LJaaAqe7v8aqDIB+1GpL7ncH4iDrHH230+tkVQ4KTLhliUe4YZNjZjOuMfvOGCuPDNFlZRPxMrLO+P2Vd8GKdg0yRWvKZ/MdPOeyVmND/bYv3mH7f+Iz3ydxZZQrosyzd+m176MYxlEN6n2aF9V8Aggfbl5Z8FSlt0dfzHcrYwYW0qVvOxGNwBqlu9v63GDW/6MuveAAd5yKra0jvAYDgxLL1xWTA/TmUXBm2Pm+Slze72GgY3YRaf4dDiY+fRIbTA/WNNy2Wng6h/l4aquGZpF0xkL4pUBYbuhfAm7y2BX/NXngigr7THksys99j67E4v+NexXwtCS0ooRoG1EHvbLn+HRY+Br5939x1tnHJWA2V7IhPizqrinmRmZzsScphZSpPih0eeyCECM1ot+M67PpVkMof0dR+3Nki0ZnU+MFNXRbUZWumweCuAajDiz+z/9Ja2Gz+MuqDq2JkbT2tmCdSn/ZTQJkFFUGaGYtpni+ELrDze1Od4UXh4WdhG5wnArXYH7unz+OfWaLm6Wv1rDMizVnZnh+gxsp1vjcnBdKvl930neLhW/Xk9cztUf2ZFa0GnR1yND+wWz+dWruxR1RvdoiMsHHy15Ju2+QcVgmIovYu3DtPx0HlfPrYo0oZ4OkOAAl+d5w1sh159JzuU1z9CZvqGuhYI09srYi/XEmaNoUSe82e3Z25YoaUqDABfT9qkdEHOZOmD+9rlnZI2hEDD3cNRmyS+sJi/dWFRMXnkQvg5P4bOXmaTAs7Trnp0Lj/k9zWeTbFXrAH3XNkwmOREM0PbnhfS2hgPq1CTQ0AsfuT45Vdys4nZ/LSaLHNxEHh9Es3I6aNjsFRCg9VDDZgNTw9SjjnRA+nc/2rcV0oStfA8hSyqcFzjUdpwDMw2IIiZHnFa0FfGTpV4OiUsMPO/gpe150Wu3xBSoRwYeHZjToqLYc/LGjEWyFHEt4QDRPhT0RiwYn5C8uuWXVmWB+b07FYtdNnWLSnVFD2/ZFKPE5irx7hDEnlRF2ZiEv4N+bHgnK1o87E6cCtoM4D449irGQ0Hc4rPByJOurJz9HBiIqNI3YH6/QzXlEqj/Z3WB+Aa92LOVhEgW/l3lyBjMd8KDZMU0pceZjQaJFdQnShtyvBYjsVSes3MO5dGDWgKSOOkbus7AeydOWKKxC25zo0lDiXyt0cMBqzAjdBFe8/6b8d1zwhXS/sSSYHh8syTA6ChrNLWheltfRtL9B8Diz6aICd5ezZn/CeHHXd9+I4PhKsOsaXebFMlYCYvjoahEJBbFvdH/6z8+1hEhqMAtPyweRe4WrJOM/KXK0gZjhuXBrhPJ3q6GHNiiOuzyoe62gr5dtlB0R4rhMX/5MDfDFswuOO5wG2H+3ryj+s40STZB62HZg5G8DidKA5xJ1rqqurzNncj1ZfwxQ+Vs5WXlbxsKNfinNwVqh0Or8Qts7PK54FJXTjqp2Ibj7ksNGD0tJTIRq7JK4hbIbq+o2zwd3jjRpqVIeSBwVOuoxY1MLvW63AZJVPOvRqNVP4MOo0RAtyfk4MnB7iJbGI0j/J+CJa5UqsBdqiQ1eUKg4G8+36iLbxe5KYgxIL4P9928ec+A54R2b1FDI8TUZUStwKbR2LqSoPalTE7Xt7n1pDT07VgrKVwIn/nM2T/51KU/g4j7pF1XqaxbpGKjuBV+68TPKQ3AI/sHKg5FWoNsf8qUylPXZ2d1LV6LvO3Ji9IwFhysDbCK+mUM95f+1LzRukdm81ZpzBZFzibpuXQR8IbLM5MjdRJRnQE2zXij4RumDQVOXJr+Cy1aY1poRijDqdzZ/6jWP1D8YEtHlgAUTew+llgFOz/88h9wNYEBljb2FBjKlWwolsrtBwRC9ylJm0zCrpmLp0VDpirnZ1OLIhCFYrqTDKcfCl6ydNr9zV2ByLjJaTadGc4OK6p95MeY88k0g4epE3lm04aU9ffQU4LnkVOI3mGpJMOoKn0hO9vlS9oRtac+D+zpZHmoK+HC97/NZN9confX8WGSEaFneyUbPRwrcdVWMfAqf3bXRfWzlMum3KV96yU9XeSdifJW2nBEkF9siNExDYDQLVKCnIaaT5NrjICVcwIeQf8ZuqNO1qdtm2LemRYSx/bu+gSX6SIduQ5zrSngTpb50fyo0TvuOkTq7e2p/dpOwFwlISvnskJcx8eNz5dUQ8f+MgEzhmQiwtBTJtnrYft/J3lCvszHr5q01ctOXxbEn1K/lJezzC/6H089+OLFsICiUAQsyKydG+46wWF7bGNo8jD7fiv24ALvTiwbY71dDZ0NdvM3gVxS/0fSnmVGk2IRTUgWW7bR1eAKPtCjw88CGnuZ/Mc4hdxD/enxL8tBolZ0jtL5PF1/nSfZ1cH/yk2hpXu1yDNCH0c+6n7ji4FkcxyNpklnRV8yJj2w3i9KW1/fwvihu/RCKPPftoMlPuzg1ri9Q3yPYBTr/4AEKtM2bJW+vv0SmtCMTGkmuzThYgTBTPIsYUuEU8TR2nC8EcsUAXTKcHKMKBXpgzVjcz1X2AkpH9yigwTlbo7Line truncated
|
||||
<rect width="800" height="64" fill="#fbf8ef" />
|
||||
<g fill="#082f55" font-family="Arial, sans-serif" font-size="25" font-weight="700" text-anchor="middle" direction="rtl">
|
||||
<text x="132" y="43">المُقدِّمة</text>
|
||||
<text x="400" y="43">السلوك</text>
|
||||
<text x="668" y="43">النتيجة اللاحقة</text>
|
||||
</g>
|
||||
<g fill="none" stroke="#082f55" stroke-linecap="round" stroke-linejoin="round" stroke-width="4">
|
||||
<path d="M235 37h60m-12-11 12 11-12 11" />
|
||||
<path d="M507 37h57m-12-11 12 11-12 11" />
|
||||
</g>
|
||||
<rect x="381" y="190" width="50" height="24" rx="3" fill="#fff" opacity=".98" />
|
||||
<text x="406" y="206" fill="#1f2933" font-family="Arial, sans-serif" font-size="9" text-anchor="middle" direction="rtl">المزيد</text>
|
||||
<rect x="611" y="190" width="65" height="24" rx="3" fill="#fff" opacity=".98" />
|
||||
<text x="643" y="205" fill="#1f2933" font-family="Arial, sans-serif" font-size="7" text-anchor="middle" direction="rtl">دقيقتان إضافيتان</text>
|
||||
<rect x="531" y="351" width="255" height="91" rx="12" fill="#e8efe8" />
|
||||
<g fill="#082f55" font-family="Arial, sans-serif" font-size="14" direction="rtl" text-anchor="middle">
|
||||
<text x="658" y="374">◷ حصلتَ على دقيقتين إضافيتين.</text>
|
||||
<text x="658" y="399">← ثم ينتهي الوقت.</text>
|
||||
<text x="658" y="424">▣ تستمر خطة «أولًا/ثم».</text>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 58 KiB |
|
After Width: | Height: | Size: 56 KiB |
|
After Width: | Height: | Size: 9.5 KiB |
|
After Width: | Height: | Size: 45 KiB |
|
After Width: | Height: | Size: 569 KiB |
|
After Width: | Height: | Size: 26 KiB |
|
After Width: | Height: | Size: 86 KiB |
|
After Width: | Height: | Size: 45 KiB |
|
After Width: | Height: | Size: 553 KiB |
|
After Width: | Height: | Size: 42 KiB |