/*
 * The public marketplace surface — Phase 2 Block 2A.
 *
 * ─── WHY THIS IS A FIFTH FILE AND NOT MORE OF components.css ───────────────
 *
 * The employer workspace is a dense administrative tool: forms, tables, status
 * lines, things a signed-in person uses for twenty minutes. The public site is
 * a consumer marketing surface a stranger judges in four seconds. Forcing one
 * component sheet to serve both is how `.card` ends up with six modifiers and a
 * rule nobody dares delete.
 *
 * So `tokens.css` is shared — one brand, one place to change it — and the
 * components are not. Nothing in this file is loaded by an authenticated page,
 * and nothing in `components.css` was changed to make this one work.
 *
 * Every value resolves to a token. No hex codes; an architecture test enforces
 * it, and this file is inside that sweep.
 *
 * MOBILE-FIRST, LIKE THE REST. Not one `max-width` media query — the
 * coming-soon page's single `max-width: 820px` breakpoint is inverted here into
 * `min-width` rules, because a max-width patch is a desktop sheet apologising
 * to a phone. Spanish is the source language and runs longer than English, so
 * every box below is sized by its content rather than by a fixed width.
 */

/* ══════════════════════════════════════════════════ shell ════════════════ */

.public-page {
    display: flex;
    min-height: 100vh;
    flex-direction: column;
    background: var(--color-background);
}

.public-main {
    flex: 1 1 auto;
}

.public-shell {
    width: 100%;
    max-width: var(--container-public);
    margin-inline: auto;
    padding-inline: var(--container-padding-public);
}

.public-section {
    padding-block: var(--space-7);
}

@media (min-width: 48em) {
    .public-section {
        padding-block: var(--space-9);
    }
}

.public-section--tint {
    background: var(--color-brand-soft);
}

/*
 * THE LEAD BLOCK AND WHAT FOLLOWS IT ARE ONE BLOCK, NOT TWO.
 *
 * ─── THE DEFECT THIS CLOSES ────────────────────────────────────────────────
 *
 * `public-section--lead` has been in the markup of six pages since Block 2A and
 * HAD NO RULE ANYWHERE IN THIS STYLESHEET. It was written as a modifier and
 * then never given behaviour, so a lead block was styled as an ordinary
 * `.public-section` and paid the full section padding on both edges — as did
 * the section immediately beneath it.
 *
 * The result, between a one-line lead sentence and the first heading under it,
 * measured on the desktop breakpoint:
 *
 *     2rem   .public-section__head margin-bottom
 *   + 6rem   .public-section--lead padding-block-end   (space-9)
 *   + 6rem   .public-section       padding-block-start (space-9)
 *   ------
 *    14rem   224px of nothing
 *
 * On a page with seven or fourteen short sections that band reads as a layout
 * fault rather than as rhythm, which is exactly how it was reported.
 *
 * ─── WHY THE FIX IS HERE AND NOT ON THE PAGES ──────────────────────────────
 *
 * Because the fault is here. Adding a page-specific class, an override or a
 * negative margin to the legal pages would leave the same 224px band on the
 * other four pages carrying this modifier and would put the reason in the
 * wrong file. This gives the modifier the rule it was always meant to have:
 * the lead stops paying for a gap the next section already pays for, and the
 * next section tightens BECAUSE it follows a lead — the adjacent-sibling
 * selector means nothing changes for a section that does not.
 *
 * `space-6` rather than zero: the pair still needs a seam. The measured result
 * is 2rem + 2rem = 4rem between the lead and the first heading, which is the
 * same rhythm the rest of the site uses between a heading and its content.
 *
 * Nothing else is touched. No other section's padding moves, no page-specific
 * selector is introduced, and the tokens are the existing scale.
 */
.public-section--lead {
    padding-block-end: 0;
}

.public-section--lead + .public-section {
    padding-block-start: var(--space-6);
}

/*
 * The head no longer caps its own width — the heading and the lead do it
 * separately, because they want different measures.
 *
 * One 68ch box for both was tuned when every section heading was two or three
 * words. "¿Por qué buscar empleo con ElTrabajador?" is thirty-nine characters
 * at display size, and inside a box sized in 16px `ch` it broke after "con",
 * leaving the brand name alone on a second line.
 */
.public-section__head {
    margin-bottom: var(--space-6);
}

/*
 * `em`, so the allowance scales with the heading's own size rather than with
 * the body text's. 26em is 936px at the desktop size — the Spanish heading
 * measures 803px and the English one 705px, so both hold one line with real
 * headroom for Segoe UI on the runtime of record, and the 1072px container
 * still catches anything longer. A heading too long for even that wraps, which
 * is the behaviour this replaced and is a perfectly good fallback.
 */
.public-section__title {
    max-width: 26em;
    margin: 0;
    color: var(--color-heading);
    font-size: var(--font-size-2xl);
    line-height: var(--line-height-heading);
    letter-spacing: var(--letter-spacing-display);
}

@media (min-width: 48em) {
    .public-section__title {
        font-size: var(--font-size-3xl);
    }
}

/* The lead keeps a reading measure of its own, now that the head does not
   impose one. */
.public-section__lead {
    max-width: var(--measure);
    margin: var(--space-3) 0 0;
    color: var(--color-text-muted);
    font-size: var(--font-size-lg);
}

/* ══════════════════════════════════════════════════ eyebrow ══════════════ */

/*
 * The coming-soon eyebrow, rule and all: uppercase, tracked out, preceded by a
 * short orange bar. The bar is the one place the undiluted brand orange is
 * used at small size — it is a decorative rule carrying no meaning, so the
 * 2.95:1 contrast is not a text-contrast question.
 */
.public-eyebrow {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
    margin: 0 0 var(--space-4);
    color: var(--color-primary);
    font-size: var(--font-size-sm);
    font-weight: var(--font-weight-bold);
    letter-spacing: var(--letter-spacing-eyebrow);
    text-transform: uppercase;
}

