feat: establish Stitch cockpit frontend contract
This commit is contained in:
@@ -28,6 +28,13 @@ Lees niet blind alle referentiedocumenten. Gebruik `docs/quality/TRACEABILITY_MA
|
|||||||
- Geen productieafhankelijkheid toevoegen zonder duidelijke noodzaak, lockfile-update, tests en documentatie.
|
- Geen productieafhankelijkheid toevoegen zonder duidelijke noodzaak, lockfile-update, tests en documentatie.
|
||||||
- Geen secrets, tokens, echte e-mailadressen of persoonlijke gegevens committen.
|
- Geen secrets, tokens, echte e-mailadressen of persoonlijke gegevens committen.
|
||||||
|
|
||||||
|
## Frontendcontract
|
||||||
|
|
||||||
|
- De productiefrontend volgt `docs/design/STITCH_FRONTEND_CONTRACT.md`; Stitch is de visuele bron van waarheid, de Django-backend blijft leidend voor werking en data.
|
||||||
|
- Gebruik uitsluitend echte of eerlijk als onbekend gemarkeerde data. Geen demo-KPI's, nepstatussen, dode controls, auto-apply of externe runtimeassets.
|
||||||
|
- Kernflows blijven server-rendered en bruikbaar zonder JavaScript; afwijkingen voor security, toegankelijkheid en ontbrekende data worden gedocumenteerd.
|
||||||
|
- Na een frontendmigratie blijft geen parallelle legacy-shell, ongebruikte componentstijl of verborgen oude UI bestaan.
|
||||||
|
|
||||||
## Codeconventies
|
## Codeconventies
|
||||||
|
|
||||||
- Python 3.12/3.13; type hints op publieke services.
|
- Python 3.12/3.13; type hints op publieke services.
|
||||||
|
|||||||
@@ -1575,6 +1575,160 @@ tasks:
|
|||||||
note: Ervarings- en senioriteitsinvloed uit score en learning verwijderd; gebruikersgescopeerde Skillsradar
|
note: Ervarings- en senioriteitsinvloed uit score en learning verwijderd; gebruikersgescopeerde Skillsradar
|
||||||
met actuele bronselectie, vraagbewijs, profieldekking, leerkansen, uitgebreide skillfilters en 2560/1440/390-browsercontrole
|
met actuele bronselectie, vraagbewijs, profieldekking, leerkansen, uitgebreide skillfilters en 2560/1440/390-browsercontrole
|
||||||
voltooid.
|
voltooid.
|
||||||
|
- id: VR-209
|
||||||
|
title: Stitch frontendcontract, audit en cockpit-shell
|
||||||
|
status: done
|
||||||
|
priority: P0
|
||||||
|
requirement_ids:
|
||||||
|
- PR-040
|
||||||
|
- PR-050
|
||||||
|
- PR-051
|
||||||
|
- NFR-004
|
||||||
|
- NFR-008
|
||||||
|
depends_on:
|
||||||
|
- VR-208
|
||||||
|
summary: Leg de volledige negen-schermenaudit en fidelitymatrix vast en vervang het globale design system, de
|
||||||
|
shell, navigatie en generieke browserstates door de Stitch Intelligence Cockpit.
|
||||||
|
acceptance_criteria:
|
||||||
|
- Contract en integratieplan bevatten alle negen schermen, afmetingen, componenten, route/data-mapping, fidelitydoelen,
|
||||||
|
responsive/accessibilityrisico's en legacy-opruiming.
|
||||||
|
- De enige productieshell gebruikt de lokale Stitch-tokens, compacte navigatie, contextbar, mobile drawer, skiplink,
|
||||||
|
focusstaten en echte statusdata zonder externe runtimeassets.
|
||||||
|
- Login, meldingen, lege toestanden en generieke foutstates gebruiken dezelfde visuele taal en blijven zonder
|
||||||
|
JavaScript bruikbaar.
|
||||||
|
verification:
|
||||||
|
- uv run pytest tests/integration/test_views.py tests/e2e/test_vr114_browser_and_accessibility.py
|
||||||
|
- uv run ruff check .
|
||||||
|
primary_paths:
|
||||||
|
- docs/design/STITCH_FRONTEND_CONTRACT.md
|
||||||
|
- docs/design/STITCH_INTEGRATION_PLAN.md
|
||||||
|
- templates/base.html
|
||||||
|
- static/css/app.css
|
||||||
|
- static/js/app.js
|
||||||
|
result:
|
||||||
|
completed_at: '2026-07-22'
|
||||||
|
note: Volledige negen-schermenaudit, fidelitymatrix, bindend frontendcontract en nieuwe lokale Stitch-cockpit-shell
|
||||||
|
met gesplitst design system, mobile navigatie en generieke foutstates; relevante tests groen.
|
||||||
|
- id: VR-210
|
||||||
|
title: Stitch dashboard en vacature-intelligence
|
||||||
|
status: ready
|
||||||
|
priority: P0
|
||||||
|
requirement_ids:
|
||||||
|
- PR-040
|
||||||
|
- PR-041
|
||||||
|
- PR-050
|
||||||
|
- NFR-004
|
||||||
|
depends_on:
|
||||||
|
- VR-209
|
||||||
|
summary: Vervang dashboard, vacatureverkenner en detail door de echte Stitch-composities met database-aggregaties,
|
||||||
|
uitlegbare scores, provenance en veilige acties.
|
||||||
|
acceptance_criteria:
|
||||||
|
- Dashboard toont echte dag-, match-, filter-, duplicaat-, sollicitatie-, activiteit- en brongezondheidsdata met
|
||||||
|
beste matches als dominante feed.
|
||||||
|
- De explorer heeft serverfilters, actieve chips, paginering en een brede master/detailcompositie die querystring
|
||||||
|
en browser-back behoudt.
|
||||||
|
- Het detail toont scorebewijs, inhoud, provenance, versies, duplicaten, sollicitatiestatus en een responsive
|
||||||
|
actiedock; ervaring telt nergens als scorefactor en externe links blijven veilig.
|
||||||
|
verification:
|
||||||
|
- uv run pytest tests/integration/test_views.py tests/unit/test_scoring.py tests/unit/test_relevance.py
|
||||||
|
primary_paths:
|
||||||
|
- apps/core/views.py
|
||||||
|
- apps/jobs/views.py
|
||||||
|
- apps/jobs/services/cockpit.py
|
||||||
|
- templates/dashboard/today.html
|
||||||
|
- templates/jobs/list.html
|
||||||
|
- templates/jobs/detail.html
|
||||||
|
- id: VR-211
|
||||||
|
title: Stitch pipeline, radarprofiel en brongezondheid
|
||||||
|
status: ready
|
||||||
|
priority: P0
|
||||||
|
requirement_ids:
|
||||||
|
- PR-002
|
||||||
|
- PR-050
|
||||||
|
- PR-051
|
||||||
|
- NFR-004
|
||||||
|
depends_on:
|
||||||
|
- VR-210
|
||||||
|
summary: Vervang sollicitaties, zoekprofiel en bronnen door de functionele Stitch-pipeline, radarconfigurator
|
||||||
|
en source-healthinterface.
|
||||||
|
acceptance_criteria:
|
||||||
|
- Sollicitaties bieden kanban en lijstfallback, gebruikersgescopeerde POST-statuswijziging, deadlines, contact-
|
||||||
|
en timelinecontext.
|
||||||
|
- Het zoekprofiel groepeert echte selecties, toont revisie-informatie en een server-rendered preview uit de laatste
|
||||||
|
echte match.
|
||||||
|
- Bronnen tonen policy, runs, fout/succes, volgende planning, parser, tellingen, retry en bestaande beveiligde
|
||||||
|
import-/mailboxflows.
|
||||||
|
verification:
|
||||||
|
- uv run pytest tests/integration/test_applications.py tests/integration/test_profile_preferences.py tests/integration/test_sources_manual_import.py
|
||||||
|
tests/integration/test_source_health.py
|
||||||
|
primary_paths:
|
||||||
|
- apps/jobs/views.py
|
||||||
|
- apps/profiles/views.py
|
||||||
|
- apps/sources/views.py
|
||||||
|
- templates/applications
|
||||||
|
- templates/profiles
|
||||||
|
- templates/sources/list.html
|
||||||
|
- id: VR-212
|
||||||
|
title: Stitch automation, activiteit en werkgeversintelligence
|
||||||
|
status: ready
|
||||||
|
priority: P0
|
||||||
|
requirement_ids:
|
||||||
|
- PR-040
|
||||||
|
- PR-050
|
||||||
|
- PR-051
|
||||||
|
- NFR-002
|
||||||
|
- NFR-004
|
||||||
|
depends_on:
|
||||||
|
- VR-211
|
||||||
|
summary: Bouw de ontbrekende activiteit- en werkgeversroutes en vervang systeemstatus door een echte afgeleide
|
||||||
|
automationcockpit.
|
||||||
|
acceptance_criteria:
|
||||||
|
- Automation toont echte pipeline-, run-, score-, notificatie-, scheduling-, database- en configuratiestatus en
|
||||||
|
labelt niet-beschikbare infrastructuur eerlijk.
|
||||||
|
- Activiteit combineert betrouwbare, gebruikersgescopeerde gebeurtenissen in een filterbare, gelinkte en gepagineerde
|
||||||
|
tijdlijn.
|
||||||
|
- Werkgevers index/detail aggregeren echte werkgever-, vacature-, score-, bron- en sollicitatiedata zonder CRM-
|
||||||
|
of marktclaims te verzinnen.
|
||||||
|
verification:
|
||||||
|
- uv run pytest tests/integration/test_intelligence_views.py tests/integration/test_views.py
|
||||||
|
primary_paths:
|
||||||
|
- apps/jobs/services/activity.py
|
||||||
|
- apps/jobs/services/employer_intelligence.py
|
||||||
|
- apps/jobs/views.py
|
||||||
|
- apps/jobs/urls.py
|
||||||
|
- apps/core/views.py
|
||||||
|
- templates/activity
|
||||||
|
- templates/employers
|
||||||
|
- templates/system/status.html
|
||||||
|
- id: VR-213
|
||||||
|
title: Stitch responsive validatie en legacyverwijdering
|
||||||
|
status: ready
|
||||||
|
priority: P0
|
||||||
|
requirement_ids:
|
||||||
|
- NFR-004
|
||||||
|
- NFR-008
|
||||||
|
- NFR-009
|
||||||
|
depends_on:
|
||||||
|
- VR-212
|
||||||
|
summary: Rond de high-fidelity replacement af met browsermatrix, accessibility, elf screenshots, legacy-audit,
|
||||||
|
documentatie en volledige regressiegate.
|
||||||
|
acceptance_criteria:
|
||||||
|
- Alle negen schermen en kernflows zijn visueel en functioneel gecontroleerd tussen 360 en 1440 px, met aanvullende
|
||||||
|
ultrawidecontrole en schone browserconsole.
|
||||||
|
- Elf verplichte validatiescreenshots bestaan; majeure afwijkingen, accessibility- en querybevindingen staan in
|
||||||
|
het eindrapport.
|
||||||
|
- Oude visuele selectors, dode partials/JS en parallelle shellmarkup zijn verwijderd; de Stitch-frontend is de
|
||||||
|
enige normale productiefrontend.
|
||||||
|
- De volledige verificatiegate slaagt en backlog, projectstatus, traceability, README en changelog zijn bijgewerkt.
|
||||||
|
verification:
|
||||||
|
- ./scripts/codex_verify.sh
|
||||||
|
- Browsermatrix 1440x900, 1280x800, 1024x768, 768x1024, 390x844 en 360x800
|
||||||
|
primary_paths:
|
||||||
|
- static/css/app.css
|
||||||
|
- static/js/app.js
|
||||||
|
- tests/e2e
|
||||||
|
- artifacts/visual-validation
|
||||||
|
- docs/design/STITCH_INTEGRATION_REPORT.md
|
||||||
- id: VR-301
|
- id: VR-301
|
||||||
title: Multi-tenant SaaS, teams en billing
|
title: Multi-tenant SaaS, teams en billing
|
||||||
status: deferred
|
status: deferred
|
||||||
|
|||||||
@@ -0,0 +1,50 @@
|
|||||||
|
# Stitch frontendcontract
|
||||||
|
|
||||||
|
## Bindende productregel
|
||||||
|
|
||||||
|
De normale VacatureRadar-productiefrontend is de functionele vertaling van de negen schermen uit `docs/design/stitch-reference/`. `screen.png` bepaalt compositie en visuele uitkomst, `code.html` verduidelijkt structuur en interacties en `intelligence_cockpit/DESIGN.md` bepaalt de visuele tokens. De voormalige MVP-frontend heeft geen behoudsprioriteit.
|
||||||
|
|
||||||
|
Backend-, beveiligings- en toegankelijkheidsregels blijven altijd leidend. Een Stitch-element wordt alleen aangepast wanneer echte data ontbreekt, een actie onveilig of niet ondersteund is, Nederlandse content meer ruimte vraagt of WCAG 2.2 AA dat vereist. Zo'n afwijking wordt in `STITCH_INTEGRATION_REPORT.md` gemotiveerd.
|
||||||
|
|
||||||
|
## Verplichte visuele taal
|
||||||
|
|
||||||
|
- Donkere navy cockpit met `#060e20`, `#0b1326`, `#131b2e`, `#171f33`, `#222a3d` en `#2d3449` als tonale lagen.
|
||||||
|
- Signal Cyan `#00f5ff` voor primaire acties en focus, Emerald `#4edea3` voor aantoonbaar gezonde/positieve status, indigo voor automation en koraal/amber voor fout of aandacht.
|
||||||
|
- Compacte technische zijbalk, contextuele topbar, brede datacanvas en adaptieve intelligence-rails.
|
||||||
|
- Geist-achtige systeemheadline, Inter-achtige systeembody en JetBrains Mono-achtige systeemmono; geen externe fontrequest.
|
||||||
|
- 8px basisritme, 16/24/32px stackafstand, fijne borders, kleine radii en terughoudende glows. Tonale hiërarchie vervangt zware shadows.
|
||||||
|
- Lokale inline SVG-iconen. Geen Material Symbols-, Tailwind-, font- of andere runtime-CDN.
|
||||||
|
|
||||||
|
## Functioneel contract
|
||||||
|
|
||||||
|
- Alle cijfers zijn database-aggregaties of tonen expliciet `Nog niet gemeten`, `Onbekend` of `Niet geconfigureerd`.
|
||||||
|
- Alle mutaties zijn POST + CSRF, gebruikersgescopeerd en gebruiken bestaande services waar bedrijfsbeslissingen nodig zijn.
|
||||||
|
- Vacature-HTML is alleen de gesanitized backendwaarde; externe links gebruiken `noopener noreferrer nofollow` en openen uitsluitend na een expliciete gebruikersactie.
|
||||||
|
- Geen automatische sollicitatie, platformloginbot, CAPTCHA-omzeiling, drag-and-drop zonder persistente fallback of control zonder werkend effect.
|
||||||
|
- Named URLs, querystrings, browser-back, paginering en kernflows werken zonder JavaScript. JavaScript verbetert alleen navigatie, disclosures, tabs, dialogen, viewtoggles en micro-interacties.
|
||||||
|
|
||||||
|
## Schermcontract
|
||||||
|
|
||||||
|
De volgende pagina's zijn verplicht en gebruiken echte data:
|
||||||
|
|
||||||
|
1. Dashboard: dagconclusie, matches, filters/deduplicatie, sollicitatieopvolging, activiteit en bronstatus.
|
||||||
|
2. Vacatureverkenner: master/detail op breed scherm, server-side filters, scorebewijs en snelle veilige acties.
|
||||||
|
3. Vacaturedetail: jobhero, score-intelligence, inhoud, provenance, versies, duplicaten en sticky acties.
|
||||||
|
4. Sollicitatiepipeline: toegankelijke kanban en lijstfallback met persistente statuswijziging.
|
||||||
|
5. Zoekprofiel: gegroepeerde radarconfigurator met echte server-rendered preview en revisie-informatie.
|
||||||
|
6. Bronnen: brongezondheid, policies, runtelemetrie, retry/review en beveiligde imports/mailboxen.
|
||||||
|
7. Automatisering: echte pipeline-, queue-, database-, notificatie- en bronstatus zonder gesimuleerde liveclaims.
|
||||||
|
8. Activiteit: gepagineerde, filterbare tijdlijn uit bestaande auditwaardige modellen.
|
||||||
|
9. Werkgevers: index/detail uit genormaliseerde werkgever-, vacature-, score-, bron- en sollicitatiedata.
|
||||||
|
|
||||||
|
Ook login, foutpagina's, lege toestanden en generieke formulieren gebruiken dezelfde visuele taal.
|
||||||
|
|
||||||
|
## Responsive en toegankelijkheid
|
||||||
|
|
||||||
|
- Desktop volgt de Stitch-silhouette op 1440×900 en 1280×800; 1920/2560px gebruikt extra kolommen of ruimte en geen smalle gecentreerde strook.
|
||||||
|
- Op 1024/768px worden rails logisch gestapeld. Op 390/360/320px wordt de zijbalk een focusveilige drawer, tabellen krijgen kaart- of horizontale scrollpresentatie en sticky acties blijven bruikbaar.
|
||||||
|
- Eén `h1`, geldige landmarks, skiplink, zichtbare `:focus-visible`, tekst naast statuskleur, expliciete labels/fouten, 44px touchdoelen waar haalbaar, reduced motion en bruikbaarheid bij 200% zoom.
|
||||||
|
|
||||||
|
## Afrondingscontract
|
||||||
|
|
||||||
|
Een frontendfase is pas klaar na relevante tests en een diffreview. De volledige vervanging vereist elf browserbeelden onder `artifacts/visual-validation/`, een schone console op de kernroutes, een legacyzoekaudit, `scripts/codex_verify.sh`, bijgewerkte backlog/traceability/projectstatus en `STITCH_INTEGRATION_REPORT.md`. Er bestaat daarna geen feature flag of alternatieve normale route naar de oude frontend.
|
||||||
@@ -1,79 +1,74 @@
|
|||||||
# Stitch-integratieplan
|
# Stitch-integratieplan
|
||||||
|
|
||||||
## Doel en bronhiërarchie
|
## Auditbaseline
|
||||||
|
|
||||||
Deze integratie vertaalt het Stitch-project **VacatureRadar Intelligence Cockpit** naar de bestaande Django-applicatie. De functionele repository blijft leidend voor routes, data, beveiliging, gebruikersscope en acties. Stitch levert de visuele taal en compositie, niet de domeinwaarheid.
|
De ontvangen ZIP `stitch_vacatureradar_intelligence_cockpit.zip` is geïnventariseerd en bytegelijk aan de blijvende referenties onder `docs/design/stitch-reference/`. Er is geen afzonderlijke aanvullende `design.md` aangetroffen. De referenties worden niet als runtimefrontend geserveerd en zijn via `.dockerignore` uit de productieimage gehouden.
|
||||||
|
|
||||||
De volledige ontvangen referentie staat in `docs/design/stitch-reference/`. Deze map en de validatiescreenshots zijn uitgesloten van de productiecontainer via `.dockerignore`; er worden geen externe Tailwind-, font-, icoon-, afbeelding- of JavaScript-CDN's aan de applicatie toegevoegd.
|
| Stitch-scherm | Afmeting | Dominante compositie | Productieroute / template |
|
||||||
|
| --- | ---: | --- | --- |
|
||||||
|
| Dashboard overzicht | 1480×1600 | KPI-strip, brede matchfeed, actie-/activiteitsrail | `/` · `dashboard/today.html` |
|
||||||
|
| Nieuwe matches | 1600×1550 | filterbar, masterlijst en geselecteerde intelligence | `/jobs/` · `jobs/list.html` |
|
||||||
|
| Vacature intelligence detail | 1600×1525 | hero, scorepaneel, inhoudtabs, intelligence-rail en actiedock | `/jobs/<uuid>/` · `jobs/detail.html` |
|
||||||
|
| Sollicitatie pipeline | 1600×1280 | horizontale kanban met lijsttoggle | `/jobs/applications/` · `applications/list.html` |
|
||||||
|
| Zoekprofiel editor | 1600×1280 | configuratiestappen met vaste radarpreview | `/profiles/<id>/` · `profiles/edit.html` |
|
||||||
|
| Bronnenbeheer & gezondheid | 1600×1340 | bronstatusrijen, disclosures en integriteitspanelen | `/sources/` · `sources/list.html` |
|
||||||
|
| Automatisering centrum | 1540×1600 | pipelinefasen, operationele KPI's, brontabel en lograil | `/system/` · `system/status.html` |
|
||||||
|
| Activiteitenlogboek | 1600×1441 | filterbare technische tijdlijn met samenvatting | `/activity/` · `activity/list.html` |
|
||||||
|
| Werkgevers intelligence | 1600×931 | employer table met intelligence-detailrail | `/employers/` en `/employers/<id>/` · employer templates |
|
||||||
|
|
||||||
## VR-119: compositie als primaire bron
|
## Tokens en herbruikbare bouwstenen
|
||||||
|
|
||||||
Na productfeedback is de integratie aangescherpt: Stitch bepaalt nu niet alleen kleur, vorm en typografie, maar ook de dominante schermcompositie en informatiedichtheid. Alleen aantoonbaar onveilige of fictieve onderdelen worden vervangen door een functioneel equivalent met echte repositorydata.
|
De negen HTML-referenties gebruiken dezelfde palette- en typografieschaal: darkest/surface `#060e20`/`#0b1326`, vijf tonale containers, Signal Cyan `#00f5ff`, Emerald `#4edea3`, lichte hoofdtekst `#dae2fd`, gedempte tekst `#b9caca`, koraal `#ffb4ab`, 8px basisritme en 2–8px radii. Headings zijn 20/32px, body 14/16px en metadata 12/13px mono. Productie vertaalt dit naar CSS custom properties en lokale SVG's.
|
||||||
|
|
||||||
- De utilitybar neemt de brede zoekbalk en directe profielactie over.
|
Herbruikbare componenten: app-shell, nav-link, contextbar, page-heading, KPI-tile, tonal-panel, status-chip, score-ring/tile, data-table, intelligence-rail, timeline-event, filterbar/drawer, disclosure, empty state, form-section, sticky action dock en mobile bottom navigation. De componenten dragen semantiek; paginatemplates leveren alleen domeindata en compositie.
|
||||||
- Het dashboard gebruikt een enkelkoloms matchfeed met vaste radar-/actierail; één resultaat blijft daardoor een volwaardig primair signaal.
|
|
||||||
- De vacaturelijst toont een dichte intelligencefeed met de laatste gebruikersgescopeerde score via databaseannotaties.
|
|
||||||
- Het detail combineert scorehero, drie echte kernmetingen, analyse en een vaste menselijke actierail.
|
|
||||||
- Pipeline, profieleditor, bronkaarten en systeemtelemetrie volgen de Stitch-kolommen en panelhiërarchie.
|
|
||||||
- Handmatige import blijft volledig beschikbaar, maar staat compact achter een disclosure zodat brongezondheid visueel voorrang krijgt.
|
|
||||||
|
|
||||||
De bedoelde “circa 90% Stitch” slaat hiermee op overgenomen visuele structuur en interactiehiërarchie, niet op het kopiëren van fictieve content, externe assets, auto-apply of routes waarvoor het domeinmodel geen betrouwbare data bezit.
|
## Data- en routeaudit
|
||||||
|
|
||||||
## VR-206: premium radar en ultrawide compositie
|
| Component | Echte databron / service | Ontbrekend of aangepaste presentatie |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| Dag-KPI's en beste matches | `JobPosting`, laatste `ScoreRun`, `Application`, `SourceRun`; nieuwe cockpit-queryservice | Geen fictieve trendpercentages; onbekende meting krijgt tekststatus. |
|
||||||
|
| Verkenner | `JobPosting` met laatste profielscore, relevance-query, filters en paginering | Stitch quick-apply wordt detail/dossier/externe bronactie. |
|
||||||
|
| Detailintelligence | `ScoreRun`, `JobSourceAlias`, `FieldProvenance`, `JobVersion`, `Feedback`, `Application` | Geen ervarings-/senioriteitsscore; geen kaart of salaris wanneer onbekend. |
|
||||||
|
| Pipeline | gebruikersgescopeerde `Application` en `ApplicationTimelineEvent` | Geen drag-and-dropvereiste; statuswijziging krijgt toegankelijke POST-selectie. |
|
||||||
|
| Radarconfiguratie | `SearchProfileForm`, profielrevisies, laatste scores | Preview is server-rendered uit laatste echte match; geen demo-vacature. |
|
||||||
|
| Brongezondheid | `Source`, laatste `SourceRun`, `collect_source_health`, mailboxverbindingen | Geen algemene healthyclaim zonder runbewijs; technische fouttekst begrensd. |
|
||||||
|
| Automatisering | readiness, `SourceRun`, jobs, scores, notificatie-outbox en configuratie | Uptime/worker/AI/browserstatus alleen indien afleidbaar, anders niet geconfigureerd. |
|
||||||
|
| Activiteit | nieuwe queryservice over `SourceRun`, `JobVersion`, `Feedback`, `ApplicationTimelineEvent`, outbox en `ProfileRevision` | Modellen vormen één read-only tijdlijn; geen nieuw generiek auditmodel nodig. |
|
||||||
|
| Werkgevers | nieuwe queryservice over `Employer`, jobs, scores, aliases, sources en applications | Sector/locatie/notities alleen indien opgeslagen; geen CRM- of contactpersoonfinder. |
|
||||||
|
|
||||||
De volledige audit maakt de Stitch-compositie ook functioneel dominant. De vacaturefeed start met
|
De twee nieuwe domeinervaringen worden read-only geïmplementeerd in `apps/jobs/services/activity.py` en `apps/jobs/services/employer_intelligence.py`. Dit houdt aggregatielogica uit views en vermijdt een migratie. Querysets gebruiken annotaties, `select_related`, `prefetch_related` en begrensde lijsten om N+1 te voorkomen.
|
||||||
een brede commandofilter, toont beslisbare score-evidence en krijgt op breed desktop een vaste
|
|
||||||
radarcontext. Vanaf 1850 px worden matchfeeds tweekoloms; de shell benut maximaal 2240 px in plaats
|
|
||||||
van de vroegere 1280 px. Op mobiel worden filters, sectiekoppen en acties lineair gestapeld.
|
|
||||||
|
|
||||||
De visuele hiërarchie is gekoppeld aan een deterministische IT-grens: niet-IT en hard uitgesloten
|
## Fidelitymatrix
|
||||||
matches zijn standaard afwezig. Het volledige archief en verborgen scores blijven expliciet
|
|
||||||
inspecteerbaar, zodat premium rust niet ten koste gaat van auditbaarheid.
|
|
||||||
|
|
||||||
## Visuele richting
|
| Scherm | Stitch-element | Echte implementatie | Fidelitydoel | Noodzakelijke aanpassing | Verificatie |
|
||||||
|
| --- | --- | --- | --- | --- | --- |
|
||||||
|
| Dashboard | vier KPI's + matchfeed + rail | actuele job/score/source/application-aggregaties | zeer hoog | trends alleen bij meetbaar tijdvenster | desktop/mobile screenshot + contexttests |
|
||||||
|
| Nieuwe matches | split explorer | gepagineerde serverlijst met geselecteerd echt resultaat | zeer hoog | URL blijft deelbaar; geen SPA-state | filters/back/pagination tests + screenshot |
|
||||||
|
| Detail | hero, score rail, tabs, dock | scorebewijs, provenance, versies, veilige acties | hoog | geen nepbeeld, quick apply of ervaringseffect | safe-link/sanitization tests + screenshots |
|
||||||
|
| Pipeline | kanban + lijsttoggle | statuskolommen en echte POST statusselectie | zeer hoog | geen drag-and-drop als enige invoer | POST/user-scope tests + screenshot |
|
||||||
|
| Zoekprofiel | secties + live preview | gegroepeerde widgets en laatste echte match | hoog | preview ververst na server-save | form/revision tests + screenshot |
|
||||||
|
| Bronnen | health rows + details | bronkaart/tabel, runbewijs, retry en imports | zeer hoog | policy/securityflows blijven zichtbaar | retry/policy tests + screenshot |
|
||||||
|
| Automatisering | pipeline en logs | afgeleide fasestatus, run- en outboxtelemetrie | hoog | onbekende infrastructuur eerlijk gelabeld | view-context tests + screenshot |
|
||||||
|
| Activiteit | filtertijdlijn | gepagineerde multi-model read service | zeer hoog | alleen objecttypen met betrouwbare auditdata | filter/scope/pagination tests + screenshot |
|
||||||
|
| Werkgevers | tabel + drawer | index met geselecteerde detailrail en aparte detail-URL | zeer hoog | geen AI-contactfinder of verzonnen sector | aggregate/scope/empty tests + screenshot |
|
||||||
|
|
||||||
- Donkere cockpit als standaard: diep marineblauw, tonale panelen, fijne slate-randen, signaalcyaan voor primaire interactie, emerald voor aantoonbaar gezonde status en koraal/amber voor fouten of aandacht.
|
## Responsive strategie en risico's
|
||||||
- Een volwaardige lichte variant met dezelfde semantische tokens en contrastverhoudingen.
|
|
||||||
- Systeemfonts als lokale, snelle fallback voor Geist/Inter; monospace systeemfonts voor metadata. Geen netwerkfonts.
|
|
||||||
- Een vaste desktopzijbalk en compacte utilitybar, op mobiel een toegankelijke uitschuifnavigatie met overlay en focusbehoud.
|
|
||||||
- Dichte informatieweergave met een consistente 8px-ritmiek, zonder decoratieve data die niet uit het domeinmodel komt.
|
|
||||||
|
|
||||||
## Schermmapping
|
De desktop shell gebruikt een 256px sidebar en een flexibele canvas tot ultrawide. Dashboard, explorer en detail schakelen bij 1180–1360px van twee/three-column naar gestapeld; op ultrawide worden rails vast en feeds ruimer. Kanban behoudt gecontroleerde horizontale inhoudscroll, niet paginascroll. Tabellen krijgen bij mobiel semantische kaartregels of een gelabelde scrollcontainer. De profielpreview wordt onder de editor geplaatst; de vacatureselectie opent als volledige detailkaart.
|
||||||
|
|
||||||
| Stitch-scherm | Django-route/template | Integratie | Bewuste aanpassing |
|
Belangrijkste accessibilityrisico's uit het prototype zijn icon-only controls, kleur-only status, lage metadata-contrastwaarden, vaste hoogtes, horizontale kanban en JavaScript-only toggles. Productie voegt namen, zichtbare statustekst, voldoende contrast, natuurlijke hoogte, keyboardbediening, serverfallbacks, focusmanagement en `prefers-reduced-motion` toe.
|
||||||
| --- | --- | --- | --- |
|
|
||||||
| Dashboard Overzicht | `/`, `dashboard/today.html` | KPI-strip, sterke-matchkaarten, bronstatus en snelle route-acties | Geen verzonnen trendgrafiek, notificaties of scannerstatus; alleen actuele databasecijfers. |
|
|
||||||
| Nieuwe Matches | `/jobs/`, `jobs/list.html` | Filterwerkbalk en responsive vacature-intelligentielijst | Geen client-side master/detail-SPA; detail blijft een eigen, deelbare route. |
|
|
||||||
| Vacature Intelligentie Detail | `/jobs/<uuid>/`, `jobs/detail.html` | Scorehero, componentanalyse, bronhistorie en sticky acties | Geen quick/direct apply, logo of kaart zonder betrouwbare brondata. Externe vacature opent alleen expliciet in een nieuw tabblad. |
|
|
||||||
| Sollicitatie Pipeline | `/jobs/applications/`, `applications/list.html` | Statuskolommen op breed scherm, lineaire kaarten op smal scherm | Alleen bestaande dossierstatussen en gebruikersgescopeerde data. Geen drag-and-drop zonder backendcontract. |
|
|
||||||
| Zoekprofiel Editor | `/profiles/` en `/profiles/<id>/` | Profielsummary, gegroepeerde editor, hulp- en risicotekst | Geen live scorepreview die servervalidatie kan tegenspreken. JSON-velden blijven functioneel maar krijgen duidelijke uitleg. |
|
|
||||||
| Bronnenbeheer & Gezondheid | `/sources/`, `sources/list.html` | KPI's, importpaneel, bronkaarten, uitklapbare runinformatie en bulkacties | Bestaande policy-, SSRF-, rate-limit- en CSRF-flows blijven intact. Geen algemene “gezond”-claim zonder readinessdata. |
|
|
||||||
| Automatisering Centrum | `/system/`, `system/status.html` | Readiness, echte bron-/vacaturestatus en veilige beheeropdrachten | Geen gefingeerde worker uptime, latency, cronprogressie of logregels. |
|
|
||||||
| Activiteitenlogboek | geen zelfstandige route | Recente, betrouwbare gebruikersactiviteit wordt op dashboard/dossiers getoond waar het model die al bezit | Geen samengestelde auditfeed: de huidige modellen hebben geen uniform, volledig gebruikersgescopeerd eventcontract. Dit wordt niet geforceerd. |
|
|
||||||
| Werkgevers Intelligentie | geen zelfstandige route | Werkgever blijft context op vacature- en sollicitatiekaarten | Geen “intelligence”-pagina zolang contact-, reputatie- en aggregatievelden niet betrouwbaar bestaan; dit voorkomt fictieve inzichten. |
|
|
||||||
|
|
||||||
## Componenten en templatestructuur
|
## Migratievolgorde en legacy-opruiming
|
||||||
|
|
||||||
- `base.html`: skiplink, merkblok, utilitybar, desktop/mobile navigatie, gebruikerspaneel, meldingen en themawisselaar.
|
1. Contract, tokens, icon sprite en volledig nieuwe shell.
|
||||||
- `templates/components/`: herbruikbare SVG-iconen, paginakop, lege toestand, statuschip en vacaturekaart waar template-includes de duplicatie daadwerkelijk verlagen.
|
2. Dashboard, explorer en detail plus benodigde querycontext.
|
||||||
- `static/css/app.css`: uitsluitend semantische tokens en componentklassen; dark/light via `data-theme`, met systeemvoorkeur als fallback.
|
3. Pipeline, zoekprofiel en bronnen zonder bestaande POST/contracts te breken.
|
||||||
- `static/js/app.js`: alleen navigatie, details-disclosures en themaopslag. Alle kernacties blijven normale links of formulieren.
|
4. Automatisering, activiteit en werkgevers met nieuwe read services/routes.
|
||||||
|
5. Login, fouttemplates, mobile/adaptive lagen, browsercontrole en accessibility.
|
||||||
|
6. Verwijder oude selectors, dubbele cards/panels, achterhaalde JS en screenshots die niet tot de huidige validatie horen; zoek alle productietemplates na op de oude shell.
|
||||||
|
|
||||||
## Functionele en veiligheidsgrenzen
|
`templates/base.html`, alle niet-e-mailtemplates onder `templates/`, `static/css/app.css` en `static/js/app.js` worden vervangen of fundamenteel herschikt. E-mailtemplates blijven buiten de cockpit omdat zij geen browserfrontend zijn. Named routes en bestaande formulier-/securitycontracts blijven behouden.
|
||||||
|
|
||||||
- Alle mutaties blijven POST + CSRF en gebruiken de bestaande services/views.
|
## Gate en artifacts
|
||||||
- Sanitized vacature-HTML blijft het enige HTML-fragment dat met `safe` wordt gerenderd.
|
|
||||||
- Externe URL's behouden `noopener noreferrer nofollow` en worden nooit automatisch geopend.
|
|
||||||
- Bronimport, retry en bulkreview veranderen inhoudelijk niet.
|
|
||||||
- Er worden geen secrets, persoonsgegevens, nepstatistieken, externe assets of nieuwe productieafhankelijkheden toegevoegd.
|
|
||||||
|
|
||||||
## Responsive en toegankelijkheid
|
Per fase: Ruff format/check, relevante tests, diffreview en logische commit. Eindmatrix: 1440×900, 1280×800, 1024×768, 768×1024, 390×844 en 360×800; aanvullende 1920/2560px ultrawidecontrole. De elf verplichte beelden komen onder `artifacts/visual-validation/`; het eindrapport legt fidelity, afwijkingen, consolebevindingen, accessibility, queries en testresultaten vast.
|
||||||
|
|
||||||
- Validatiebreedtes: 2560×1440, 1920×1080, 1440×900, 1024×768, 390×844 en 320×800.
|
|
||||||
- Eén `h1` per pagina, semantische landmarks, zichtbare focus, toetsenbordbediening, 44px touchdoelen waar praktisch, tekstlabels naast statuskleur en een robuuste skiplink.
|
|
||||||
- Tabellen schakelen op smalle schermen naar kaartachtige rijen of blijven gecontroleerd horizontaal scrolbaar wanneer kolomvergelijking essentieel is.
|
|
||||||
- Animatie is subtiel en volledig uitgeschakeld bij `prefers-reduced-motion`.
|
|
||||||
|
|
||||||
## Verificatie
|
|
||||||
|
|
||||||
Na elke implementatiefase worden formattering, Ruff en relevante tests uitgevoerd. De afronding vereist `scripts/codex_verify.sh`, responsieve browserflows, console-/netwerkcontrole en screenshots per hoofdroute in beide thema's waar dat het ontwerp valideert.
|
|
||||||
|
|||||||
+6
-778
@@ -1,778 +1,6 @@
|
|||||||
:root,
|
@import url("tokens.css");
|
||||||
:root[data-theme="dark"] {
|
@import url("base.css");
|
||||||
color-scheme: dark;
|
@import url("layout.css");
|
||||||
--bg: #0b1326;
|
@import url("components.css");
|
||||||
--bg-deep: #060e20;
|
@import url("pages.css");
|
||||||
--surface: #171f33;
|
@import url("responsive.css");
|
||||||
--surface-low: #131b2e;
|
|
||||||
--surface-high: #222a3d;
|
|
||||||
--surface-bright: #2d3449;
|
|
||||||
--surface-soft: #111a2d;
|
|
||||||
--text: #dae2fd;
|
|
||||||
--text-soft: #b9caca;
|
|
||||||
--muted: #849495;
|
|
||||||
--border: #3a494a;
|
|
||||||
--border-strong: #59696a;
|
|
||||||
--primary: #00f5ff;
|
|
||||||
--primary-strong: #63f7ff;
|
|
||||||
--primary-ink: #003739;
|
|
||||||
--primary-soft: rgba(0, 245, 255, .1);
|
|
||||||
--accent: var(--primary);
|
|
||||||
--positive: #4edea3;
|
|
||||||
--positive-soft: rgba(78, 222, 163, .11);
|
|
||||||
--warning: #ffca69;
|
|
||||||
--warning-soft: rgba(255, 202, 105, .11);
|
|
||||||
--danger: #ff8f87;
|
|
||||||
--danger-soft: rgba(255, 143, 135, .11);
|
|
||||||
--indigo: #9ea5ff;
|
|
||||||
--shadow: 0 18px 55px rgba(0, 0, 0, .24);
|
|
||||||
--signal-glow: 0 0 0 1px rgba(22, 229, 238, .12), 0 0 28px rgba(22, 229, 238, .07);
|
|
||||||
--radius: 8px;
|
|
||||||
--radius-sm: 4px;
|
|
||||||
--sidebar: 256px;
|
|
||||||
--utility: 64px;
|
|
||||||
}
|
|
||||||
|
|
||||||
:root[data-theme="light"] {
|
|
||||||
color-scheme: light;
|
|
||||||
--bg: #f3f6fa;
|
|
||||||
--bg-deep: #e9eef5;
|
|
||||||
--surface: #ffffff;
|
|
||||||
--surface-low: #f7f9fc;
|
|
||||||
--surface-high: #edf2f7;
|
|
||||||
--surface-bright: #e2e9f2;
|
|
||||||
--surface-soft: #f6f9fc;
|
|
||||||
--text: #142033;
|
|
||||||
--text-soft: #2c3c52;
|
|
||||||
--muted: #617187;
|
|
||||||
--border: #d7e0eb;
|
|
||||||
--border-strong: #aebdce;
|
|
||||||
--primary: #007f87;
|
|
||||||
--primary-strong: #005e64;
|
|
||||||
--primary-ink: #f4ffff;
|
|
||||||
--primary-soft: rgba(0, 127, 135, .09);
|
|
||||||
--positive: #087f5b;
|
|
||||||
--positive-soft: rgba(8, 127, 91, .1);
|
|
||||||
--warning: #8b5b00;
|
|
||||||
--warning-soft: rgba(183, 119, 0, .11);
|
|
||||||
--danger: #b42318;
|
|
||||||
--danger-soft: rgba(180, 35, 24, .09);
|
|
||||||
--indigo: #4e56c8;
|
|
||||||
--shadow: 0 16px 44px rgba(27, 43, 65, .08);
|
|
||||||
--signal-glow: 0 0 0 1px rgba(0, 127, 135, .12), 0 12px 34px rgba(0, 70, 75, .08);
|
|
||||||
}
|
|
||||||
|
|
||||||
* { box-sizing: border-box; }
|
|
||||||
html { min-height: 100%; scroll-behavior: smooth; }
|
|
||||||
body {
|
|
||||||
min-width: 0;
|
|
||||||
min-height: 100vh;
|
|
||||||
margin: 0;
|
|
||||||
overflow-x: hidden;
|
|
||||||
background:
|
|
||||||
linear-gradient(rgba(110, 150, 190, .025) 1px, transparent 1px),
|
|
||||||
linear-gradient(90deg, rgba(110, 150, 190, .025) 1px, transparent 1px),
|
|
||||||
var(--bg);
|
|
||||||
background-size: 48px 48px;
|
|
||||||
color: var(--text);
|
|
||||||
font: 16px/1.55 Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
|
|
||||||
-webkit-font-smoothing: antialiased;
|
|
||||||
}
|
|
||||||
a { color: var(--primary-strong); text-decoration: none; }
|
|
||||||
a:hover { text-decoration: underline; }
|
|
||||||
button, input, select, textarea { font: inherit; }
|
|
||||||
button { cursor: pointer; }
|
|
||||||
code, pre, .eyebrow, .pill, .meta-label, .nav-group-label, .job-meta, .utility-title, .kpi-label {
|
|
||||||
font-family: ui-monospace, SFMono-Regular, Consolas, "Liberation Mono", monospace;
|
|
||||||
}
|
|
||||||
pre { overflow: auto; margin: 0; border-radius: var(--radius-sm); background: var(--bg-deep); padding: 18px; border: 1px solid var(--border); }
|
|
||||||
svg { fill: none; stroke: currentColor; stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; }
|
|
||||||
.icon { width: 20px; height: 20px; flex: 0 0 auto; }
|
|
||||||
.icon-sprite { position: absolute; width: 0; height: 0; overflow: hidden; }
|
|
||||||
.skip-link { position: fixed; left: 12px; top: -90px; z-index: 1000; padding: 11px 15px; background: var(--primary); color: var(--primary-ink); border-radius: var(--radius-sm); font-weight: 800; }
|
|
||||||
.skip-link:focus { top: 12px; }
|
|
||||||
.sr-only { position: absolute !important; width: 1px !important; height: 1px !important; padding: 0 !important; margin: -1px !important; overflow: hidden !important; clip: rect(0, 0, 0, 0) !important; white-space: nowrap !important; border: 0 !important; }
|
|
||||||
.mobile-only { display: none !important; }
|
|
||||||
|
|
||||||
.app-frame { min-height: 100vh; }
|
|
||||||
.sidebar {
|
|
||||||
position: fixed;
|
|
||||||
inset: 0 auto 0 0;
|
|
||||||
z-index: 60;
|
|
||||||
width: var(--sidebar);
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
padding: 18px 14px;
|
|
||||||
background: color-mix(in srgb, var(--bg-deep) 96%, transparent);
|
|
||||||
border-right: 1px solid var(--border);
|
|
||||||
box-shadow: 12px 0 42px rgba(0, 0, 0, .08);
|
|
||||||
}
|
|
||||||
.sidebar-brand { display: flex; align-items: center; justify-content: space-between; padding: 2px 6px 22px; }
|
|
||||||
.brand { min-width: 0; display: flex; align-items: center; gap: 12px; color: var(--text); }
|
|
||||||
.brand:hover { text-decoration: none; }
|
|
||||||
.brand > span:last-child { min-width: 0; display: grid; line-height: 1.2; }
|
|
||||||
.brand strong { font-size: 18px; letter-spacing: -.025em; }
|
|
||||||
.brand small { margin-top: 3px; color: var(--muted); font: 10px/1.2 ui-monospace, SFMono-Regular, Consolas, monospace; letter-spacing: .1em; text-transform: uppercase; }
|
|
||||||
.brand-mark { width: 42px; height: 42px; display: inline-grid; place-items: center; border-radius: 7px; background: var(--primary); color: var(--primary-ink); box-shadow: 0 0 24px color-mix(in srgb, var(--primary) 25%, transparent); }
|
|
||||||
.brand-mark .icon { width: 25px; height: 25px; stroke-width: 1.6; }
|
|
||||||
.nav-stack { min-height: 0; overflow-y: auto; display: grid; align-content: start; gap: 4px; padding: 4px 0; }
|
|
||||||
.nav-group-label { margin: 18px 13px 5px; color: var(--muted); font-size: 10px; text-transform: uppercase; letter-spacing: .13em; }
|
|
||||||
.nav-group-label:first-child { margin-top: 3px; }
|
|
||||||
.nav-link { position: relative; display: grid; grid-template-columns: 24px minmax(0, 1fr); align-items: center; gap: 12px; min-height: 54px; padding: 8px 12px; border: 1px solid transparent; border-radius: 7px; color: var(--muted); }
|
|
||||||
.nav-link > span { min-width: 0; display: grid; color: var(--text-soft); font-weight: 700; line-height: 1.25; }
|
|
||||||
.nav-link small { color: var(--muted); font-size: 11px; font-weight: 400; }
|
|
||||||
.nav-link:hover { background: var(--surface); border-color: var(--border); text-decoration: none; color: var(--primary); }
|
|
||||||
.nav-link.active { background: linear-gradient(90deg, var(--primary-soft), transparent); border-color: color-mix(in srgb, var(--primary) 25%, var(--border)); color: var(--primary); box-shadow: inset 3px 0 var(--primary); }
|
|
||||||
.nav-link.active > span, .nav-link.active small { color: var(--primary-strong); }
|
|
||||||
.sidebar-user { margin-top: auto; min-width: 0; display: grid; grid-template-columns: 38px minmax(0, 1fr) 10px; align-items: center; gap: 10px; padding: 15px 8px 2px; border-top: 1px solid var(--border); }
|
|
||||||
.sidebar-user > span:nth-child(2) { min-width: 0; display: grid; line-height: 1.25; }
|
|
||||||
.sidebar-user strong, .sidebar-user small { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
||||||
.sidebar-user strong { font-size: 13px; }
|
|
||||||
.sidebar-user small { color: var(--muted); font-size: 11px; }
|
|
||||||
.avatar { width: 38px; height: 38px; display: grid; place-items: center; border: 1px solid var(--border-strong); border-radius: 50%; background: var(--surface-high); color: var(--primary-strong); font-weight: 800; }
|
|
||||||
.status-dot, .signal-dot { width: 8px; height: 8px; border-radius: 50%; background: var(--positive); box-shadow: 0 0 0 4px var(--positive-soft); }
|
|
||||||
.workspace { min-height: 100vh; margin-left: var(--sidebar); }
|
|
||||||
.utilitybar { position: sticky; top: 0; z-index: 40; height: var(--utility); display: flex; align-items: center; justify-content: space-between; gap: 20px; padding: 0 clamp(18px, 3vw, 38px); background: color-mix(in srgb, var(--bg) 86%, transparent); border-bottom: 1px solid var(--border); backdrop-filter: blur(16px); }
|
|
||||||
.utilitybar-start, .utility-actions, .utility-title, .utility-link { display: flex; align-items: center; }
|
|
||||||
.utility-title { gap: 10px; color: var(--muted); font-size: 11px; text-transform: uppercase; letter-spacing: .08em; }
|
|
||||||
.utility-actions { gap: 8px; }
|
|
||||||
.utility-actions form { margin: 0; }
|
|
||||||
.utility-link, .icon-button { min-height: 42px; border: 1px solid transparent; border-radius: 6px; background: transparent; color: var(--muted); }
|
|
||||||
.utility-link { gap: 8px; padding: 8px 10px; font-weight: 650; }
|
|
||||||
button.utility-link { width: auto; }
|
|
||||||
.utility-link:hover, .icon-button:hover { border-color: var(--border); background: var(--surface-high); color: var(--text); text-decoration: none; }
|
|
||||||
.icon-button { width: 42px; display: inline-grid; place-items: center; padding: 0; }
|
|
||||||
.theme-icon-light { display: none; }
|
|
||||||
:root[data-theme="light"] .theme-icon-dark { display: none; }
|
|
||||||
:root[data-theme="light"] .theme-icon-light { display: block; }
|
|
||||||
.nav-scrim { display: none; }
|
|
||||||
.main-content { width: 100%; max-width: 2240px; margin: 0 auto; padding: clamp(28px, 2.2vw, 48px) clamp(22px, 2.4vw, 54px) 80px; }
|
|
||||||
|
|
||||||
.page-header { display: flex; align-items: flex-end; justify-content: space-between; gap: 26px; margin-bottom: 28px; }
|
|
||||||
.page-header h1, .job-hero h1 { margin: 0; font-size: clamp(30px, 3.2vw, 42px); line-height: 1.08; letter-spacing: -.035em; }
|
|
||||||
.page-header p { max-width: 760px; margin: 9px 0 0; color: var(--muted); }
|
|
||||||
.eyebrow { margin: 0 0 9px !important; color: var(--primary) !important; font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: .13em; }
|
|
||||||
.page-actions { display: flex; flex-wrap: wrap; gap: 9px; }
|
|
||||||
.section-heading { display: flex; align-items: end; justify-content: space-between; gap: 20px; margin: 34px 0 17px; }
|
|
||||||
.section-heading h2 { margin: 0; font-size: 23px; letter-spacing: -.025em; }
|
|
||||||
.section-heading p { margin: 5px 0 0; color: var(--muted); }
|
|
||||||
.section-kicker { color: var(--primary); font: 11px ui-monospace, SFMono-Regular, Consolas, monospace; text-transform: uppercase; letter-spacing: .11em; }
|
|
||||||
.title-row { display: flex; flex-wrap: wrap; align-items: center; gap: 10px; }
|
|
||||||
|
|
||||||
.button { min-height: 42px; display: inline-flex; align-items: center; justify-content: center; gap: 8px; padding: 8px 14px; border: 1px solid transparent; border-radius: 6px; background: transparent; color: var(--text); font-weight: 750; line-height: 1.2; text-decoration: none; }
|
|
||||||
.button:hover { text-decoration: none; transform: translateY(-1px); }
|
|
||||||
.button:focus-visible, .icon-button:focus-visible, .utility-link:focus-visible, input:focus-visible, select:focus-visible, textarea:focus-visible, summary:focus-visible, a:focus-visible { outline: 3px solid color-mix(in srgb, var(--primary) 40%, transparent); outline-offset: 3px; }
|
|
||||||
.button-primary { background: var(--primary); color: var(--primary-ink); box-shadow: 0 8px 22px color-mix(in srgb, var(--primary) 16%, transparent); }
|
|
||||||
.button-primary:hover { background: var(--primary-strong); }
|
|
||||||
.button-secondary { background: var(--surface-high); border-color: var(--border); }
|
|
||||||
.button-secondary:hover, .button-ghost:hover { background: var(--surface-bright); border-color: var(--border-strong); }
|
|
||||||
.button-ghost { border-color: var(--border); color: var(--text-soft); }
|
|
||||||
.button-link { color: var(--primary-strong); background: var(--primary-soft); border-color: color-mix(in srgb, var(--primary) 20%, var(--border)); }
|
|
||||||
.button-danger { color: var(--danger); border-color: color-mix(in srgb, var(--danger) 30%, var(--border)); }
|
|
||||||
.button-block { width: 100%; }
|
|
||||||
.button-small { min-height: 34px; padding: 5px 10px; font-size: 13px; }
|
|
||||||
|
|
||||||
.stats-grid { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 12px; }
|
|
||||||
.stat-card, .panel, .job-card, .auth-card, .filter-bar, .table-wrap, .source-card { background: color-mix(in srgb, var(--surface) 95%, transparent); border: 1px solid var(--border); border-radius: var(--radius); box-shadow: var(--shadow); }
|
|
||||||
.stat-card { position: relative; min-height: 126px; display: grid; align-content: center; gap: 2px; padding: 20px; overflow: hidden; }
|
|
||||||
.stat-card::after { content: ""; position: absolute; right: -38px; bottom: -48px; width: 110px; height: 110px; border: 1px solid color-mix(in srgb, var(--primary) 12%, transparent); border-radius: 50%; }
|
|
||||||
.stat-card span, .stat-card small { position: relative; z-index: 1; color: var(--muted); }
|
|
||||||
.stat-card strong { position: relative; z-index: 1; margin: 2px 0; font-size: 34px; line-height: 1; letter-spacing: -.045em; }
|
|
||||||
.stat-card.signal { border-color: color-mix(in srgb, var(--primary) 35%, var(--border)); box-shadow: var(--signal-glow); }
|
|
||||||
.kpi-label { font-size: 10px; text-transform: uppercase; letter-spacing: .09em; }
|
|
||||||
.panel { padding: clamp(19px, 2.5vw, 30px); margin-bottom: 18px; }
|
|
||||||
.panel > h2:first-child, .panel > h3:first-child { margin-top: 0; }
|
|
||||||
.panel-subtle { background: var(--surface-low); box-shadow: none; }
|
|
||||||
|
|
||||||
.dashboard-grid { display: grid; grid-template-columns: minmax(0, 1fr) 320px; gap: 20px; align-items: start; }
|
|
||||||
.dashboard-rail { position: sticky; top: calc(var(--utility) + 20px); }
|
|
||||||
.radar-summary { position: relative; overflow: hidden; background: radial-gradient(circle at 88% 10%, var(--primary-soft), transparent 35%), var(--surface); }
|
|
||||||
.radar-summary::after { content: ""; position: absolute; right: -50px; top: -55px; width: 190px; height: 190px; border: 1px solid color-mix(in srgb, var(--primary) 18%, transparent); border-radius: 50%; box-shadow: inset 0 0 0 38px color-mix(in srgb, var(--primary) 2%, transparent), inset 0 0 0 74px color-mix(in srgb, var(--primary) 3%, transparent); pointer-events: none; }
|
|
||||||
.radar-summary > * { position: relative; z-index: 1; }
|
|
||||||
.rail-list { display: grid; gap: 10px; margin: 16px 0 0; }
|
|
||||||
.rail-item { display: flex; align-items: center; justify-content: space-between; gap: 14px; padding: 12px 0; border-top: 1px solid var(--border); }
|
|
||||||
.rail-item:first-child { border-top: 0; }
|
|
||||||
.rail-item span { color: var(--muted); }
|
|
||||||
|
|
||||||
.job-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 15px; }
|
|
||||||
.job-card { position: relative; display: flex; flex-direction: column; gap: 15px; padding: 20px; transition: border-color .16s ease, transform .16s ease, box-shadow .16s ease; }
|
|
||||||
.job-card:hover { border-color: var(--border-strong); transform: translateY(-2px); }
|
|
||||||
.job-card.strong-match { border-color: color-mix(in srgb, var(--primary) 38%, var(--border)); box-shadow: var(--signal-glow); background: linear-gradient(145deg, var(--primary-soft), transparent 45%), var(--surface); }
|
|
||||||
.job-card-head { display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: 15px; align-items: start; }
|
|
||||||
.job-card h2, .job-card h3 { margin: 6px 0 3px; font-size: 20px; line-height: 1.24; letter-spacing: -.02em; }
|
|
||||||
.job-card h2 a, .job-card h3 a { color: var(--text); }
|
|
||||||
.job-card p { margin: 0; color: var(--muted); }
|
|
||||||
.score-block { min-width: 72px; display: grid; justify-items: end; }
|
|
||||||
.score-block strong { color: var(--primary); font-size: 27px; line-height: 1; letter-spacing: -.04em; }
|
|
||||||
.score-block small { margin-top: 5px; color: var(--muted); font: 9px ui-monospace, SFMono-Regular, Consolas, monospace; text-transform: uppercase; letter-spacing: .06em; }
|
|
||||||
.job-meta { display: flex; flex-wrap: wrap; gap: 7px; color: var(--muted); font-size: 11px; }
|
|
||||||
.job-meta span { display: inline-flex; align-items: center; gap: 5px; padding: 5px 7px; border-radius: 4px; background: var(--surface-high); border: 1px solid color-mix(in srgb, var(--border) 70%, transparent); }
|
|
||||||
.job-meta .icon { width: 13px; height: 13px; }
|
|
||||||
.job-actions { display: flex; flex-wrap: wrap; gap: 8px; margin-top: auto; }
|
|
||||||
.pill { display: inline-flex; align-items: center; gap: 6px; width: fit-content; padding: 4px 8px; border: 1px solid var(--border); border-radius: 999px; background: var(--surface-high); color: var(--muted); font-size: 10px; font-weight: 750; text-transform: uppercase; letter-spacing: .04em; }
|
|
||||||
.pill::before { content: ""; width: 6px; height: 6px; border-radius: 50%; background: currentColor; }
|
|
||||||
.pill-strong, .pill-allow, .pill-active, .pill-success, .pill-offer { background: var(--positive-soft); border-color: color-mix(in srgb, var(--positive) 24%, var(--border)); color: var(--positive); }
|
|
||||||
.pill-possible, .pill-review, .pill-uncertain, .pill-partial, .pill-interview { background: var(--warning-soft); border-color: color-mix(in srgb, var(--warning) 24%, var(--border)); color: var(--warning); }
|
|
||||||
.pill-hidden, .pill-deny, .pill-failed, .pill-quarantined, .pill-rejected { background: var(--danger-soft); border-color: color-mix(in srgb, var(--danger) 24%, var(--border)); color: var(--danger); }
|
|
||||||
.pill-new, .pill-applied, .pill-trial { background: var(--primary-soft); border-color: color-mix(in srgb, var(--primary) 24%, var(--border)); color: var(--primary-strong); }
|
|
||||||
.compact-list { margin: 0; padding-left: 20px; }
|
|
||||||
.compact-list li + li { margin-top: 6px; }
|
|
||||||
.compact-list.positive li::marker { color: var(--positive); }
|
|
||||||
.compact-list.caution li::marker { color: var(--warning); }
|
|
||||||
.attention { padding: 10px 12px; border-left: 3px solid var(--warning); border-radius: 4px; background: var(--warning-soft); color: var(--warning) !important; font-size: 13px; }
|
|
||||||
|
|
||||||
.filter-bar { display: flex; align-items: end; gap: 10px; padding: 14px; margin-bottom: 18px; box-shadow: none; }
|
|
||||||
.filter-bar label { display: grid; gap: 5px; }
|
|
||||||
.filter-bar .grow { flex: 1; }
|
|
||||||
.field-label { color: var(--muted); font: 10px ui-monospace, SFMono-Regular, Consolas, monospace; text-transform: uppercase; letter-spacing: .07em; }
|
|
||||||
input, select, textarea { width: 100%; min-height: 43px; padding: 9px 11px; border: 1px solid var(--border); border-radius: 5px; background: var(--surface-low); color: var(--text); }
|
|
||||||
input[type="checkbox"], input[type="radio"] { width: 22px; min-height: 22px; height: 22px; padding: 0; accent-color: var(--primary); }
|
|
||||||
input::placeholder, textarea::placeholder { color: color-mix(in srgb, var(--muted) 78%, transparent); }
|
|
||||||
textarea { resize: vertical; }
|
|
||||||
.table-wrap { overflow-x: auto; }
|
|
||||||
.data-table { width: 100%; border-collapse: collapse; min-width: 760px; }
|
|
||||||
.data-table th { text-align: left; padding: 12px 15px; background: var(--surface-low); color: var(--muted); font: 10px ui-monospace, SFMono-Regular, Consolas, monospace; text-transform: uppercase; letter-spacing: .07em; }
|
|
||||||
.data-table td { padding: 14px 15px; border-top: 1px solid var(--border); vertical-align: middle; }
|
|
||||||
.data-table tbody tr:hover { background: var(--surface-low); }
|
|
||||||
.data-table td small, .table-title + small { display: block; margin-top: 3px; color: var(--muted); }
|
|
||||||
.table-title { color: var(--text); font-weight: 760; }
|
|
||||||
.pagination { display: flex; justify-content: center; align-items: center; gap: 18px; padding: 22px; }
|
|
||||||
|
|
||||||
.detail-layout { display: grid; grid-template-columns: minmax(0, 1fr) 320px; gap: 20px; align-items: start; }
|
|
||||||
.back-link { display: inline-flex; align-items: center; gap: 6px; margin-bottom: 15px; color: var(--muted); }
|
|
||||||
.job-hero { position: relative; overflow: hidden; display: flex; justify-content: space-between; gap: 28px; padding: clamp(24px, 4vw, 40px); margin-bottom: 18px; border: 1px solid color-mix(in srgb, var(--primary) 30%, var(--border)); border-radius: var(--radius); background: radial-gradient(circle at 88% 25%, var(--primary-soft), transparent 32%), linear-gradient(145deg, var(--surface), var(--surface-low)); box-shadow: var(--signal-glow); }
|
|
||||||
.hero-subtitle { margin: 11px 0 15px; color: var(--text-soft); font-size: 17px; }
|
|
||||||
.hero-score { min-width: 136px; display: grid; justify-items: end; align-content: start; }
|
|
||||||
.hero-score strong { color: var(--primary); font-size: 54px; line-height: .95; letter-spacing: -.06em; }
|
|
||||||
.hero-score span { margin-top: 8px; color: var(--primary-strong); font: 10px ui-monospace, SFMono-Regular, Consolas, monospace; text-transform: uppercase; letter-spacing: .08em; }
|
|
||||||
.hero-score small { margin-top: 3px; color: var(--muted); }
|
|
||||||
.sticky-card { position: sticky; top: calc(var(--utility) + 20px); padding: 20px; background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius); box-shadow: var(--shadow); }
|
|
||||||
.sticky-card h2 { margin: 0 0 4px; }
|
|
||||||
.sticky-card > p { margin-top: 0; color: var(--muted); }
|
|
||||||
.stack-actions { display: grid; gap: 9px; }
|
|
||||||
.hide-form { display: grid; gap: 8px; padding-top: 9px; border-top: 1px solid var(--border); }
|
|
||||||
.hide-form label { color: var(--muted); font-size: 12px; }
|
|
||||||
.score-components { display: grid; gap: 13px; margin: 18px 0 25px; }
|
|
||||||
.score-components > div { display: grid; grid-template-columns: 120px minmax(120px, 1fr) 52px; gap: 12px; align-items: center; }
|
|
||||||
.score-components strong { text-align: right; }
|
|
||||||
.progress { height: 7px; overflow: hidden; border-radius: 999px; background: var(--surface-high); }
|
|
||||||
.progress i { display: block; height: 100%; min-width: 2px; border-radius: inherit; background: linear-gradient(90deg, var(--primary), var(--indigo)); }
|
|
||||||
.two-columns { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 22px; }
|
|
||||||
.prose { max-width: 82ch; color: var(--text-soft); }
|
|
||||||
.prose h2, .prose h3, .prose h4 { margin-top: 1.5em; color: var(--text); }
|
|
||||||
.definition-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 10px; }
|
|
||||||
.definition-grid div { padding: 12px; border: 1px solid var(--border); border-radius: 5px; background: var(--surface-low); }
|
|
||||||
.definition-grid dt { color: var(--muted); font: 10px ui-monospace, SFMono-Regular, Consolas, monospace; text-transform: uppercase; }
|
|
||||||
.definition-grid dd { margin: 4px 0 0; font-weight: 700; }
|
|
||||||
.source-list { list-style: none; padding: 0; }
|
|
||||||
.source-list li { display: flex; justify-content: space-between; gap: 16px; padding: 11px 0; border-top: 1px solid var(--border); }
|
|
||||||
.source-list span { color: var(--muted); font-size: 12px; }
|
|
||||||
|
|
||||||
.pipeline { display: grid; grid-template-columns: repeat(6, minmax(210px, 1fr)); gap: 12px; overflow-x: auto; padding: 1px 1px 14px; scroll-snap-type: x proximity; }
|
|
||||||
.pipeline-column { min-height: 320px; padding: 12px; border: 1px solid var(--border); border-radius: var(--radius); background: var(--surface-low); scroll-snap-align: start; }
|
|
||||||
.pipeline-head { display: flex; justify-content: space-between; align-items: center; gap: 10px; padding: 3px 3px 12px; }
|
|
||||||
.pipeline-head h2 { margin: 0; font-size: 14px; }
|
|
||||||
.pipeline-count { min-width: 25px; height: 25px; display: grid; place-items: center; border: 1px solid var(--border); border-radius: 50%; color: var(--muted); font-size: 11px; }
|
|
||||||
.pipeline-cards { display: grid; gap: 9px; }
|
|
||||||
.pipeline-card { padding: 14px; border: 1px solid var(--border); border-radius: 6px; background: var(--surface); box-shadow: 0 8px 20px rgba(0, 0, 0, .08); }
|
|
||||||
.pipeline-card h3 { margin: 8px 0 3px; font-size: 15px; line-height: 1.3; }
|
|
||||||
.pipeline-card p { margin: 0 0 12px; color: var(--muted); font-size: 12px; }
|
|
||||||
.pipeline-empty { padding: 20px 8px; color: var(--muted); font-size: 12px; text-align: center; border: 1px dashed var(--border); border-radius: 5px; }
|
|
||||||
|
|
||||||
.card-list { display: grid; gap: 12px; }
|
|
||||||
.profile-card, .application-card { display: flex; justify-content: space-between; align-items: center; gap: 24px; margin: 0; }
|
|
||||||
.profile-card h2, .application-card h2 { margin: 5px 0; }
|
|
||||||
.form-panel { max-width: 1100px; }
|
|
||||||
.form-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 17px; }
|
|
||||||
.form-grid label { display: grid; align-content: start; gap: 6px; color: var(--muted); font-size: 13px; font-weight: 650; }
|
|
||||||
.form-grid label > input, .form-grid label > select, .form-grid label > textarea { color: var(--text); font-weight: 400; }
|
|
||||||
.choice-field { min-width: 0; margin: 0; padding: 0; border: 0; }
|
|
||||||
.choice-field legend { margin-bottom: 9px; color: var(--muted); font-size: 13px; font-weight: 700; }
|
|
||||||
.choice-field > div { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 9px; }
|
|
||||||
.choice-field > div > div { min-width: 0; }
|
|
||||||
.choice-field label { display: flex; align-items: center; gap: 9px; min-height: 44px; padding: 9px 11px; border: 1px solid var(--border); border-radius: 10px; background: color-mix(in srgb, var(--surface) 92%, transparent); color: var(--text); font-size: 13px; font-weight: 600; cursor: pointer; }
|
|
||||||
.choice-field label:has(input:checked) { border-color: color-mix(in srgb, var(--primary) 62%, var(--border)); background: color-mix(in srgb, var(--primary) 10%, var(--surface)); }
|
|
||||||
.choice-field small { display: block; margin-top: 7px; }
|
|
||||||
.choice-field > .grouped-choices { grid-template-columns: repeat(2, minmax(0, 1fr)); align-items: start; }
|
|
||||||
.choice-field .grouped-choices > div { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); align-content: start; gap: 7px; padding: 13px; border: 1px solid var(--border); border-radius: var(--radius-sm); background: var(--surface-low); }
|
|
||||||
.choice-field .grouped-choices > div > label { grid-column: 1 / -1; min-height: 0; margin: 0 0 3px; padding: 0; border: 0; border-radius: 0; background: transparent; color: var(--text-soft); font-size: 12px; font-weight: 800; letter-spacing: .02em; cursor: default; }
|
|
||||||
.choice-field .grouped-choices > div > label:has(input:checked) { border: 0; background: transparent; }
|
|
||||||
.choice-field .grouped-choices > div > div { min-width: 0; }
|
|
||||||
.choice-field .grouped-choices > div > div > label { height: 100%; }
|
|
||||||
.form-section { grid-column: 1 / -1; margin: 12px 0 -2px; padding-top: 18px; border-top: 1px solid var(--border); }
|
|
||||||
.form-section h2 { margin: 0; font-size: 17px; }
|
|
||||||
.form-section p { margin: 4px 0 0; color: var(--muted); font-size: 12px; }
|
|
||||||
.span-2 { grid-column: 1 / -1; }
|
|
||||||
.field-error { color: var(--danger); }
|
|
||||||
.form-actions { position: sticky; bottom: 12px; z-index: 5; display: flex; justify-content: flex-end; gap: 10px; margin-top: 24px; padding: 12px; border: 1px solid var(--border); border-radius: 7px; background: color-mix(in srgb, var(--surface) 92%, transparent); backdrop-filter: blur(12px); }
|
|
||||||
|
|
||||||
.source-overview { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 12px; margin-bottom: 20px; }
|
|
||||||
.source-alert-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 12px; margin-top: 16px; }
|
|
||||||
.source-alert-card { display: flex; align-items: flex-end; justify-content: space-between; gap: 18px; padding: 16px; border: 1px solid var(--border); border-radius: var(--radius-sm); background: var(--surface-soft); }
|
|
||||||
.source-alert-card p { margin: 6px 0 0; color: var(--muted); }
|
|
||||||
.source-alert-card .button { flex: 0 0 auto; }
|
|
||||||
.source-alert-card small { display: block; margin-top: 8px; color: var(--muted); }
|
|
||||||
.source-alert-card > .job-actions { flex: 0 0 auto; justify-content: flex-end; }
|
|
||||||
.mailbox-disclosure { margin-top: 16px; }
|
|
||||||
.mailbox-form-grid { margin-bottom: 14px; }
|
|
||||||
.checkbox-row { display: inline-flex; align-items: center; gap: 9px; color: var(--text); font-size: 14px; font-weight: 700; }
|
|
||||||
.checkbox-row input { width: 18px; height: 18px; accent-color: var(--accent); }
|
|
||||||
.platform-help-grid { margin-top: 12px; }
|
|
||||||
.button:disabled { cursor: not-allowed; opacity: .48; }
|
|
||||||
.source-card { overflow: hidden; box-shadow: none; }
|
|
||||||
.source-card-head { display: flex; align-items: flex-start; justify-content: space-between; gap: 20px; padding: 18px; }
|
|
||||||
.source-identity { min-width: 0; display: flex; gap: 13px; }
|
|
||||||
.source-icon { width: 42px; height: 42px; display: grid; place-items: center; flex: 0 0 auto; border: 1px solid var(--border); border-radius: 6px; background: var(--surface-high); color: var(--primary); }
|
|
||||||
.source-identity h2 { margin: 0; font-size: 17px; }
|
|
||||||
.source-identity p { margin: 4px 0 0; color: var(--muted); font-size: 12px; overflow-wrap: anywhere; }
|
|
||||||
.source-actions { display: flex; align-items: center; gap: 10px; }
|
|
||||||
.source-details { padding: 18px; border-top: 1px solid var(--border); background: var(--surface-low); }
|
|
||||||
.source-details-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 18px; }
|
|
||||||
.technical-log { max-height: 180px; overflow: auto; padding: 14px; border: 1px solid var(--border); border-radius: 5px; background: var(--bg-deep); color: var(--muted); font: 11px/1.6 ui-monospace, SFMono-Regular, Consolas, monospace; overflow-wrap: anywhere; }
|
|
||||||
.disclosure-icon { transition: transform .16s ease; }
|
|
||||||
[aria-expanded="true"] .disclosure-icon { transform: rotate(180deg); }
|
|
||||||
.import-layout { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 16px; }
|
|
||||||
|
|
||||||
.messages { display: grid; gap: 8px; margin-bottom: 18px; }
|
|
||||||
.message { padding: 12px 14px; border: 1px solid color-mix(in srgb, var(--primary) 24%, var(--border)); border-left: 3px solid var(--primary); border-radius: 5px; background: var(--primary-soft); color: var(--primary-strong); }
|
|
||||||
.message.success { background: var(--positive-soft); border-color: color-mix(in srgb, var(--positive) 25%, var(--border)); border-left-color: var(--positive); color: var(--positive); }
|
|
||||||
.message.warning { background: var(--warning-soft); border-color: color-mix(in srgb, var(--warning) 25%, var(--border)); border-left-color: var(--warning); color: var(--warning); }
|
|
||||||
.message.error { background: var(--danger-soft); border-color: color-mix(in srgb, var(--danger) 25%, var(--border)); border-left-color: var(--danger); color: var(--danger); }
|
|
||||||
.empty-state { grid-column: 1 / -1; padding: 48px 24px; text-align: center; border: 1px dashed var(--border-strong); border-radius: var(--radius); background: var(--surface-low); }
|
|
||||||
.empty-state h2 { margin-bottom: 6px; }
|
|
||||||
.empty-state p { max-width: 600px; margin: 0 auto 16px; color: var(--muted); }
|
|
||||||
.empty-icon { width: 54px; height: 54px; display: grid; place-items: center; margin: 0 auto 14px; border: 1px solid var(--border); border-radius: 50%; color: var(--primary); background: var(--primary-soft); }
|
|
||||||
.empty-icon .icon { width: 27px; height: 27px; }
|
|
||||||
.muted { color: var(--muted); }
|
|
||||||
.small { font-size: 12px; }
|
|
||||||
.auth-main { min-height: 100vh; display: grid; place-items: center; padding: 24px; background: radial-gradient(circle at 18% 14%, var(--primary-soft), transparent 32%), radial-gradient(circle at 83% 85%, rgba(104, 112, 255, .08), transparent 28%), var(--bg); }
|
|
||||||
.auth-card { width: min(450px, 100%); padding: 30px; }
|
|
||||||
.auth-brand { display: flex; align-items: center; gap: 14px; margin-bottom: 24px; }
|
|
||||||
.auth-card h1 { margin: 0; }
|
|
||||||
.auth-card p { margin: 4px 0; color: var(--muted); }
|
|
||||||
.stack { display: grid; gap: 16px; }
|
|
||||||
.stack label { display: grid; gap: 6px; font-weight: 650; }
|
|
||||||
|
|
||||||
@media (max-width: 1180px) {
|
|
||||||
.stats-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
|
|
||||||
.dashboard-grid { grid-template-columns: 1fr; }
|
|
||||||
.dashboard-rail { position: static; display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 15px; }
|
|
||||||
.detail-layout { grid-template-columns: 1fr; }
|
|
||||||
.sticky-card { position: static; }
|
|
||||||
.source-overview { grid-template-columns: repeat(2, minmax(0, 1fr)); }
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-width: 900px) {
|
|
||||||
.mobile-only { display: inline-grid !important; }
|
|
||||||
.sidebar { visibility: hidden; transform: translateX(-105%); transition: transform .18s ease, visibility 0s linear .18s; }
|
|
||||||
.workspace { margin-left: 0; }
|
|
||||||
.nav-open .sidebar { visibility: visible; transform: translateX(0); transition-delay: 0s; }
|
|
||||||
.nav-scrim { position: fixed; inset: 0; z-index: 50; width: 100%; height: 100%; border: 0; background: rgba(1, 8, 17, .62); backdrop-filter: blur(2px); }
|
|
||||||
.nav-open .nav-scrim { display: block; }
|
|
||||||
.utilitybar { padding: 0 16px; }
|
|
||||||
.main-content { padding-top: 26px; }
|
|
||||||
.job-grid { grid-template-columns: 1fr; }
|
|
||||||
.dashboard-rail { grid-template-columns: 1fr 1fr; }
|
|
||||||
.pipeline { grid-template-columns: repeat(6, 245px); }
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-width: 680px) {
|
|
||||||
.desktop-label { display: none; }
|
|
||||||
.utility-link { width: 42px; justify-content: center; padding: 0; border: 1px solid transparent; }
|
|
||||||
.main-content { padding: 22px 14px 54px; }
|
|
||||||
.page-header, .profile-card, .application-card, .job-hero { align-items: stretch; flex-direction: column; }
|
|
||||||
.page-header .button, .page-actions { width: 100%; }
|
|
||||||
.page-actions .button { flex: 1; }
|
|
||||||
.stats-grid, .two-columns, .form-grid, .definition-grid, .source-overview, .source-alert-grid, .source-details-grid, .import-layout, .dashboard-rail { grid-template-columns: 1fr; }
|
|
||||||
.source-alert-card { align-items: flex-start; flex-direction: column; }
|
|
||||||
.filter-bar { align-items: stretch; flex-direction: column; }
|
|
||||||
.span-2, .form-section { grid-column: auto; }
|
|
||||||
.job-card-head { grid-template-columns: 1fr; }
|
|
||||||
.score-block { justify-items: start; }
|
|
||||||
.job-hero { gap: 20px; }
|
|
||||||
.hero-score { min-width: 0; justify-items: start; }
|
|
||||||
.hero-score strong { font-size: 46px; }
|
|
||||||
.score-components > div { grid-template-columns: 86px minmax(80px, 1fr) 40px; font-size: 12px; }
|
|
||||||
.source-list li { flex-direction: column; gap: 3px; }
|
|
||||||
.source-card-head { flex-direction: column; }
|
|
||||||
.source-actions { width: 100%; justify-content: space-between; }
|
|
||||||
.choice-field > div { grid-template-columns: 1fr; }
|
|
||||||
.choice-field > .grouped-choices { grid-template-columns: 1fr; }
|
|
||||||
.choice-field .grouped-choices > div { grid-template-columns: 1fr; }
|
|
||||||
.form-actions { bottom: 8px; }
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-width: 420px) {
|
|
||||||
.utility-title span:last-child { display: none; }
|
|
||||||
.stats-grid { gap: 9px; }
|
|
||||||
.stat-card { min-height: 112px; padding: 16px; }
|
|
||||||
.stat-card strong { font-size: 29px; }
|
|
||||||
.page-header h1, .job-hero h1 { font-size: 30px; }
|
|
||||||
.panel, .job-card { padding: 17px; }
|
|
||||||
.job-actions form, .job-actions .button { flex: 1 1 auto; }
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (prefers-reduced-motion: reduce) {
|
|
||||||
*, *::before, *::after { scroll-behavior: auto !important; transition: none !important; animation: none !important; }
|
|
||||||
}
|
|
||||||
|
|
||||||
/* VR-119: Stitch-compositie is leidend voor shell, feeds en intelligencepanelen. */
|
|
||||||
.utilitybar-start { min-width: 0; flex: 1; gap: 18px; }
|
|
||||||
.global-search { position: relative; width: min(440px, 44vw); }
|
|
||||||
.global-search .icon { position: absolute; left: 13px; top: 50%; z-index: 1; width: 17px; height: 17px; transform: translateY(-50%); color: var(--muted); pointer-events: none; }
|
|
||||||
.global-search input { min-height: 42px; padding-left: 42px; background: var(--surface-low); }
|
|
||||||
.utility-radar { white-space: nowrap; }
|
|
||||||
.utility-cta { min-height: 40px; font-size: 13px; }
|
|
||||||
.section-link, .view-indicator { display: inline-flex; align-items: center; gap: 8px; }
|
|
||||||
.section-link .icon, .view-indicator .icon { width: 17px; height: 17px; }
|
|
||||||
.view-indicator { min-height: 42px; padding: 8px 13px; border: 1px solid var(--border); border-radius: var(--radius-sm); background: var(--surface-low); color: var(--text-soft); font: 12px ui-monospace, SFMono-Regular, Consolas, monospace; text-transform: uppercase; letter-spacing: .06em; }
|
|
||||||
|
|
||||||
.dashboard-header { align-items: end; }
|
|
||||||
.dashboard-header strong { color: var(--primary); }
|
|
||||||
.radar-status { display: inline-flex; align-items: center; gap: 9px; max-width: 430px; padding: 10px 14px; border: 1px solid var(--border); border-radius: 999px; background: var(--surface-low); color: var(--text-soft); font: 11px ui-monospace, SFMono-Regular, Consolas, monospace; }
|
|
||||||
.radar-status a { margin-left: 3px; }
|
|
||||||
.dashboard-grid { grid-template-columns: minmax(0, 1fr) 310px; gap: 28px; }
|
|
||||||
.dashboard-matches { min-width: 0; }
|
|
||||||
.job-feed, .results-feed { display: grid; gap: 12px; }
|
|
||||||
.feed-card { display: grid; grid-template-columns: minmax(0, 1fr) auto; align-items: stretch; padding: 0; gap: 0; overflow: hidden; }
|
|
||||||
.job-card-main { min-width: 0; display: grid; gap: 14px; padding: 20px; }
|
|
||||||
.feed-card-actions { min-width: 152px; display: flex; flex-direction: column; align-items: stretch; justify-content: center; gap: 9px; padding: 18px; border-left: 1px solid var(--border); background: var(--surface-low); }
|
|
||||||
.feed-card-actions form { margin: 0; }
|
|
||||||
.feed-card-actions .save-button { width: 100%; border-color: var(--border); }
|
|
||||||
.confidence-label { color: var(--muted); font: 10px ui-monospace, SFMono-Regular, Consolas, monospace; text-transform: uppercase; letter-spacing: .06em; }
|
|
||||||
.signal-list { display: grid; gap: 7px; margin: 0; padding: 0; list-style: none; color: var(--text-soft); }
|
|
||||||
.signal-list li { position: relative; padding-left: 20px; }
|
|
||||||
.signal-list li::before { content: ""; position: absolute; left: 2px; top: .62em; width: 6px; height: 6px; border-radius: 50%; background: var(--positive); }
|
|
||||||
.rail-panel { padding: 22px; }
|
|
||||||
.rail-panel h2 { margin: 8px 0 10px; font-size: 20px; }
|
|
||||||
.rail-application-list { display: grid; margin: 8px 0 16px; border-top: 1px solid var(--border); }
|
|
||||||
.rail-application { min-width: 0; display: flex; align-items: center; justify-content: space-between; gap: 12px; padding: 12px 0; border-bottom: 1px solid var(--border); color: var(--text); }
|
|
||||||
.rail-application:hover { color: var(--primary-strong); text-decoration: none; }
|
|
||||||
.rail-application > span { min-width: 0; display: grid; }
|
|
||||||
.rail-application strong, .rail-application small { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
||||||
.rail-application small { margin-top: 3px; color: var(--muted); }
|
|
||||||
.rail-application time { color: var(--warning); font: 11px ui-monospace, SFMono-Regular, Consolas, monospace; }
|
|
||||||
.compact-empty { min-height: 235px; display: grid; place-content: center; }
|
|
||||||
|
|
||||||
.filter-console { display: grid; grid-template-columns: minmax(250px, 1fr) 170px 170px auto; align-items: end; gap: 10px; padding: 16px; margin-bottom: 26px; border: 1px solid var(--border); border-radius: var(--radius); background: var(--surface); }
|
|
||||||
.filter-console label { display: grid; gap: 6px; }
|
|
||||||
.input-with-icon { position: relative; display: block; }
|
|
||||||
.input-with-icon .icon { position: absolute; left: 12px; top: 50%; width: 16px; height: 16px; transform: translateY(-50%); color: var(--muted); }
|
|
||||||
.input-with-icon input { padding-left: 38px; }
|
|
||||||
.results-toolbar { display: flex; align-items: end; justify-content: space-between; gap: 16px; margin: 0 0 14px; }
|
|
||||||
.results-toolbar h2 { margin: 4px 0 0; font-size: 20px; }
|
|
||||||
.result-card { position: relative; min-width: 0; display: grid; grid-template-columns: 4px minmax(0, 1fr) auto; overflow: hidden; border: 1px solid var(--border); border-radius: var(--radius); background: var(--surface); transition: border-color .16s ease, transform .16s ease; }
|
|
||||||
.result-card:hover { border-color: var(--border-strong); transform: translateY(-1px); }
|
|
||||||
.result-card.strong-match { border-color: color-mix(in srgb, var(--primary) 38%, var(--border)); box-shadow: var(--signal-glow); }
|
|
||||||
.result-accent { background: var(--surface-bright); }
|
|
||||||
.result-card.strong-match .result-accent { background: var(--primary); }
|
|
||||||
.result-body { min-width: 0; display: grid; gap: 13px; padding: 19px 22px; }
|
|
||||||
.result-heading { display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: 18px; }
|
|
||||||
.result-heading h2 { margin: 7px 0 2px; font-size: 21px; line-height: 1.25; }
|
|
||||||
.result-heading h2 a { color: var(--text); }
|
|
||||||
.result-heading p, .result-summary { margin: 0; color: var(--muted); }
|
|
||||||
.result-summary { max-width: 78ch; font-size: 13px; }
|
|
||||||
.result-actions { min-width: 174px; display: grid; place-content: center; padding: 18px; border-left: 1px solid var(--border); background: var(--surface-low); }
|
|
||||||
|
|
||||||
.intelligence-strip { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 12px; margin: 0 0 18px; }
|
|
||||||
.intelligence-strip > div { min-height: 110px; display: grid; align-content: center; gap: 4px; padding: 18px; border: 1px solid var(--border); border-radius: var(--radius); background: var(--surface); }
|
|
||||||
.intelligence-strip strong { color: var(--primary-strong); font-size: 24px; letter-spacing: -.025em; }
|
|
||||||
.intelligence-strip small { color: var(--muted); }
|
|
||||||
|
|
||||||
.pipeline-page-header { align-items: end; }
|
|
||||||
.pipeline-page-header h1 { font-size: clamp(30px, 3vw, 40px); }
|
|
||||||
.pipeline { min-height: calc(100vh - 250px); grid-template-columns: repeat(6, minmax(270px, 1fr)); gap: 16px; }
|
|
||||||
.pipeline-column { min-height: 460px; padding: 14px; background: color-mix(in srgb, var(--surface-low) 72%, transparent); }
|
|
||||||
.pipeline-head h2 { display: flex; align-items: center; gap: 8px; font-family: ui-monospace, SFMono-Regular, Consolas, monospace; font-weight: 500; }
|
|
||||||
.column-dot { width: 8px; height: 8px; border-radius: 50%; background: var(--border-strong); }
|
|
||||||
.pipeline-column:nth-child(2) .column-dot, .pipeline-column:nth-child(3) .column-dot { background: var(--primary); }
|
|
||||||
.pipeline-column:nth-child(4) .column-dot { background: var(--positive); }
|
|
||||||
.pipeline-column:nth-child(5) .column-dot, .pipeline-column:nth-child(6) .column-dot { background: var(--danger); }
|
|
||||||
.pipeline-card { padding: 17px; background: var(--surface); }
|
|
||||||
|
|
||||||
.editor-layout { display: grid; grid-template-columns: minmax(0, 1fr) 300px; gap: 22px; align-items: start; }
|
|
||||||
.profile-editor { max-width: none; padding: 28px; }
|
|
||||||
.profile-editor .form-section { display: flex; align-items: center; gap: 12px; margin-top: 20px; }
|
|
||||||
.profile-editor .form-section:first-child { margin-top: 0; padding-top: 0; border-top: 0; }
|
|
||||||
.form-section-icon { width: 38px; height: 38px; display: grid; place-items: center; flex: 0 0 auto; border: 1px solid var(--border); border-radius: var(--radius-sm); background: var(--surface-high); color: var(--primary); }
|
|
||||||
.editor-rail { position: sticky; top: calc(var(--utility) + 20px); }
|
|
||||||
.radar-preview { position: relative; overflow: hidden; }
|
|
||||||
.radar-preview::after { content: ""; position: absolute; right: -45px; top: -45px; width: 150px; height: 150px; border: 1px solid color-mix(in srgb, var(--primary) 15%, transparent); border-radius: 50%; box-shadow: inset 0 0 0 32px color-mix(in srgb, var(--primary) 3%, transparent), inset 0 0 0 62px color-mix(in srgb, var(--primary) 3%, transparent); pointer-events: none; }
|
|
||||||
.radar-preview > * { position: relative; z-index: 1; }
|
|
||||||
.preview-score { display: grid; margin: 20px 0; padding: 17px; border: 1px solid color-mix(in srgb, var(--primary) 30%, var(--border)); border-radius: var(--radius); background: var(--bg-deep); }
|
|
||||||
.preview-score strong { color: var(--primary); font-size: 36px; line-height: 1; }
|
|
||||||
.preview-score span { margin-top: 5px; color: var(--muted); font: 10px ui-monospace, SFMono-Regular, Consolas, monospace; text-transform: uppercase; }
|
|
||||||
|
|
||||||
.import-disclosure { padding: 0; overflow: hidden; }
|
|
||||||
.import-disclosure > summary { min-height: 82px; display: grid; grid-template-columns: 44px minmax(0, 1fr) auto; align-items: center; gap: 14px; padding: 16px 20px; cursor: pointer; list-style: none; }
|
|
||||||
.import-disclosure > summary::-webkit-details-marker { display: none; }
|
|
||||||
.import-disclosure > summary > span:nth-child(2) { display: grid; gap: 3px; }
|
|
||||||
.import-disclosure > summary strong { font-size: 17px; }
|
|
||||||
.import-disclosure > summary small { color: var(--muted); }
|
|
||||||
.import-disclosure[open] > summary { border-bottom: 1px solid var(--border); }
|
|
||||||
.import-disclosure[open] > summary .disclosure-icon { transform: rotate(180deg); }
|
|
||||||
.import-disclosure-body { padding: 20px; }
|
|
||||||
.source-list-heading { align-items: end; }
|
|
||||||
.inline-bulk-action { display: flex; align-items: end; gap: 8px; }
|
|
||||||
.inline-bulk-action select { min-width: 170px; }
|
|
||||||
.source-card { border-left: 3px solid var(--border-strong); }
|
|
||||||
.source-card:has(.pill-success) { border-left-color: var(--positive); }
|
|
||||||
.source-card:has(.pill-failed) { border-left-color: var(--danger); }
|
|
||||||
.source-card:has(.pill-review), .source-card:has(.pill-partial) { border-left-color: var(--warning); }
|
|
||||||
|
|
||||||
.system-hero { display: grid; grid-template-columns: minmax(300px, 1.25fr) minmax(300px, 1fr); gap: 14px; }
|
|
||||||
.system-readiness { min-height: 134px; display: grid; grid-template-columns: 48px minmax(0, 1fr) 10px; align-items: center; gap: 16px; padding: 22px; border: 1px solid var(--border); border-radius: var(--radius); background: var(--surface); }
|
|
||||||
.system-readiness.is-healthy { border-color: color-mix(in srgb, var(--positive) 35%, var(--border)); }
|
|
||||||
.system-readiness h2 { margin: 5px 0 2px; font-size: 22px; }
|
|
||||||
.system-readiness p { margin: 0; color: var(--muted); }
|
|
||||||
.system-counts { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 10px; }
|
|
||||||
.system-counts article { display: grid; align-content: center; gap: 6px; padding: 17px; border: 1px solid var(--border); border-radius: var(--radius); background: var(--surface-low); }
|
|
||||||
.system-counts strong { font-size: 25px; }
|
|
||||||
.health-grid { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 12px; }
|
|
||||||
.health-card { padding: 19px; border: 1px solid var(--border); border-radius: var(--radius); background: var(--surface); }
|
|
||||||
.health-card-head { display: flex; align-items: start; justify-content: space-between; gap: 12px; }
|
|
||||||
.health-card h3 { margin: 5px 0 0; font-size: 17px; }
|
|
||||||
.health-card dl { display: grid; grid-template-columns: repeat(2, 1fr); gap: 8px; margin: 17px 0 0; }
|
|
||||||
.health-card dl div { padding: 9px; border: 1px solid var(--border); border-radius: var(--radius-sm); background: var(--surface-low); }
|
|
||||||
.health-card dt { color: var(--muted); font: 9px ui-monospace, SFMono-Regular, Consolas, monospace; text-transform: uppercase; }
|
|
||||||
.health-card dd { margin: 3px 0 0; font-weight: 750; }
|
|
||||||
.command-panel { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 14px; }
|
|
||||||
.command-panel > div { display: grid; gap: 6px; }
|
|
||||||
.command-panel pre { height: 100%; }
|
|
||||||
|
|
||||||
@media (max-width: 1180px) {
|
|
||||||
.utility-radar { display: none; }
|
|
||||||
.dashboard-grid { grid-template-columns: minmax(0, 1fr) 280px; }
|
|
||||||
.dashboard-rail { position: sticky; display: block; }
|
|
||||||
.editor-layout { grid-template-columns: 1fr; }
|
|
||||||
.editor-rail { position: static; display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 14px; }
|
|
||||||
.health-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-width: 900px) {
|
|
||||||
.main-content { padding-inline: 20px; }
|
|
||||||
.global-search { width: min(420px, 55vw); }
|
|
||||||
.utility-cta { width: 42px; padding: 0; }
|
|
||||||
.dashboard-grid { grid-template-columns: 1fr; }
|
|
||||||
.dashboard-rail { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); position: static; }
|
|
||||||
.feed-card { grid-template-columns: 1fr; }
|
|
||||||
.feed-card-actions { min-width: 0; flex-direction: row; justify-content: flex-start; border-top: 1px solid var(--border); border-left: 0; }
|
|
||||||
.feed-card-actions .save-button { width: 42px; }
|
|
||||||
.filter-console { grid-template-columns: minmax(0, 1fr) repeat(2, 150px); }
|
|
||||||
.filter-console .button { grid-column: 1 / -1; }
|
|
||||||
.system-hero { grid-template-columns: 1fr; }
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-width: 680px) {
|
|
||||||
.main-content { padding-inline: 14px; }
|
|
||||||
.utilitybar { gap: 8px; padding-inline: 12px; }
|
|
||||||
.utilitybar-start { gap: 8px; }
|
|
||||||
.global-search { width: min(100%, 260px); }
|
|
||||||
.global-search input { min-height: 40px; }
|
|
||||||
.dashboard-header { align-items: stretch; }
|
|
||||||
.radar-status { width: 100%; max-width: none; flex-wrap: wrap; border-radius: var(--radius); }
|
|
||||||
.dashboard-rail, .editor-rail, .health-grid { grid-template-columns: 1fr; }
|
|
||||||
.feed-card-actions .button { flex: 1; }
|
|
||||||
.filter-console { grid-template-columns: 1fr; }
|
|
||||||
.filter-console .button { grid-column: auto; }
|
|
||||||
.results-toolbar, .source-list-heading { align-items: stretch; flex-direction: column; }
|
|
||||||
.result-card { grid-template-columns: 3px minmax(0, 1fr); }
|
|
||||||
.result-actions { grid-column: 2; min-width: 0; place-content: stretch; padding-top: 0; border-top: 0; border-left: 0; background: var(--surface); }
|
|
||||||
.result-heading { grid-template-columns: 1fr; }
|
|
||||||
.result-heading .score-block { justify-items: start; }
|
|
||||||
.intelligence-strip { grid-template-columns: 1fr; }
|
|
||||||
.intelligence-strip > div { min-height: 92px; }
|
|
||||||
.pipeline { grid-template-columns: repeat(6, 258px); min-height: 410px; }
|
|
||||||
.pipeline-column { min-height: 380px; }
|
|
||||||
.profile-editor { padding: 18px; }
|
|
||||||
.inline-bulk-action { align-items: stretch; flex-direction: column; }
|
|
||||||
.inline-bulk-action select { min-width: 0; }
|
|
||||||
.system-counts, .command-panel { grid-template-columns: 1fr; }
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-width: 420px) {
|
|
||||||
.global-search { width: 46vw; }
|
|
||||||
.global-search input::placeholder { color: transparent; }
|
|
||||||
.utility-actions { gap: 2px; }
|
|
||||||
.utility-actions .icon-button, .utility-actions .utility-link, .utility-cta { width: 38px; min-height: 38px; }
|
|
||||||
.radar-status span:nth-child(3), .radar-status a { display: none; }
|
|
||||||
.feed-card-actions { padding: 13px; }
|
|
||||||
.result-body { padding: 17px; }
|
|
||||||
.result-actions { padding: 0 17px 17px; }
|
|
||||||
}
|
|
||||||
|
|
||||||
/* VR-206: premium, title-led IT radar and genuinely adaptive widescreen composition. */
|
|
||||||
.jobs-page-header { align-items: center; }
|
|
||||||
.jobs-page-header .radar-status strong { color: var(--primary-strong); }
|
|
||||||
.premium-filter-console {
|
|
||||||
grid-template-columns: minmax(220px, 1fr) repeat(5, minmax(112px, 145px)) auto;
|
|
||||||
gap: 12px;
|
|
||||||
padding: 18px;
|
|
||||||
background:
|
|
||||||
linear-gradient(135deg, color-mix(in srgb, var(--primary) 4%, transparent), transparent 45%),
|
|
||||||
var(--surface);
|
|
||||||
box-shadow: 0 16px 42px rgba(0, 0, 0, .1);
|
|
||||||
}
|
|
||||||
.premium-filter-console select,
|
|
||||||
.premium-filter-console input { background: color-mix(in srgb, var(--surface-low) 94%, transparent); }
|
|
||||||
.results-layout { display: grid; grid-template-columns: minmax(0, 1fr); gap: 28px; align-items: start; }
|
|
||||||
.results-column { min-width: 0; }
|
|
||||||
.jobs-insight-rail { display: none; }
|
|
||||||
.result-card {
|
|
||||||
background:
|
|
||||||
linear-gradient(135deg, color-mix(in srgb, var(--primary) 2%, transparent), transparent 48%),
|
|
||||||
var(--surface);
|
|
||||||
box-shadow: 0 12px 32px rgba(0, 0, 0, .09);
|
|
||||||
}
|
|
||||||
.result-card:hover { box-shadow: 0 18px 42px rgba(0, 0, 0, .14); }
|
|
||||||
.result-heading h2 { font-size: clamp(20px, 1.3vw, 25px); letter-spacing: -.025em; }
|
|
||||||
.result-heading > div:first-child { min-width: 0; }
|
|
||||||
.score-block strong { font-size: 32px; }
|
|
||||||
.score-block small { max-width: 110px; text-align: right; line-height: 1.35; }
|
|
||||||
.score-pending strong { color: var(--muted); }
|
|
||||||
.pill-it { color: var(--primary-strong); border-color: color-mix(in srgb, var(--primary) 30%, var(--border)); background: var(--primary-soft); }
|
|
||||||
.evidence-line { display: flex; align-items: flex-start; gap: 9px; margin: 0; padding: 10px 12px; border-radius: var(--radius-sm); font-size: 13px; line-height: 1.45; }
|
|
||||||
.evidence-line .icon { width: 17px; height: 17px; margin-top: 1px; }
|
|
||||||
.positive-evidence { border-left: 2px solid var(--positive); background: var(--positive-soft); color: var(--text-soft); }
|
|
||||||
.positive-evidence .icon { color: var(--positive); }
|
|
||||||
.concern-evidence { border-left: 2px solid var(--warning); background: var(--warning-soft); color: var(--text-soft); }
|
|
||||||
.concern-evidence > span:first-child { color: var(--warning); font-weight: 900; }
|
|
||||||
.result-actions { align-content: center; gap: 9px; }
|
|
||||||
.result-actions form { margin: 0; }
|
|
||||||
.result-actions .button { width: 100%; }
|
|
||||||
.profile-signal-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 9px; margin: 16px 0; }
|
|
||||||
.profile-signal-grid > div { display: grid; gap: 3px; padding: 11px; border: 1px solid var(--border); border-radius: var(--radius-sm); background: var(--surface-low); }
|
|
||||||
.profile-signal-grid span { color: var(--muted); font: 9px ui-monospace, SFMono-Regular, Consolas, monospace; text-transform: uppercase; letter-spacing: .08em; }
|
|
||||||
.profile-signal-grid strong { font-size: 18px; }
|
|
||||||
.radar-steps { display: grid; gap: 0; margin: 14px 0 0; padding: 0; list-style: none; counter-reset: radar-step; }
|
|
||||||
.radar-steps li { position: relative; display: grid; gap: 2px; padding: 12px 0 12px 34px; border-top: 1px solid var(--border); counter-increment: radar-step; }
|
|
||||||
.radar-steps li::before { content: counter(radar-step); position: absolute; left: 0; top: 12px; width: 22px; height: 22px; display: grid; place-items: center; border: 1px solid color-mix(in srgb, var(--primary) 30%, var(--border)); border-radius: 50%; color: var(--primary-strong); font: 10px ui-monospace, SFMono-Regular, Consolas, monospace; }
|
|
||||||
.radar-steps span { color: var(--muted); font-size: 12px; }
|
|
||||||
.job-hero .title-row { margin-bottom: 12px; }
|
|
||||||
.detail-main .panel { box-shadow: 0 12px 38px rgba(0, 0, 0, .08); }
|
|
||||||
|
|
||||||
@media (min-width: 1350px) {
|
|
||||||
.results-layout { grid-template-columns: minmax(0, 1fr) 320px; }
|
|
||||||
.jobs-insight-rail { position: sticky; top: calc(var(--utility) + 20px); display: block; }
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (min-width: 1850px) {
|
|
||||||
.utilitybar { padding-inline: clamp(30px, 2.5vw, 60px); }
|
|
||||||
.global-search { width: min(620px, 42vw); }
|
|
||||||
.dashboard-grid { grid-template-columns: minmax(0, 1fr) 360px; gap: 34px; }
|
|
||||||
.dashboard-grid .job-feed { grid-template-columns: repeat(2, minmax(0, 1fr)); align-items: start; }
|
|
||||||
.dashboard-grid .section-heading { grid-column: 1 / -1; }
|
|
||||||
.results-layout { grid-template-columns: minmax(0, 1fr) 360px; gap: 34px; }
|
|
||||||
.results-feed { grid-template-columns: repeat(2, minmax(0, 1fr)); align-items: start; }
|
|
||||||
.result-card { height: 100%; grid-template-columns: 4px minmax(0, 1fr); }
|
|
||||||
.result-actions { grid-column: 2; grid-template-columns: minmax(0, 1fr) auto; padding: 0 22px 20px; border: 0; background: transparent; }
|
|
||||||
.result-actions .button-ghost { width: auto; }
|
|
||||||
.detail-layout { grid-template-columns: minmax(0, 1fr) 380px; gap: 34px; }
|
|
||||||
.health-grid { grid-template-columns: repeat(4, minmax(0, 1fr)); }
|
|
||||||
.editor-layout { grid-template-columns: minmax(0, 1fr) 360px; gap: 34px; }
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-width: 1180px) {
|
|
||||||
.premium-filter-console { grid-template-columns: minmax(0, 1fr) repeat(2, minmax(145px, .35fr)); }
|
|
||||||
.premium-filter-console .button { grid-column: 1 / -1; }
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-width: 680px) {
|
|
||||||
body { font-size: 15px; }
|
|
||||||
.section-heading { align-items: flex-start; flex-direction: column; gap: 10px; }
|
|
||||||
.section-heading > .pill { align-self: flex-start; }
|
|
||||||
.jobs-page-header { align-items: stretch; }
|
|
||||||
.premium-filter-console { grid-template-columns: 1fr; padding: 14px; }
|
|
||||||
.premium-filter-console .button { grid-column: auto; }
|
|
||||||
.result-actions { gap: 8px; }
|
|
||||||
.result-actions form { width: 100%; }
|
|
||||||
.result-heading h2 { font-size: 20px; }
|
|
||||||
.score-block small { max-width: none; text-align: left; }
|
|
||||||
}
|
|
||||||
|
|
||||||
/* VR-208: personal market-demand and learning radar. */
|
|
||||||
.skills-page-header { align-items: center; }
|
|
||||||
.insight-hero {
|
|
||||||
position: relative;
|
|
||||||
display: grid;
|
|
||||||
grid-template-columns: 126px minmax(0, 1fr) auto;
|
|
||||||
align-items: center;
|
|
||||||
gap: 24px;
|
|
||||||
margin-bottom: 16px;
|
|
||||||
padding: 26px;
|
|
||||||
overflow: hidden;
|
|
||||||
border: 1px solid color-mix(in srgb, var(--primary) 32%, var(--border));
|
|
||||||
border-radius: var(--radius);
|
|
||||||
background:
|
|
||||||
radial-gradient(circle at 8% 50%, color-mix(in srgb, var(--primary) 13%, transparent), transparent 30%),
|
|
||||||
linear-gradient(125deg, color-mix(in srgb, var(--primary) 5%, var(--surface)), var(--surface));
|
|
||||||
box-shadow: 0 18px 48px rgba(0, 0, 0, .12);
|
|
||||||
}
|
|
||||||
.insight-hero h2 { margin: 7px 0; font-size: clamp(22px, 2vw, 31px); letter-spacing: -.025em; }
|
|
||||||
.insight-hero p { max-width: 80ch; margin: 0; color: var(--muted); }
|
|
||||||
.insight-orbit { position: relative; width: 104px; height: 104px; display: grid; place-items: center; }
|
|
||||||
.insight-orbit span { position: absolute; inset: 0; border: 1px solid color-mix(in srgb, var(--primary) 50%, transparent); border-radius: 50%; }
|
|
||||||
.insight-orbit span:nth-child(2) { inset: 13px; opacity: .65; }
|
|
||||||
.insight-orbit span:nth-child(3) { inset: 26px; opacity: .4; }
|
|
||||||
.insight-orbit strong { z-index: 1; color: var(--primary-strong); font-size: 24px; }
|
|
||||||
.skill-metrics { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 12px; margin-bottom: 16px; }
|
|
||||||
.skill-metrics article { display: grid; gap: 5px; padding: 18px 20px; border: 1px solid var(--border); border-radius: var(--radius); background: var(--surface); }
|
|
||||||
.skill-metrics article.metric-accent { border-color: color-mix(in srgb, var(--warning) 38%, var(--border)); background: linear-gradient(145deg, var(--warning-soft), var(--surface)); }
|
|
||||||
.skill-metrics span { color: var(--text-soft); font-size: 12px; }
|
|
||||||
.skill-metrics strong { font-size: 28px; line-height: 1; }
|
|
||||||
.skill-metrics small { color: var(--muted); }
|
|
||||||
.skills-filter-console { grid-template-columns: minmax(220px, 1fr) minmax(190px, .65fr) auto auto; }
|
|
||||||
.skills-insight-layout { display: grid; grid-template-columns: minmax(0, 1fr) 320px; gap: 28px; align-items: start; }
|
|
||||||
.skill-demand-list { display: grid; gap: 12px; }
|
|
||||||
.skill-demand-card { min-width: 0; display: grid; grid-template-columns: minmax(0, 1fr) auto; overflow: hidden; border: 1px solid var(--border); border-radius: var(--radius); background: var(--surface); box-shadow: 0 12px 32px rgba(0, 0, 0, .08); }
|
|
||||||
.skill-demand-card.is-gap { border-left: 3px solid var(--warning); }
|
|
||||||
.skill-demand-main { min-width: 0; display: grid; gap: 13px; padding: 20px 22px; }
|
|
||||||
.skill-card-heading { display: flex; align-items: start; justify-content: space-between; gap: 16px; }
|
|
||||||
.skill-card-heading h3 { margin: 4px 0 0; font-size: 21px; }
|
|
||||||
.skill-category { color: var(--muted); font: 10px ui-monospace, SFMono-Regular, Consolas, monospace; text-transform: uppercase; letter-spacing: .07em; }
|
|
||||||
.demand-meter { height: 7px; overflow: hidden; border-radius: 999px; background: var(--surface-high); }
|
|
||||||
.demand-meter span { display: block; min-width: 3px; height: 100%; border-radius: inherit; background: linear-gradient(90deg, var(--primary), var(--primary-strong)); }
|
|
||||||
.skill-demand-stats { display: flex; flex-wrap: wrap; gap: 8px 18px; color: var(--muted); font-size: 12px; }
|
|
||||||
.skill-demand-stats strong { color: var(--text); }
|
|
||||||
.learning-focus { display: grid; gap: 3px; margin: 0; padding: 12px 14px; border-left: 2px solid var(--warning); background: var(--warning-soft); color: var(--text-soft); font-size: 13px; }
|
|
||||||
.skill-job-examples { display: grid; gap: 7px; margin: 0; padding: 0; list-style: none; }
|
|
||||||
.skill-job-examples li { min-width: 0; display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: 12px; padding-top: 7px; border-top: 1px solid var(--border); font-size: 12px; }
|
|
||||||
.skill-job-examples a, .skill-job-examples span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
||||||
.skill-job-examples span { color: var(--muted); }
|
|
||||||
.skill-demand-action { min-width: 174px; display: grid; place-content: center; padding: 20px; border-left: 1px solid var(--border); background: var(--surface-low); }
|
|
||||||
.skills-method-rail { position: sticky; top: calc(var(--utility) + 20px); display: grid; gap: 14px; }
|
|
||||||
.legend-list { display: grid; gap: 12px; margin: 16px 0 0; padding: 0; list-style: none; }
|
|
||||||
.legend-list li { display: flex; align-items: start; gap: 10px; }
|
|
||||||
.legend-list li > div { display: grid; gap: 2px; }
|
|
||||||
.legend-list small { color: var(--muted); line-height: 1.4; }
|
|
||||||
.legend-dot { width: 9px; height: 9px; flex: 0 0 auto; margin-top: 5px; border-radius: 50%; }
|
|
||||||
.legend-dot.exact { background: var(--positive); }
|
|
||||||
.legend-dot.inferred { border: 2px solid var(--primary); }
|
|
||||||
|
|
||||||
@media (min-width: 1850px) {
|
|
||||||
.skills-insight-layout { grid-template-columns: minmax(0, 1fr) 370px; gap: 34px; }
|
|
||||||
.skill-demand-list { grid-template-columns: repeat(2, minmax(0, 1fr)); align-items: start; }
|
|
||||||
.skill-demand-card { height: 100%; grid-template-columns: 1fr; }
|
|
||||||
.skill-demand-action { min-width: 0; place-content: stretch; padding: 0 22px 20px; border: 0; background: transparent; }
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-width: 1020px) {
|
|
||||||
.insight-hero { grid-template-columns: 104px minmax(0, 1fr); }
|
|
||||||
.insight-hero > .button { grid-column: 2; justify-self: start; }
|
|
||||||
.skill-metrics { grid-template-columns: repeat(2, minmax(0, 1fr)); }
|
|
||||||
.skills-insight-layout { grid-template-columns: 1fr; }
|
|
||||||
.skills-method-rail { position: static; grid-template-columns: repeat(3, minmax(0, 1fr)); }
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-width: 680px) {
|
|
||||||
.insight-hero { grid-template-columns: 1fr; padding: 20px; }
|
|
||||||
.insight-orbit { width: 88px; height: 88px; }
|
|
||||||
.insight-hero > .button { grid-column: auto; justify-self: stretch; }
|
|
||||||
.skill-metrics, .skills-method-rail { grid-template-columns: 1fr; }
|
|
||||||
.skills-filter-console { grid-template-columns: 1fr; }
|
|
||||||
.skill-demand-card { grid-template-columns: 1fr; }
|
|
||||||
.skill-demand-main { padding: 18px; }
|
|
||||||
.skill-demand-action { min-width: 0; place-content: stretch; padding: 0 18px 18px; border: 0; background: var(--surface); }
|
|
||||||
.skill-card-heading { align-items: flex-start; flex-direction: column; gap: 8px; }
|
|
||||||
.skill-job-examples li { grid-template-columns: 1fr; gap: 2px; }
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -0,0 +1,49 @@
|
|||||||
|
*, *::before, *::after { box-sizing: border-box; }
|
||||||
|
html { min-width: 320px; background: var(--surface); scroll-behavior: smooth; }
|
||||||
|
body { margin: 0; min-height: 100vh; overflow-x: hidden; background: var(--surface); color: var(--text); font: 400 15px/1.55 var(--font-body); }
|
||||||
|
body::before { position: fixed; inset: 0; z-index: -1; content: ""; pointer-events: none; background: radial-gradient(circle at 84% 0%, rgba(0,245,255,.055), transparent 31rem), radial-gradient(circle at 35% 100%, rgba(99,102,241,.04), transparent 36rem); }
|
||||||
|
a { color: inherit; text-decoration: none; }
|
||||||
|
a:hover { color: var(--text-strong); }
|
||||||
|
button, input, select, textarea { color: inherit; font: inherit; }
|
||||||
|
button, summary, select { cursor: pointer; }
|
||||||
|
button { border: 0; }
|
||||||
|
img, svg { max-width: 100%; }
|
||||||
|
h1, h2, h3, h4, p { margin-block-start: 0; }
|
||||||
|
h1, h2, h3, h4 { color: var(--text-strong); font-family: var(--font-headline); line-height: 1.2; }
|
||||||
|
h1 { margin-bottom: 8px; font-size: clamp(28px, 2.1vw, 38px); letter-spacing: -.035em; }
|
||||||
|
h2 { margin-bottom: 8px; font-size: 20px; letter-spacing: -.015em; }
|
||||||
|
h3 { margin-bottom: 6px; font-size: 16px; }
|
||||||
|
p { margin-bottom: 12px; }
|
||||||
|
small { color: var(--text-muted); }
|
||||||
|
code, pre, kbd { font-family: var(--font-mono); }
|
||||||
|
pre { overflow: auto; }
|
||||||
|
hr { border: 0; border-top: 1px solid color-mix(in srgb, var(--outline) 55%, transparent); }
|
||||||
|
::selection { background: var(--cyan); color: var(--cyan-ink); }
|
||||||
|
:focus-visible { outline: 2px solid var(--cyan); outline-offset: 3px; }
|
||||||
|
::placeholder { color: var(--text-faint); opacity: 1; }
|
||||||
|
::-webkit-scrollbar { width: 8px; height: 8px; }
|
||||||
|
::-webkit-scrollbar-track { background: var(--surface-lowest); }
|
||||||
|
::-webkit-scrollbar-thumb { background: var(--surface-highest); border-radius: 8px; }
|
||||||
|
.icon { width: 20px; height: 20px; flex: 0 0 auto; fill: none; stroke: currentColor; stroke-linecap: round; stroke-linejoin: round; stroke-width: 1.7; }
|
||||||
|
.icon-sprite { position: absolute; width: 0; height: 0; overflow: hidden; }
|
||||||
|
.sr-only { position: absolute !important; width: 1px !important; height: 1px !important; padding: 0 !important; margin: -1px !important; overflow: hidden !important; clip: rect(0,0,0,0) !important; white-space: nowrap !important; border: 0 !important; }
|
||||||
|
.skip-link { position: fixed; z-index: 1000; top: 10px; left: 10px; padding: 10px 14px; transform: translateY(-160%); background: var(--cyan); color: var(--cyan-ink); font-weight: 700; }
|
||||||
|
.skip-link:focus { transform: translateY(0); }
|
||||||
|
.muted { color: var(--text-muted); }
|
||||||
|
.small { font-size: 13px; }
|
||||||
|
.mobile-only { display: none; }
|
||||||
|
[hidden] { display: none !important; }
|
||||||
|
.theme-icon-light { display: none; }
|
||||||
|
:root[data-theme="light"] .theme-icon-dark { display: none; }
|
||||||
|
:root[data-theme="light"] .theme-icon-light { display: block; }
|
||||||
|
|
||||||
|
input:not([type="checkbox"]):not([type="radio"]):not([type="range"]), select, textarea {
|
||||||
|
width: 100%; min-height: 44px; padding: 10px 12px; border: 1px solid color-mix(in srgb, var(--outline) 72%, transparent); border-radius: var(--radius-sm); outline: 0; background: var(--surface-low); transition: border-color .16s, background .16s, box-shadow .16s;
|
||||||
|
}
|
||||||
|
textarea { min-height: 130px; resize: vertical; }
|
||||||
|
input:hover, select:hover, textarea:hover { border-color: var(--outline-strong); }
|
||||||
|
input:focus, select:focus, textarea:focus { border-color: var(--cyan); box-shadow: 0 0 0 3px color-mix(in srgb, var(--cyan) 12%, transparent); }
|
||||||
|
input[type="checkbox"], input[type="radio"] { width: 18px; height: 18px; margin: 0; accent-color: var(--cyan); }
|
||||||
|
label { display: grid; gap: 7px; color: var(--text); }
|
||||||
|
.field-label, label > span:first-child { color: var(--text-muted); font: 600 11px/1.2 var(--font-mono); letter-spacing: .075em; text-transform: uppercase; }
|
||||||
|
.field-error { color: var(--coral); }
|
||||||
@@ -0,0 +1,67 @@
|
|||||||
|
.button { display: inline-flex; min-height: 42px; align-items: center; justify-content: center; gap: 8px; padding: 9px 14px; border: 1px solid transparent; border-radius: var(--radius-sm); font: 650 13px/1 var(--font-body); transition: transform .14s, filter .14s, background .14s, border-color .14s; }
|
||||||
|
.button:hover { transform: translateY(-1px); }
|
||||||
|
.button:active { transform: scale(.98); }
|
||||||
|
.button-primary { border-color: var(--cyan); background: var(--cyan); color: var(--cyan-ink); box-shadow: 0 0 18px color-mix(in srgb, var(--cyan) 12%, transparent); }
|
||||||
|
.button-primary:hover { color: var(--cyan-ink); filter: brightness(1.06); }
|
||||||
|
.button-secondary { border-color: color-mix(in srgb, var(--cyan) 36%, var(--outline)); background: color-mix(in srgb, var(--cyan) 9%, var(--surface-high)); color: var(--text-strong); }
|
||||||
|
.button-ghost { border-color: color-mix(in srgb, var(--outline) 62%, transparent); background: var(--surface-high); color: var(--text); }
|
||||||
|
.button-link, .section-link { color: var(--cyan-dim); }
|
||||||
|
.button-danger { border-color: color-mix(in srgb, var(--danger) 55%, transparent); background: color-mix(in srgb, var(--danger) 14%, var(--surface-high)); color: var(--coral); }
|
||||||
|
.button-block { width: 100%; }
|
||||||
|
.button[disabled] { cursor: not-allowed; opacity: .48; filter: grayscale(.5); }
|
||||||
|
.icon-button { display: inline-grid; width: 42px; height: 42px; flex: 0 0 auto; place-items: center; border: 1px solid transparent; border-radius: var(--radius-sm); background: transparent; color: var(--text-muted); }
|
||||||
|
.icon-button:hover { border-color: color-mix(in srgb, var(--outline) 50%, transparent); background: var(--surface-high); color: var(--text-strong); }
|
||||||
|
.avatar { display: grid; width: 35px; height: 35px; flex: 0 0 auto; place-items: center; border: 1px solid var(--outline); border-radius: 50%; background: var(--surface-highest); color: var(--text-strong); font: 700 12px var(--font-mono); }
|
||||||
|
.avatar-small { width: 32px; height: 32px; }
|
||||||
|
.command-search { display: flex; width: min(560px, 62vw); min-height: 42px; align-items: center; gap: 10px; padding: 0 11px; border: 1px solid color-mix(in srgb, var(--outline) 56%, transparent); border-radius: var(--radius-sm); background: var(--surface-low); }
|
||||||
|
.command-search input { min-height: 38px !important; padding: 0 !important; border: 0 !important; background: transparent !important; box-shadow: none !important; }
|
||||||
|
.command-search kbd { padding: 2px 6px; border: 1px solid var(--outline); border-radius: 3px; color: var(--text-faint); font-size: 10px; }
|
||||||
|
.system-live { display: flex; min-height: 38px; align-items: center; gap: 8px; padding: 0 10px; color: var(--emerald); font: 600 11px var(--font-mono); letter-spacing: .07em; text-transform: uppercase; }
|
||||||
|
.status-beacon { width: 8px; height: 8px; flex: 0 0 auto; border-radius: 50%; background: var(--text-faint); }
|
||||||
|
.status-beacon.is-live { background: var(--emerald); box-shadow: 0 0 0 4px color-mix(in srgb, var(--emerald) 10%, transparent), 0 0 12px color-mix(in srgb, var(--emerald) 50%, transparent); }
|
||||||
|
.account-menu { position: relative; }
|
||||||
|
.account-menu summary { display: flex; min-height: 42px; align-items: center; gap: 5px; list-style: none; }
|
||||||
|
.account-menu summary::-webkit-details-marker { display: none; }
|
||||||
|
.account-popover { position: absolute; top: calc(100% + 12px); right: 0; display: grid; width: 230px; gap: 5px; padding: 14px; border: 1px solid var(--outline); background: var(--surface-container); box-shadow: 0 16px 50px rgba(0,0,0,.28); }
|
||||||
|
.account-popover strong, .account-popover small { display: block; }
|
||||||
|
.account-popover a, .account-popover button { display: flex; width: 100%; min-height: 40px; align-items: center; gap: 8px; margin-top: 8px; padding: 8px; background: transparent; color: var(--text); text-align: left; }
|
||||||
|
.account-popover a:hover, .account-popover button:hover { background: var(--surface-high); }
|
||||||
|
.panel, .tonal-panel, .job-card, .result-card, .source-card, .health-card, .profile-card, .form-panel { border: 1px solid color-mix(in srgb, var(--outline) 48%, transparent); border-radius: var(--radius-md); background: var(--surface-container); }
|
||||||
|
.panel, .tonal-panel, .health-card, .form-panel { padding: 20px; }
|
||||||
|
.panel-subtle { background: var(--surface-low); }
|
||||||
|
.strong-match { border-color: color-mix(in srgb, var(--cyan) 35%, var(--outline)); box-shadow: var(--shadow-signal); }
|
||||||
|
.stat-card { position: relative; min-height: 128px; overflow: hidden; padding: 18px; border: 1px solid color-mix(in srgb, var(--outline) 48%, transparent); background: linear-gradient(145deg, color-mix(in srgb, var(--surface-container) 92%, var(--cyan)), var(--surface-container)); }
|
||||||
|
.stat-card::after { position: absolute; right: -28px; bottom: -45px; width: 100px; height: 100px; content: ""; border: 1px solid color-mix(in srgb, var(--cyan) 17%, transparent); border-radius: 50%; }
|
||||||
|
.stat-card.signal { border-color: color-mix(in srgb, var(--cyan) 45%, var(--outline)); }
|
||||||
|
.stat-card strong { display: block; margin: 13px 0 3px; color: var(--text-strong); font: 650 34px/1 var(--font-headline); }
|
||||||
|
.stat-card small { font: 11px var(--font-mono); }
|
||||||
|
.pill { display: inline-flex; min-height: 24px; align-items: center; gap: 6px; padding: 4px 8px; border: 1px solid var(--outline); border-radius: var(--radius-xs); background: var(--surface-high); color: var(--text-muted); font: 600 10px/1 var(--font-mono); letter-spacing: .055em; text-transform: uppercase; }
|
||||||
|
.pill-success, .pill-active, .pill-strong { border-color: color-mix(in srgb, var(--emerald) 44%, transparent); background: color-mix(in srgb, var(--emerald) 10%, var(--surface-high)); color: var(--emerald); }
|
||||||
|
.pill-review, .pill-partial, .pill-possible { border-color: color-mix(in srgb, var(--amber) 44%, transparent); background: color-mix(in srgb, var(--amber) 9%, var(--surface-high)); color: var(--amber); }
|
||||||
|
.pill-failed, .pill-hidden, .pill-error { border-color: color-mix(in srgb, var(--coral) 44%, transparent); background: color-mix(in srgb, var(--coral) 9%, var(--surface-high)); color: var(--coral); }
|
||||||
|
.pill-it, .pill-new, .pill-active { color: var(--cyan-dim); }
|
||||||
|
.title-row { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; }
|
||||||
|
.score-block { min-width: 76px; text-align: right; }
|
||||||
|
.score-block strong { display: block; color: var(--cyan); font: 650 28px/1 var(--font-headline); }
|
||||||
|
.score-block small { display: block; margin-top: 5px; font: 10px var(--font-mono); text-transform: uppercase; }
|
||||||
|
.score-pending strong { color: var(--text-faint); }
|
||||||
|
.job-meta { display: flex; flex-wrap: wrap; gap: 8px 16px; color: var(--text-muted); font-size: 13px; }
|
||||||
|
.job-meta span { display: inline-flex; align-items: center; gap: 6px; }
|
||||||
|
.job-meta .icon { width: 15px; height: 15px; }
|
||||||
|
.signal-list, .compact-list { margin: 12px 0 0; padding-left: 20px; }
|
||||||
|
.signal-list li::marker { color: var(--emerald); }
|
||||||
|
.attention { padding: 9px 11px; border-left: 2px solid var(--amber); background: color-mix(in srgb, var(--amber) 6%, var(--surface-high)); color: var(--text-muted); font-size: 13px; }
|
||||||
|
.empty-state { display: grid; min-height: 240px; place-items: center; align-content: center; padding: 32px; border: 1px dashed var(--outline); background: var(--surface-low); text-align: center; }
|
||||||
|
.compact-empty { min-height: 180px; }
|
||||||
|
.empty-icon { display: grid; width: 48px; height: 48px; place-items: center; margin-bottom: 14px; border: 1px solid var(--outline); color: var(--cyan); }
|
||||||
|
.toast-stack { position: fixed; z-index: 100; top: 84px; right: 20px; display: grid; width: min(420px, calc(100vw - 40px)); gap: 8px; }
|
||||||
|
.toast { display: flex; align-items: flex-start; gap: 10px; padding: 13px 15px; border: 1px solid color-mix(in srgb, var(--cyan) 40%, var(--outline)); background: color-mix(in srgb, var(--surface-container) 94%, transparent); color: var(--text-strong); box-shadow: 0 14px 40px rgba(0,0,0,.25); backdrop-filter: blur(14px); }
|
||||||
|
.toast.error { border-color: color-mix(in srgb, var(--coral) 50%, var(--outline)); }
|
||||||
|
.message { padding: 11px 13px; border-left: 2px solid var(--cyan); background: var(--surface-high); }
|
||||||
|
.message.error { border-color: var(--coral); color: var(--coral); }
|
||||||
|
.technical-log { max-height: 210px; overflow: auto; padding: 12px; background: var(--surface-lowest); color: var(--text-muted); font: 11px/1.6 var(--font-mono); }
|
||||||
|
.disclosure-icon { transition: transform .18s; }
|
||||||
|
[aria-expanded="true"] .disclosure-icon, details[open] > summary .disclosure-icon { transform: rotate(180deg); }
|
||||||
|
.auth-card { width: min(460px, 100%); padding: 30px; border: 1px solid color-mix(in srgb, var(--cyan) 25%, var(--outline)); background: color-mix(in srgb, var(--surface-container) 94%, transparent); box-shadow: 0 30px 90px rgba(0,0,0,.3), var(--shadow-signal); }
|
||||||
|
.auth-brand { display: flex; align-items: flex-start; gap: 15px; margin-bottom: 26px; }
|
||||||
|
.auth-brand .brand-radar, .auth-brand .brand-mark { display: grid; width: 48px; height: 48px; flex: 0 0 auto; place-items: center; border: 1px solid var(--cyan); color: var(--cyan); }
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
.cockpit { min-height: 100vh; }
|
||||||
|
.cockpit-sidebar { position: fixed; inset: 0 auto 0 0; z-index: 50; display: flex; width: var(--sidebar-width); flex-direction: column; border-right: 1px solid color-mix(in srgb, var(--outline) 38%, transparent); background: var(--surface-lowest); }
|
||||||
|
.sidebar-heading { display: flex; min-height: var(--topbar-height); align-items: center; justify-content: space-between; padding: 14px 20px; border-bottom: 1px solid color-mix(in srgb, var(--outline) 25%, transparent); }
|
||||||
|
.cockpit-brand { display: flex; min-width: 0; align-items: center; gap: 11px; }
|
||||||
|
.cockpit-brand strong { display: block; color: var(--text-strong); font: 700 18px/1.1 var(--font-headline); letter-spacing: -.025em; }
|
||||||
|
.cockpit-brand small { display: block; margin-top: 5px; color: var(--text-faint); font: 500 10px/1 var(--font-mono); letter-spacing: .13em; text-transform: uppercase; }
|
||||||
|
.brand-radar { display: grid; width: 35px; height: 35px; place-items: center; border: 1px solid color-mix(in srgb, var(--cyan) 50%, transparent); border-radius: var(--radius-md); background: color-mix(in srgb, var(--cyan) 12%, transparent); color: var(--cyan); box-shadow: var(--shadow-signal); }
|
||||||
|
.cockpit-nav { flex: 1; overflow-y: auto; padding: 19px 12px; }
|
||||||
|
.nav-caption { margin: 18px 12px 7px; color: var(--text-faint); font: 600 10px/1 var(--font-mono); letter-spacing: .16em; text-transform: uppercase; }
|
||||||
|
.nav-caption:first-child { margin-top: 0; }
|
||||||
|
.nav-item { position: relative; display: flex; min-height: 43px; align-items: center; gap: 13px; margin: 2px 0; padding: 9px 12px; border-radius: var(--radius-sm); color: var(--text-muted); font: 500 13px/1.2 var(--font-mono); transition: color .15s, background .15s, transform .15s; }
|
||||||
|
.nav-item:hover { transform: translateX(2px); background: var(--surface-low); color: var(--text-strong); }
|
||||||
|
.nav-item.is-active { background: color-mix(in srgb, var(--cyan) 12%, var(--surface-container)); color: var(--text-strong); }
|
||||||
|
.nav-item.is-active::after { position: absolute; inset: 0 -1px 0 auto; width: 2px; content: ""; background: var(--cyan); box-shadow: 0 0 12px var(--cyan); }
|
||||||
|
.nav-item.is-active .icon { color: var(--cyan); }
|
||||||
|
.sidebar-radar-card { display: flex; align-items: center; gap: 10px; margin: 0 12px 12px; padding: 12px; border: 1px solid color-mix(in srgb, var(--outline) 42%, transparent); background: var(--surface-low); }
|
||||||
|
.sidebar-radar-card strong, .sidebar-radar-card small { display: block; }
|
||||||
|
.sidebar-radar-card strong { font-size: 12px; }
|
||||||
|
.sidebar-radar-card small { margin-top: 3px; font: 10px/1.2 var(--font-mono); }
|
||||||
|
.sidebar-account { display: flex; align-items: center; gap: 10px; padding: 15px 18px; border-top: 1px solid color-mix(in srgb, var(--outline) 28%, transparent); background: var(--surface-low); }
|
||||||
|
.sidebar-account strong, .sidebar-account small { display: block; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
||||||
|
.sidebar-account small { font: 10px var(--font-mono); text-transform: uppercase; }
|
||||||
|
.cockpit-workspace { min-height: 100vh; margin-left: var(--sidebar-width); }
|
||||||
|
.cockpit-topbar { position: fixed; inset: 0 0 auto var(--sidebar-width); z-index: 40; display: flex; height: var(--topbar-height); align-items: center; justify-content: space-between; gap: 20px; padding: 0 var(--content-gutter); border-bottom: 1px solid color-mix(in srgb, var(--outline) 32%, transparent); background: color-mix(in srgb, var(--surface) 88%, transparent); backdrop-filter: blur(18px); }
|
||||||
|
.topbar-start, .topbar-actions { display: flex; min-width: 0; align-items: center; gap: 12px; }
|
||||||
|
.topbar-start { flex: 1; }
|
||||||
|
.cockpit-main { width: 100%; min-height: 100vh; padding: calc(var(--topbar-height) + 36px) var(--content-gutter) 56px; }
|
||||||
|
.cockpit-main > * { width: 100%; max-width: 2240px; margin-right: auto; margin-left: auto; }
|
||||||
|
.nav-scrim, .mobile-tabbar { display: none; }
|
||||||
|
.auth-shell { display: grid; min-height: 100vh; place-items: center; padding: 24px; background: radial-gradient(circle at 50% 12%, rgba(0,245,255,.09), transparent 27rem), var(--surface-lowest); }
|
||||||
|
.page-header, .cockpit-heading { display: flex; align-items: flex-end; justify-content: space-between; gap: 24px; margin-bottom: 26px; }
|
||||||
|
.page-header p:last-child, .cockpit-heading p:last-child { max-width: 780px; margin-bottom: 0; color: var(--text-muted); }
|
||||||
|
.eyebrow, .section-kicker, .kpi-label { display: block; margin-bottom: 8px; color: var(--cyan-dim); font: 600 11px/1.1 var(--font-mono); letter-spacing: .13em; text-transform: uppercase; }
|
||||||
|
.section-heading { display: flex; align-items: end; justify-content: space-between; gap: 20px; margin: 30px 0 15px; }
|
||||||
|
.section-heading p { margin-bottom: 0; color: var(--text-muted); }
|
||||||
|
.page-actions, .job-actions, .form-actions, .stack-actions { display: flex; flex-wrap: wrap; align-items: center; gap: 9px; }
|
||||||
|
.form-actions { justify-content: flex-end; margin-top: 22px; }
|
||||||
|
.stack { display: grid; gap: 16px; }
|
||||||
|
.form-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 18px; }
|
||||||
|
.span-2 { grid-column: span 2; }
|
||||||
|
.card-list { display: grid; gap: 12px; }
|
||||||
|
.stats-grid { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 12px; margin-bottom: 26px; }
|
||||||
|
.dashboard-grid { display: grid; grid-template-columns: minmax(0, 1.7fr) minmax(290px, .65fr); align-items: start; gap: 22px; }
|
||||||
|
.dashboard-rail { position: sticky; top: calc(var(--topbar-height) + 22px); display: grid; gap: 14px; }
|
||||||
|
.results-layout { display: grid; grid-template-columns: minmax(0, 1fr) minmax(280px, .38fr); align-items: start; gap: 20px; }
|
||||||
|
.results-feed, .job-feed { display: grid; gap: 12px; }
|
||||||
|
.definition-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 10px; }
|
||||||
|
.health-grid { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 14px; }
|
||||||
@@ -0,0 +1,68 @@
|
|||||||
|
.radar-status { display: flex; min-height: 40px; align-items: center; gap: 8px; padding: 9px 12px; border: 1px solid color-mix(in srgb, var(--outline) 50%, transparent); background: var(--surface-low); color: var(--text-muted); font: 11px var(--font-mono); }
|
||||||
|
.rail-panel { padding: 18px; }
|
||||||
|
.rail-list { display: grid; margin-top: 15px; }
|
||||||
|
.rail-item, .rail-application { display: flex; min-height: 46px; align-items: center; justify-content: space-between; gap: 12px; border-top: 1px solid color-mix(in srgb, var(--outline) 34%, transparent); }
|
||||||
|
.rail-application strong, .rail-application small { display: block; }
|
||||||
|
.rail-application small { margin-top: 2px; }
|
||||||
|
.feed-card, .result-card { position: relative; display: grid; grid-template-columns: minmax(0, 1fr) auto; overflow: hidden; }
|
||||||
|
.job-card-main, .result-body { padding: 20px; }
|
||||||
|
.job-card-head, .result-heading { display: flex; justify-content: space-between; gap: 18px; }
|
||||||
|
.job-card h3, .result-card h2 { margin: 9px 0 5px; font-size: 19px; }
|
||||||
|
.feed-card-actions, .result-actions { display: flex; min-width: 150px; flex-direction: column; justify-content: center; gap: 9px; padding: 16px; border-left: 1px solid color-mix(in srgb, var(--outline) 34%, transparent); background: var(--surface-low); }
|
||||||
|
.result-accent { position: absolute; inset: 0 auto 0 0; width: 2px; background: var(--cyan); opacity: .5; }
|
||||||
|
.result-summary { margin: 12px 0 0; color: var(--text-muted); }
|
||||||
|
.evidence-line { display: flex; align-items: flex-start; gap: 8px; margin: 12px 0 0; padding: 9px 10px; background: var(--surface-high); color: var(--text-muted); font-size: 13px; }
|
||||||
|
.positive-evidence { border-left: 2px solid var(--emerald); }
|
||||||
|
.concern-evidence { border-left: 2px solid var(--amber); }
|
||||||
|
.filter-console { display: grid; grid-template-columns: minmax(220px, 1.8fr) repeat(5, minmax(125px, 1fr)) auto; align-items: end; gap: 10px; margin-bottom: 24px; padding: 15px; border: 1px solid color-mix(in srgb, var(--outline) 48%, transparent); background: var(--surface-low); }
|
||||||
|
.input-with-icon { position: relative; display: block; }
|
||||||
|
.input-with-icon .icon { position: absolute; z-index: 1; top: 12px; left: 11px; color: var(--text-faint); }
|
||||||
|
.input-with-icon input { padding-left: 39px !important; }
|
||||||
|
.results-toolbar { display: flex; align-items: end; justify-content: space-between; gap: 16px; margin-bottom: 12px; }
|
||||||
|
.view-indicator { display: inline-flex; align-items: center; gap: 7px; color: var(--text-muted); font: 11px var(--font-mono); }
|
||||||
|
.pagination { display: flex; justify-content: center; gap: 8px; margin-top: 20px; }
|
||||||
|
.job-detail-grid { display: grid; grid-template-columns: minmax(0, 1fr) 340px; align-items: start; gap: 20px; }
|
||||||
|
.detail-sidebar { position: sticky; top: calc(var(--topbar-height) + 20px); display: grid; gap: 13px; }
|
||||||
|
.detail-hero { position: relative; overflow: hidden; padding: 26px; border: 1px solid color-mix(in srgb, var(--cyan) 25%, var(--outline)); background: linear-gradient(135deg, color-mix(in srgb, var(--surface-container) 90%, var(--cyan)), var(--surface-low)); }
|
||||||
|
.detail-hero::after { position: absolute; top: -110px; right: -70px; width: 300px; height: 300px; content: ""; border: 1px solid color-mix(in srgb, var(--cyan) 14%, transparent); border-radius: 50%; box-shadow: inset 0 0 0 42px color-mix(in srgb, var(--cyan) 2%, transparent), inset 0 0 0 83px color-mix(in srgb, var(--cyan) 3%, transparent); }
|
||||||
|
.detail-hero > * { position: relative; z-index: 1; }
|
||||||
|
.detail-section { margin-top: 14px; padding: 22px; border: 1px solid color-mix(in srgb, var(--outline) 42%, transparent); background: var(--surface-container); }
|
||||||
|
.sticky-actions { position: sticky; bottom: 16px; z-index: 20; display: flex; justify-content: center; gap: 8px; width: fit-content; max-width: calc(100vw - 32px); margin: 20px auto 0; padding: 8px; border: 1px solid var(--outline); background: color-mix(in srgb, var(--surface-lowest) 90%, transparent); backdrop-filter: blur(16px); }
|
||||||
|
.pipeline-board { display: grid; grid-auto-columns: minmax(270px, 1fr); grid-auto-flow: column; gap: 12px; overflow-x: auto; padding-bottom: 12px; }
|
||||||
|
.pipeline-column { min-height: 370px; padding: 12px; border: 1px solid color-mix(in srgb, var(--outline) 42%, transparent); background: var(--surface-low); }
|
||||||
|
.pipeline-column-header { display: flex; align-items: center; justify-content: space-between; margin-bottom: 12px; padding: 4px; font: 600 11px var(--font-mono); letter-spacing: .08em; text-transform: uppercase; }
|
||||||
|
.application-card { margin-bottom: 10px; padding: 15px; border: 1px solid color-mix(in srgb, var(--outline) 52%, transparent); background: var(--surface-container); }
|
||||||
|
.application-card h3 { margin: 6px 0; }
|
||||||
|
.profile-editor-layout { display: grid; grid-template-columns: minmax(0, 1fr) 330px; align-items: start; gap: 20px; }
|
||||||
|
.profile-preview { position: sticky; top: calc(var(--topbar-height) + 20px); }
|
||||||
|
.form-section { margin-bottom: 14px; padding: 20px; border: 1px solid color-mix(in srgb, var(--outline) 45%, transparent); background: var(--surface-container); }
|
||||||
|
.grouped-choices, .grouped-choices > div { display: grid; gap: 8px; }
|
||||||
|
.grouped-choices > div > label, ul.grouped-choices label { display: flex; min-height: 40px; grid-template-columns: auto 1fr; align-items: center; gap: 9px; padding: 8px 10px; border: 1px solid color-mix(in srgb, var(--outline) 38%, transparent); background: var(--surface-low); font: 500 13px var(--font-body); text-transform: none; }
|
||||||
|
.source-card { overflow: hidden; }
|
||||||
|
.source-card-head { display: flex; align-items: center; justify-content: space-between; gap: 18px; padding: 16px 18px; }
|
||||||
|
.source-identity { display: flex; min-width: 0; align-items: center; gap: 13px; }
|
||||||
|
.source-identity h2 { margin: 0; font-size: 17px; }
|
||||||
|
.source-icon { display: grid; width: 42px; height: 42px; flex: 0 0 auto; place-items: center; border: 1px solid color-mix(in srgb, var(--outline) 65%, transparent); background: var(--surface-high); color: var(--cyan); }
|
||||||
|
.source-actions { display: flex; align-items: center; gap: 10px; }
|
||||||
|
.source-details { padding: 15px 18px; border-top: 1px solid color-mix(in srgb, var(--outline) 35%, transparent); background: var(--surface-low); }
|
||||||
|
.source-details-grid { display: grid; grid-template-columns: 1.3fr 1fr; gap: 16px; }
|
||||||
|
.source-alert-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 10px; }
|
||||||
|
.source-alert-card { display: flex; align-items: center; justify-content: space-between; gap: 14px; padding: 14px; border: 1px solid color-mix(in srgb, var(--outline) 40%, transparent); background: var(--surface-low); }
|
||||||
|
.import-disclosure { padding: 0; overflow: hidden; }
|
||||||
|
.import-disclosure > summary { display: flex; align-items: center; gap: 12px; padding: 17px; list-style: none; }
|
||||||
|
.import-disclosure > summary > span:nth-child(2) { display: grid; flex: 1; }
|
||||||
|
.import-disclosure-body { padding: 18px; border-top: 1px solid color-mix(in srgb, var(--outline) 34%, transparent); }
|
||||||
|
.import-layout, .mailbox-form-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 14px; }
|
||||||
|
.health-card dl, .definition-grid { margin: 14px 0 0; }
|
||||||
|
.health-card dl { display: grid; grid-template-columns: repeat(2, 1fr); gap: 8px; }
|
||||||
|
.health-card dt, .definition-grid dt { color: var(--text-faint); font: 10px var(--font-mono); text-transform: uppercase; }
|
||||||
|
.health-card dd, .definition-grid dd { margin: 3px 0 0; color: var(--text-strong); }
|
||||||
|
.system-hero { display: grid; grid-template-columns: minmax(0, 1.3fr) minmax(0, .7fr); gap: 12px; }
|
||||||
|
.system-readiness { display: flex; align-items: center; gap: 15px; padding: 21px; border: 1px solid color-mix(in srgb, var(--outline) 50%, transparent); background: var(--surface-container); }
|
||||||
|
.system-readiness .status-beacon { margin-left: auto; }
|
||||||
|
.system-counts { display: grid; grid-template-columns: repeat(2, 1fr); gap: 8px; }
|
||||||
|
.system-counts article { padding: 14px; border: 1px solid color-mix(in srgb, var(--outline) 40%, transparent); background: var(--surface-low); }
|
||||||
|
.system-counts strong { display: block; margin-top: 8px; font: 650 24px var(--font-headline); }
|
||||||
|
.command-panel { display: grid; grid-template-columns: repeat(2, 1fr); gap: 12px; }
|
||||||
|
.command-panel pre { margin: 6px 0 0; padding: 10px; background: var(--surface-lowest); color: var(--emerald); }
|
||||||
|
.auth-card .stack label { color: var(--text); font-size: 13px; }
|
||||||
@@ -0,0 +1,72 @@
|
|||||||
|
@media (min-width: 1850px) {
|
||||||
|
.results-feed { grid-template-columns: repeat(2, minmax(0, 1fr)); }
|
||||||
|
.results-feed .result-card { grid-template-columns: 1fr; }
|
||||||
|
.results-feed .result-actions { min-width: 0; flex-direction: row; justify-content: flex-start; border-top: 1px solid color-mix(in srgb, var(--outline) 34%, transparent); border-left: 0; }
|
||||||
|
.health-grid { grid-template-columns: repeat(4, minmax(0, 1fr)); }
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 1450px) {
|
||||||
|
.filter-console { grid-template-columns: minmax(240px, 2fr) repeat(3, minmax(130px, 1fr)); }
|
||||||
|
.filter-console > :nth-last-child(-n+3) { grid-row: 2; }
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 1180px) {
|
||||||
|
.dashboard-grid, .results-layout, .job-detail-grid, .profile-editor-layout { grid-template-columns: 1fr; }
|
||||||
|
.dashboard-rail, .detail-sidebar, .profile-preview { position: static; grid-template-columns: repeat(2, minmax(0, 1fr)); }
|
||||||
|
.stats-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
|
||||||
|
.health-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
|
||||||
|
.filter-console { grid-template-columns: repeat(3, minmax(0, 1fr)); }
|
||||||
|
.filter-console > * { grid-row: auto !important; }
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 820px) {
|
||||||
|
:root { --content-gutter: 16px; --topbar-height: 64px; }
|
||||||
|
.mobile-only { display: inline-grid; }
|
||||||
|
.desktop-label { display: none !important; }
|
||||||
|
.cockpit-sidebar { width: min(296px, 86vw); transform: translateX(-102%); transition: transform .2s ease; }
|
||||||
|
.nav-open .cockpit-sidebar { transform: translateX(0); }
|
||||||
|
.cockpit-workspace { margin-left: 0; }
|
||||||
|
.cockpit-topbar { left: 0; padding: 0 14px; }
|
||||||
|
.nav-scrim { position: fixed; z-index: 45; inset: 0; display: block; visibility: hidden; background: rgba(2,7,17,.7); opacity: 0; transition: opacity .2s, visibility .2s; }
|
||||||
|
.nav-open .nav-scrim { visibility: visible; opacity: 1; }
|
||||||
|
.command-search { width: min(100%, 510px); }
|
||||||
|
.cockpit-main { padding-bottom: 92px; }
|
||||||
|
.mobile-tabbar { position: fixed; z-index: 38; inset: auto 0 0; display: grid; grid-template-columns: repeat(4, 1fr); border-top: 1px solid var(--outline); background: color-mix(in srgb, var(--surface-lowest) 94%, transparent); backdrop-filter: blur(18px); }
|
||||||
|
.mobile-tabbar a { display: grid; min-height: 66px; place-items: center; align-content: center; gap: 4px; color: var(--text-faint); font: 10px var(--font-mono); }
|
||||||
|
.mobile-tabbar a.is-active { color: var(--cyan); }
|
||||||
|
.mobile-tabbar .icon { width: 19px; height: 19px; }
|
||||||
|
.page-header, .cockpit-heading, .section-heading { align-items: flex-start; flex-direction: column; }
|
||||||
|
.page-header { margin-bottom: 20px; }
|
||||||
|
.form-grid, .source-details-grid, .import-layout, .mailbox-form-grid, .system-hero { grid-template-columns: 1fr; }
|
||||||
|
.span-2 { grid-column: auto; }
|
||||||
|
.filter-console { grid-template-columns: repeat(2, minmax(0, 1fr)); }
|
||||||
|
.filter-console .grow { grid-column: span 2; }
|
||||||
|
.dashboard-rail, .detail-sidebar, .profile-preview { grid-template-columns: 1fr; }
|
||||||
|
.source-alert-grid { grid-template-columns: 1fr; }
|
||||||
|
.feed-card, .result-card { grid-template-columns: 1fr; }
|
||||||
|
.feed-card-actions, .result-actions { min-width: 0; flex-direction: row; justify-content: flex-start; border-top: 1px solid color-mix(in srgb, var(--outline) 34%, transparent); border-left: 0; }
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 560px) {
|
||||||
|
h1 { font-size: 27px; }
|
||||||
|
.cockpit-main { padding-top: calc(var(--topbar-height) + 24px); }
|
||||||
|
.command-search kbd, .system-live { display: none; }
|
||||||
|
.topbar-actions { gap: 3px; }
|
||||||
|
.stats-grid, .health-grid, .filter-console, .command-panel, .system-counts { grid-template-columns: 1fr; }
|
||||||
|
.filter-console .grow { grid-column: auto; }
|
||||||
|
.stat-card { min-height: 105px; }
|
||||||
|
.job-card-main, .result-body, .panel, .tonal-panel, .health-card, .form-panel, .form-section, .detail-section { padding: 16px; }
|
||||||
|
.job-card-head, .result-heading { align-items: flex-start; flex-direction: column; }
|
||||||
|
.score-block { min-width: 0; text-align: left; }
|
||||||
|
.source-card-head, .source-alert-card { align-items: flex-start; flex-direction: column; }
|
||||||
|
.source-actions { width: 100%; justify-content: space-between; }
|
||||||
|
.page-actions, .job-actions, .form-actions { width: 100%; }
|
||||||
|
.page-actions .button, .form-actions .button { flex: 1; }
|
||||||
|
.sticky-actions { bottom: 76px; width: 100%; overflow-x: auto; justify-content: flex-start; }
|
||||||
|
.auth-shell { padding: 14px; }
|
||||||
|
.auth-card { padding: 22px 18px; }
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (prefers-reduced-motion: reduce) {
|
||||||
|
*, *::before, *::after { scroll-behavior: auto !important; animation-duration: .01ms !important; animation-iteration-count: 1 !important; transition-duration: .01ms !important; }
|
||||||
|
}
|
||||||
@@ -0,0 +1,63 @@
|
|||||||
|
:root {
|
||||||
|
color-scheme: dark;
|
||||||
|
--surface-lowest: #060e20;
|
||||||
|
--surface: #0b1326;
|
||||||
|
--surface-low: #131b2e;
|
||||||
|
--surface-container: #171f33;
|
||||||
|
--surface-high: #222a3d;
|
||||||
|
--surface-highest: #2d3449;
|
||||||
|
--surface-bright: #31394d;
|
||||||
|
--text: #dae2fd;
|
||||||
|
--text-strong: #e9feff;
|
||||||
|
--text-muted: #aababb;
|
||||||
|
--text-faint: #7f9095;
|
||||||
|
--outline: #3a494a;
|
||||||
|
--outline-strong: #526365;
|
||||||
|
--cyan: #00f5ff;
|
||||||
|
--cyan-dim: #00dce5;
|
||||||
|
--cyan-ink: #002b2e;
|
||||||
|
--emerald: #4edea3;
|
||||||
|
--emerald-dim: #00a572;
|
||||||
|
--indigo: #8f91ff;
|
||||||
|
--amber: #f5c451;
|
||||||
|
--coral: #ffb4ab;
|
||||||
|
--danger: #ff7b72;
|
||||||
|
--sidebar-width: 256px;
|
||||||
|
--topbar-height: 72px;
|
||||||
|
--content-gutter: clamp(20px, 2.2vw, 40px);
|
||||||
|
--radius-xs: 2px;
|
||||||
|
--radius-sm: 4px;
|
||||||
|
--radius-md: 8px;
|
||||||
|
--radius-lg: 12px;
|
||||||
|
--shadow-signal: 0 0 24px rgba(0, 245, 255, 0.08);
|
||||||
|
--font-headline: "Segoe UI Variable Display", "Aptos Display", "Segoe UI", sans-serif;
|
||||||
|
--font-body: "Segoe UI Variable Text", "Aptos", "Segoe UI", sans-serif;
|
||||||
|
--font-mono: "Cascadia Mono", "SFMono-Regular", Consolas, monospace;
|
||||||
|
}
|
||||||
|
|
||||||
|
:root[data-theme="light"] {
|
||||||
|
color-scheme: light;
|
||||||
|
--surface-lowest: #e6eff2;
|
||||||
|
--surface: #f5f9fa;
|
||||||
|
--surface-low: #edf4f5;
|
||||||
|
--surface-container: #ffffff;
|
||||||
|
--surface-high: #e3edef;
|
||||||
|
--surface-highest: #d6e3e5;
|
||||||
|
--surface-bright: #cadadd;
|
||||||
|
--text: #12262b;
|
||||||
|
--text-strong: #07181d;
|
||||||
|
--text-muted: #475e63;
|
||||||
|
--text-faint: #64787d;
|
||||||
|
--outline: #b7c9cc;
|
||||||
|
--outline-strong: #82999d;
|
||||||
|
--cyan: #007c83;
|
||||||
|
--cyan-dim: #006b71;
|
||||||
|
--cyan-ink: #f2ffff;
|
||||||
|
--emerald: #087a55;
|
||||||
|
--emerald-dim: #d6f5e7;
|
||||||
|
--indigo: #5558bf;
|
||||||
|
--amber: #8a5b00;
|
||||||
|
--coral: #a7332d;
|
||||||
|
--danger: #a82d27;
|
||||||
|
--shadow-signal: 0 0 20px rgba(0, 124, 131, 0.1);
|
||||||
|
}
|
||||||
+35
-29
@@ -1,46 +1,35 @@
|
|||||||
(() => {
|
(() => {
|
||||||
|
"use strict";
|
||||||
const root = document.documentElement;
|
const root = document.documentElement;
|
||||||
let storedTheme = null;
|
|
||||||
try {
|
|
||||||
storedTheme = localStorage.getItem("vacatureradar-theme");
|
|
||||||
} catch {
|
|
||||||
// Private or embedded browser contexts may disable persistent storage.
|
|
||||||
}
|
|
||||||
if (storedTheme === "light" || storedTheme === "dark") root.dataset.theme = storedTheme;
|
|
||||||
|
|
||||||
const themeToggle = document.querySelector("[data-theme-toggle]");
|
|
||||||
themeToggle?.addEventListener("click", () => {
|
|
||||||
const theme = root.dataset.theme === "light" ? "dark" : "light";
|
|
||||||
root.dataset.theme = theme;
|
|
||||||
try {
|
|
||||||
localStorage.setItem("vacatureradar-theme", theme);
|
|
||||||
} catch {
|
|
||||||
// The selected theme still applies for the current page.
|
|
||||||
}
|
|
||||||
themeToggle.setAttribute("aria-label", theme === "light" ? "Donker thema activeren" : "Licht thema activeren");
|
|
||||||
});
|
|
||||||
|
|
||||||
const frame = document.querySelector("[data-app-frame]");
|
const frame = document.querySelector("[data-app-frame]");
|
||||||
const sidebar = document.querySelector("[data-sidebar]");
|
const sidebar = document.querySelector("[data-sidebar]");
|
||||||
const toggle = document.querySelector("[data-nav-toggle]");
|
const navToggle = document.querySelector("[data-nav-toggle]");
|
||||||
const closeButtons = document.querySelectorAll("[data-nav-close], [data-nav-scrim]");
|
|
||||||
let returnFocus = null;
|
let returnFocus = null;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const storedTheme = localStorage.getItem("vacatureradar-theme");
|
||||||
|
if (storedTheme === "light" || storedTheme === "dark") root.dataset.theme = storedTheme;
|
||||||
|
} catch { /* Persistent storage is optional. */ }
|
||||||
|
|
||||||
|
document.querySelector("[data-theme-toggle]")?.addEventListener("click", (event) => {
|
||||||
|
const next = root.dataset.theme === "light" ? "dark" : "light";
|
||||||
|
root.dataset.theme = next;
|
||||||
|
event.currentTarget.setAttribute("aria-label", next === "light" ? "Donker thema activeren" : "Licht thema activeren");
|
||||||
|
try { localStorage.setItem("vacatureradar-theme", next); } catch { /* Current page still updates. */ }
|
||||||
|
});
|
||||||
|
|
||||||
const closeNavigation = () => {
|
const closeNavigation = () => {
|
||||||
frame?.classList.remove("nav-open");
|
frame?.classList.remove("nav-open");
|
||||||
toggle?.setAttribute("aria-expanded", "false");
|
navToggle?.setAttribute("aria-expanded", "false");
|
||||||
if (returnFocus instanceof HTMLElement) returnFocus.focus();
|
if (returnFocus instanceof HTMLElement) returnFocus.focus();
|
||||||
};
|
};
|
||||||
toggle?.addEventListener("click", () => {
|
navToggle?.addEventListener("click", () => {
|
||||||
returnFocus = document.activeElement;
|
returnFocus = document.activeElement;
|
||||||
frame?.classList.add("nav-open");
|
frame?.classList.add("nav-open");
|
||||||
toggle.setAttribute("aria-expanded", "true");
|
navToggle.setAttribute("aria-expanded", "true");
|
||||||
sidebar?.querySelector("a, button")?.focus();
|
sidebar?.querySelector("a, button")?.focus();
|
||||||
});
|
});
|
||||||
closeButtons.forEach((button) => button.addEventListener("click", closeNavigation));
|
document.querySelectorAll("[data-nav-close], [data-nav-scrim]").forEach((control) => control.addEventListener("click", closeNavigation));
|
||||||
document.addEventListener("keydown", (event) => {
|
|
||||||
if (event.key === "Escape") closeNavigation();
|
|
||||||
});
|
|
||||||
|
|
||||||
document.querySelectorAll("[data-disclosure]").forEach((button) => {
|
document.querySelectorAll("[data-disclosure]").forEach((button) => {
|
||||||
button.addEventListener("click", () => {
|
button.addEventListener("click", () => {
|
||||||
@@ -51,4 +40,21 @@
|
|||||||
target.hidden = expanded;
|
target.hidden = expanded;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
document.querySelectorAll("[data-view-switch]").forEach((button) => {
|
||||||
|
button.addEventListener("click", () => {
|
||||||
|
const group = button.closest("[data-view-controls]");
|
||||||
|
const view = button.dataset.viewSwitch;
|
||||||
|
group?.querySelectorAll("[data-view-switch]").forEach((item) => item.setAttribute("aria-pressed", String(item === button)));
|
||||||
|
document.querySelectorAll("[data-view]").forEach((panel) => { panel.hidden = panel.dataset.view !== view; });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
document.addEventListener("keydown", (event) => {
|
||||||
|
if (event.key === "Escape") closeNavigation();
|
||||||
|
if (event.key === "/" && !/INPUT|TEXTAREA|SELECT/.test(document.activeElement?.tagName || "")) {
|
||||||
|
event.preventDefault();
|
||||||
|
document.getElementById("global-search")?.focus();
|
||||||
|
}
|
||||||
|
});
|
||||||
})();
|
})();
|
||||||
|
|||||||
@@ -0,0 +1,3 @@
|
|||||||
|
{% extends "base.html" %}
|
||||||
|
{% block title %}Geen toegang · VacatureRadar{% endblock %}
|
||||||
|
{% block content %}<section class="empty-state"><div class="empty-icon"><svg class="icon"><use href="#icon-alert"></use></svg></div><p class="eyebrow">403 · Toegang geweigerd</p><h1>Deze intelligence is niet voor jou beschikbaar</h1><p>Je account heeft geen toegang tot dit onderdeel. Er zijn geen gegevens gewijzigd.</p><a class="button button-primary" href="{% url 'dashboard:today' %}">Terug naar overzicht</a></section>{% endblock %}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
{% extends "base.html" %}
|
||||||
|
{% block title %}Niet gevonden · VacatureRadar{% endblock %}
|
||||||
|
{% block content %}<section class="empty-state"><div class="empty-icon"><svg class="icon"><use href="#icon-radar"></use></svg></div><p class="eyebrow">404 · Geen radarsignaal</p><h1>Deze pagina is niet gevonden</h1><p>De link kan verouderd zijn of het object bestaat niet meer.</p><a class="button button-primary" href="{% url 'dashboard:today' %}">Terug naar overzicht</a></section>{% endblock %}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
{% extends "base.html" %}
|
||||||
|
{% block title %}Systeemfout · VacatureRadar{% endblock %}
|
||||||
|
{% block content %}<section class="empty-state"><div class="empty-icon"><svg class="icon"><use href="#icon-system"></use></svg></div><p class="eyebrow">500 · Systeemsignaal</p><h1>De radar kon dit verzoek niet verwerken</h1><p>Probeer het later opnieuw. Er worden hier geen technische details of gevoelige gegevens getoond.</p><a class="button button-primary" href="/">Terug naar overzicht</a></section>{% endblock %}
|
||||||
+74
-75
@@ -5,92 +5,91 @@
|
|||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
<meta name="color-scheme" content="dark light">
|
<meta name="color-scheme" content="dark light">
|
||||||
<meta name="theme-color" content="#07101f">
|
<meta name="theme-color" content="#060e20">
|
||||||
<title>{% block title %}{{ app_name }}{% endblock %}</title>
|
<title>{% block title %}{{ app_name }}{% endblock %}</title>
|
||||||
<link rel="icon" href="{% static 'favicon.svg' %}" type="image/svg+xml">
|
<link rel="icon" href="{% static 'favicon.svg' %}" type="image/svg+xml">
|
||||||
<link rel="stylesheet" href="{% static 'css/app.css' %}?v={{ app_version }}">
|
<link rel="stylesheet" href="{% static 'css/app.css' %}?v={{ app_version }}">
|
||||||
<script src="{% static 'js/app.js' %}?v={{ app_version }}" defer></script>
|
<script src="{% static 'js/app.js' %}?v={{ app_version }}" defer></script>
|
||||||
|
{% block extra_head %}{% endblock %}
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body class="{% block body_class %}{% endblock %}">
|
||||||
{% include "components/icon_sprite.html" %}
|
{% include "components/icon_sprite.html" %}
|
||||||
<a class="skip-link" href="#main">Naar hoofdinhoud</a>
|
<a class="skip-link" href="#main">Naar hoofdinhoud</a>
|
||||||
{% if user.is_authenticated %}
|
{% if user.is_authenticated %}
|
||||||
<div class="app-frame" data-app-frame>
|
<div class="cockpit" data-app-frame>
|
||||||
<aside class="sidebar" data-sidebar aria-label="Hoofdnavigatie">
|
<aside class="cockpit-sidebar" data-sidebar aria-label="Hoofdnavigatie">
|
||||||
<div class="sidebar-brand">
|
<div class="sidebar-heading">
|
||||||
<a class="brand" href="{% url 'dashboard:today' %}">
|
<a class="cockpit-brand" href="{% url 'dashboard:today' %}" aria-label="VacatureRadar dashboard">
|
||||||
<span class="brand-mark" aria-hidden="true"><svg class="icon"><use href="#icon-radar"></use></svg></span>
|
<span class="brand-radar" aria-hidden="true"><svg class="icon"><use href="#icon-radar"></use></svg></span>
|
||||||
<span><strong>VacatureRadar</strong><small>Intelligence cockpit</small></span>
|
<span><strong>VacatureRadar</strong><small>Intelligence cockpit</small></span>
|
||||||
</a>
|
</a>
|
||||||
<button class="icon-button sidebar-close mobile-only" type="button" data-nav-close aria-label="Menu sluiten">
|
<button class="icon-button nav-close mobile-only" type="button" data-nav-close aria-label="Menu sluiten"><svg class="icon"><use href="#icon-close"></use></svg></button>
|
||||||
<svg class="icon"><use href="#icon-close"></use></svg>
|
</div>
|
||||||
</button>
|
|
||||||
|
<nav class="cockpit-nav">
|
||||||
|
<p class="nav-caption">Intelligence</p>
|
||||||
|
<a class="nav-item {% if request.resolver_match.namespace == 'dashboard' and request.resolver_match.url_name == 'today' %}is-active{% endif %}" href="{% url 'dashboard:today' %}"><svg class="icon"><use href="#icon-dashboard"></use></svg><span>Overzicht</span></a>
|
||||||
|
<a class="nav-item {% if request.resolver_match.namespace == 'jobs' and request.resolver_match.url_name == 'list' %}is-active{% endif %}" href="{% url 'jobs:list' %}"><svg class="icon"><use href="#icon-radar"></use></svg><span>Nieuwe matches</span></a>
|
||||||
|
<a class="nav-item {% if request.resolver_match.namespace == 'jobs' and request.resolver_match.url_name == 'detail' %}is-active{% endif %}" href="{% url 'jobs:list' %}?focus=all"><svg class="icon"><use href="#icon-briefcase"></use></svg><span>Alle vacatures</span></a>
|
||||||
|
<a class="nav-item {% if request.resolver_match.namespace == 'jobs' and request.resolver_match.url_name == 'skill-insights' %}is-active{% endif %}" href="{% url 'jobs:skill-insights' %}"><svg class="icon"><use href="#icon-chart"></use></svg><span>Skillsradar</span></a>
|
||||||
|
<a class="nav-item {% if request.resolver_match.namespace == 'jobs' and request.resolver_match.url_name == 'applications' or request.resolver_match.url_name == 'application-edit' %}is-active{% endif %}" href="{% url 'jobs:applications' %}"><svg class="icon"><use href="#icon-pipeline"></use></svg><span>Sollicitaties</span></a>
|
||||||
|
|
||||||
|
<p class="nav-caption">Radarbeheer</p>
|
||||||
|
<a class="nav-item {% if request.resolver_match.namespace == 'profiles' %}is-active{% endif %}" href="{% url 'profiles:list' %}"><svg class="icon"><use href="#icon-profile"></use></svg><span>Zoekprofiel</span></a>
|
||||||
|
<a class="nav-item {% if request.resolver_match.namespace == 'sources' %}is-active{% endif %}" href="{% url 'sources:list' %}"><svg class="icon"><use href="#icon-sources"></use></svg><span>Bronnen</span></a>
|
||||||
|
<a class="nav-item {% if request.resolver_match.namespace == 'dashboard' and request.resolver_match.url_name == 'system' %}is-active{% endif %}" href="{% url 'dashboard:system' %}"><svg class="icon"><use href="#icon-system"></use></svg><span>Automatisering</span></a>
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
<div class="sidebar-radar-card">
|
||||||
|
<span class="status-beacon {% if navigation_profile %}is-live{% endif %}" aria-hidden="true"></span>
|
||||||
|
<span><strong>{% if navigation_profile %}Radar actief{% else %}Profiel nodig{% endif %}</strong><small>{% if navigation_profile %}{{ navigation_profile.name }}{% else %}Configureer je zoekprofiel{% endif %}</small></span>
|
||||||
|
</div>
|
||||||
|
<div class="sidebar-account">
|
||||||
|
<span class="avatar" aria-hidden="true">{{ user.first_name|default:user.username|slice:":1"|upper }}</span>
|
||||||
|
<span><strong>{{ user.get_full_name|default:user.username }}</strong><small>Persoonlijke radar</small></span>
|
||||||
|
</div>
|
||||||
|
</aside>
|
||||||
|
|
||||||
|
<button class="nav-scrim" type="button" data-nav-scrim aria-label="Menu sluiten" tabindex="-1"></button>
|
||||||
|
<div class="cockpit-workspace">
|
||||||
|
<header class="cockpit-topbar">
|
||||||
|
<div class="topbar-start">
|
||||||
|
<button class="icon-button mobile-only" type="button" data-nav-toggle aria-label="Menu openen" aria-expanded="false"><svg class="icon"><use href="#icon-menu"></use></svg></button>
|
||||||
|
<form class="command-search" method="get" action="{% url 'jobs:list' %}" role="search">
|
||||||
|
<label class="sr-only" for="global-search">Zoek door vacatures</label>
|
||||||
|
<svg class="icon" aria-hidden="true"><use href="#icon-search"></use></svg>
|
||||||
|
<input id="global-search" type="search" name="q" value="{% if request.resolver_match.namespace == 'jobs' and request.resolver_match.url_name == 'list' %}{{ request.GET.q }}{% endif %}" placeholder="Zoek vacatures, werkgevers of skills…">
|
||||||
|
<kbd aria-hidden="true">/</kbd>
|
||||||
|
</form>
|
||||||
</div>
|
</div>
|
||||||
<nav class="nav-stack">
|
<div class="topbar-actions">
|
||||||
<p class="nav-group-label">Radar</p>
|
<a class="system-live" href="{% url 'dashboard:system' %}"><span class="status-beacon is-live" aria-hidden="true"></span><span class="desktop-label">Systeemstatus</span></a>
|
||||||
<a class="nav-link {% if request.resolver_match.namespace == 'dashboard' and request.resolver_match.url_name == 'today' %}active{% endif %}" href="{% url 'dashboard:today' %}">
|
<button class="icon-button" type="button" data-theme-toggle aria-label="Licht thema activeren" title="Thema wisselen"><svg class="icon theme-icon-dark"><use href="#icon-sun"></use></svg><svg class="icon theme-icon-light"><use href="#icon-moon"></use></svg></button>
|
||||||
<svg class="icon"><use href="#icon-dashboard"></use></svg><span>Vandaag<small>beste matches</small></span>
|
<details class="account-menu">
|
||||||
</a>
|
<summary aria-label="Gebruikersmenu openen"><span class="avatar avatar-small" aria-hidden="true">{{ user.first_name|default:user.username|slice:":1"|upper }}</span><svg class="icon desktop-label"><use href="#icon-chevron"></use></svg></summary>
|
||||||
<a class="nav-link {% if request.resolver_match.namespace == 'jobs' and request.resolver_match.url_name == 'list' %}active{% endif %}" href="{% url 'jobs:list' %}">
|
<div class="account-popover"><strong>{{ user.get_full_name|default:user.username }}</strong><small>{{ user.username }}</small>{% if navigation_profile %}<a href="{% url 'profiles:edit' navigation_profile.pk %}">Zoekprofiel aanpassen</a>{% endif %}<form action="{% url 'logout' %}" method="post">{% csrf_token %}<button type="submit"><svg class="icon"><use href="#icon-logout"></use></svg>Afmelden</button></form></div>
|
||||||
<svg class="icon"><use href="#icon-search"></use></svg><span>Vacatures<small>zoeken en filteren</small></span>
|
</details>
|
||||||
</a>
|
|
||||||
<a class="nav-link {% if request.resolver_match.namespace == 'jobs' and request.resolver_match.url_name == 'skill-insights' %}active{% endif %}" href="{% url 'jobs:skill-insights' %}">
|
|
||||||
<svg class="icon"><use href="#icon-radar"></use></svg><span>Skillsradar<small>vraag en leerkansen</small></span>
|
|
||||||
</a>
|
|
||||||
<a class="nav-link {% if request.resolver_match.namespace == 'jobs' and request.resolver_match.url_name == 'applications' or request.resolver_match.url_name == 'application-edit' %}active{% endif %}" href="{% url 'jobs:applications' %}">
|
|
||||||
<svg class="icon"><use href="#icon-pipeline"></use></svg><span>Sollicitaties<small>persoonlijke pipeline</small></span>
|
|
||||||
</a>
|
|
||||||
<p class="nav-group-label">Beheer</p>
|
|
||||||
<a class="nav-link {% if request.resolver_match.namespace == 'profiles' %}active{% endif %}" href="{% url 'profiles:list' %}">
|
|
||||||
<svg class="icon"><use href="#icon-profile"></use></svg><span>Zoekprofiel<small>regels en voorkeuren</small></span>
|
|
||||||
</a>
|
|
||||||
<a class="nav-link {% if request.resolver_match.namespace == 'sources' %}active{% endif %}" href="{% url 'sources:list' %}">
|
|
||||||
<svg class="icon"><use href="#icon-sources"></use></svg><span>Bronnen<small>import en gezondheid</small></span>
|
|
||||||
</a>
|
|
||||||
<a class="nav-link {% if request.resolver_match.namespace == 'dashboard' and request.resolver_match.url_name == 'system' %}active{% endif %}" href="{% url 'dashboard:system' %}">
|
|
||||||
<svg class="icon"><use href="#icon-system"></use></svg><span>Systeem<small>readiness en beheer</small></span>
|
|
||||||
</a>
|
|
||||||
</nav>
|
|
||||||
<div class="sidebar-user">
|
|
||||||
<span class="avatar" aria-hidden="true">{{ user.username|slice:":1"|upper }}</span>
|
|
||||||
<span><strong>{{ user.get_full_name|default:user.username }}</strong><small>Lokale gebruiker</small></span>
|
|
||||||
<span class="status-dot" title="Aangemeld"></span>
|
|
||||||
</div>
|
</div>
|
||||||
</aside>
|
</header>
|
||||||
<button class="nav-scrim" type="button" data-nav-scrim aria-label="Menu sluiten" tabindex="-1"></button>
|
|
||||||
<div class="workspace">
|
<main id="main" class="cockpit-main" tabindex="-1">
|
||||||
<header class="utilitybar">
|
{% if messages %}<div class="toast-stack" aria-live="polite" aria-atomic="true">{% for message in messages %}<div class="toast {{ message.tags }}"><svg class="icon" aria-hidden="true"><use href="{% if message.tags == 'error' %}#icon-alert{% else %}#icon-check{% endif %}"></use></svg><span>{{ message }}</span></div>{% endfor %}</div>{% endif %}
|
||||||
<div class="utilitybar-start">
|
|
||||||
<button class="icon-button mobile-only" type="button" data-nav-toggle aria-label="Menu openen" aria-expanded="false">
|
|
||||||
<svg class="icon"><use href="#icon-menu"></use></svg>
|
|
||||||
</button>
|
|
||||||
<form class="global-search" method="get" action="{% url 'jobs:list' %}" role="search">
|
|
||||||
<label class="sr-only" for="global-search">Zoek door vacatures</label>
|
|
||||||
<svg class="icon" aria-hidden="true"><use href="#icon-search"></use></svg>
|
|
||||||
<input id="global-search" type="search" name="q" value="{% if request.resolver_match.namespace == 'jobs' and request.resolver_match.url_name == 'list' %}{{ request.GET.q }}{% endif %}" placeholder="Zoek door vacatures...">
|
|
||||||
</form>
|
|
||||||
<div class="utility-title utility-radar"><span class="signal-dot" aria-hidden="true"></span><span>Radar actief</span></div>
|
|
||||||
</div>
|
|
||||||
<div class="utility-actions">
|
|
||||||
<button class="icon-button" type="button" data-theme-toggle aria-label="Thema wisselen" title="Thema wisselen">
|
|
||||||
<svg class="icon theme-icon-dark"><use href="#icon-sun"></use></svg>
|
|
||||||
<svg class="icon theme-icon-light"><use href="#icon-moon"></use></svg>
|
|
||||||
</button>
|
|
||||||
{% if navigation_profile %}<a class="button button-primary utility-cta" href="{% url 'profiles:edit' navigation_profile.pk %}"><svg class="icon"><use href="#icon-profile"></use></svg><span class="desktop-label">Zoekprofiel aanpassen</span></a>{% endif %}
|
|
||||||
<form action="{% url 'logout' %}" method="post">{% csrf_token %}<button class="utility-link" type="submit" aria-label="Afmelden"><svg class="icon"><use href="#icon-logout"></use></svg><span class="desktop-label">Afmelden</span></button></form>
|
|
||||||
</div>
|
|
||||||
</header>
|
|
||||||
<main id="main" class="main-content" tabindex="-1">
|
|
||||||
{% else %}
|
{% else %}
|
||||||
<main id="main" class="auth-main" tabindex="-1">
|
<main id="main" class="auth-shell" tabindex="-1">
|
||||||
|
{% if messages %}<div class="toast-stack" aria-live="polite">{% for message in messages %}<div class="toast {{ message.tags }}">{{ message }}</div>{% endfor %}</div>{% endif %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if messages %}
|
{% block content %}{% endblock %}
|
||||||
<div class="messages" aria-live="polite" aria-atomic="true">
|
</main>
|
||||||
{% for message in messages %}<div class="message {{ message.tags }}">{{ message }}</div>{% endfor %}
|
{% if user.is_authenticated %}
|
||||||
</div>
|
<nav class="mobile-tabbar" aria-label="Mobiele hoofdnavigatie">
|
||||||
{% endif %}
|
<a class="{% if request.resolver_match.namespace == 'dashboard' %}is-active{% endif %}" href="{% url 'dashboard:today' %}"><svg class="icon"><use href="#icon-dashboard"></use></svg><span>Overzicht</span></a>
|
||||||
{% block content %}{% endblock %}
|
<a class="{% if request.resolver_match.namespace == 'jobs' and request.resolver_match.url_name == 'list' %}is-active{% endif %}" href="{% url 'jobs:list' %}"><svg class="icon"><use href="#icon-radar"></use></svg><span>Matches</span></a>
|
||||||
</main>
|
<a class="{% if request.resolver_match.url_name == 'applications' %}is-active{% endif %}" href="{% url 'jobs:applications' %}"><svg class="icon"><use href="#icon-pipeline"></use></svg><span>Pipeline</span></a>
|
||||||
{% if user.is_authenticated %}</div></div>{% endif %}
|
<a class="{% if request.resolver_match.namespace == 'profiles' %}is-active{% endif %}" href="{% url 'profiles:list' %}"><svg class="icon"><use href="#icon-profile"></use></svg><span>Profiel</span></a>
|
||||||
|
</nav>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
{% block extra_js %}{% endblock %}
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -21,4 +21,12 @@
|
|||||||
<symbol id="icon-external" viewBox="0 0 24 24"><path d="M14 4h6v6M20 4l-9 9"/><path d="M18 13v7H4V6h7"/></symbol>
|
<symbol id="icon-external" viewBox="0 0 24 24"><path d="M14 4h6v6M20 4l-9 9"/><path d="M18 13v7H4V6h7"/></symbol>
|
||||||
<symbol id="icon-chevron" viewBox="0 0 24 24"><path d="m7 9 5 5 5-5"/></symbol>
|
<symbol id="icon-chevron" viewBox="0 0 24 24"><path d="m7 9 5 5 5-5"/></symbol>
|
||||||
<symbol id="icon-upload" viewBox="0 0 24 24"><path d="M12 16V4M7 9l5-5 5 5M4 20h16"/></symbol>
|
<symbol id="icon-upload" viewBox="0 0 24 24"><path d="M12 16V4M7 9l5-5 5 5M4 20h16"/></symbol>
|
||||||
|
<symbol id="icon-chart" viewBox="0 0 24 24"><path d="M4 19V9M10 19V4M16 19v-7M22 19V7"/></symbol>
|
||||||
|
<symbol id="icon-activity" viewBox="0 0 24 24"><path d="M3 12h4l2-6 4 12 2-6h6"/></symbol>
|
||||||
|
<symbol id="icon-building" viewBox="0 0 24 24"><path d="M4 21V5l8-3v19M12 8h8v13M7 7h2M7 11h2M7 15h2M15 11h2M15 15h2M2 21h20"/></symbol>
|
||||||
|
<symbol id="icon-filter" viewBox="0 0 24 24"><path d="M3 5h18M6 12h12M10 19h4"/></symbol>
|
||||||
|
<symbol id="icon-refresh" viewBox="0 0 24 24"><path d="M20 7v5h-5M4 17v-5h5"/><path d="M18.5 9A7 7 0 0 0 6 6.5L4 9M5.5 15A7 7 0 0 0 18 17.5l2-2.5"/></symbol>
|
||||||
|
<symbol id="icon-list" viewBox="0 0 24 24"><path d="M9 6h12M9 12h12M9 18h12M3 6h.01M3 12h.01M3 18h.01"/></symbol>
|
||||||
|
<symbol id="icon-calendar" viewBox="0 0 24 24"><rect x="3" y="5" width="18" height="16" rx="2"/><path d="M8 3v4M16 3v4M3 10h18"/></symbol>
|
||||||
|
<symbol id="icon-mail" viewBox="0 0 24 24"><rect x="3" y="5" width="18" height="14" rx="2"/><path d="m4 7 8 6 8-6"/></symbol>
|
||||||
</svg>
|
</svg>
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 2.8 KiB After Width: | Height: | Size: 3.7 KiB |
Reference in New Issue
Block a user