/*
 * Dashboard design system — drivinginstructorwicklow.ie (redesign U2, KTD2).
 *
 * Token-first: every colour below resolves to a `--db-` custom property
 * extracted from the approved mockups (design/mockups/*.html) and
 * design/DASHBOARD-HANDOFF.md. One shared stylesheet for the public site;
 * the admin area is frozen on its own snapshot (public/css/admin-legacy.css)
 * and never loads this file.
 *
 * Figtree is the LATIN family (R11): self-hosted variable woff2, byte-identical
 * to Google's Figtree v9 subsets (and to the file embedded in
 * design/dashboard-reference.html). Non-Latin scripts render on system stacks —
 * see "Per-script font stacks" below.
 *
 * DIRECTION (U5 — R8, KTD8). This stylesheet is written in LOGICAL properties
 * (margin-inline-*, padding-inline-*, inset-inline-*, text-align: start/end,
 * border-inline-*) so Arabic and Urdu mirror from the `dir="rtl"` attribute
 * alone; flex and grid mirror themselves. The only `[dir="rtl"]` rules in the
 * file are the handful of things a logical property cannot express — they are
 * marked as such, and tests/rtl-markup.test.js fails the build if a physical
 * direction declaration reappears anywhere else.
 */

/*
 * latin: the subset the site has always shipped. The unicode-range is Google's
 * own for this exact file — it does not narrow what renders (a font is skipped
 * per-glyph when it has no glyph either way), it stops the browser fetching a
 * Latin subset to satisfy a Cyrillic or Arabic character.
 */