.public-eyebrow::before {
    content: "";
    width: var(--space-6);
    height: 3px;
    border-radius: var(--radius-pill);
    background: var(--color-accent);
}

/* ══════════════════════════════════════════════════ header ═══════════════ */

.public-header {
    position: relative;
    z-index: 2;
    border-bottom: 1px solid var(--color-border);
    background: var(--color-background);
}

.public-header__inner {
    display: flex;
    align-items: center;
    gap: var(--space-4);
    padding-block: var(--space-4);
}

/*
 * The row tightens at exactly the width where the mark grows, so a 45% larger
 * logo costs the header ten pixels of height rather than twenty. `align-items:
 * center` above is what keeps the logo, the navigation, the employer CTA and
 * the language pill on one optical centre line while their heights differ.
 */
@media (min-width: 70em) {
    .public-header__inner {
        padding-block: var(--space-3);
    }
}

.public-header__brand {
    display: inline-flex;
    align-items: center;
    margin-right: auto;
    text-decoration: none;
}

/*
 * SIZED BY WIDTH, and that swap is half of the fix.
 *
 * ─── WHAT WENT WRONG THE FIRST TIME ────────────────────────────────────────
 *
 * The earlier asset was cropped with a trim that treated any non-zero alpha as
 * ink. The supplied PNG carries a near-invisible halo across its full height,
 * so the trim kept all of it: 200px of image holding 102px of artwork, with 51
 * transparent pixels above and 47 below. Sizing THAT by height meant the CSS
 * number described the padding as much as the mark — a 64px-tall image was a
 * 33px-tall logo, and the reported "181 × 64" overstated what a reader could
 * see by a factor of two. The reviewer was right and the measurement was
 * wrong.
 *
 * The asset is now cropped to the ink (alpha > 8), so the image IS the artwork:
 * 832 × 150, no transparent margin on any side. Every pixel the CSS allots is
 * now visible mark.
 *
 * ─── AND WIDTH IS THE HONEST DIMENSION FOR A 5.5:1 LOCKUP ──────────────────
 *
 * The header's spare room is horizontal — a 1366px page leaves hundreds of
 * pixels between the brand and the navigation — while vertical room is what a
 * header has least of. Setting width and letting height follow spends the
 * dimension that is free. It is also what makes the tagline legible: at 22.5rem
 * the mark is 360 × 65, and "TRABAJO PARA UN MEJOR FUTURO" resolves to about
 * six pixels of cap height, which is the point where it stops being a smudge.
 * Sizes from 200px to 440px were rendered at 1x and read before this one was
 * chosen; the block report has the ladder.
 *
 * Four steps, because 360px would take the entire usable width of a 320px
 * screen. Desktop tagline legibility is the requirement; on a phone the brand
 * name is, and the tagline is accepted as decorative there.
 */
.public-header__logo {
    display: block;
    width: 9rem;
    height: auto;
}

@media (min-width: 22.5em) {
    .public-header__logo {
        width: 11rem;
    }
}

@media (min-width: 48em) {
    .public-header__logo {
        width: 17.5rem;
    }
}

/*
 * 20rem = 320px, and it is a measured ceiling rather than a taste. With the
 * navigation gap tightened, the Spanish header holds one row at 320px and
 * wraps at 340px; English has 180px to spare either way. Spanish is the source
 * language, so Spanish sets the size.
 */
@media (min-width: 70em) {
    .public-header__logo {
        width: 20rem;
    }
}

/*
 * The full navigation is hidden below the tablet breakpoint and the disclosure
 * takes over; above it, the reverse. `display` rather than visibility, so the
 * hidden one is not in the tab order — a keyboard user tabbing through an
 * invisible duplicate menu is the classic version of this bug.
 */
.public-nav {
    display: none;
}

.public-nav__list {
    display: flex;
    align-items: center;
    /* One step tighter than it was, so the horizontal room goes to the brand.
       The links themselves are unchanged in size, weight and spacing from each
       other's baselines — only the space between them. */
    gap: var(--space-4);
    margin: 0;
    padding: 0;
    list-style: none;
}

.public-nav__link {
    color: var(--color-text);
    font-weight: var(--font-weight-medium);
    text-decoration: none;
}

.public-nav__link:hover {
    color: var(--color-primary);
}

/*
 * SIGN OUT IS A BUTTON DRESSED AS A LINK — Rapid MVP Batch C.
 *
 * It has to be a <button> inside a POST form, because a GET logout is
 * CSRF-triggerable by an <img> tag on any page on the internet. But it belongs
 * beside the other navigation items visually, so the browser's default button
 * chrome is stripped and the .public-nav__link rules above carry the rest.
 *
 * `font: inherit` rather than a font stack: a <button> does NOT inherit type
 * from its parent by default, and without this the control renders in the
 * browser's UI font, at its own size, next to items that do not.
 *
 * No focus rule here on purpose — the stylesheet's global :focus-visible
 * treatment applies to a <button> exactly as it does to an <a>, and a local
 * override is how a focus indicator quietly gets worse.
 */
.public-nav__logout {
    display: flex;
    margin: 0;
}

.public-nav__link--button {
    padding: 0;
    border: 0;
    background: none;
    font: inherit;
    font-weight: var(--font-weight-medium);
    line-height: inherit;
    cursor: pointer;
}

/*
 * The header's own language switcher appears with the full navigation, and not
 * before: at 320px a logo, a language pill and a menu button do not fit on one
 * row, and the switcher was the item pushing the header 16px past the edge of
 * the screen. Below this width the switcher is inside the disclosure panel,
 * which is also where a phone user expects to find it.
 */
.public-header__inner > .public-locale {
    display: none;
}

