/colabora (/en/contribute, /ca/col-labora) — the calls' own section, so
it lists what /colabora/{slug} are — brings the open calls, the good
first issues across the collective's repositories, and a five-step
guide from picking a task to having it reviewed. With nothing open and
nothing labelled, the guide and the other ways to help are still there.
The landing's "contribute" section links to it.
Also fixes the landing's issue list, which @pi/ui's `li + li` margin
spaced twice over.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
/radar (/en/radar, /ca/radar) shows every technology the collective has
placed, by ring: logo, category, what it is, why it sits there, its
website and how many projects use it. The landing's radar summary links
to it.
It is the first listing — a page this site composes rather than one
record's — so resolveRoute learns about those (LISTINGS), and builds
their addresses and hreflang alternates from the same table.
Logos sit on a light tile in both themes. A brand's mark is not ours to
recolour, and Rust's, which is black, would vanish in dark mode.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The API has always served a project's and a member's detail, with
`path` and `alternates` in three languages, and the site had nowhere to
show it. A project now has its page — tagline, status, technologies,
description, technical decisions, what was learned, gallery, client,
dates, lead, team, repositories and open calls — and so does a member:
headline, bio, links, technologies and projects. Cards on the landing
link to both.
Addresses follow the API's rule read backwards (core/routes.ts):
pages at the root, records under a section named in their language,
the default language unprefixed. One catch-all route serves them all.
Not a route per shape: Analog registers each loader as an internal
Nitro route, and Nitro's router cannot hold `/:prefix/:slug` beside
`/:prefix/:section/:slug` — the first became unreachable.
The frame around a record (header, footer, lights, 404/503, title,
description, canonical, hreflang) is one component now, PageShell, and
each type only brings its body, all sharing one layout partial.
Also fixes chips that stepped down one row after the first, on the
landing's cards too: @pi/ui's reset gives every `li + li` a top margin,
right for prose and wrong for a row spaced by `gap`.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The landing was one address. It now also answers on the pages the panel
publishes -- "Sobre nosotros" and the like -- at the addresses the API
itself chooses: `path` and `alternates` put the default language
unprefixed and every other one under its code, so `/sobre-nosotros`,
`/en/about-us`, `/ca/qui-som` are the routes. `core/api.ts#contentRoute`
is the single rule for that shape, and `/es/…` is deliberately not one
of them, so a page has one address rather than two.
Two routes share one load function, because Analog wants a `.server.ts`
beside each `.page.ts`: `[slug]` for the default language, and
`[locale]/[slug]` for the rest.
Whether a slug exists is the API's answer, not something the route table
knows, so the 404 middleware lets page-shaped addresses through and
`ui/content-page.ts` sets the status itself from `ngOnInit` -- the same
`injectResponse()` the landing uses for its 503, and `ngOnInit` because
an input has no value in the constructor. Body HTML comes from the
panel's editor and goes through Angular's sanitiser.
The header shows the featured pages beside the section anchors; the
footer lists them all. On a page the language switcher follows the API's
`alternates`, and a language with no translation leads to the landing in
that language instead of a 404.
Verified: lint, 20 unit tests, build, and 6 e2e -- including a new one
that a page-shaped address the API does not know answers 404 with
`robots: noindex`.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The label read "Horas por semana" struck through by the field's own
border: 5px above the wrapper instead of 18, straddling the 2px outline.
Angular Material measures an outlined field's floating label once on
init and writes the result to the element as an inline transform. This
field is the only one inside an @if, so it is measured before it has any
geometry and keeps the unfloated offset. @pi/ui already publishes the
right values for every other field and its rule matches this one too --
it simply loses to an inline style, so the fix restates the library's
own numbers with `!important`, which is what outranks one.
It also gains a placeholder, which every other field already had.
The e2e config now derives the dev server's port from BASE_URL instead
of pinning 4200. With `reuseExistingServer`, an unrelated project's dev
server on that port gets adopted silently and all five tests fail with
"element(s) not found" while the page under test was never served --
which is exactly what happened while verifying this.
Verified: all four labels now land at -18 with a 48px box, light and
dark; lint, 15 unit tests, build and 5 e2e green.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>