@font-face {
    font-family: 'Figtree';
    font-style: normal;
    font-weight: 400 800;
    font-display: swap;
    src: url('/fonts/figtree-latin.woff2') format('woff2');
    unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

/*
 * latin-ext: Polish ł/ą/ę/ś/ż/ź/ć/ń, Lithuanian č/ė/į/š/ų/ū/ž, Romanian
 * ă/â/î/ș/ț, Turkish ğ/ı/ş. All of them sit ABOVE U+00FF, so before this face
 * existed those four locales rendered their diacritics in a system fallback
 * font mid-word — visibly a different typeface inside the same sentence.
 */
@font-face {
    font-family: 'Figtree';
    font-style: normal;
    font-weight: 400 800;
    font-display: swap;
    src: url('/fonts/figtree-latin-ext.woff2') format('woff2');
    unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

/* ------------------------------------------------------------------ *
 * Per-script font stacks (U5 — KTD8). views/partials/header.ejs puts the
 * active locale's SCRIPT on <html data-font>, and each bucket points
 * --db-font-sans at a stack that can actually draw it. Self-hosting Arabic,
 * Nastaliq or Simplified-Chinese families would add megabytes to a marketing
 * page for a handful of visitors, so those four buckets are system fonts only
 * — deliberate stacks, not an accidental `sans-serif`.
 * ------------------------------------------------------------------ */
:root {
    /* latin + latin-ext: Figtree, then the platform UI face. */
    --db-font-sans: 'Figtree', system-ui, -apple-system, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
}

/* Russian. Figtree has no Cyrillic, so it is not even offered. */
html[data-font="cyrillic"] {
    --db-font-sans: system-ui, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
}

/* Arabic (Naskh). Segoe UI on Windows, Geeza Pro on Apple, Noto Naskh on
   Linux/Android — all four have full Arabic coverage and sensible metrics. */
html[data-font="arabic"] {
    --db-font-sans: 'Segoe UI', Tahoma, 'Geeza Pro', 'Noto Naskh Arabic', 'Noto Sans Arabic', system-ui, sans-serif;
}

/* Urdu prefers NASTALIQ, a different script style from Arabic Naskh: the
   baseline cascades diagonally, so the same string needs a taller line box.
   Naskh faces are the fallback — readable, just not idiomatic. */
html[data-font="urdu"] {
    --db-font-sans: 'Noto Nastaliq Urdu', 'Jameel Noori Nastaleeq', 'Segoe UI', 'Noto Naskh Arabic', Tahoma, system-ui, sans-serif;
}

/* Simplified Chinese. */
html[data-font="hans"] {
    --db-font-sans: system-ui, 'PingFang SC', 'Hiragino Sans GB', 'Microsoft YaHei', 'Noto Sans SC', 'Source Han Sans SC', sans-serif;
}

:root {
    /* Dashboard tokens (design/DASHBOARD-HANDOFF.md). */
    --db-bg: #F4F7FB;      /* page background (cool off-white) */
    --db-ink: #17202B;     /* headings, body text */
    --db-blu: #2F5FD8;     /* primary: buttons, highlight words, progress */
    --db-blu-deep: #1E3F97;/* dark end of the one gradient (CTA panel) */
    --db-blu-hover: #2149AE;
    --db-teal: #159A8C;    /* secondary/status: chip dots, checkmarks */
    --db-mut: #5E6B7A;     /* muted text */
    --db-card: #FFFFFF;    /* cards and panels */
    --db-line: #DFE6EF;    /* hairline borders (decorative surfaces only) */
    /*
     * Form-control boundary (U7, WCAG 1.4.11). --db-line is 1.26:1 on white and
     * the input fill (#FAFCFE) is 1.03:1 against the card, so neither the
     * border nor the fill identified a field's extent. This darker line is
     * 3.4:1 on #FAFCFE and 3.5:1 on white; it is used ONLY by .db-form
     * controls — hairlines elsewhere stay decorative on --db-line.
     */
    --db-line-strong: #7C8AA0;
    --db-amber: #F2A413;   /* amber accents: icon tiles, avatar, warn callout */
    /*
     * Star-rating glyphs (U7, WCAG 1.4.11). The stars are the only visible
     * statement of the rating, and --db-amber is 2.08:1 on white. This deeper
     * amber clears 3.6:1 while keeping the hue.
     */
    /*
     * Teal as TEXT (U7, WCAG 1.4.3). --db-teal is 3.15:1 on the pale teal
     * tints, fine for glyphs but not for text; this deeper teal clears 4.5:1
     * on white and on #EAF6F4 (5.85:1), and also carries .alert-success.
     */
    --db-teal-deep: #0B6A60;
    /*
     * Error token (R5): form validation. #B3261E clears 4.5:1 on white and on
     * --db-bg, so it can carry inline error text as well as the field border.
     */
    --db-err: #B3261E;

    /* Tint/prose shades that used to repeat as raw hex literals. */
    --db-blu-tint: #EDF2FB;  /* pale blue icon-tile / row background */
    --db-prose: #33404E;     /* long-form body text, darker than --db-mut */
    --db-warn-tint: #FBEBEA; /* pale red tone-bad / warning background */
    --db-teal-tint: #E7F5F3; /* pale teal tone-good background */

    --db-radius: 12px;     /* system radius; cards go up to 16-20px */
    --db-radius-card: 16px;
    --db-radius-panel: 20px;
    --db-shadow-btn: 0 6px 16px rgba(47, 95, 216, .3);
    --db-shadow-card: 0 24px 60px rgba(23, 32, 43, .12);
}

* {
    box-sizing: border-box;
}

html, body {
    margin: 0;
    padding: 0;
}

/*
 * Longhands, not the `font:` shorthand: the family now comes from a custom
 * property, and a shorthand that resolves to an invalid value drops the whole
 * declaration — font-size and line-height included — rather than just the part
 * that went wrong.
 */
body {
    background: var(--db-bg);
    color: var(--db-ink);
    font-weight: 400;
    font-size: 16.5px;
    line-height: 1.62;
    font-family: var(--db-font-sans);
}

/* Nastaliq's diagonal baseline needs the extra leading or descenders from one
   line collide with the line beneath it. */
html[data-font="urdu"] body {
    line-height: 1.9;
}

img, svg {
    max-width: 100%;
}

/*
 * Global keyboard focus indicator (WCAG 2.2 2.4.7, 1.4.11). The Dashboard
 * system has light surfaces everywhere, so a single 3px --db-blu ring clears
 * 3:1 on the page background, on white cards, and on the hairline borders.
 * On the one dark surface (the blue CTA panel) the white halo underneath
 * carries the separation. :focus-visible, not :focus, so mouse clicks do not
 * draw a ring.
 */
:focus-visible {
    outline: 3px solid var(--db-blu);
    outline-offset: 2px;
    box-shadow: 0 0 0 5px #FFFFFF;
    border-radius: 6px;
}

/*
 * Skip link (WCAG 2.2 2.4.1) — markup in views/partials/header.ejs.
 * Off-screen rather than display:none/visibility:hidden, which would remove it
 * from the focus order — the only users who need it could never reach it.
 */
.skip-link {
    position: absolute;
    top: -100px;
    /* inset-inline-start, so it drops in from the reading-start corner in both
       directions — the corner a keyboard user's eye is already at. */
    inset-inline-start: 1rem;
    z-index: 1100;
    padding: 0.75rem 1.25rem;
    background-color: var(--db-card);
    color: var(--db-ink);
    font-weight: 600;
    text-decoration: none;
    border: 1px solid var(--db-line);
    border-radius: 0 0 var(--db-radius) var(--db-radius);
}

.skip-link:focus {
    top: 0;
}

/* The skip target takes focus programmatically only; no ring around the page. */
main:focus {
    outline-color: transparent;
    box-shadow: none;
}

/* ------------------------------------------------------------------ *
 * Shared primitives (extracted from design/mockups/*.html)
 * ------------------------------------------------------------------ */

.wrap {
    max-width: 1140px;
    margin: 0 auto;
    padding: 0 24px;
}

.disp {
    font-weight: 700;
    line-height: 1.08;
    letter-spacing: -.02em;
    margin: 0;
    text-wrap: balance;
}

.btn {
    display: inline-block;
    text-decoration: none;
    font-weight: 700;
    font-size: 15.5px;
    padding: 14px 26px;
    border-radius: var(--db-radius);
    border: 0;
    cursor: pointer;
}

.btn-blu {
    background: var(--db-blu);
    color: #fff;
    box-shadow: var(--db-shadow-btn);
}

.btn-blu:hover {
    background: var(--db-blu-hover);
}

/* --db-line-strong, not --db-line: a control's boundary must clear 3:1
   (WCAG 1.4.11), and --db-line is 1.26:1 on both grounds this button sits on.
   The decorative hairlines elsewhere in the sheet keep --db-line. */
.btn-ln {
    border: 1.5px solid var(--db-line-strong);
    color: var(--db-ink);
    background: var(--db-card);
}

.btn-ln:hover {
    border-color: var(--db-ink);
}

/* Status chip with the teal dot — trust signals. */
.chip {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    background: var(--db-card);
    border: 1px solid var(--db-line);
    border-radius: 999px;
    padding: 8px 16px;
    font-size: 13.5px;
    font-weight: 700;
    color: var(--db-mut);
}

.chip::before {
    content: "";
    width: 8px;
    height: 8px;
    border-radius: 50%;
    background: var(--db-teal);
}

/* White card with hairline border — the system's base surface. */
.db-card {
    background: var(--db-card);
    border: 1px solid var(--db-line);
    border-radius: var(--db-radius-panel);
    padding: 28px;
}

/* The CTA panel — the only gradient in the system. */
.db-panel {
    background: linear-gradient(135deg, var(--db-blu) 0%, var(--db-blu-deep) 100%);
    border-radius: 24px;
    color: #fff;
    text-align: center;
    padding: 64px 34px;
}

.db-panel h2 {
    font-size: clamp(28px, 4.2vw, 46px);
}

.db-panel p {
    /* 4.6:1 on the gradient's LIGHT end (--db-blu); the old #CBD8F5 was
       3.92:1 there (U7, WCAG 1.4.3). */
    color: #E1E9FB;
    max-width: 34em;
    margin: 14px auto 28px;
}

.db-panel .btn {
    background: #fff;
    color: var(--db-blu);
}

.db-panel .btn:hover {
    background: #E4EBFA;
}

/* Inline SVG icons (views/partials/icon.ejs) — 24px viewBox, 2px stroke. */
.icon {
    width: 1.2em;
    height: 1.2em;
    flex: none;
    vertical-align: -0.24em;
}

/* ------------------------------------------------------------------ *
 * Form conventions (R5): fields are white-on-card inputs with hairline
 * borders; an invalid field gets the error border plus inline error text.
 * ------------------------------------------------------------------ */

.db-form label {
    display: block;
    font-weight: 700;
    font-size: 14px;
    margin-bottom: 6px;
}

.db-form input,
.db-form select,
.db-form textarea {
    width: 100%;
    font: inherit;
    /* 16px is a FUNCTIONAL floor, not a type choice: iOS Safari zooms the
       viewport when a focused control is under 16px and does not zoom back
       out on blur, so 15px here scaled the page up on first tap and pushed
       the rest of the form off-screen. */
    font-size: 16px;
    color: var(--db-ink);
    background: #FAFCFE;
    /* --db-line-strong, not --db-line: the field boundary must clear 3:1
       (WCAG 1.4.11) — see the token's comment in :root. */
    border: 1.5px solid var(--db-line-strong);
    border-radius: var(--db-radius);
    padding: 12px 14px;
}

/* --db-mut clears 5.3:1 on the #FAFCFE fill; the old #98A4B3 was 2.46:1. */
.db-form input::placeholder,
.db-form textarea::placeholder {
    color: var(--db-mut);
}

/* Border colour signals focus; the global :focus-visible ring stays intact. */
.db-form input:focus,
.db-form select:focus,
.db-form textarea:focus {
    border-color: var(--db-blu);
    background: #fff;
}

.db-form textarea {
    min-height: 120px;
    resize: vertical;
}

/* Invalid state: coloured border + inline error text, same hairline weight. */
.db-form input[aria-invalid="true"],
.db-form select[aria-invalid="true"],
.db-form textarea[aria-invalid="true"] {
    border-color: var(--db-err);
}

/* Forward contract for inline validation errors — no view renders it yet. */
.field-error {
    color: var(--db-err);
    font-size: 13px;
    font-weight: 600;
    margin-top: 6px;
}

/* ------------------------------------------------------------------ *
 * Header / navigation
 *
 * Class names (.nav-links, .nav-link, .dropdown, .dropdown-content,
 * .dropdown-link, .mobile-menu-btn) are carried over from the previous shell
 * deliberately: the keyboard-accessibility contract on them is asserted by
 * tests/a11y-markup.test.js and survives the reskin.
 * ------------------------------------------------------------------ */

.site-header {
    background: rgba(244, 247, 251, .92);
    border-bottom: 1px solid var(--db-line);
    position: sticky;
    top: 0;
    z-index: 1000;
    backdrop-filter: blur(6px);
}

.nav-container {
    max-width: 1140px;
    margin: 0 auto;
    padding: 12px 24px;
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 14px;
}

/*
 * THE BRAND IS THE ONLY THING IN THE BAR ALLOWED TO SHRINK (WCAG 1.4.10).
 *
 * The collapsed bar is brand + language button + hamburger, all of it nowrap,
 * which came to roughly 414px -- so 360px, 375px and 390px phones scrolled
 * sideways on every page. U7 chased that with breakpoints (hide the CTA at
 * 560, shrink the brand at 380, hide the wordmark at 340) and still left the
 * common widths overflowing.
 *
 * A flex row cannot overflow if one item can shrink and the rest fit, so the
 * wordmark truncates instead. `min-width: 0` is the load-bearing line: a flex
 * item defaults to `min-width: auto`, which refuses to shrink below its own
 * content -- which is exactly how a nowrap wordmark pushed the bar wider than
 * the screen. This holds at every width, including handsets not yet made.
 */
.brand {
    display: flex;
    align-items: center;
    gap: 10px;
    font-weight: 800;
    font-size: 17px;
    color: var(--db-ink);
    text-decoration: none;
    min-width: 0;
    overflow: hidden;
}

/* Truncates rather than pushes. The text stays in the accessibility tree --
   the "W" mark beside it is aria-hidden, so clipping the wordmark is the only
   way to narrow the brand without stripping the link's accessible name. */
.brand-name {
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* The rounded-square W mark (R6). */
.brand-mark {
    width: 28px;
    height: 28px;
    border-radius: 9px;
    background: var(--db-blu);
    color: #fff;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 15px;
    font-weight: 800;
    flex: none;
}

.nav-links {
    display: flex;
    align-items: center;
    gap: 6px;
    list-style: none;
    margin: 0;
    padding: 0;
}

.nav-links > li {
    position: relative;
    display: flex;
    align-items: center;
}

.nav-link {
    text-decoration: none;
    font-weight: 600;
    font-size: 14.5px;
    color: var(--db-mut);
    padding-block: 10px;
    padding-inline: 12px 6px;
    white-space: nowrap;
}

.nav-link:hover {
    color: var(--db-ink);
}

/* Chevron toggle beside each group trigger: Enter/Space toggles the panel
   without stealing the link's own navigation. */
.dropdown-toggle {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    background: none;
    border: 0;
    margin: 0;
    padding-block: 10px;
    padding-inline: 2px 10px;
    cursor: pointer;
    color: var(--db-mut);
    font: inherit;
}

.dropdown-toggle .icon {
    width: 14px;
    height: 14px;
    transition: transform .15s;
}

.dropdown-toggle:hover {
    color: var(--db-ink);
}

.dropdown.open .dropdown-toggle .icon {
    transform: rotate(180deg);
}

/* The dropdown panel: white card, hairline border, system radius. */
.dropdown-content {
    position: absolute;
    top: 100%;
    inset-inline-start: 0;
    min-width: 220px;
    background: var(--db-card);
    border: 1px solid var(--db-line);
    border-radius: var(--db-radius);
    box-shadow: 0 12px 32px rgba(23, 32, 43, .10);
    padding: 8px;
    opacity: 0;
    visibility: hidden;
    transform: translateY(8px);
    transition: opacity .15s, transform .15s, visibility .15s;
    z-index: 1000;
}

/*
 * Open on hover, and on :focus-within so every panel destination is reachable
 * by keyboard: focusing the trigger reveals the panel, which puts its links
 * back into the tab order. `.open` is the click/tap fallback nav.js drives.
 */
.dropdown:hover .dropdown-content,
.dropdown:focus-within .dropdown-content,
.dropdown.open .dropdown-content {
    opacity: 1;
    visibility: visible;
    transform: translateY(0);
}

.dropdown-link {
    display: block;
    padding: 10px 14px;
    color: var(--db-ink);
    text-decoration: none;
    font-weight: 500;
    font-size: 14.5px;
    border-radius: 8px;
}

.dropdown-link:hover {
    background: var(--db-bg);
    color: var(--db-blu);
}

/* ------------------------------------------------------------------ *
 * Language switcher (site-i18n U4 — R6). views/partials/language-switcher.ejs
 * renders BOTH presentations on every page and this stylesheet shows exactly
 * one of them: above the nav breakpoint the `.dropdown` panel, below it the
 * `<details>` picker. The mobile rules live in the @media block further down,
 * beside the rules that make them necessary.
 * ------------------------------------------------------------------ */

.lang-toggle {
    gap: 6px;
    padding-block: 10px;
    padding-inline: 12px 10px;
    font-weight: 600;
    font-size: 14.5px;
}

.lang-toggle .icon-globe {
    width: 17px;
    height: 17px;
}

/* Only the chevron flips with the panel; the globe is not a state indicator. */
.dropdown.open .lang-toggle .icon-globe {
    transform: none;
}

/* The switcher is the LAST nav item, so its panel hangs from the inline-END
   edge or it would overflow the viewport. Logical, so it hangs from the left in
   RTL — where the end of the nav row now is. */
.lang-menu {
    inset-inline-start: auto;
    inset-inline-end: 0;
    min-width: 200px;
    max-height: 70vh;
    overflow-y: auto;
}

/* The entry for the language already showing: marked, not hidden — a switcher
   that drops the current language loses the "you are here" it exists to give. */
.lang-menu .lang-link[aria-current] {
    color: var(--db-blu);
    font-weight: 700;
}

/* The bar control, hidden at desktop widths (the .dropdown above is the
   switcher there). display:none rather than visibility so its links leave the
   tab order entirely — two switchers in the tab order would be a defect, not a
   redundancy. `position: relative` is the anchor its panel hangs from and is
   inert while the element is hidden. */
.lang-bar {
    display: none;
    position: relative;
}

/* The disclosure triangle is furniture here; the globe is the affordance. */
.lang-bar > summary::-webkit-details-marker {
    display: none;
}

.lang-bar > summary {
    list-style: none;
}

/* "Book lessons" keeps the mockup's compact nav sizing. */
.btn-nav {
    padding: 11px 20px;
    font-size: 14px;
    white-space: nowrap;
}

/*
 * TAP TO CALL — the bar's third control, and the only one that converts.
 *
 * Hidden above the nav breakpoint for the same reason the hamburger is: a
 * desktop visitor is not holding the phone they would dial with, and the
 * footer carries the number for them. It matches the hamburger and the
 * language button exactly — 40px square, same border token (1.4.11), same
 * radius — because three controls in a row that disagree on size read as an
 * accident.
 *
 * `flex: none` for the reason the other two have it: the brand is the item
 * that yields, never a tap target (2.5.5).
 */
.btn-call {
    display: none;
    align-items: center;
    justify-content: center;
    flex: none;
    width: 40px;
    height: 40px;
    border: 1px solid var(--db-line-strong);
    border-radius: 10px;
    color: var(--db-ink);
    text-decoration: none;
}

.btn-call:hover {
    border-color: var(--db-ink);
}

.btn-call .icon {
    width: 20px;
    height: 20px;
}

/* Hamburger — a real <button>; hidden above the breakpoint.
   `flex: none` because the brand beside it is now shrinkable: without this the
   flex algorithm would take width off a 40px tap target too, trading a 1.4.10
   problem for a 2.5.5 one. --db-line-strong for the same 1.4.11 reason as the
   form fields — this and the language button are the ONLY two controls in the
   collapsed bar, and a border at 1.26:1 makes a button look like text. */
.mobile-menu-btn {
    display: none;
    align-items: center;
    justify-content: center;
    flex: none;
    background: none;
    border: 1px solid var(--db-line-strong);
    border-radius: 10px;
    margin: 0;
    padding: 8px;
    color: var(--db-ink);
    cursor: pointer;
    -webkit-appearance: none;
    appearance: none;
}

.mobile-menu-btn .icon {
    width: 22px;
    height: 22px;
}

/* The two glyphs swap with the open state (nav.js toggles .active). */
.mobile-menu-btn .icon-close {
    display: none;
}

/*
 * Reflow (U7, WCAG 1.4.10). The collapsed bar is brand (nowrap) + the
 * "Book lessons" button + the hamburger — ~467px of fixed content, so every
 * page scrolled horizontally on narrow phones (320px viewports measured
 * scrollWidth 467). All three can never fit below ~493px, so the redundant
 * header CTA goes at the small-phone breakpoint: /contact stays one tap away
 * via the menu's Contact link and the in-content booking CTAs.
 */
@media (max-width: 560px) {
    .btn-nav {
        display: none;
    }

    /* 16px gutters instead of 24px. Cheap width back for the wordmark before
       it has to start truncating. */
    .nav-container {
        padding-inline: 16px;
    }
}

/*
 * The last squeeze: at very narrow widths the wordmark itself condenses so
 * brand + hamburger clear a 320px viewport without clipping.
 *
 * The bar now also carries the language button, which is another ~50px of
 * nowrap content in a row that had roughly ten pixels of slack at 320px. So
 * below 340px the wordmark gives way and the "W" mark carries the brand alone.
 * That is the right thing to sacrifice: on a 320px screen the visitor who
 * cannot read the wordmark needs the language control more than the business
 * needs its name repeated above a page that already says it.
 *
 * IT IS HIDDEN VISUALLY, NOT REMOVED. `display: none` would take the wordmark
 * out of the accessibility tree, and the "W" mark beside it is aria-hidden
 * (it is decorative — it repeats the wordmark's initial). The brand link would
 * then have NO accessible name at all, which is a 2.4.4/4.1.2 failure at the
 * one breakpoint nobody tests on. Clipping keeps the text where a screen
 * reader can still reach it.
 */
@media (max-width: 380px) {
    .brand {
        font-size: 14.5px;
        gap: 8px;
    }

    .brand-mark {
        width: 24px;
        height: 24px;
        font-size: 13px;
        border-radius: 8px;
    }
}

@media (max-width: 340px) {
    .brand-name {
        position: absolute;
        width: 1px;
        height: 1px;
        margin: -1px;
        padding: 0;
        overflow: hidden;
        clip-path: inset(50%);
        white-space: nowrap;
        border: 0;
    }
}

.mobile-menu-btn.active .icon-menu {
    display: none;
}

.mobile-menu-btn.active .icon-close {
    display: block;
}

@media (max-width: 991px) {
    .mobile-menu-btn {
        display: inline-flex;
    }

    .btn-call {
        display: inline-flex;
    }

    /*
     * The mobile menu: a full-width card panel below the bar — white surface,
     * hairline border, system radius — with the nav stacked and every submenu
     * rendered as an expanded group. Hidden with display:none (not
     * transform), so its links leave the tab order entirely while closed.
     */
    .nav-links {
        display: none;
        position: absolute;
        top: 100%;
        inset-inline: 0;
        margin: 0 16px;
        flex-direction: column;
        align-items: stretch;
        gap: 0;
        background: var(--db-card);
        border: 1px solid var(--db-line);
        border-radius: var(--db-radius-card);
        box-shadow: 0 18px 44px rgba(23, 32, 43, .14);
        padding: 10px;
        /*
         * THE PANEL'S LAST ROW USED TO BE UNREACHABLE. On mobile browsers
         * `100vh` is the URL-bar-HIDDEN viewport, so with the bar showing the
         * visible area is ~56px shorter than this cap allows for. The panel's
         * box therefore ended below the fold; page scroll could not bring it
         * up (it is absolutely positioned against a sticky header, so it moves
         * with it) and the panel's own scroll ran out with the final row still
         * off-screen. Whatever was last in the menu was simply unusable.
         *
         * `dvh` is the dynamic viewport height — it tracks the URL bar as it
         * shows and hides, which is exactly the measurement wanted here. The
         * `vh` line above it stays as the fallback for browsers that do not
         * know `dvh` (pre-2022); they keep the old behaviour rather than none.
         */
        max-height: calc(100vh - 90px);
        max-height: calc(100dvh - 90px);
        overflow-y: auto;
        /* Keep a flick inside the panel from scrolling the page behind it. */
        overscroll-behavior: contain;
    }

    .nav-links.active {
        display: flex;
    }

    .nav-links > li {
        display: block;
        border-bottom: 1px solid var(--db-line);
        padding: 4px 0;
    }

    .nav-links > li:last-child {
        border-bottom: none;
    }

    .nav-link {
        display: block;
        padding: 12px 10px;
        font-size: 16px;
        color: var(--db-ink);
    }

    /* Submenus are expanded groups on mobile; the toggle has no job here. */
    .dropdown-toggle {
        display: none;
    }

    .dropdown-content {
        position: static;
        opacity: 1;
        visibility: visible;
        transform: none;
        box-shadow: none;
        border: none;
        border-radius: 0;
        /* The indent that reads the group as nested goes on the START edge. */
        padding-block: 0 8px;
        padding-inline: 10px 0;
        min-width: 0;
    }

    .dropdown-link {
        color: var(--db-mut);
        padding: 9px 12px;
    }

    /*
     * THE LANGUAGE SWITCHER SWAPS PRESENTATION HERE (U4 — R6).
     *
     * The two rules above are why the in-menu copy cannot be reused: every
     * `.dropdown-content` in this panel is force-expanded and every
     * `.dropdown-toggle` is hidden, so the switcher's panel — up to ELEVEN
     * language links — would sit permanently open inside the collapsed menu.
     * The whole nav item is therefore hidden here and the bar control outside
     * the panel takes over.
     */
    .nav-lang {
        display: none;
    }

    .lang-bar {
        display: block;
        /* THE FLEX ITEM IS THIS ELEMENT, not the <summary> inside it.
           .nav-container is the flex container and <details class="lang-bar">
           is its direct child, so this is where shrinking has to be refused —
           the brand is the item that yields (see the .brand note above). */
        flex: none;
    }

    /*
     * .nav-container is justify-content: space-between, which was right while
     * the bar held two children (brand at one end, hamburger at the other).
     * The language button makes three, and space-between would strand it in
     * the MIDDLE of the bar. Letting the brand absorb the slack keeps the two
     * controls together at the end, where a thumb already is.
     */
    .brand {
        margin-inline-end: auto;
    }

    /*
     * A 40px icon button matching the hamburger beside it. Icon-only is a
     * width decision as much as a visual one — see the reflow note further
     * down and the partial's comment on WCAG 2.5.3.
     */
    .lang-bar-summary {
        display: inline-flex;
        align-items: center;
        justify-content: center;
        width: 40px;
        height: 40px;
        /* 1.4.11, as on the hamburger it sits beside. */
        border: 1px solid var(--db-line-strong);
        border-radius: 10px;
        color: var(--db-ink);
        cursor: pointer;
    }

    .lang-bar-summary .icon-globe {
        width: 20px;
        height: 20px;
    }

    /*
     * The panel hangs from the button rather than sitting in flow, so opening
     * it never reflows the bar. It is capped and scrollable in its own right:
     * twelve languages on a short phone is taller than the space below the
     * header.
     */
    .lang-bar-list {
        position: absolute;
        top: calc(100% + 8px);
        inset-inline-end: 0;
        z-index: 1001;
        min-width: 190px;
        max-height: 60vh;
        max-height: 60dvh;
        overflow-y: auto;
        overscroll-behavior: contain;
        list-style: none;
        margin: 0;
        padding: 8px;
        background: var(--db-card);
        border: 1px solid var(--db-line);
        border-radius: var(--db-radius-card);
        box-shadow: 0 18px 44px rgba(23, 32, 43, .14);
    }

    .lang-bar-list .lang-link {
        display: block;
        padding: 10px 12px;
        color: var(--db-mut);
        text-decoration: none;
        font-size: 15px;
        border-radius: 8px;
    }

    .lang-bar-list .lang-link[aria-current] {
        color: var(--db-blu);
        font-weight: 700;
    }
}

/* ------------------------------------------------------------------ *
 * Translation disclaimer (site-i18n U4 — R4). A slim strip between the header
 * and <main>, on non-English pages only. Furniture, not an alert: the pale blue
 * tint, no icon and no border colour keep it from reading as a warning about
 * the page's content. --db-prose on --db-blu-tint is well past 4.5:1, and there
 * is no dismiss control by design (see the partial).
 * ------------------------------------------------------------------ */

.translation-disclaimer {
    background: var(--db-blu-tint);
    border-bottom: 1px solid var(--db-line);
}

.translation-disclaimer p {
    max-width: 1140px;
    margin: 0 auto;
    padding: 10px 24px;
    font-size: 13.5px;
    line-height: 1.5;
    color: var(--db-prose);
}

/* ------------------------------------------------------------------ *
 * Footer — the 4-column Dashboard footer (views/partials/footer.ejs).
 * Muted text (--db-mut) and headings (--db-ink) both clear 4.5:1 on the
 * --db-bg background; recomputed in tests/a11y-markup.test.js.
 * ------------------------------------------------------------------ */

.site-footer {
    font-size: 14px;
    color: var(--db-mut);
    border-top: 1px solid var(--db-line);
    background: var(--db-bg);
}

.footer-content {
    max-width: 1140px;
    margin: 0 auto;
    display: grid;
    grid-template-columns: 2fr 1fr 1fr 1.4fr;
    gap: 32px;
    padding: 48px 24px 32px;
}

/*
 * Footer headings are h2/h3 (U7): as h4s they skipped levels on every page —
 * the last in-content heading is an h1 or h2, so h4 broke 1.3.1 heading order
 * site-wide. Sized down to the footer's scale regardless of level.
 */
.footer-section h2,
.footer-section h3 {
    color: var(--db-ink);
    font-size: 15px;
    margin: 0 0 12px;
}

.footer-section p {
    margin: 0 0 8px;
    line-height: 1.6;
}

.footer-section .service-areas-title {
    margin-top: 18px;
}

/* The owner's other properties, listed under the contact block. A plain list
   rather than the stacked <p> the single "Sister School" line used, because
   there is more than one now and they are peers — a reader scanning for "which
   of these is mine" should see them as one set. Markers off: the surrounding
   footer has no bullets anywhere, and adding them here would make this block
   read as page content rather than as navigation. */
.footer-sites {
    list-style: none;
    margin: 0;
    padding: 0;
}

.footer-sites li + li {
    margin-top: 6px;
}

.footer-section .icon {
    color: var(--db-blu);
    margin-inline-end: 6px;
}

.footer-links {
    list-style: none;
    margin: 0;
    padding: 0;
    display: grid;
    gap: 8px;
}

.site-footer a {
    color: var(--db-mut);
    text-decoration: none;
}

.site-footer a:hover {
    color: var(--db-ink);
}

.footer-bottom {
    border-top: 1px solid var(--db-line);
    padding: 20px 24px;
    text-align: center;
    font-size: 13px;
}

.footer-bottom p {
    margin: 0;
}

@media (max-width: 880px) {
    .footer-content {
        grid-template-columns: 1fr 1fr;
    }
}

@media (max-width: 560px) {
    .footer-content {
        grid-template-columns: 1fr;
    }
}

/* ------------------------------------------------------------------ *
 * Home page (U3) — extracted from design/mockups/home-progress-hero.html.
 * The section shells (.db-feat, .db-fgrid, .db-f, .db-quotes, …) are the
 * mockups' shared vocabulary and will be reused as later units land.
 * ------------------------------------------------------------------ */

/* Hero: one column. It was a 6fr/5fr split carrying the progress-card
   illustration (R10) beside the headline; with the card removed and only the
   text left, keeping the split would have stranded it at just over half the
   width with dead space beside it. The 920px media query that collapsed the
   two columns went with it — there is nothing left to collapse. */
.db-hero .wrap {
    padding: 70px 24px 60px;
    display: grid;
    grid-template-columns: minmax(0, 1fr);
    gap: 56px;
    align-items: center;
}

.db-hero h1 {
    font-size: clamp(36px, 5vw, 56px);
}

.db-hero h1 em {
    font-style: normal;
    color: var(--db-blu);
}

.db-hero .lede {
    color: var(--db-mut);
    font-size: 17.5px;
    max-width: 34em;
    margin: 18px 0 28px;
}

/*
 * BUTTON ROW — THE BASE RULE, WHICH FOR A LONG TIME DID NOT EXIST.
 *
 * `.cta-row` was only ever styled through an ancestor: `.db-hero .cta-row`,
 * `.db-panel .cta-row`, `.db-phero .cta-row`, `.db-status .cta-row`. Every use
 * in the repo happened to sit inside one of those four, so the class looked
 * self-sufficient and was not.
 *
 * It isn't. Used anywhere else the selector simply does not match: no flex, no
 * gap, and the buttons fall back to inline-block, each shrinking to its own
 * label. On a narrow screen they wrap one per line at three different widths —
 * a visible stair, reported from a phone on the test-centre page, where two
 * rows sat inside plain `.db-card`s.
 *
 * The four rules above declare these same three properties and differ only in
 * their alignment extras, so hoisting the shared part changes nothing that
 * renders today; they are left in place rather than trimmed, because editing
 * four working rules to save three lines is how a layout regression gets in.
 *
 * NOTE WHAT IS *NOT* HERE: no `justify-content`. The two hero variants align
 * their buttons to the start on purpose, and centring in the base would break
 * both — they have no override to protect them.
 */
.cta-row {
    display: flex;
    gap: 12px;
    flex-wrap: wrap;
}

/*
 * The CTA panel's second row: the two channels that do not go through the
 * form. Separated from the primary row by a rule rather than by spacing
 * alone, so it reads as "or reach me directly" instead of as three equal
 * buttons — the form is still the one we want.
 */
.cta-contact {
    display: flex;
    gap: 12px;
    flex-wrap: wrap;
    justify-content: center;
    margin-top: 22px;
    padding-top: 22px;
    border-top: 1px solid rgba(255, 255, 255, .22);
}

.cta-contact .btn {
    display: inline-flex;
    align-items: center;
    gap: 8px;
}

/* Cards whose body is centred want their buttons centred too. */
.db-sister .cta-row {
    justify-content: center;
}

.db-hero .cta-row {
    display: flex;
    gap: 12px;
    flex-wrap: wrap;
    align-items: center;
}

.db-hero .chips {
    display: flex;
    gap: 10px;
    flex-wrap: wrap;
    margin-top: 24px;
}

/* The app-style card. The hero no longer uses it — that instance and its
   disclaiming caption were removed — but the About section still renders
   the flat variant, so these rules are live. */
.db-app {
    background: var(--db-card);
    border: 1px solid var(--db-line);
    border-radius: var(--db-radius-panel);
    box-shadow: var(--db-shadow-card);
    padding: 24px;
    display: grid;
    gap: 16px;
}

/* The about section's side card sits on a white surface — no float. */
.db-app-flat {
    box-shadow: none;
}

.db-app .bar {
    display: flex;
    justify-content: space-between;
    align-items: center;
}

.db-app .bar b {
    font-size: 15.5px;
}

.db-app .bar span {
    font-size: 12.5px;
    color: var(--db-mut);
}

.db-note {
    border: 1px solid var(--db-line);
    border-radius: 14px;
    padding: 16px;
}

/* Checklist variant inside the flat about card. */
.db-note-flat {
    border: none;
    padding: 0;
}

.db-note .hd {
    display: flex;
    justify-content: space-between;
    font-size: 13px;
    color: var(--db-mut);
    margin-bottom: 10px;
}

.db-note ul {
    list-style: none;
    margin: 0;
    padding: 0;
    display: grid;
    gap: 8px;
    font-size: 14px;
}

.db-note-flat ul {
    font-size: 15px;
    gap: 12px;
}

.db-note li::before {
    content: "✓";
    color: var(--db-teal);
    font-weight: 800;
    margin-inline-end: 9px;
}

.db-note li.todo::before {
    content: "○";
    color: var(--db-blu);
}

.db-next {
    display: flex;
    justify-content: space-between;
    align-items: center;
    background: #EAF6F4;
    border-radius: 14px;
    padding: 14px 16px;
    font-size: 14px;
}

.db-next b {
    color: var(--db-teal-deep);
}

/* Trust strip. */
.db-logos {
    border-top: 1px solid var(--db-line);
    border-bottom: 1px solid var(--db-line);
    background: var(--db-card);
}

.db-logos .wrap {
    display: flex;
    justify-content: center;
    gap: 44px;
    flex-wrap: wrap;
    padding: 18px 24px;
    font-size: 13px;
    font-weight: 700;
    letter-spacing: .1em;
    text-transform: uppercase;
    color: var(--db-mut);
}

/* Feature sections (My Services, Why Learn With Me, Learner Resources). */
.db-feat .wrap {
    padding: 76px 24px;
}

.db-feat h2 {
    font-size: clamp(28px, 3.8vw, 44px);
    text-align: center;
}

.db-feat .sub {
    text-align: center;
    color: var(--db-mut);
    max-width: 38em;
    margin: 14px auto 44px;
}

/* A heading with no subline still needs the gap before its grid. */
.db-feat h2 + .db-fgrid {
    margin-top: 44px;
}

.db-fgrid {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: 18px;
}

@media (max-width: 1000px) {
    .db-fgrid {
        grid-template-columns: 1fr 1fr;
    }
}

@media (max-width: 560px) {
    .db-fgrid {
        grid-template-columns: 1fr;
    }
}

.db-fgrid.four {
    grid-template-columns: repeat(4, 1fr);
}

@media (max-width: 1000px) {
    .db-fgrid.four {
        grid-template-columns: 1fr 1fr;
    }
}

@media (max-width: 560px) {
    .db-fgrid.four {
        grid-template-columns: 1fr;
    }
}

.db-f {
    background: var(--db-card);
    border: 1px solid var(--db-line);
    border-radius: var(--db-radius-card);
    padding: 24px 22px;
    display: flex;
    flex-direction: column;
}

.db-f .ic {
    width: 40px;
    height: 40px;
    border-radius: 12px;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 18px;
    margin-bottom: 14px;
    background: var(--db-blu-tint);
    color: var(--db-blu);
}

.db-f h3 {
    font-size: 17px;
    margin: 0 0 6px;
    font-weight: 700;
}

.db-f p {
    margin: 0 0 12px;
    font-size: 14px;
    color: var(--db-mut);
}

.db-f .go {
    margin-top: auto;
    font-size: 14px;
    font-weight: 700;
    color: var(--db-blu);
    text-decoration: none;
}

/* The mockup writes the arrow into the label; keeping it in CSS keeps the
   locale strings clean and the arrow out of screen-reader announcements. */
.db-f .go::after {
    content: " →";
}

a.db-f {
    text-decoration: none;
    color: inherit;
    transition: border-color .15s;
}

a.db-f:hover {
    border-color: var(--db-blu);
}

a.db-f:hover .go {
    text-decoration: underline;
}

/* About Your Instructor. */
.db-about {
    background: var(--db-card);
    border-top: 1px solid var(--db-line);
}

.db-about .wrap {
    padding: 76px 24px;
    display: grid;
    grid-template-columns: minmax(0, 7fr) minmax(0, 4fr);
    gap: 56px;
    align-items: start;
}

@media (max-width: 920px) {
    .db-about .wrap {
        grid-template-columns: 1fr;
    }
}

.db-about h2 {
    font-size: clamp(28px, 3.8vw, 44px);
    margin-bottom: 18px;
}

.db-about p {
    color: var(--db-prose);
    margin: 0 0 16px;
    font-size: 16px;
}

/* What My Students Say. */
.db-quotes {
    background: var(--db-card);
    border-top: 1px solid var(--db-line);
}

.db-quotes .wrap {
    padding: 76px 24px;
}

.db-quotes h2 {
    font-size: clamp(28px, 3.8vw, 44px);
    text-align: center;
    margin-bottom: 40px;
}

.db-qgrid {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: 18px;
}

@media (max-width: 880px) {
    .db-qgrid {
        grid-template-columns: 1fr;
    }
}

.db-q {
    border: 1px solid var(--db-line);
    border-radius: var(--db-radius-card);
    padding: 24px;
}

.db-q p {
    margin: 10px 0 12px;
    font-size: 15px;
}

.db-q .who {
    display: flex;
    align-items: center;
    gap: 10px;
    font-size: 13.5px;
    color: var(--db-mut);
    font-weight: 700;
}

.db-q .av {
    width: 34px;
    height: 34px;
    border-radius: 50%;
    background: var(--db-blu);
    color: #fff;
    display: flex;
    align-items: center;
    justify-content: center;
    font-weight: 800;
    font-size: 14px;
    flex: none;
}

.db-q .av-teal {
    background: var(--db-teal);
}

.db-q .av-amber {
    background: var(--db-amber);
}

/* The closing CTA panel's outer spacing (.db-panel is a shared primitive). */
.db-cta .wrap {
    padding: 80px 24px;
}

/* ------------------------------------------------------------------ *
 * Lesson pages (U4) — extracted from design/mockups/lesson-edt.html and
 * generalised across views/lessons/*.ejs. Reuses .wrap/.disp/.chip/.btn,
 * .db-note, .db-f/.db-fgrid, and the .db-cta/.db-panel primitives above.
 * ------------------------------------------------------------------ */

/* Page hero: H1 + optional intro subhead + lede + fact chips. */
.db-phero .wrap {
    padding: 64px 24px 44px;
    max-width: 860px;
}

.db-phero h1 {
    font-size: clamp(30px, 4.2vw, 46px);
}

/* The page's original intro heading, kept as a subhead over the lede. */
.db-phero h2 {
    font-size: 16px;
    font-weight: 700;
    color: var(--db-blu);
    margin: 26px 0 0;
}

.db-phero .lede {
    color: var(--db-mut);
    font-size: 17px;
    margin: 10px 0 0;
}

.db-phero .chips {
    display: flex;
    gap: 10px;
    flex-wrap: wrap;
    margin-top: 24px;
}

/* Content sections on the page background. */
.db-sec .wrap {
    padding: 44px 24px;
}

.db-sec > .wrap > h2 {
    font-size: clamp(26px, 3.4vw, 38px);
    text-align: center;
    margin: 0 0 30px;
}

/* Section intro line between the heading and its grid. */
.db-sec .sub {
    text-align: center;
    color: var(--db-mut);
    max-width: 46em;
    margin: -16px auto 30px;
}

/* Mid-section subheading ("The six reduced EDT sessions are:"). */
.db-sec .mid {
    text-align: center;
    font-size: 19px;
    margin: 34px 0 18px;
}

/* Numbered card grid (EDT's 12 lessons, session lists, step sequences). */
.db-lgrid {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: 18px;
}

@media (max-width: 1000px) {
    .db-lgrid {
        grid-template-columns: 1fr 1fr;
    }
}

@media (max-width: 560px) {
    .db-lgrid {
        grid-template-columns: 1fr;
    }
}

.db-l {
    background: var(--db-card);
    border: 1px solid var(--db-line);
    border-radius: var(--db-radius-card);
    padding: 22px;
    display: flex;
    gap: 16px;
    align-items: flex-start;
}

.db-l .num {
    width: 40px;
    height: 40px;
    border-radius: 50%;
    background: var(--db-blu-tint);
    color: var(--db-blu);
    display: flex;
    align-items: center;
    justify-content: center;
    font-weight: 800;
    font-size: 15px;
    flex: none;
}

.db-l h3 {
    font-size: 15.5px;
    margin: 0 0 4px;
    font-weight: 700;
}

.db-l p {
    margin: 0;
    font-size: 13.5px;
    color: var(--db-mut);
}

/* Two-column white band: checklist card + resources (EDT). */
.db-info {
    background: var(--db-card);
    border-top: 1px solid var(--db-line);
    border-bottom: 1px solid var(--db-line);
}

.db-info .wrap {
    padding: 56px 24px;
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 40px;
    align-items: start;
}

@media (max-width: 880px) {
    .db-info .wrap {
        grid-template-columns: 1fr;
    }
}

.db-info h2 {
    font-size: clamp(24px, 3vw, 32px);
    margin: 0 0 16px;
}

.db-info .sub {
    text-align: start;
    margin: 0 0 4px;
}

/* External PDF resource cards. */
.db-pgrid {
    display: grid;
    gap: 12px;
    margin-top: 16px;
}

.db-pdf {
    display: flex;
    gap: 14px;
    align-items: center;
    background: var(--db-card);
    border: 1px solid var(--db-line);
    border-radius: 14px;
    padding: 16px 18px;
    text-decoration: none;
    color: inherit;
    transition: border-color .15s;
}

.db-pdf:hover {
    border-color: var(--db-blu);
}

.db-pdf .ic {
    width: 38px;
    height: 38px;
    border-radius: 10px;
    background: var(--db-warn-tint);
    color: var(--db-err);
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 16px;
    flex: none;
}

.db-pdf h3 {
    display: block;
    font-size: 14.5px;
    font-weight: 700;
    margin: 0;
}

.db-pdf span {
    font-size: 12.5px;
    color: var(--db-mut);
}

/* Single wide fact card (the "Driving Test Facts" info block). */
.db-fact {
    display: flex;
    gap: 14px;
    align-items: flex-start;
    background: var(--db-card);
    border: 1px solid var(--db-line);
    border-radius: 14px;
    padding: 18px 20px;
    max-width: 860px;
    margin: 0 auto;
}

.db-fact .ic {
    width: 40px;
    height: 40px;
    border-radius: 12px;
    background: var(--db-blu-tint);
    color: var(--db-blu);
    display: flex;
    align-items: center;
    justify-content: center;
    flex: none;
}

.db-fact p {
    margin: 0;
    color: var(--db-prose);
}

/* A checklist card used as a standalone section body. */
.db-sec .db-note {
    background: var(--db-card);
    max-width: 860px;
    margin: 0 auto;
    padding: 20px;
}

/* Prose section on a white card. */
.db-prose {
    background: var(--db-card);
    border: 1px solid var(--db-line);
    border-radius: var(--db-radius-card);
    padding: 28px;
    max-width: 860px;
    margin: 0 auto;
}

.db-prose h2 {
    font-size: clamp(22px, 3vw, 28px);
    margin: 0 0 12px;
}

.db-prose p {
    color: var(--db-prose);
    margin: 0 0 14px;
}

.db-prose p:last-child {
    margin-bottom: 0;
}

/* Callout note card (application notes, "Important!" warnings). */
/* The 4px accent rule sits on the READING-START edge — the edge the eye enters
   the callout from, which is the right-hand one in Arabic and Urdu. */
.db-callout {
    border: 1px solid var(--db-line);
    border-inline-start: 4px solid var(--db-blu);
    background: var(--db-card);
    border-radius: 14px;
    padding: 18px 20px;
    max-width: 860px;
    margin: 26px auto 0;
}

.db-callout h3 {
    margin: 0 0 6px;
    font-size: 16px;
}

.db-callout p {
    margin: 0;
    color: var(--db-prose);
}

.db-callout-warn {
    border-inline-start-color: var(--db-amber);
}

/* Pull quote (the nervous-drivers testimonial). */
.db-pull {
    background: var(--db-card);
    border: 1px solid var(--db-line);
    border-radius: var(--db-radius-card);
    padding: 30px;
    max-width: 820px;
    margin: 0 auto;
    text-align: center;
}

.db-pull blockquote {
    margin: 0;
}

.db-pull p {
    margin: 0;
    font-size: 17px;
    font-style: italic;
    color: var(--db-prose);
}

/* Grid variants and card tweaks the lesson pages need. */
.db-fgrid.two {
    grid-template-columns: 1fr 1fr;
}

@media (max-width: 700px) {
    .db-fgrid.two {
        grid-template-columns: 1fr;
    }
}

/* The lessons index cards keep their original h2s. */
.db-f h2 {
    font-size: 17px;
    margin: 0 0 6px;
    font-weight: 700;
}

/* Outcome-card icon tones (reduced EDT): approved / partial / rejected. */
.db-f .ic.tone-good {
    background: var(--db-teal-tint);
    color: var(--db-teal);
}

.db-f .ic.tone-warn {
    background: #FDF3E0;
    color: var(--db-amber);
}

.db-f .ic.tone-bad {
    background: var(--db-warn-tint);
    color: var(--db-err);
}

/* Secondary (ghost) button inside the gradient CTA panel. */
.db-panel .cta-row {
    display: flex;
    gap: 12px;
    justify-content: center;
    flex-wrap: wrap;
}

.db-panel .btn-ghost {
    background: transparent;
    color: #fff;
    border: 1.5px solid rgba(255, 255, 255, .65);
    box-shadow: none;
}

.db-panel .btn-ghost:hover {
    background: rgba(255, 255, 255, .1);
    border-color: #fff;
}

/* ------------------------------------------------------------------ *
 * Resource articles (U5) — the learner-info guides and the location page.
 * Long-form reading pages: everything reuses the .db-phero/.db-sec shells
 * and the .db-l/.db-f/.db-note/.db-pdf/.db-callout primitives above; the
 * additions below are the article-specific arrangements (a one-column card
 * stack with a readable measure, the two NDLS application-method cards, and
 * the FAQ accordion restyled onto the tokens).
 * ------------------------------------------------------------------ */

/* CTA button pair inside a page hero (the location page). */
.db-phero .cta-row {
    display: flex;
    gap: 12px;
    flex-wrap: wrap;
    margin-top: 26px;
}

/* Centered action line under a section body (booking buttons). */
.db-actions {
    text-align: center;
    margin: 26px 0 0;
}

/* One-column card stack at prose measure — numbered tips, requirement rows,
   stacked Q/A cards. Long-form text reads down a single column. */
.db-article {
    display: grid;
    gap: 18px;
    max-width: 860px;
    margin-inline: auto;
}

/* Article cards carry full paragraphs, not blurbs: body size, roomier line. */
.db-article .db-l p {
    font-size: 15px;
    line-height: 1.6;
}

.db-article .db-l h2 {
    font-size: 19px;
    margin: 6px 0 8px;
}

.db-article .db-l h3 {
    font-size: 16.5px;
}

.db-article .db-callout {
    margin: 14px 0 0;
}

/* Privacy policy (views/privacy.ejs): plain lists inside the prose cards. */
.privacy-policy .db-prose ul {
    margin: 10px 0 0;
    padding-inline-start: 22px;
    display: grid;
    gap: 8px;
    color: var(--db-prose);
    line-height: 1.6;
}

.privacy-policy .db-actions .sub {
    display: block;
    margin: 0 0 12px;
}

/* Tick list inside an article or method card (same glyph as .db-note). */
.db-ticks {
    list-style: none;
    margin: 12px 0;
    padding: 0;
    display: grid;
    gap: 8px;
    font-size: 14.5px;
}

.db-ticks li::before {
    content: "✓";
    color: var(--db-teal);
    font-weight: 800;
    margin-inline-end: 9px;
}

/* Requirement rows swap the number badge for a teal check. */
.db-l .num.tick {
    background: var(--db-teal-tint);
    color: var(--db-teal);
}

/* Step grids that read better at two-up than three-up. */
.db-lgrid.db-lgrid-two {
    grid-template-columns: 1fr 1fr;
    max-width: 860px;
    margin: 0 auto;
}

@media (max-width: 700px) {
    .db-lgrid.db-lgrid-two {
        grid-template-columns: 1fr;
    }
}

/* A lone .db-pdf resource card at article measure. */
.db-pgrid.db-pgrid-narrow {
    max-width: 860px;
    margin-inline: auto;
}

.db-pdf .db-extlink {
    display: block;
    margin-top: 2px;
}

/* Inline external-link affordance (tips download link, PDF card label). */
.db-extlink {
    color: var(--db-blu);
    font-weight: 700;
    font-size: 14px;
    text-decoration: none;
}

a.db-extlink:hover {
    text-decoration: underline;
}

a.db-extlink::after {
    content: " ↗";
}

/* Centered prose card (the tips page's booking section). */
.db-prose-center {
    text-align: center;
}

/* Highlight grid directly after a prose card. */
.db-gap-top {
    margin-top: 18px;
}

/* The two NDLS application-method cards (permit page). */
.db-methods {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 18px;
    max-width: 940px;
    margin: 0 auto;
}

@media (max-width: 760px) {
    .db-methods {
        grid-template-columns: 1fr;
    }
}

.db-method {
    background: var(--db-card);
    border: 1px solid var(--db-line);
    border-radius: var(--db-radius-card);
    padding: 24px;
    display: flex;
    flex-direction: column;
}

.db-method h3 {
    font-size: 18px;
    margin: 0 0 10px;
    display: flex;
    align-items: center;
    gap: 10px;
    flex-wrap: wrap;
}

.db-method h4 {
    font-size: 14.5px;
    margin: 8px 0 0;
}

.db-method p {
    margin: 0 0 8px;
    color: var(--db-mut);
    font-size: 14.5px;
}

.db-method .btn {
    margin-top: auto;
    text-align: center;
}

/* Service cards on the location page: link button pinned to the bottom. */
.db-service .btn {
    margin-top: auto;
    align-self: flex-start;
}

/* Stacked Q/A cards (location page FAQ). */
.db-qa h3 {
    margin: 0 0 8px;
    font-size: 16.5px;
}

.db-qa p {
    margin: 0;
    color: var(--db-prose);
    font-size: 15px;
    line-height: 1.6;
}

/*
 * Statistic cards (test-centre page): the figure is the content.
 *
 * These three cards carry the waiting time, the pass rate and the fee, and the
 * first version shipped them at body size — the numbers a visitor came for read
 * as ordinary paragraph text and the label above them looked more important.
 * The label stays small and muted, the figure is the largest thing in the card,
 * and the source note sits under it in the muted tone.
 */
.db-stat h3 {
    margin: 0 0 6px;
    font-size: 14px;
    font-weight: 600;
    color: var(--db-mut);
}

.db-stat .stat-value {
    margin: 0 0 6px;
    font-size: 34px;
    line-height: 1.1;
    font-weight: 700;
    color: var(--db-blu);
    /*
     * plaintext, NOT dir="ltr". Every value here begins with a Latin digit and
     * may end in a translated word ('13 أسبوعًا'), so the paragraph direction
     * has to follow the FIRST strong character rather than be forced — forcing
     * it is what blanked the Arabic contact page.
     */
    unicode-bidi: plaintext;
}

.db-stat .stat-note {
    margin: 0;
    font-size: 13.5px;
    line-height: 1.5;
    color: var(--db-mut);
    unicode-bidi: plaintext;
}

/* The test centre's address block, and the source line under the statistics. */
.addr {
    margin: 0 0 14px;
    font-size: 16px;
    font-weight: 600;
    color: var(--db-ink);
}

.src {
    margin: 14px 0 0;
    color: var(--db-mut);
    line-height: 1.6;
}

/* Sister-school card (location page): centered, article measure. */
.db-sister {
    max-width: 640px;
    margin: 0 auto;
    text-align: center;
}

.db-sister .ic {
    width: 40px;
    height: 40px;
    border-radius: 12px;
    background: var(--db-blu-tint);
    color: var(--db-blu);
    display: flex;
    align-items: center;
    justify-content: center;
    margin: 0 auto 12px;
}

.db-sister h3 {
    margin: 0 0 8px;
    font-size: 18px;
}

.db-sister p {
    margin: 0 0 16px;
    color: var(--db-mut);
}

/* --- FAQ accordion (learners-info/faq), restyled onto the tokens. The
   disclosure contract (h3 > button, aria-expanded, .faq-js gating) is
   unchanged from U1 — see the view. --- */
.faq-category {
    font-size: clamp(20px, 2.6vw, 26px);
    margin: 22px 0 0;
    letter-spacing: -.01em;
}

.faq-category:first-child {
    margin-top: 0;
}

.faq-item {
    background: var(--db-card);
    border: 1px solid var(--db-line);
    border-radius: var(--db-radius-card);
    overflow: hidden;
}

/* The <h3> is only the outline node; the button carries the visuals. */
.faq-item h3 {
    margin: 0;
    font-size: 16px;
}

/* Resets the native button chrome, then paints the question bar. */
.faq-question {
    display: block;
    width: 100%;
    box-sizing: border-box;
    text-align: start;
    padding: 16px 20px;
    margin: 0;
    border: none;
    background: var(--db-card);
    color: var(--db-ink);
    font-family: inherit;
    font-size: inherit;
    font-weight: 600;
    line-height: inherit;
    cursor: pointer;
    transition: background-color .15s;
    -webkit-appearance: none;
    appearance: none;
}

.faq-question:hover {
    background: var(--db-bg);
}

/* Open-state emphasis driven by the ARIA state itself, so the visual and
   accessible state cannot drift apart. */
.faq-question[aria-expanded="true"] {
    font-weight: 700;
    color: var(--db-blu);
}

/*
 * Answers are only hidden once the script has confirmed it can reveal them
 * again (.faq-js) — without that gate a visitor with scripting disabled
 * would get eleven buttons that do nothing and no answers at all.
 */
.faq-categories.faq-js .answer {
    display: none;
}

.faq-categories.faq-js .faq-item.is-open .answer {
    display: block;
}

.answer {
    padding: 18px 20px;
    border-top: 1px solid var(--db-line);
}

.answer p {
    margin: 0 0 12px;
    line-height: 1.6;
    color: var(--db-prose);
}

.answer p:last-child {
    margin-bottom: 0;
}

.answer ul,
.answer ol {
    margin: 12px 0;
    /* The marker gutter follows the text direction, or RTL bullets hang off
       the wrong side of their own list. */
    padding-inline-start: 22px;
}

.answer li {
    margin-bottom: 8px;
    line-height: 1.6;
    color: var(--db-prose);
}

/* ------------------------------------------------------------------ *
 * Contact + utility pages (U6) — extracted from design/mockups/contact.html.
 *
 * The contact page: .db-phero hero, then the Get In Touch info card beside
 * the .db-card.db-form form card. The base .db-form field rules live with
 * the shared primitives near the top of this file; everything here is the
 * page layout plus the hooks public/js/contact.js owns (.form-text, the
 * #formMessage alert classes, the .hp-field honeypot).
 *
 * Thank-you, 404 and 500 reuse ONE sub-pattern of this page — the info card
 * on its own (.db-status): a single centred .db-card with heading, message
 * and CTA. No page-level <style> blocks remain anywhere on the public site,
 * which is why the LEGACY section that used to close this file is gone: the
 * four U6 pages were its last consumers (the admin area is frozen on
 * public/css/admin-legacy.css and never loads this file).
 * ------------------------------------------------------------------ */

.db-contact .wrap {
    padding: 24px 24px 80px;
    display: grid;
    grid-template-columns: minmax(0, 4fr) minmax(0, 7fr);
    gap: 32px;
    align-items: start;
}

@media (max-width: 880px) {
    .db-contact .wrap {
        grid-template-columns: 1fr;
    }
}

.db-contact .db-card h2 {
    font-size: 22px;
    margin: 0 0 18px;
}

/* Info-card rows: icon bubble + label/value pair. */
.db-ci {
    display: flex;
    gap: 14px;
    align-items: flex-start;
    padding: 14px 0;
    border-top: 1px solid var(--db-line);
}

.db-ci:first-of-type {
    border-top: 0;
}

.db-ci .ic {
    width: 38px;
    height: 38px;
    border-radius: 10px;
    background: var(--db-blu-tint);
    color: var(--db-blu);
    display: flex;
    align-items: center;
    justify-content: center;
    flex: none;
}

.db-ci b {
    display: block;
    font-size: 14.5px;
    color: var(--db-ink);
}

.db-ci a,
.db-ci span {
    font-size: 14.5px;
    color: var(--db-mut);
    text-decoration: none;
    word-break: break-all;
}

.db-ci a:hover {
    color: var(--db-blu);
}

.db-contact .chips {
    margin-top: 18px;
    display: flex;
    gap: 10px;
    flex-wrap: wrap;
}

/* Two-up field rows and the country-code + number pair. */
.db-form .row {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 18px;
}

@media (max-width: 640px) {
    .db-form .row {
        grid-template-columns: 1fr;
    }
}

.db-form .tel {
    display: grid;
    grid-template-columns: 110px 1fr;
    gap: 10px;
}

/* The phone hint. The class name is public/js/contact.js's hook —
   phoneInput.closest('.form-group').querySelector('.form-text') — so it is
   .form-text here, with the mockup's .help treatment. */
.db-form .form-text {
    display: block;
    font-size: 12.5px;
    color: var(--db-mut);
    margin-top: 6px;
}

/* The privacy notice under the submit button (views/contact.ejs). */
/* THE SUBMIT BLOCK IS THE FORM'S ACTION, NOT ANOTHER FIELD.
   The margin is larger than .row's 18px gap on purpose: inside the form that
   gap separates PEERS, so reusing it here would say the button is one more
   field. This says the fields have ended.

   Full width because of what sits above it. Since the message box and the
   theory question were removed, the button follows the two-column
   transmission/location row directly, and an inline-block button under a
   half-width column reads as part of that column with dead space beside it.
   Spanning the card removes the ambiguity, and it is the right target size on
   a phone, where that row collapses to one column anyway. */
.db-form .form-actions {
    margin-top: 24px;
}

.db-form .form-actions .btn {
    width: 100%;
}

.db-form .form-privacy {
    margin-top: 12px;
}

.db-form .form-privacy a {
    color: inherit;
    text-decoration: underline;
}

/* Honeypot (views/contact.ejs): off-screen, never display:none — the field
   must stay fillable by bots for routes/contact.js's check to mean anything. */
.hp-field {
    position: absolute;
    /* inset-inline-start, not `left`: CSS ignores overflow in the inline-START
       direction when it computes the scrollable area, so this stays unreachable
       in both directions. A hard `left: -9999px` on an RTL page would overflow
       towards the END edge instead and give every Arabic/Urdu page 9999px of
       horizontal scroll. */
    inset-inline-start: -9999px;
}

/* The submission result sink. public/js/contact.js REPLACES #formMessage's
   className wholesale with 'alert alert-success' / 'alert alert-danger', so
   those exact names carry the styling; visibility is driven by the element's
   inline style, which the same script owns. */
.alert {
    margin-top: 18px;
    padding: 14px 16px;
    border: 1.5px solid;
    border-radius: var(--db-radius);
    background: var(--db-card);
    font-weight: 600;
}

/* --db-teal-deep clears 4.5:1 on white; the teal token stays on the border. */
.alert-success {
    border-color: var(--db-teal);
    color: var(--db-teal-deep);
}

/* Error state on the error token (R5): --db-err clears 4.5:1 on white, so it
   can carry the inline text as well as the border. */
.alert-danger {
    border-color: var(--db-err);
    color: var(--db-err);
}

/* ------------------------------------------------------------------ *
 * Formless status pages (thank-you / 404 / 500) — the contact info-card
 * sub-pattern on its own: one centred card, heading, message, CTA.
 * ------------------------------------------------------------------ */

.db-status .wrap {
    max-width: 720px;
    padding: 64px 24px 96px;
    text-align: center;
}

.db-status .db-card {
    padding: 48px 32px;
}

.db-status .ic {
    width: 56px;
    height: 56px;
    border-radius: 16px;
    background: #E7F4F2;
    color: var(--db-teal);
    display: inline-flex;
    align-items: center;
    justify-content: center;
    margin-bottom: 18px;
}

.db-status .ic .icon {
    width: 28px;
    height: 28px;
}

.db-status h1 {
    font-size: clamp(28px, 4vw, 40px);
    margin: 0 0 14px;
}

.db-status .lede {
    font-size: 17px;
    margin: 0 0 8px;
}

.db-status .sub {
    color: var(--db-mut);
    margin: 0;
}

.db-status .cta-row {
    display: flex;
    gap: 12px;
    justify-content: center;
    flex-wrap: wrap;
    margin-top: 28px;
}

/* Development-only error dump on views/error.ejs. */
.db-status .error-details {
    margin-top: 24px;
    padding: 16px;
    background: var(--db-bg);
    border: 1px solid var(--db-line);
    border-radius: var(--db-radius);
    text-align: start;
    overflow-x: auto;
}

.db-status .error-details pre {
    margin: 0;
    white-space: pre-wrap;
    word-wrap: break-word;
}

/* ------------------------------------------------------------------ *
 * RTL SPOT FIXES (U5 — R8, KTD8).
 *
 * THE ONLY `[dir="rtl"]` RULES IN THIS FILE, and the bar for adding another is
 * high: everything a logical property can express is already expressed
 * logically above, and tests/rtl-markup.test.js enforces that. What is left
 * here is the one class of thing logical properties cannot reach — glyphs whose
 * MEANING is a direction, baked into `content:` strings. An arrow that says
 * "onward" must point left in a right-to-left language; left unmirrored it
 * points back at the text the reader just came from.
 * ------------------------------------------------------------------ */

[dir="rtl"] .db-f .go::after {
    content: " ←";
}

[dir="rtl"] a.db-extlink::after {
    content: " ↖";
}

/* The theory cross-promotion note (U-A3). The call to action is a button
   rather than inline link text: as link text it took the visited-link purple
   on a return visit, which read as "already dealt with" on the one line the
   panel exists to get acted on. */
.theory-promo p {
    margin: 0;
}

.theory-promo .btn {
    margin-top: 14px;
}

/* Full width on narrow screens, where a 26px-padded inline-block otherwise
   sits stranded against the left edge of a full-bleed card. */
@media (max-width: 560px) {
    .theory-promo .btn {
        display: block;
        text-align: center;
    }
}

/* ENGLISH LABELS INSIDE AN RTL PAGE (the enquiry form is English on every
   locale — see views/contact.ejs).

   Left to inherit `rtl` on the Arabic and Urdu pages, the bidi algorithm moves
   each label's TRAILING punctuation to the front, because '*' and '?' are
   neutral characters at the end of an LTR run inside an RTL paragraph. It
   rendered as `* First Name` and, worse, `* ?Have you passed your theory test`.

   `plaintext` sets each label's direction from its own first strong character
   — the CSS equivalent of dir="auto", applied to all of them at once — so the
   punctuation stays where it was written.

   IT IS DELIBERATELY NOT dir="ltr" ON THE <form>. That was the first attempt
   and it rendered the entire Arabic contact page BLANK: the form is a grid
   item in an RTL layout, and flipping the container's direction collapsed it.
   Every test still passed, because they assert markup and this was a paint
   failure. Alignment is untouched here — the labels stay right-aligned with
   the rest of the RTL page; only the character order inside them is fixed. */
.db-form label,
.db-form legend {
    unicode-bidi: plaintext;
}