/*
 * 70em, not 62em — AND THAT CORRECTS A DEFECT THAT PREDATES THE LARGER LOGO.
 *
 * The full navigation used to appear at 992px. Measured, the Spanish header
 * does not fit on one line there at any logo size at all: brand, four nav
 * items and the language pill need about 1060px and a 992px viewport offers
 * 944px of container. The row wrapped into a two-line, 124px-tall header, and
 * it did so before this pass — the English header fits comfortably, which is
 * why it was never noticed.
 *
 * The disclosure menu is compact and works perfectly well at 1024px, so it now
 * holds until 1120px, which is the width where the container reaches its
 * 70rem cap and the Spanish row genuinely fits. Degrading to a tidy menu
 * button beats degrading to a wrapped header.
 */
@media (min-width: 70em) {
    .public-nav {
        display: block;
    }

    .public-header__inner > .public-locale {
        display: block;
    }

    .public-menu {
        display: none;
    }
}

/* ── mobile disclosure ─────────────────────────────────────────────────── */

/*
 * NATIVE <details>/<summary>. No JavaScript in this block, and none needed:
 * the element is focusable, operable with Enter and Space, exposes its expanded
 * state to assistive technology on its own, and works before any script would
 * have loaded. A scripted menu would have to reimplement all four.
 */
.public-menu {
    position: relative;
}

.public-menu__summary {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
    padding: var(--space-2) var(--space-3);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-lg);
    background: var(--color-background);
    color: var(--color-heading);
    font-weight: var(--font-weight-medium);
    cursor: pointer;
    list-style: none;
}

/* The default disclosure triangle, removed in both engines. */
.public-menu__summary::-webkit-details-marker {
    display: none;
}

.public-menu__bars {
    display: grid;
    gap: 4px;
}

.public-menu__bars span {
    display: block;
    width: 18px;
    height: 2px;
    border-radius: var(--radius-pill);
    background: var(--color-heading);
}

.public-menu__panel {
    position: absolute;
    right: 0;
    z-index: 5;
    min-width: 15rem;
    margin-top: var(--space-2);
    padding: var(--space-4);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-xl);
    background: var(--color-surface-raised);
    box-shadow: var(--shadow-lg);
}

.public-menu__list {
    display: grid;
    gap: var(--space-3);
    margin: 0 0 var(--space-4);
    padding: 0;
    list-style: none;
}

.public-menu__list a {
    color: var(--color-text);
    font-weight: var(--font-weight-medium);
    text-decoration: none;
}

.public-menu__actions {
    display: grid;
    gap: var(--space-2);
}

.public-menu__panel .public-locale {
    margin-top: var(--space-4);
}

/*
 * The header row wraps rather than overflowing. Nothing in it should ever need
 * to — the switcher above is hidden precisely so it does not — but a brand mark
 * is a client-supplied asset that can change shape, and a header that overflows
 * gives a phone a horizontal scrollbar on every page of the site.
 */
.public-header__inner {
    flex-wrap: wrap;
    row-gap: var(--space-3);
}

/* ══════════════════════════════════════════════════ buttons ══════════════ */

.pbutton {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: var(--space-2);
    padding: var(--space-3) var(--space-5);
    border: 1px solid transparent;
    border-radius: var(--radius-pill);
    font-weight: var(--font-weight-medium);
    line-height: var(--line-height-heading);
    text-align: center;
    text-decoration: none;
    transition: background-color var(--duration-base) var(--ease-entrance),
        border-color var(--duration-base) var(--ease-entrance),
        box-shadow var(--duration-base) var(--ease-entrance),
        transform var(--duration-fast) var(--ease-entrance);
}

.pbutton--primary {
    background: var(--color-primary);
    color: var(--color-primary-contrast);
    box-shadow: var(--shadow-md);
}

.pbutton--primary:hover {
    background: var(--color-primary-hover);
    box-shadow: var(--shadow-lg);
    transform: translateY(-1px);
}

.pbutton--secondary {
    border-color: var(--color-border-strong);
    background: var(--color-background);
    color: var(--color-heading);
}

.pbutton--secondary:hover {
    border-color: var(--color-primary);
    color: var(--color-primary);
}

/*
 * The accent call to action — the client's true brand orange as a SOLID fill,
 * which is what a search button on this site should look like, and which
 * DEFECT 1 appeared to rule out.
 *
 * It does not, once the two contrast questions are asked separately:
 *
 *   the LABEL on the fill      ink #10213b on #ff6508 = 5.46:1, past AA
 *   the CONTROL against white  the border #c74a00     = 4.76:1, past the 3:1
 *                              non-text floor
 *
 * What DEFECT 1 actually forbids is the brand orange carrying meaning on its
 * own — orange text on white at 2.95:1, or an orange fill with a white label.
 * A dark label on the fill, with a darker rim doing the boundary, keeps the
 * brand value exactly and measures clean. `FocusIndicatorTest` asserts both
 * numbers.
 */
.pbutton--accent {
    border-color: var(--color-accent-strong);
    background: var(--color-accent);
    color: var(--color-text);
    box-shadow: var(--shadow-accent);
}

/*
 * Hover deepens the existing orange glow rather than changing any colour. A
 * background swap would have to take the label with it — ink is 5.46:1 on the
 * brand orange and 1.3:1 on the darker one — and a button whose text flips from
 * near-black to white under the cursor reads as two buttons.
 */
.pbutton--accent:hover {
    box-shadow: var(--shadow-lg), var(--shadow-accent);
    transform: translateY(-1px);
}

.pbutton--block {
    width: 100%;
}

/* ══════════════════════════════════════════════════ hero ═════════════════ */

/*
 * The top is one step down the spacing scale from the bottom — `--space-6`
 * against `--space-8`. It was `--space-7`, and 48px of air under a header that
 * has just grown taller pushed the eyebrow further down the fold than the page
 * could afford. One token step, not a hand-picked number and not a negative
 * margin: the headline's own margins are untouched, so the hero is tighter
 * against the header without being tighter inside itself.
 */
.public-hero {
    position: relative;
    overflow-x: clip;
    padding-block: var(--space-6) var(--space-8);
}

/*
 * The two ambient washes from the coming-soon page, at its own alphas. Purely
 * decorative, `aria-hidden` by being pseudo-elements, and `pointer-events:
 * none` so they can never swallow a click.
 */
.public-hero::before,
.public-hero::after {
    content: "";
    position: absolute;
    z-index: -1;
    border-radius: var(--radius-pill);
    pointer-events: none;
}

.public-hero::before {
    top: -20rem;
    right: -12rem;
    width: 34rem;
    height: 34rem;
    background: var(--wash-brand);
}

.public-hero::after {
    bottom: -18rem;
    left: -12rem;
    width: 30rem;
    height: 30rem;
    background: var(--wash-accent);
}

.public-hero__title {
    max-width: 18ch;
    margin: 0;
    color: var(--color-heading);
    font-size: var(--font-size-3xl);
    line-height: var(--line-height-display);
    letter-spacing: var(--letter-spacing-display);
}

@media (min-width: 48em) {
    .public-hero__title {
        font-size: var(--font-size-4xl);
    }
}

/*
 * TWO SENTENCES, ONE PER LINE — and the break is a block, never a <br>.
 *
 * "Empleos reales." and "Empleadores de confianza." are a claim and its
 * qualifier, and they land harder read as two beats than as one run-on line. A
 * `<br>` would do that too, and would then sit in the middle of "Empleadores de
 * confianza." at 320px where the sentence has to wrap on its own. Each sentence
 * being its own block gets the desktop rhythm and the phone wrapping from the
 * same rule.
 *
 * `text-wrap: balance` shares the words evenly when a sentence does wrap, which
 * is what keeps "confianza." from being left alone on a line of its own at
 * narrow widths. It is progressive: a browser without it wraps normally, which
 * is the behaviour that was already acceptable. No `nowrap`, no fixed width, no
 * clipping — those are banned here and would be the wrong answer anyway.
 */
.public-hero__title-lead,
.public-hero__title-accent {
    display: block;
    text-wrap: balance;
}

/*
 * The trust half carries the accent, in `--color-accent-strong` rather than the
 * brand orange: at 2.95:1 the brand value fails even the 3:1 large-text floor,
 * which is the defect the coming-soon <h1> carries. Measured here at 4.76:1 on
 * white, and `FocusIndicatorTest` asserts it.
 *
 * ONE of the two sentences is coloured, not scattered words inside both. The
 * first line stays deep blue and reads as the statement; the second is the
 * differentiator and is the only thing on the page in that orange at display
 * size.
 */
.public-hero__title-accent {
    color: var(--color-accent-strong);
}

/*
 * Body size on a phone, one step up from tablet width. The trust-first
 * supporting copy is 261 characters in Spanish; at 18px in a 272px column that
 * is eleven lines, and it pushed the search panel almost entirely off a 320px
 * screen. At body size it is ten shorter lines and roughly sixty pixels less.
 *
 * The HEADLINE is untouched at every width — the hierarchy is preserved by
 * making the supporting copy supporting, not by shrinking the thing above it.
 */
.public-hero__lead {
    max-width: var(--measure);
    margin: var(--space-5) 0 0;
    color: var(--color-text-muted);
    font-size: var(--font-size-base);
}

@media (min-width: 48em) {
    .public-hero__lead {
        font-size: var(--font-size-lg);
    }
}

/*
 * The third step of the hierarchy, and deliberately the smallest move that
 * reads as one: the opening sentence takes the heading colour and the medium
 * weight, inside the paragraph, at the paragraph's own size. Making it larger
 * would turn it into a fourth heading competing with the two above it.
 */
.public-hero__lead-emphasis {
    color: var(--color-heading);
    font-weight: var(--font-weight-medium);
}

.public-hero__actions {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-3);
    margin-top: var(--space-6);
}

/*
 * ─── THE HERO CENTRES ITSELF ONCE THERE IS ROOM ────────────────────────────
 *
 * Left-aligned on a phone, where the column IS the page and centring short
 * lines of Spanish just makes them harder to scan. Centred from tablet up, and
 * that is a correction rather than a preference: the copy block is naturally
 * about twenty characters wide, so left-aligned in a 70rem container it sat in
 * the top-left corner with half the hero empty beside it, under a full-width
 * search panel. The page read as a draft.
 *
 * The COPY is centred, not the hero: the search panel inside it keeps its
 * labels, fields and notice left-aligned, because a centred form is a form
 * whose fields no longer share an edge to read down.
 */
@media (min-width: 48em) {
    .public-hero .public-eyebrow {
        display: flex;
        justify-content: center;
    }

    .public-hero__title,
    .public-hero__lead {
        margin-inline: auto;
        text-align: center;
    }

    /*
     * Generous, and it no longer controls the line count. Each sentence is its
     * own block, so the two-line presentation is structural — this measure only
     * decides whether a sentence wraps WITHIN itself. 30ch leaves headroom for
     * "Empleadores de confianza." at 25 characters, which matters because the
     * runtime of record is Windows: the system stack resolves to Segoe UI
     * there and to something wider here, and a headline that fits in the
     * sandbox by two characters is a headline that wraps on the client's
     * machine.
     */
    .public-hero__title {
        max-width: 30ch;
    }

    /*
     * 52ch → 64ch, because the trust-first supporting copy is roughly twice as
     * long as the sentence it replaced and ran to five lines at the old
     * measure. Four candidates were rendered and read: 60ch drops Spanish to
     * four lines but leaves English with "English." alone on a fourth;
     * 64ch gives Spanish four well-filled lines and English three with no
     * widow in either. Still inside a comfortable reading measure — the body
     * `--measure` is 68ch.
     */
    .public-hero__lead {
        max-width: 64ch;
    }

    .public-hero__actions {
        justify-content: center;
    }
}

/* ── the search shell ──────────────────────────────────────────────────── */

/*
 * One step tighter than it was (`--space-5` → `--space-4`). Nothing inside it
 * shrank: the inputs are still 51.6px tall, the button still clears the 44px
 * touch target, the labels are still full-size and the disclosure is still
 * there in full. What came off is the frame, which is where a panel looks
 * generous rather than reads generous.
 */
.public-search {
    margin-top: var(--space-6);
    padding: var(--space-4);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-2xl);
    background: var(--color-surface-raised);
    box-shadow: var(--shadow-xl);
}

.public-search__fields {
    display: grid;
    gap: var(--space-4);
}

@media (min-width: 48em) {
    .public-search__fields {
        grid-template-columns: 1fr 1fr auto;
        align-items: end;
    }
}

.public-field__label {
    display: block;
    margin-bottom: var(--space-2);
    color: var(--color-heading);
    font-size: var(--font-size-sm);
    font-weight: var(--font-weight-medium);
}

/*
 * THE SUBMIT BUTTON MATCHES THE INPUT BESIDE IT, and it does so by sharing the
 * input's box arithmetic rather than by being given a height.
 *
 * Both carry `--space-3` of vertical padding and a 1px border; what differed was
 * the line box — a button uses `--line-height-heading` (1.25) and an input
 * inherits the body's 1.6 — so the button came out 46px against the input's
 * 51.6px and sat 5.6px short of a shared top edge. Matching the line height
 * matches the heights exactly, and keeps doing so if the label is translated
 * into something longer, which a fixed height would not. A fixed height is also
 * what `SpanishExpansionTest` forbids on anything holding text, correctly.
 */
.public-search__fields .pbutton {
    line-height: var(--line-height-body);
}

.public-field__input {
    width: 100%;
    padding: var(--space-3) var(--space-4);
    border: 1px solid var(--color-border-strong);
    border-radius: var(--radius-lg);
    background: var(--color-background);
    /* 16px floor: below it iOS zooms the viewport on focus and leaves the
       reader scrolled sideways on the form they were filling in. */
    font-size: max(var(--font-size-base), 1rem);
}

.public-search__note {
    margin: var(--space-3) 0 0;
    color: var(--color-text-muted);
    font-size: var(--font-size-sm);
}

/* ══════════════════════════════════════════════════ trust strip ══════════ */

.public-trust {
    display: grid;
    gap: var(--space-4);
    margin: 0;
    padding: 0;
    list-style: none;
}

/*
 * `auto-fit`, not a fixed column count. A hardcoded `repeat(4, 1fr)` laid
 * three items across three quarters of the container and left a quarter of
 * empty grid on the right — a layout that looked broken while every rule in it
 * was doing what it said. This tracks the item count instead, so a strip of
 * three, four or five is balanced without anyone editing the sheet.
 */
@media (min-width: 40em) {
    .public-trust {
        grid-template-columns: repeat(auto-fit, minmax(13rem, 1fr));
    }
}

.public-trust__item {
    padding: var(--space-5);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-xl);
    background: var(--color-surface-raised);
}

.public-trust__term {
    margin: 0 0 var(--space-2);
    color: var(--color-heading);
    font-weight: var(--font-weight-medium);
}

.public-trust__detail {
    margin: 0;
    color: var(--color-text-muted);
    font-size: var(--font-size-sm);
}

/* ══════════════════════════════════════════════════ cards ════════════════ */

.public-cards {
    display: grid;
    gap: var(--space-5);
    margin: 0;
    padding: 0;
    list-style: none;
}

@media (min-width: 48em) {
    .public-cards {
        /* `minmax(0, 1fr)` rather than a fixed track: a Spanish job title is
           routinely half again as long as its English equivalent, and a fixed
           column is what makes it overflow instead of wrap. */
        grid-template-columns: repeat(2, minmax(0, 1fr));
    }
}

@media (min-width: 62em) {
    .public-cards {
        grid-template-columns: repeat(3, minmax(0, 1fr));
    }
}

.job-card {
    display: flex;
    height: 100%;
    flex-direction: column;
    gap: var(--space-3);
    padding: var(--space-5);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-2xl);
    background: var(--color-surface-raised);
    box-shadow: var(--shadow-sm);
    transition: box-shadow var(--duration-base) var(--ease-entrance),
        transform var(--duration-base) var(--ease-entrance);
}

.job-card:hover {
    box-shadow: var(--shadow-lg);
    transform: translateY(-2px);
}

.job-card--promoted {
    border-color: var(--color-primary);
    box-shadow: var(--shadow-md);
}

.job-card__badges {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-2);
}

.job-card__title {
    margin: 0;
    color: var(--color-heading);
    font-size: var(--font-size-lg);
    line-height: var(--line-height-heading);
}

.job-card__title a {
    color: inherit;
    text-decoration: none;
}

.job-card__company {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    margin: 0;
    color: var(--color-text-muted);
    font-size: var(--font-size-sm);
}

.job-card__meta {
    display: grid;
    gap: var(--space-2);
    margin: 0;
    /* `auto` on the top track pushes the footer down so cards in a row end
       level without any of them being given a height. */
    margin-top: auto;
    padding-top: var(--space-3);
    border-top: 1px solid var(--color-border);
}

.company-card {
    display: flex;
    /* Wraps rather than squeezing: the verified badge is two words in Spanish
       and belongs under the name on a narrow card, not crushed beside it. */
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-4);
    padding: var(--space-5);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-xl);
    background: var(--color-surface-raised);
}

.company-card__name {
    margin: 0;
    color: var(--color-heading);
    font-size: var(--font-size-lg);
}

/* Matches the job card: the whole card reads as one target, so the title does
   not need a second underline to announce itself. The underline returns on
   hover, where it confirms what is about to be clicked. */
.company-card__name a {
    color: inherit;
    text-decoration: none;
}

.company-card__name a:hover,
.job-card__title a:hover {
    text-decoration: underline;
}

.company-card__meta {
    margin: var(--space-1) 0 0;
    color: var(--color-text-muted);
    font-size: var(--font-size-sm);
}

/* ══════════════════════════════════════════════════ badges and rows ══════ */

.badge-pill {
    display: inline-flex;
    align-items: center;
    gap: var(--space-1);
    padding: var(--space-1) var(--space-3);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-pill);
    background: var(--color-brand-soft);
    color: var(--color-brand-deep);
    font-size: var(--font-size-sm);
    font-weight: var(--font-weight-medium);
}

.badge-pill--verified {
    border-color: var(--color-success);
    background: var(--color-success-surface);
    color: var(--color-success);
}

/*
 * The promoted marker. Deliberately quiet: `UrgencyBoundaryTest` refuses any
 * template that presents a promotion as a quality signal, and a worker should
 * not read "featured" as "better job". It says where the posting sits, nothing
 * more.
 */
.badge-pill--promoted {
    border-color: var(--color-accent);
    background: var(--color-accent-soft);
    color: var(--color-accent-strong);
}

.meta-row {
    display: flex;
    align-items: baseline;
    gap: var(--space-2);
    margin: 0;
    color: var(--color-text);
    font-size: var(--font-size-sm);
}

.meta-row__label {
    flex: 0 0 auto;
    color: var(--color-text-muted);
}

.meta-row__value {
    font-weight: var(--font-weight-medium);
}

.meta-chip {
    display: inline-flex;
    padding: var(--space-1) var(--space-3);
    border-radius: var(--radius-pill);
    background: var(--color-surface);
    color: var(--color-text);
    font-size: var(--font-size-sm);
}

/* ══════════════════════════════════════════════════ panels ═══════════════ */

.empty-state {
    padding: var(--space-6);
    border: 1px dashed var(--color-border-strong);
    border-radius: var(--radius-2xl);
    background: var(--color-surface-raised);
    text-align: center;
}

.empty-state__title {
    margin: 0 0 var(--space-2);
    color: var(--color-heading);
    font-size: var(--font-size-lg);
}

.empty-state__body {
    max-width: var(--measure);
    margin: 0 auto;
    color: var(--color-text-muted);
}

/* A panel that closes a section needs air above it; without this it butts
   straight against the last card in the list it follows. */
.public-trust + .cta-panel,
.public-cards + .cta-panel {
    margin-top: var(--space-6);
}

.cta-panel {
    display: grid;
    gap: var(--space-5);
    padding: var(--space-6);
    border-radius: var(--radius-2xl);
    background: var(--color-brand-deep);
    color: var(--color-text-inverse);
}

@media (min-width: 48em) {
    .cta-panel {
        grid-template-columns: minmax(0, 1.4fr) minmax(0, 1fr);
        align-items: center;
        padding: var(--space-7);
    }
}

.cta-panel__title {
    margin: 0;
    font-size: var(--font-size-2xl);
    line-height: var(--line-height-heading);
    letter-spacing: var(--letter-spacing-display);
}

.cta-panel__body {
    margin: var(--space-3) 0 0;
    /* Not `--color-text-muted`: that token is tuned for white and soft blue,
       and on the deep blue panel it would be unreadable. White at reduced
       opacity keeps the hierarchy and stays above 7:1. */
    color: var(--color-text-on-brand);
}

.cta-panel__actions {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-3);
}

.cta-panel .pbutton--secondary {
    border-color: var(--color-border-on-brand);
    background: transparent;
    color: var(--color-text-inverse);
}

.cta-panel .pbutton--secondary:hover {
    border-color: var(--color-text-inverse);
    background: var(--color-surface-on-brand);
    color: var(--color-text-inverse);
}

/* ══════════════════════════════════════════════════ breadcrumbs, paging ══ */

.breadcrumbs__list {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-2);
    margin: 0;
    padding: 0;
    list-style: none;
    color: var(--color-text-muted);
    font-size: var(--font-size-sm);
}

.breadcrumbs__list li + li::before {
    content: "/";
    margin-right: var(--space-2);
    color: var(--color-border-strong);
}

.pagination {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--space-3);
    margin-top: var(--space-6);
}

.pagination__status {
    color: var(--color-text-muted);
    font-size: var(--font-size-sm);
}

.pagination__link {
    padding: var(--space-2) var(--space-4);
    border: 1px solid var(--color-border-strong);
    border-radius: var(--radius-pill);
    color: var(--color-heading);
    text-decoration: none;
}

.pagination__link[aria-disabled="true"] {
    border-color: var(--color-border);
    color: var(--color-text-muted);
    pointer-events: none;
}

/* ══════════════════════════════════════════════════ footer ═══════════════ */

.public-footer {
    border-top: 1px solid var(--color-border);
    background: var(--color-brand-soft);
}

.public-footer__grid {
    display: grid;
    gap: var(--space-6);
    padding-block: var(--space-7);
}

@media (min-width: 48em) {
    .public-footer__grid {
        grid-template-columns: repeat(3, minmax(0, 1fr));
    }
}

.public-footer__heading {
    margin: 0 0 var(--space-3);
    color: var(--color-heading);
    font-size: var(--font-size-sm);
    font-weight: var(--font-weight-bold);
    letter-spacing: var(--letter-spacing-eyebrow);
    text-transform: uppercase;
}

.public-footer__list {
    display: grid;
    gap: var(--space-2);
    margin: 0;
    padding: 0;
    list-style: none;
}

.public-footer__list a {
    color: var(--color-text);
    text-decoration: none;
}

.public-footer__list a:hover {
    color: var(--color-primary);
}

/*
 * A page that does not exist yet is rendered as TEXT, not as a link to a 404.
 * The Phase 0 footer left those links out entirely for the same reason; naming
 * them greyed-out is the smallest honest improvement on that, and it stops
 * anyone adding the href before the page is there.
 */
.public-footer__pending {
    color: var(--color-text-muted);
}

.public-footer__bar {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-3);
    padding-block: var(--space-4);
    border-top: 1px solid var(--color-border);
    color: var(--color-text-muted);
    font-size: var(--font-size-sm);
}

/* ══════════════════════════════════════════════════ locale switch ════════ */

.public-locale__list {
    display: flex;
    gap: var(--space-2);
    margin: 0;
    padding: 0;
    list-style: none;
}

.public-locale__list a {
    padding: var(--space-1) var(--space-3);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-pill);
    color: var(--color-heading);
    font-size: var(--font-size-sm);
    text-decoration: none;
}

.public-locale__list a:hover {
    border-color: var(--color-primary);
    color: var(--color-primary);
}

/* ══════════════════════════════════════════════════ motion ═══════════════ */

/*
 * The coming-soon entrance, staggered. Opacity and a small translate only —
 * nothing that reflows, so it cannot shift text under a reader's finger.
 */
@keyframes public-enter {
    from {
        opacity: 0;
        transform: translateY(16px);
    }

    to {
        opacity: 1;
        transform: translateY(0);
    }
}

.public-enter {
    animation: public-enter var(--duration-slow) var(--ease-entrance) both;
}

.public-enter--1 {
    animation-delay: 80ms;
}

.public-enter--2 {
    animation-delay: 160ms;
}

.public-enter--3 {
    animation-delay: 240ms;
}

/*
 * base.css already neutralises every animation and transition under the
 * preference. This block is the belt: it removes the ENTRANCE OFFSET as well,
 * so a reduced-motion reader gets the finished layout rather than a layout that
 * arrives instantly from 16px below.
 */
@media (prefers-reduced-motion: reduce) {
    .public-enter {
        animation: none;
        opacity: 1;
        transform: none;
    }

    .job-card:hover,
    .pbutton--primary:hover,
    .pbutton--accent:hover {
        transform: none;
    }
}

/* ══════════════════════════════════ Block 2B — the public marketplace ═════
 *
 * Everything below reuses the Block 2A system: the same tokens, the same
 * spacing scale, the same radii, the same card. No new colour is introduced —
 * `SpanishExpansionTest::testNoComponentHardCodesABrandColour()` scans for
 * every colour notation with comments stripped, so a literal in any form fails
 * the build — and no second button, badge or card system is created.
 *
 * Mobile-first throughout, like the rest of the sheet: the narrow layout is the
 * unqualified rule and every `@media` below only widens.
 *
 * NO TRUNCATION ANYWHERE. No `text-overflow`, no line clamp, no `nowrap`, no
 * fixed text height. Spanish runs half again as long as English and the layout
 * has to grow rather than hide the difference.
 */

/* ── a list of job cards ─────────────────────────────────────────────────── */

.public-jobs {
    display: grid;
    gap: var(--space-4);
    margin: 0;
    padding: 0;
    list-style: none;
}

/*
 * The same `auto-fit` reasoning as `.public-trust`: a hardcoded column count
 * leaves empty grid on the right when the row is short, and a homepage showing
 * two postings should not look like a page that failed to load four.
 */
@media (min-width: 40em) {
    .public-jobs {
        grid-template-columns: repeat(auto-fit, minmax(17rem, 1fr));
    }
}

/*
 * The card is a grid item and the <li> is what the grid places, so the card
 * must fill its cell for the row to stay level. `.job-card` already pushes its
 * meta block down with `margin-top: auto`; this is what gives it a height to
 * push against.
 */
.public-jobs > li {
    display: flex;
}

.public-jobs > li > .job-card {
    width: 100%;
}

.public-jobs__more {
    margin: var(--space-6) 0 0;
}

/* ── one posting ─────────────────────────────────────────────────────────── */

.job-detail__head {
    margin-block-end: var(--space-6);
}

.job-detail__badges {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-2);
    margin: 0 0 var(--space-3);
}

.job-detail__title {
    margin: 0 0 var(--space-2);
    color: var(--color-heading);
    font-size: var(--font-size-2xl);
    line-height: var(--line-height-heading);
    /* No max-width in ch here: a job title is one line of employer-authored
       text and clamping its measure would leave a short title floating in a
       wide column while a long one wrapped early. */
    text-wrap: balance;
}

@media (min-width: 48em) {
    .job-detail__title {
        font-size: var(--font-size-3xl);
    }
}

.job-detail__employer {
    margin: 0 0 var(--space-2);
    font-size: var(--font-size-lg);
    font-weight: var(--font-weight-medium);
}

.job-detail__published {
    margin: 0;
    color: var(--color-text-muted);
    font-size: var(--font-size-sm);
}

/*
 * ONE COLUMN FIRST. The aside carries the employer panel and the apply notice,
 * and on a phone both belong after the description rather than before it — a
 * worker reads what the job is before they read who is offering it.
 */
.job-detail__layout {
    display: grid;
    gap: var(--space-6);
}

@media (min-width: 60em) {
    .job-detail__layout {
        grid-template-columns: minmax(0, 1fr) 20rem;
        align-items: start;
    }
}

.job-detail__heading {
    margin: 0 0 var(--space-3);
    color: var(--color-heading);
    font-size: var(--font-size-xl);
    line-height: var(--line-height-heading);
}

/*
 * ─── THE DESCRIPTION'S LINE BREAKS ARE CSS, NOT MARKUP ───────────────────
 *
 * `pre-line` collapses runs of spaces and keeps newlines, so an employer's
 * paragraphs survive while the text still wraps normally. The alternative —
 * converting "\n" to "<br>" and rendering the result unescaped — would make
 * every posting on this site an injection point, which is why
 * `PresentationBoundaryTest` fails the build on `|noescape`.
 */
.job-detail__body {
    max-width: var(--measure);
    white-space: pre-line;
    line-height: var(--line-height-body);
    overflow-wrap: break-word;
}

.job-detail__notice {
    margin: var(--space-5) 0 0;
    padding: var(--space-4);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-lg);
    background: var(--color-surface);
    color: var(--color-text-muted);
    font-size: var(--font-size-sm);
    max-width: var(--measure);
}

/* ── the decision fields ─────────────────────────────────────────────────── */

.job-facts {
    display: grid;
    gap: var(--space-3);
    margin: 0 0 var(--space-6);
    padding: var(--space-5);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-xl);
    background: var(--color-surface-raised);
}

@media (min-width: 40em) {
    .job-facts {
        grid-template-columns: repeat(auto-fit, minmax(11rem, 1fr));
    }
}

/*
 * A <div> wrapper per pair, because a <dl> whose <dt> and <dd> are direct grid
 * items places them in separate cells and a long term silently drifts away from
 * its value. Wrapping keeps the pair together whatever either one's length.
 */
.job-facts__row {
    display: flex;
    flex-direction: column;
    gap: var(--space-1);
}

.job-facts__term {
    margin: 0;
    color: var(--color-text-muted);
    font-size: var(--font-size-sm);
}

.job-facts__value {
    margin: 0;
    color: var(--color-heading);
    font-weight: var(--font-weight-medium);
}

/* ── the employer panel ──────────────────────────────────────────────────── */

.job-employer,
.job-apply {
    padding: var(--space-5);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-xl);
    background: var(--color-surface-raised);
}

.job-apply {
    margin-block-start: var(--space-4);
    background: var(--color-surface);
}

/*
 * NOT A BUTTON, AND NOT SHAPED LIKE ONE. Ruling P-2. The panel is deliberately
 * quiet: no accent fill, no border weight that reads as a control, no cursor
 * change, nothing focusable. A disabled-looking button is still a promise.
 */
.job-apply__title {
    margin: 0 0 var(--space-2);
    color: var(--color-heading);
    font-weight: var(--font-weight-medium);
}

.job-apply__body {
    margin: 0;
    color: var(--color-text-muted);
    font-size: var(--font-size-sm);
    line-height: var(--line-height-body);
}

.job-employer__identity {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    margin-block-end: var(--space-4);
}

.job-employer__name {
    margin: 0;
    color: var(--color-heading);
    font-weight: var(--font-weight-medium);
}

.job-employer__description {
    margin: var(--space-4) 0 0;
    color: var(--color-text-muted);
    font-size: var(--font-size-sm);
    line-height: var(--line-height-body);
}

.job-employer__link {
    margin: var(--space-4) 0 0;
}

/* ── the employer page ───────────────────────────────────────────────────── */

.company-head {
    display: flex;
    flex-direction: column;
    gap: var(--space-4);
}

@media (min-width: 40em) {
    .company-head {
        flex-direction: row;
        align-items: flex-start;
    }
}

.company-head__name {
    margin: 0 0 var(--space-2);
    color: var(--color-heading);
    font-size: var(--font-size-2xl);
    line-height: var(--line-height-heading);
    text-wrap: balance;
}

.company-head__badges {
    margin: 0 0 var(--space-3);
}

.company-head__facts {
    display: grid;
    gap: var(--space-1);
}

/*
 * ─── THE LOGO BOX IS A FIXED SIZE AND THE IMAGE IS NOT ────────────────────
 *
 * `object-fit: contain` inside a square box, so a wide logo and a tall one both
 * fit without being stretched, and the box reserves the same space either way.
 * That matters more here than usual: the route answers with a real logo or a
 * neutral placeholder and the page cannot know which, so the layout must not
 * move depending on the answer.
 *
 * These are `.company-head__logo` and `.job-employer__logo` rather than the
 * shared shape rule they look like, and both are declared in
 * `SpanishExpansionTest::SHAPE_SELECTORS` — a fixed size on anything that
 * carries TEXT is the thing that rule forbids, and an image box carries none.
 */
.company-head__logo,
.job-employer__logo {
    flex: none;
    width: 4rem;
    height: 4rem;
    border: 1px solid var(--color-border);
    border-radius: var(--radius-lg);
    background: var(--color-surface-raised);
    object-fit: contain;
}

@media (min-width: 40em) {
    .company-head__logo {
        width: 6rem;
        height: 6rem;
    }
}

/* ── public error pages ──────────────────────────────────────────────────── */

.public-error {
    text-align: start;
}

.public-error__title {
    margin: 0 0 var(--space-3);
    color: var(--color-heading);
    font-size: var(--font-size-2xl);
    line-height: var(--line-height-heading);
    text-wrap: balance;
}

.public-error__body {
    margin: 0 0 var(--space-5);
    max-width: var(--measure);
    color: var(--color-text-muted);
    line-height: var(--line-height-body);
}

.public-error__reference {
    margin: 0 0 var(--space-5);
    color: var(--color-text-muted);
    font-size: var(--font-size-sm);
}

/*
 * Wraps rather than scrolls. Two buttons whose Spanish labels run long must
 * stack on a 320px screen; a row that refuses to wrap is how a page gains
 * horizontal overflow, which this block asserts is exactly zero.
 */
.public-error__actions {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-3);
    margin: 0;
}

/* ══════════════════════════════════════════ static prose pages ════════════ */

/*
 * ─── ABOUT, CONTACT, PRIVACY, TERMS — RAPID MVP BATCH A ───────────────────
 *
 * Four pages that are nothing but headings and paragraphs, so the only thing
 * that matters is that they are comfortable to read. A measure rather than a
 * width: the column stops growing once a line reaches the length an eye can
 * track back from, and on a phone it is simply the shell's width.
 *
 * No fixed heights, no truncation, no `nowrap` — these are the longest Spanish
 * strings in the product and every one of them has to wrap.
 */
.public-prose {
    max-width: 44rem;
}

.public-prose__section + .public-prose__section {
    margin-block-start: var(--space-6);
}

.public-prose__heading {
    margin: 0 0 var(--space-3);
    color: var(--color-heading);
    font-size: var(--font-size-xl);
    line-height: var(--line-height-heading);
}

.public-prose__body {
    margin: 0;
    color: var(--color-text);
    line-height: var(--line-height-body);
}

/*
 * The way back out of a search that matched nothing.
 */
.public-search__reset {
    margin-block-start: var(--space-5);
}
