/* ============================================================
   Jobmadic — application stylesheet
   The single source of truth for the whole product UI: every
   page links this file and adds no <style> block of its own.
   If a pattern is missing, extend this file rather than
   special-casing a page.
   ============================================================ */

/* ---------- 1. Tokens ---------- */
:root{
  --bg:#f8fafc; --surface:#ffffff; --surface-hover:#f1f5f9; --surface-alt:#f8fafc;
  --surface-dark:#0f172a; --surface-dark-hover:#1e293b;
  --border:#e2e8f0; --border-strong:#cbd5e1;
  --text-primary:#0f172a; --text-secondary:#475569; --text-muted:#94a3b8;
  --text-on-dark:#ffffff; --text-on-dark-muted:#94a3b8; --border-on-dark:#334155; --surface-on-dark-hover:#1e293b;
  --accent:#2563eb; --accent-hover:#1d4ed8; --accent-light:#eff6ff; --accent-border:#bfdbfe;
  --success:#16a34a; --success-bg:#f0fdf4; --success-border:#bbf7d0;
  --warning:#d97706; --warning-bg:#fffbeb; --warning-border:#fde68a;
  --destructive:#dc2626; --destructive-bg:#fef2f2; --destructive-border:#fecaca;
  --info:#2563eb; --info-bg:#eff6ff; --info-border:#bfdbfe;
  --success-text:#14532d; --destructive-text:#7f1d1d; --warning-text:#78350f; --info-text:#1e3a8a;
  --skeleton-shine:#e9eef5; --switch-knob:#ffffff;
  --radius-sm:6px; --radius-md:8px; --radius-lg:12px; --radius-full:999px;
  --shadow-xs:0 1px 2px rgba(15,23,42,.04);
  --shadow-sm:0 4px 16px rgba(15,23,42,.06);
  --shadow-md:0 8px 24px rgba(15,23,42,.10);
  --shadow-lg:0 20px 48px rgba(15,23,42,.18);
  --font:-apple-system,"Segoe UI",Roboto,Helvetica,Arial,sans-serif;
  --mono:ui-monospace,"SFMono-Regular",Menlo,Consolas,monospace;
  --serif:Georgia,"Iowan Old Style","Times New Roman",Times,serif;
  --sidebar-w:260px; --topbar-h:60px;
}

/* ---------- 1b. Dark theme ----------
   Only tokens are redefined, so every component inherits the theme. The
   "dark surface" role inverts to a light surface: .btn-dark, .cta-band and
   the logo mark are the highest-contrast elements on the page, which on a
   dark background means light. The CV document surface (§28) is deliberately
   excluded — it is a printed page, not app chrome, and stays white.

   Three states: an explicit choice sets data-theme on <html>; with no choice
   the OS preference decides. */
@media (prefers-color-scheme:dark){
  :root:not([data-theme="light"]){
    --bg:#0b1120; --surface:#151d2f; --surface-hover:#1e293b; --surface-alt:#0b1120;
    --surface-dark:#e2e8f0; --surface-dark-hover:#cbd5e1;
    --border:#1f2a3d; --border-strong:#334155;
    --text-primary:#e2e8f0; --text-secondary:#94a3b8; --text-muted:#64748b;
    --text-on-dark:#0f172a; --text-on-dark-muted:#475569; --border-on-dark:#cbd5e1; --surface-on-dark-hover:#cbd5e1;
    --accent:#3b82f6; --accent-hover:#60a5fa; --accent-light:#132444; --accent-border:#1e3a5f;
    --success:#4ade80; --success-bg:#0e2019; --success-border:#166534; --success-text:#bbf7d0;
    --warning:#fbbf24; --warning-bg:#241a08; --warning-border:#854d0e; --warning-text:#fde68a;
    --destructive:#f87171; --destructive-bg:#2a1315; --destructive-border:#991b1b; --destructive-text:#fecaca;
    --info:#3b82f6; --info-bg:#132444; --info-border:#1e3a5f; --info-text:#bfdbfe;
    --skeleton-shine:#243049; --switch-knob:#e2e8f0;
    --shadow-xs:0 1px 2px rgba(0,0,0,.35);
    --shadow-sm:0 4px 16px rgba(0,0,0,.40);
    --shadow-md:0 8px 24px rgba(0,0,0,.48);
    --shadow-lg:0 20px 48px rgba(0,0,0,.60);
    color-scheme:dark;
  }
}
:root[data-theme="dark"]{
  --bg:#0b1120; --surface:#151d2f; --surface-hover:#1e293b; --surface-alt:#0b1120;
  --surface-dark:#e2e8f0; --surface-dark-hover:#cbd5e1;
  --border:#1f2a3d; --border-strong:#334155;
  --text-primary:#e2e8f0; --text-secondary:#94a3b8; --text-muted:#64748b;
  --text-on-dark:#0f172a; --text-on-dark-muted:#475569; --border-on-dark:#cbd5e1; --surface-on-dark-hover:#cbd5e1;
  --accent:#3b82f6; --accent-hover:#60a5fa; --accent-light:#132444; --accent-border:#1e3a5f;
  --success:#4ade80; --success-bg:#0e2019; --success-border:#166534; --success-text:#bbf7d0;
  --warning:#fbbf24; --warning-bg:#241a08; --warning-border:#854d0e; --warning-text:#fde68a;
  --destructive:#f87171; --destructive-bg:#2a1315; --destructive-border:#991b1b; --destructive-text:#fecaca;
  --info:#3b82f6; --info-bg:#132444; --info-border:#1e3a5f; --info-text:#bfdbfe;
  --skeleton-shine:#243049; --switch-knob:#e2e8f0;
  --shadow-xs:0 1px 2px rgba(0,0,0,.35);
  --shadow-sm:0 4px 16px rgba(0,0,0,.40);
  --shadow-md:0 8px 24px rgba(0,0,0,.48);
  --shadow-lg:0 20px 48px rgba(0,0,0,.60);
  color-scheme:dark;
}
:root[data-theme="light"]{color-scheme:light}

/* ---------- 2. Base ---------- */
*{box-sizing:border-box;margin:0;padding:0}
html{-webkit-text-size-adjust:100%}
/* The flex column is the sticky footer: layouts/public.ejs is a plain
   nav + main + footer, so on a short page the footer used to float 84px+ above
   the viewport bottom. layouts/app.ejs never had this — `.shell` carries its
   own min-height:100vh — and this is inert there. */
/* ── Self-hosted CV fonts (also measured server-side for page breaks) ── */
/* Inter: latin plus latin-ext, Cyrillic, Greek and Vietnamese subsets (unicode-range), Poppins, and Noto Sans Arabic/Hebrew mapped into both families so the browser draws the same glyphs the renderer measures (src/renderer/font-metrics.js). */
@font-face{font-family:"Inter";font-style:normal;font-weight:300;font-display:swap;src:url("/assets/fonts/Inter/inter-cyrillic-ext-300-normal.woff2") format("woff2");unicode-range:U+0460-052F,U+1C80-1C8A,U+20B4,U+2DE0-2DFF,U+A640-A69F,U+FE2E-FE2F}
@font-face{font-family:"Inter";font-style:normal;font-weight:300;font-display:swap;src:url("/assets/fonts/Inter/inter-cyrillic-300-normal.woff2") format("woff2");unicode-range:U+0301,U+0400-045F,U+0490-0491,U+04B0-04B1,U+2116}
@font-face{font-family:"Inter";font-style:normal;font-weight:300;font-display:swap;src:url("/assets/fonts/Inter/inter-greek-ext-300-normal.woff2") format("woff2");unicode-range:U+1F00-1FFF}
@font-face{font-family:"Inter";font-style:normal;font-weight:300;font-display:swap;src:url("/assets/fonts/Inter/inter-greek-300-normal.woff2") format("woff2");unicode-range:U+0370-0377,U+037A-037F,U+0384-038A,U+038C,U+038E-03A1,U+03A3-03FF}
@font-face{font-family:"Inter";font-style:normal;font-weight:300;font-display:swap;src:url("/assets/fonts/Inter/inter-vietnamese-300-normal.woff2") format("woff2");unicode-range:U+0102-0103,U+0110-0111,U+0128-0129,U+0168-0169,U+01A0-01A1,U+01AF-01B0,U+0300-0301,U+0303-0304,U+0308-0309,U+0323,U+0329,U+1EA0-1EF9,U+20AB}
@font-face{font-family:"Inter";font-style:normal;font-weight:300;font-display:swap;src:url("/assets/fonts/Inter/inter-latin-ext-300-normal.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}
@font-face{font-family:"Inter";font-style:normal;font-weight:300;font-display:swap;src:url("/assets/fonts/Inter/inter-latin-300-normal.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}
@font-face{font-family:"Inter";font-style:normal;font-weight:400;font-display:swap;src:url("/assets/fonts/Inter/inter-cyrillic-ext-400-normal.woff2") format("woff2");unicode-range:U+0460-052F,U+1C80-1C8A,U+20B4,U+2DE0-2DFF,U+A640-A69F,U+FE2E-FE2F}
@font-face{font-family:"Inter";font-style:normal;font-weight:400;font-display:swap;src:url("/assets/fonts/Inter/inter-cyrillic-400-normal.woff2") format("woff2");unicode-range:U+0301,U+0400-045F,U+0490-0491,U+04B0-04B1,U+2116}
@font-face{font-family:"Inter";font-style:normal;font-weight:400;font-display:swap;src:url("/assets/fonts/Inter/inter-greek-ext-400-normal.woff2") format("woff2");unicode-range:U+1F00-1FFF}
@font-face{font-family:"Inter";font-style:normal;font-weight:400;font-display:swap;src:url("/assets/fonts/Inter/inter-greek-400-normal.woff2") format("woff2");unicode-range:U+0370-0377,U+037A-037F,U+0384-038A,U+038C,U+038E-03A1,U+03A3-03FF}
@font-face{font-family:"Inter";font-style:normal;font-weight:400;font-display:swap;src:url("/assets/fonts/Inter/inter-vietnamese-400-normal.woff2") format("woff2");unicode-range:U+0102-0103,U+0110-0111,U+0128-0129,U+0168-0169,U+01A0-01A1,U+01AF-01B0,U+0300-0301,U+0303-0304,U+0308-0309,U+0323,U+0329,U+1EA0-1EF9,U+20AB}
@font-face{font-family:"Inter";font-style:normal;font-weight:400;font-display:swap;src:url("/assets/fonts/Inter/inter-latin-ext-400-normal.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}
@font-face{font-family:"Inter";font-style:normal;font-weight:400;font-display:swap;src:url("/assets/fonts/Inter/inter-latin-400-normal.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}
@font-face{font-family:"Inter";font-style:normal;font-weight:500;font-display:swap;src:url("/assets/fonts/Inter/inter-cyrillic-ext-500-normal.woff2") format("woff2");unicode-range:U+0460-052F,U+1C80-1C8A,U+20B4,U+2DE0-2DFF,U+A640-A69F,U+FE2E-FE2F}
@font-face{font-family:"Inter";font-style:normal;font-weight:500;font-display:swap;src:url("/assets/fonts/Inter/inter-cyrillic-500-normal.woff2") format("woff2");unicode-range:U+0301,U+0400-045F,U+0490-0491,U+04B0-04B1,U+2116}
@font-face{font-family:"Inter";font-style:normal;font-weight:500;font-display:swap;src:url("/assets/fonts/Inter/inter-greek-ext-500-normal.woff2") format("woff2");unicode-range:U+1F00-1FFF}
@font-face{font-family:"Inter";font-style:normal;font-weight:500;font-display:swap;src:url("/assets/fonts/Inter/inter-greek-500-normal.woff2") format("woff2");unicode-range:U+0370-0377,U+037A-037F,U+0384-038A,U+038C,U+038E-03A1,U+03A3-03FF}
@font-face{font-family:"Inter";font-style:normal;font-weight:500;font-display:swap;src:url("/assets/fonts/Inter/inter-vietnamese-500-normal.woff2") format("woff2");unicode-range:U+0102-0103,U+0110-0111,U+0128-0129,U+0168-0169,U+01A0-01A1,U+01AF-01B0,U+0300-0301,U+0303-0304,U+0308-0309,U+0323,U+0329,U+1EA0-1EF9,U+20AB}
@font-face{font-family:"Inter";font-style:normal;font-weight:500;font-display:swap;src:url("/assets/fonts/Inter/inter-latin-ext-500-normal.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}
@font-face{font-family:"Inter";font-style:normal;font-weight:500;font-display:swap;src:url("/assets/fonts/Inter/inter-latin-500-normal.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}
@font-face{font-family:"Inter";font-style:normal;font-weight:600;font-display:swap;src:url("/assets/fonts/Inter/inter-cyrillic-ext-600-normal.woff2") format("woff2");unicode-range:U+0460-052F,U+1C80-1C8A,U+20B4,U+2DE0-2DFF,U+A640-A69F,U+FE2E-FE2F}
@font-face{font-family:"Inter";font-style:normal;font-weight:600;font-display:swap;src:url("/assets/fonts/Inter/inter-cyrillic-600-normal.woff2") format("woff2");unicode-range:U+0301,U+0400-045F,U+0490-0491,U+04B0-04B1,U+2116}
@font-face{font-family:"Inter";font-style:normal;font-weight:600;font-display:swap;src:url("/assets/fonts/Inter/inter-greek-ext-600-normal.woff2") format("woff2");unicode-range:U+1F00-1FFF}
@font-face{font-family:"Inter";font-style:normal;font-weight:600;font-display:swap;src:url("/assets/fonts/Inter/inter-greek-600-normal.woff2") format("woff2");unicode-range:U+0370-0377,U+037A-037F,U+0384-038A,U+038C,U+038E-03A1,U+03A3-03FF}
@font-face{font-family:"Inter";font-style:normal;font-weight:600;font-display:swap;src:url("/assets/fonts/Inter/inter-vietnamese-600-normal.woff2") format("woff2");unicode-range:U+0102-0103,U+0110-0111,U+0128-0129,U+0168-0169,U+01A0-01A1,U+01AF-01B0,U+0300-0301,U+0303-0304,U+0308-0309,U+0323,U+0329,U+1EA0-1EF9,U+20AB}
@font-face{font-family:"Inter";font-style:normal;font-weight:600;font-display:swap;src:url("/assets/fonts/Inter/inter-latin-ext-600-normal.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}
@font-face{font-family:"Inter";font-style:normal;font-weight:600;font-display:swap;src:url("/assets/fonts/Inter/inter-latin-600-normal.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}
@font-face{font-family:"Inter";font-style:normal;font-weight:700;font-display:swap;src:url("/assets/fonts/Inter/inter-cyrillic-ext-700-normal.woff2") format("woff2");unicode-range:U+0460-052F,U+1C80-1C8A,U+20B4,U+2DE0-2DFF,U+A640-A69F,U+FE2E-FE2F}
@font-face{font-family:"Inter";font-style:normal;font-weight:700;font-display:swap;src:url("/assets/fonts/Inter/inter-cyrillic-700-normal.woff2") format("woff2");unicode-range:U+0301,U+0400-045F,U+0490-0491,U+04B0-04B1,U+2116}
@font-face{font-family:"Inter";font-style:normal;font-weight:700;font-display:swap;src:url("/assets/fonts/Inter/inter-greek-ext-700-normal.woff2") format("woff2");unicode-range:U+1F00-1FFF}
@font-face{font-family:"Inter";font-style:normal;font-weight:700;font-display:swap;src:url("/assets/fonts/Inter/inter-greek-700-normal.woff2") format("woff2");unicode-range:U+0370-0377,U+037A-037F,U+0384-038A,U+038C,U+038E-03A1,U+03A3-03FF}
@font-face{font-family:"Inter";font-style:normal;font-weight:700;font-display:swap;src:url("/assets/fonts/Inter/inter-vietnamese-700-normal.woff2") format("woff2");unicode-range:U+0102-0103,U+0110-0111,U+0128-0129,U+0168-0169,U+01A0-01A1,U+01AF-01B0,U+0300-0301,U+0303-0304,U+0308-0309,U+0323,U+0329,U+1EA0-1EF9,U+20AB}
@font-face{font-family:"Inter";font-style:normal;font-weight:700;font-display:swap;src:url("/assets/fonts/Inter/inter-latin-ext-700-normal.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}
@font-face{font-family:"Inter";font-style:normal;font-weight:700;font-display:swap;src:url("/assets/fonts/Inter/inter-latin-700-normal.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}
@font-face{font-family:"Poppins";font-style:normal;font-weight:400;font-display:swap;src:url("/assets/fonts/Poppins/Poppins-Regular.ttf") format("truetype")}
@font-face{font-family:"Poppins";font-style:normal;font-weight:600;font-display:swap;src:url("/assets/fonts/Poppins/Poppins-SemiBold.ttf") format("truetype")}
@font-face{font-family:"Poppins";font-style:normal;font-weight:700;font-display:swap;src:url("/assets/fonts/Poppins/Poppins-Bold.ttf") format("truetype")}
@font-face{font-family:"Inter";font-style:normal;font-weight:400;font-display:swap;src:url("/assets/fonts/Noto/NotoSansArabic-Regular.ttf") format("truetype");unicode-range:U+0600-06FF,U+0750-077F,U+0870-08FF,U+FB50-FDFF,U+FE70-FEFF}
@font-face{font-family:"Inter";font-style:normal;font-weight:400;font-display:swap;src:url("/assets/fonts/Noto/NotoSansHebrew-Regular.ttf") format("truetype");unicode-range:U+0590-05FF,U+FB1D-FB4F}
@font-face{font-family:"Inter";font-style:normal;font-weight:700;font-display:swap;src:url("/assets/fonts/Noto/NotoSansArabic-Bold.ttf") format("truetype");unicode-range:U+0600-06FF,U+0750-077F,U+0870-08FF,U+FB50-FDFF,U+FE70-FEFF}
@font-face{font-family:"Inter";font-style:normal;font-weight:700;font-display:swap;src:url("/assets/fonts/Noto/NotoSansHebrew-Bold.ttf") format("truetype");unicode-range:U+0590-05FF,U+FB1D-FB4F}
@font-face{font-family:"Poppins";font-style:normal;font-weight:400;font-display:swap;src:url("/assets/fonts/Noto/NotoSansArabic-Regular.ttf") format("truetype");unicode-range:U+0600-06FF,U+0750-077F,U+0870-08FF,U+FB50-FDFF,U+FE70-FEFF}
@font-face{font-family:"Poppins";font-style:normal;font-weight:400;font-display:swap;src:url("/assets/fonts/Noto/NotoSansHebrew-Regular.ttf") format("truetype");unicode-range:U+0590-05FF,U+FB1D-FB4F}
@font-face{font-family:"Poppins";font-style:normal;font-weight:700;font-display:swap;src:url("/assets/fonts/Noto/NotoSansArabic-Bold.ttf") format("truetype");unicode-range:U+0600-06FF,U+0750-077F,U+0870-08FF,U+FB50-FDFF,U+FE70-FEFF}
@font-face{font-family:"Poppins";font-style:normal;font-weight:700;font-display:swap;src:url("/assets/fonts/Noto/NotoSansHebrew-Bold.ttf") format("truetype");unicode-range:U+0590-05FF,U+FB1D-FB4F}
@font-face{font-family:"Poppins";font-style:normal;font-weight:400;font-display:swap;src:url("/assets/fonts/Inter/inter-cyrillic-ext-400-normal.woff2") format("woff2");unicode-range:U+0460-052F,U+1C80-1C8A,U+20B4,U+2DE0-2DFF,U+A640-A69F,U+FE2E-FE2F}
@font-face{font-family:"Poppins";font-style:normal;font-weight:400;font-display:swap;src:url("/assets/fonts/Inter/inter-cyrillic-400-normal.woff2") format("woff2");unicode-range:U+0301,U+0400-045F,U+0490-0491,U+04B0-04B1,U+2116}
@font-face{font-family:"Poppins";font-style:normal;font-weight:400;font-display:swap;src:url("/assets/fonts/Inter/inter-greek-ext-400-normal.woff2") format("woff2");unicode-range:U+1F00-1FFF}
@font-face{font-family:"Poppins";font-style:normal;font-weight:400;font-display:swap;src:url("/assets/fonts/Inter/inter-greek-400-normal.woff2") format("woff2");unicode-range:U+0370-0377,U+037A-037F,U+0384-038A,U+038C,U+038E-03A1,U+03A3-03FF}
@font-face{font-family:"Poppins";font-style:normal;font-weight:600;font-display:swap;src:url("/assets/fonts/Inter/inter-cyrillic-ext-600-normal.woff2") format("woff2");unicode-range:U+0460-052F,U+1C80-1C8A,U+20B4,U+2DE0-2DFF,U+A640-A69F,U+FE2E-FE2F}
@font-face{font-family:"Poppins";font-style:normal;font-weight:600;font-display:swap;src:url("/assets/fonts/Inter/inter-cyrillic-600-normal.woff2") format("woff2");unicode-range:U+0301,U+0400-045F,U+0490-0491,U+04B0-04B1,U+2116}
@font-face{font-family:"Poppins";font-style:normal;font-weight:600;font-display:swap;src:url("/assets/fonts/Inter/inter-greek-ext-600-normal.woff2") format("woff2");unicode-range:U+1F00-1FFF}
@font-face{font-family:"Poppins";font-style:normal;font-weight:600;font-display:swap;src:url("/assets/fonts/Inter/inter-greek-600-normal.woff2") format("woff2");unicode-range:U+0370-0377,U+037A-037F,U+0384-038A,U+038C,U+038E-03A1,U+03A3-03FF}
@font-face{font-family:"Poppins";font-style:normal;font-weight:700;font-display:swap;src:url("/assets/fonts/Inter/inter-cyrillic-ext-700-normal.woff2") format("woff2");unicode-range:U+0460-052F,U+1C80-1C8A,U+20B4,U+2DE0-2DFF,U+A640-A69F,U+FE2E-FE2F}
@font-face{font-family:"Poppins";font-style:normal;font-weight:700;font-display:swap;src:url("/assets/fonts/Inter/inter-cyrillic-700-normal.woff2") format("woff2");unicode-range:U+0301,U+0400-045F,U+0490-0491,U+04B0-04B1,U+2116}
@font-face{font-family:"Poppins";font-style:normal;font-weight:700;font-display:swap;src:url("/assets/fonts/Inter/inter-greek-ext-700-normal.woff2") format("woff2");unicode-range:U+1F00-1FFF}
@font-face{font-family:"Poppins";font-style:normal;font-weight:700;font-display:swap;src:url("/assets/fonts/Inter/inter-greek-700-normal.woff2") format("woff2");unicode-range:U+0370-0377,U+037A-037F,U+0384-038A,U+038C,U+038E-03A1,U+03A3-03FF}
/* Faces the CV engine measures with (src/renderer/font-metrics.js): Georgia/Times stacks are measured
   with Gelasio (metric-compatible), RTL text with Noto. The editor's live re-wrap (cv-editor/text-wrap.js)
   measures in these, so its line breaks match the server's. */
@font-face{font-family:"Gelasio";font-style:normal;font-weight:400;font-display:swap;src:url("/assets/fonts/Gelasio/Gelasio-Regular.ttf") format("truetype")}
@font-face{font-family:"Gelasio";font-style:normal;font-weight:700;font-display:swap;src:url("/assets/fonts/Gelasio/Gelasio-Bold.ttf") format("truetype")}
@font-face{font-family:"Gelasio";font-style:normal;font-weight:400;font-display:swap;src:url("/assets/fonts/Noto/NotoSansArabic-Regular.ttf") format("truetype");unicode-range:U+0600-06FF,U+0750-077F,U+0870-08FF,U+FB50-FDFF,U+FE70-FEFF}
@font-face{font-family:"Gelasio";font-style:normal;font-weight:700;font-display:swap;src:url("/assets/fonts/Noto/NotoSansArabic-Bold.ttf") format("truetype");unicode-range:U+0600-06FF,U+0750-077F,U+0870-08FF,U+FB50-FDFF,U+FE70-FEFF}
@font-face{font-family:"Gelasio";font-style:normal;font-weight:400;font-display:swap;src:url("/assets/fonts/Noto/NotoSansHebrew-Regular.ttf") format("truetype");unicode-range:U+0590-05FF,U+FB1D-FB4F}
@font-face{font-family:"Gelasio";font-style:normal;font-weight:700;font-display:swap;src:url("/assets/fonts/Noto/NotoSansHebrew-Bold.ttf") format("truetype");unicode-range:U+0590-05FF,U+FB1D-FB4F}
@font-face{font-family:"Noto Sans Arabic";font-style:normal;font-weight:400;font-display:swap;src:url("/assets/fonts/Noto/NotoSansArabic-Regular.ttf") format("truetype")}
@font-face{font-family:"Noto Sans Arabic";font-style:normal;font-weight:700;font-display:swap;src:url("/assets/fonts/Noto/NotoSansArabic-Bold.ttf") format("truetype")}
@font-face{font-family:"Noto Sans Hebrew";font-style:normal;font-weight:400;font-display:swap;src:url("/assets/fonts/Noto/NotoSansHebrew-Regular.ttf") format("truetype")}
@font-face{font-family:"Noto Sans Hebrew";font-style:normal;font-weight:700;font-display:swap;src:url("/assets/fonts/Noto/NotoSansHebrew-Bold.ttf") format("truetype")}
body{font-family:var(--font);color:var(--text-primary);background:var(--bg);line-height:1.5;font-size:14px;-webkit-font-smoothing:antialiased;
  min-height:100vh;display:flex;flex-direction:column}
/* `<main>` exists in exactly one template (layouts/public.ejs), so this is
   scoped to the layout that needs it without naming the layout. */
main{flex:1}
/* Reserve the admin bar's footprint (58px) so it stops sitting on top of the
   footer at the bottom of a scrolled page. `:has()` is verified in Chromium,
   WebKit and Firefox; for everyone but admins the selector never matches. */
body:has(.admin-bar){padding-bottom:72px}
a{color:inherit;text-decoration:none}
button,input,select,textarea{font-family:inherit;font-size:inherit}
button{cursor:pointer}
img,svg{display:block;max-width:100%}
/* The `hidden` attribute is the app's one hide mechanism (menus, dialogs, tab
   panels, filtered rows), so it has to beat any component that sets `display`
   — `.card{display:flex}` would otherwise keep a hidden card on screen. This
   is the only !important in the file, and it earns it. */
[hidden]{display:none!important}
/* Safety net: an inline icon that declares no size must never expand to fill its
   parent. :where() holds this at zero specificity so every component rule below
   (.icon, .btn svg, .stat-top svg, .empty-state svg, …) still wins. SVGs that are
   meant to be large — charts and the CV document — set width/height attributes
   and are excluded by the :not() guards. */
:where(svg:not([width]):not([height])){width:18px;height:18px;flex-shrink:0}
:focus-visible{outline:2px solid var(--accent);outline-offset:2px}
.icon{width:18px;height:18px;flex-shrink:0}
.icon-sm{width:15px;height:15px;flex-shrink:0}
.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}

/* ---------- 3. Typography helpers ---------- */
h1{font-size:24px;font-weight:700;letter-spacing:-.01em}
h2{font-size:18px;font-weight:600}
h3{font-size:15px;font-weight:600}
.muted{color:var(--text-secondary)}
.text-xs{font-size:12px}
.text-sm{font-size:13px}
.mono{font-family:var(--mono)}
/* Code is code wherever it appears. `.prose code` used to be the only rule, so
   inline <code> in admin documentation rendered as plain body text. */
code{font-family:var(--mono);font-size:.9em;background:var(--surface-hover);
  border:1px solid var(--border);border-radius:var(--radius-sm);padding:1px 5px}
pre{background:var(--surface-dark);color:var(--text-on-dark);border-radius:var(--radius-md);
  padding:14px 16px;overflow-x:auto;font-size:12.5px;line-height:1.6;max-width:100%}
pre code{background:transparent;border:0;color:inherit;padding:0;font-size:inherit}
/* A quoted block that lives on the page surface instead of the dark code
   surface: prompt bodies, extracted document text, AI output. It is prose, not
   code, so it WRAPS rather than scrolling sideways. Three call sites used to
   hand-roll this as an inline `background:var(--surface-hover)` on a bare
   <pre>, which overrode the background but not the light-on-dark colour above —
   near-white text on a near-white panel. */
pre.quote{background:var(--surface-hover);color:var(--text-primary);
  border:1px solid var(--border);white-space:pre-wrap;overflow-wrap:anywhere;
  overflow-x:hidden;overflow-y:auto;padding:12px 14px}
.truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
/* .truncate does nothing on an inline span (overflow needs a block box); this
   is the one-line-per-row version for stacked text inside a flex item. */
.truncate-line{display:block;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}

/* ---------- 4. App shell ---------- */
.shell{display:flex;min-height:100vh}
.sidebar{width:var(--sidebar-w);flex-shrink:0;background:var(--surface);border-right:1px solid var(--border);display:flex;flex-direction:column;position:fixed;inset:0 auto 0 0;z-index:50;transition:transform .2s ease}
.main{flex:1;margin-left:var(--sidebar-w);min-width:0;display:flex;flex-direction:column}
.topbar{height:var(--topbar-h);background:var(--surface);border-bottom:1px solid var(--border);display:flex;align-items:center;gap:12px;padding:0 24px;position:sticky;top:0;z-index:30}
.content{padding:28px 24px 72px;flex:1}
.content-narrow{max-width:820px}
.backdrop{display:none;position:fixed;inset:0;background:rgba(15,23,42,.4);z-index:40}
.backdrop.show{display:block}

/* ---------- 5. Sidebar ---------- */
.sb-logo{height:var(--topbar-h);display:flex;align-items:center;gap:9px;padding:0 18px;border-bottom:1px solid var(--border);font-size:15.5px;font-weight:700;flex-shrink:0}
.sb-logo .mark{width:28px;height:28px;border-radius:7px;background:var(--accent);display:flex;align-items:center;justify-content:center;flex-shrink:0}
.sb-logo .mark .brand-logo{height:19px;width:auto}
/* The pin in white on a brand-blue square; the briefcase cut-out shows the blue. */
.brand-logo-on-accent .brand-logo-pin{fill:#fff}
.sb-logo .eyebrow{font-size:10px;text-transform:uppercase;letter-spacing:.06em;color:var(--accent);font-weight:700;line-height:1}
.sb-search-wrap{padding:14px 14px 10px}
.sb-search{display:flex;align-items:center;gap:8px;height:38px;padding:0 10px;border:1px solid var(--border-strong);border-radius:var(--radius-md);background:var(--surface)}
.sb-search svg{width:16px;height:16px;color:var(--text-muted);flex-shrink:0}
.kbd{font-size:10.5px;font-weight:600;color:var(--text-muted);border:1px solid var(--border);border-radius:4px;padding:1px 5px;background:var(--surface-hover);flex-shrink:0}

.mode-switch{display:flex;gap:2px;margin:0 14px 10px;background:var(--surface-hover);border:1px solid var(--border);border-radius:var(--radius-md);padding:3px}
.mode-btn{flex:1;height:30px;border:0;background:transparent;color:var(--text-secondary);font-size:12.5px;font-weight:600;border-radius:6px;transition:background .15s ease,color .15s ease}
.mode-btn.is-active{background:var(--surface);color:var(--text-primary);box-shadow:var(--shadow-xs)}
.mode-btn:not(.is-active):hover{color:var(--text-primary)}

.sb-nav{flex:1;overflow-y:auto;padding:2px 10px 14px}
/* One panel per console; the inactive ones carry the `hidden` attribute. */
.mode-panel{display:block}
.sb-label{font-size:11px;font-weight:600;text-transform:uppercase;letter-spacing:.05em;color:var(--text-muted);padding:14px 10px 6px}
.sb-item{display:flex;align-items:center;gap:10px;height:36px;padding:0 10px;border-radius:var(--radius-md);font-size:13.5px;font-weight:500;color:var(--text-secondary)}
.sb-item svg{width:17px;height:17px;flex-shrink:0;color:var(--text-muted)}
.sb-item:hover{background:var(--surface-hover);color:var(--text-primary)}
.sb-item.active{background:var(--accent-light);color:var(--accent)}
.sb-item.active svg{color:var(--accent)}
.sb-parent{display:flex;align-items:center;gap:10px;height:36px;padding:0 10px;border-radius:var(--radius-md);font-size:13.5px;font-weight:500;color:var(--text-secondary);width:100%;border:0;background:transparent;text-align:left}
.sb-parent svg.leading{width:17px;height:17px;flex-shrink:0;color:var(--text-muted)}
.sb-parent:hover{background:var(--surface-hover);color:var(--text-primary)}
.sb-parent .chev{width:14px;height:14px;margin-left:auto;color:var(--text-muted);transition:transform .15s ease}
.sb-parent[aria-expanded="true"] .chev{transform:rotate(90deg)}
.sb-count{font-size:11px;color:var(--text-muted);background:var(--surface-hover);border:1px solid var(--border);border-radius:var(--radius-full);padding:1px 7px;margin-left:auto}
.sb-parent[aria-expanded="true"] .sb-count{display:none}
.sb-children{display:none;padding-left:27px;border-left:1px solid var(--border);margin-left:20px}
.sb-children.open{display:block}
.sb-children .sb-item{height:32px;font-size:13px}
.sb-item[data-hidden],.sb-parent[data-hidden],.sb-children[data-hidden],.sb-label[data-hidden]{display:none}
/* Plan-withheld nav entry: still reachable, so the page can explain the upgrade. */
.sb-lock{margin-left:auto;font-size:10px;padding:1px 6px}

.sb-foot{padding:14px;border-top:1px solid var(--border);flex-shrink:0}
.dark-card{background:var(--surface-dark);color:var(--text-on-dark);border-radius:var(--radius-md);padding:14px}
.dark-card .lbl{font-size:12.5px;font-weight:600}
.dark-card .sub{font-size:12px;color:var(--text-on-dark-muted);margin-top:2px}
.dark-card code{font-family:var(--mono);font-size:13px;letter-spacing:.05em}
.dark-card .row{display:flex;align-items:center;justify-content:space-between;gap:8px;margin-top:6px}
.dark-card a.cta{display:block;margin-top:10px;font-size:12.5px;font-weight:600;color:var(--text-on-dark);border:1px solid var(--border-on-dark);border-radius:var(--radius-sm);text-align:center;padding:7px 0}
.dark-card a.cta:hover{background:var(--surface-on-dark-hover)}
.copy-btn{background:var(--surface-on-dark-hover);border:0;color:var(--text-on-dark-muted);width:26px;height:26px;border-radius:6px;display:flex;align-items:center;justify-content:center;flex-shrink:0}
.copy-btn:hover{background:var(--border-on-dark)}
.copy-btn svg{width:13px;height:13px}
/* A copy button pinned inside a code sample, so long snippets (API examples)
   can be copied without selecting across the scrollable block. */
.code-block{position:relative}
.code-block pre{padding-right:48px;margin:0}
.code-block .copy-btn{position:absolute;top:10px;right:10px}
.plan-card{background:var(--accent-light);border:1px solid var(--accent-border);border-radius:var(--radius-md);padding:14px}
.plan-card .name{font-size:12.5px;font-weight:700;color:var(--accent)}
.plan-card .sub{font-size:12px;color:var(--text-secondary);margin-top:2px}

/* ---------- 6. Topbar ---------- */
.hamburger{display:none;width:36px;height:36px;align-items:center;justify-content:center;border:1px solid var(--border);border-radius:var(--radius-md);background:var(--surface);flex-shrink:0}
.topbar-search{flex:1;max-width:420px;display:flex;align-items:center;gap:8px;height:36px;padding:0 10px;border:1px solid var(--border-strong);border-radius:var(--radius-md)}
.topbar-search svg{width:15px;height:15px;color:var(--text-muted)}
.topbar-right{display:flex;align-items:center;gap:6px;margin-left:auto}
.icon-btn{width:36px;height:36px;display:flex;align-items:center;justify-content:center;border:0;background:transparent;border-radius:var(--radius-md);color:var(--text-secondary);position:relative}
/* Theme toggle: show the icon for the theme you would switch *to*. */
.theme-toggle .theme-icon-dark{display:none}
.theme-toggle .theme-icon-light{display:block}
@media (prefers-color-scheme:dark){
  :root:not([data-theme="light"]) .theme-toggle .theme-icon-light{display:none}
  :root:not([data-theme="light"]) .theme-toggle .theme-icon-dark{display:block}
}
:root[data-theme="dark"] .theme-toggle .theme-icon-light{display:none}
:root[data-theme="dark"] .theme-toggle .theme-icon-dark{display:block}
:root[data-theme="light"] .theme-toggle .theme-icon-light{display:block}
:root[data-theme="light"] .theme-toggle .theme-icon-dark{display:none}
.icon-btn:hover{background:var(--surface-hover)}
.dot{position:absolute;top:7px;right:7px;width:7px;height:7px;border-radius:50%;background:var(--destructive);border:2px solid var(--surface)}
/* Service status indicator — inline, unlike the .dot notification bubble. */
.dot-status{width:8px;height:8px;border-radius:50%;background:var(--destructive);flex-shrink:0}
.dot-status.is-ok{background:var(--success)}
.dot-status.is-off{background:var(--text-muted)}
.user-chip{display:flex;align-items:center;gap:8px;height:36px;padding:0 8px 0 4px;border-radius:var(--radius-md);border:0;background:transparent}
.user-chip:hover{background:var(--surface-hover)}
.user-chip .name{font-size:13.5px;font-weight:600}
.avatar{width:28px;height:28px;border-radius:var(--radius-full);background:var(--accent-light);color:var(--accent);display:flex;align-items:center;justify-content:center;font-weight:700;font-size:12px;flex-shrink:0}
.avatar-lg{width:56px;height:56px;font-size:20px}
.avatar-sm{width:24px;height:24px;font-size:10px}

/* ---------- 7. Buttons ---------- */
.btn{display:inline-flex;align-items:center;justify-content:center;gap:8px;height:40px;padding:0 16px;border-radius:var(--radius-md);font-size:14px;font-weight:600;border:1px solid transparent;transition:background .15s ease,border-color .15s ease;white-space:nowrap}
.btn svg{width:16px;height:16px;flex-shrink:0}
.btn-dark{background:var(--surface-dark);color:var(--text-on-dark)}
.btn-dark:hover{background:var(--surface-dark-hover)}
.btn-accent{background:var(--accent);color:#fff}
.btn-accent:hover{background:var(--accent-hover)}
.btn-outline{background:var(--surface);color:var(--text-primary);border-color:var(--border-strong)}
.btn-outline:hover{background:var(--surface-hover)}
.btn-ghost{background:transparent;color:var(--text-secondary)}
.btn-ghost:hover{background:var(--surface-hover);color:var(--text-primary)}
.btn-destructive{background:var(--destructive);color:#fff}
.btn-destructive:hover{filter:brightness(.93)}
.btn-danger-outline{background:var(--surface);color:var(--destructive);border-color:var(--destructive-border)}
.btn-danger-outline:hover{background:var(--destructive-bg)}
.btn-sm{height:34px;padding:0 12px;font-size:13px}
.btn-xs{height:28px;padding:0 9px;font-size:12px;border-radius:var(--radius-sm)}
.btn-lg{height:46px;padding:0 22px;font-size:15px}
.btn-block{width:100%}
.btn:disabled,.btn[aria-disabled="true"]{opacity:.5;pointer-events:none}
.btn-group{display:flex;gap:8px;flex-wrap:wrap}

/* ---------- 8. Forms ---------- */
.field{margin-bottom:16px}
.field-row{display:flex;align-items:center;justify-content:space-between;gap:8px;margin-bottom:6px}
label,.label{display:block;font-size:13px;font-weight:600;margin-bottom:6px}
/* Text-entry fields are styled by EXCLUSION, not by listing `input[type=…]`.
   An allowlist silently misses two things: `<input>` with no type attribute —
   valid HTML that defaults to text — and any type nobody thought to add
   (time, datetime-local, week). Both fall through to the 20px native box with
   no padding, which is how text ends up clipped.
   The exclusions sit in :where() so the whole selector keeps the specificity of
   a bare `input` (0,0,1). That is deliberate: every component override below
   (.check input, .switch input, .search-field input …) is (0,1,1) and must win
   on specificity rather than on source order. */
.input,
input:not(:where([type=checkbox],[type=radio],[type=range],[type=file],[type=submit],[type=button],[type=reset],[type=image],[type=color],[type=hidden])),
select,textarea{
  width:100%;height:40px;padding:9px 12px;border:1px solid var(--border-strong);border-radius:var(--radius-md);
  font-size:13.5px;line-height:1.4;color:var(--text-primary);background:var(--surface)}
textarea{height:auto;min-height:96px;padding:10px 12px;resize:vertical;line-height:1.55}

/* File inputs keep their native control but sit on the same rail as every other
   field, with the picker restyled as a small secondary button. */
input:where([type=file]){width:100%;min-height:40px;padding:8px 12px;border:1px solid var(--border-strong);
  border-radius:var(--radius-md);font-size:13px;color:var(--text-secondary);background:var(--surface);cursor:pointer}
input:where([type=file])::file-selector-button{margin-right:10px;padding:5px 11px;border:1px solid var(--border-strong);
  border-radius:var(--radius-sm);background:var(--surface-hover);color:var(--text-primary);
  font-family:inherit;font-size:12.5px;font-weight:600;cursor:pointer}
input:where([type=file])::file-selector-button:hover{background:var(--border)}
input:where([type=file]):focus-visible{outline:0;border-color:var(--accent);box-shadow:0 0 0 3px var(--accent-light)}
.input:focus,input:focus,select:focus,textarea:focus{outline:0;border-color:var(--accent);box-shadow:0 0 0 3px var(--accent-light)}
input:disabled,select:disabled,textarea:disabled{background:var(--surface-hover);color:var(--text-muted);cursor:not-allowed}
select{appearance:none;background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%2394a3b8' stroke-width='2'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E");background-repeat:no-repeat;background-position:right 10px center;background-size:16px;padding-right:34px}
.input-icon{position:relative}
.input-icon>svg{position:absolute;left:12px;top:50%;transform:translateY(-50%);width:16px;height:16px;color:var(--text-muted);pointer-events:none}
.input-icon input{padding-left:36px}
.help{font-size:12px;color:var(--text-muted);margin-top:5px}
.error-text{font-size:12px;color:var(--destructive);margin-top:5px}
/* Paired with .error-text for JS-set status lines. Without it, a connection
   test's success and failure read identically. */
.ok-text{font-size:12px;color:var(--success);margin-top:5px}
.check{display:flex;align-items:flex-start;gap:9px;font-size:13.5px;font-weight:400;margin-bottom:0}
.check input{width:16px;height:16px;height:16px;margin-top:2px;flex-shrink:0;accent-color:var(--accent)}
/* Pairs up when there is room and drops to one column when there is not —
   keyed to the CONTAINER, not the viewport. A hard 2-column grid squeezed a
   wide-screen sidebar panel down to ~125px fields and clipped their contents. */
.form-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(210px,1fr));gap:16px}

/* Reorderable list entries (profile experience/education). The order of these
   cards is the order they are saved in and the order a generated CV prints, so
   they need a way to be moved that is not only drag. */
.entry-actions{display:flex;align-items:center;gap:2px;margin-left:auto}
.entry-move{width:28px;height:28px;display:flex;align-items:center;justify-content:center;
  border:0;background:transparent;border-radius:var(--radius-sm);color:var(--text-muted);cursor:pointer}
.entry-move:hover:not(:disabled){background:var(--surface-hover);color:var(--text-primary)}
.entry-move:disabled{opacity:.3;cursor:default}
.entry-move svg{width:15px;height:15px}
/* `touch-action:none` so a drag from the handle is not stolen by the page's
   own scrolling. The drag is pointer-event based, so it works with a finger
   too and the handle stays visible on touch. */
.entry-grip{display:flex;align-items:center;color:var(--text-muted);cursor:grab;padding:2px;
  border:0;background:transparent;border-radius:var(--radius-sm);touch-action:none}
.entry-grip:active{cursor:grabbing}
.entry-grip svg{width:15px;height:15px}
[data-entry].is-dragging{opacity:.5;outline:2px solid var(--accent);outline-offset:-2px}
.switch{position:relative;width:38px;height:22px;flex-shrink:0;display:inline-block}
.switch input{opacity:0;width:0;height:0;position:absolute}
.switch .slider{position:absolute;inset:0;background:var(--border-strong);border-radius:var(--radius-full);transition:.15s;cursor:pointer}
.switch .slider::before{content:"";position:absolute;width:16px;height:16px;left:3px;top:3px;background:var(--switch-knob);border-radius:50%;transition:.15s}
.switch input:checked + .slider{background:var(--accent)}
.switch input:checked + .slider::before{transform:translateX(16px)}
.switch input:focus-visible + .slider{box-shadow:0 0 0 3px var(--accent-light)}

/* Range input. The global input rule targets specific `type=` values, so a range
   is otherwise left as the unstyled platform widget. */
input[type=range]{-webkit-appearance:none;appearance:none;width:100%;height:6px;padding:0;border:0;
  border-radius:var(--radius-full);background:var(--surface-hover);cursor:pointer}
input[type=range]::-webkit-slider-thumb{-webkit-appearance:none;appearance:none;width:18px;height:18px;
  border-radius:50%;background:var(--accent);border:2px solid var(--surface);box-shadow:var(--shadow-xs);cursor:pointer}
input[type=range]::-moz-range-thumb{width:14px;height:14px;border-radius:50%;background:var(--accent);
  border:2px solid var(--surface);cursor:pointer}
input[type=range]:focus-visible{outline:0;box-shadow:0 0 0 3px var(--accent-light)}
/* position:relative gives the visually-hidden radio a containing block —
   without it the absolutely positioned input escapes to whatever ancestor
   happens to be positioned. */
.radio-card{display:block;cursor:pointer;position:relative}
.radio-card input{position:absolute;opacity:0}
/* `.body` is a <span>. It carries a border, padding and a background, so it
   MUST be a block — as an inline box the border painted as wrapped fragments
   and the text spilled outside the card. Two call sites used to patch this
   with an inline `display:block`, which is why only the template pickers
   looked right. `.chip-group .body` sets its display for the same reason. */
.radio-card .body{display:block;border:1px solid var(--border);border-radius:var(--radius-lg);padding:14px;transition:border-color .15s ease,background .15s ease}
.radio-card input:checked + .body{border-color:var(--accent);background:var(--accent-light)}
/* Withheld data source — dimmed and unselectable, but still shown so the
   capability is discoverable rather than silently missing. */
/* Positions list in the job editor. One row per role so a multi-role post can
   be read at a glance; the detail lives in a dialog. */
.position-row{display:flex;align-items:center;gap:8px;border:1px solid var(--border);border-radius:var(--radius-md);background:var(--surface);padding:4px 8px 4px 4px}
.position-row + .position-row{margin-top:8px}
.position-open{flex:1;min-width:0;display:flex;flex-direction:column;gap:2px;text-align:left;padding:10px 12px;border:0;border-radius:var(--radius-sm);background:none;cursor:pointer}
.position-open:hover{background:var(--surface-hover)}
.position-open .help{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.position-remove{flex-shrink:0}
@media (max-width:600px){
  .position-row{align-items:stretch;flex-direction:column;padding:4px}
  .position-remove{align-self:flex-end}
}

/* Step navigation for the quick-apply card. Sits above the step body so Back
   and Try again are in one predictable place rather than repeated per step. */
.qa-stepnav{display:flex;align-items:center;gap:4px;margin-bottom:12px;min-height:28px}
.qa-stepnav .btn svg{width:15px;height:15px}
/* Back stays left; everything else sits at the far right. Grouped rather than
   pushing one button, so the alignment holds whichever of them is shown. */
.qa-stepnav-right{margin-left:auto;display:flex;align-items:center;gap:4px}

/* Mobile Money checkout fields. Label-above-value boxes; the design system has
   no static-value pattern, and a disabled <input> would read as broken rather
   than as "this is the amount you are paying". */
.mm-field{display:flex;flex-direction:column;gap:2px;border:1px solid var(--border);border-radius:var(--radius-lg);padding:10px 14px;margin-bottom:12px;background:var(--surface)}
.mm-field-label{font-size:12px;color:var(--text-muted)}
.mm-field-value{font-size:17px;color:var(--text-primary)}
.mm-field-note{font-size:11.5px;color:var(--text-muted)}
.mm-field-input:focus-within{border-color:var(--accent);box-shadow:0 0 0 3px var(--accent-light)}
.mm-phone-row{display:flex;align-items:center;gap:8px}
.mm-phone-row input{flex:1;min-width:0;border:0;background:none;padding:0;height:26px;font-size:17px;color:var(--text-primary)}
.mm-phone-row input:focus{outline:none;box-shadow:none}
.mm-prefix{display:inline-flex;align-items:center;gap:5px;flex-shrink:0;padding:3px 7px 3px 4px;border:1px solid var(--border);border-radius:var(--radius-md);background:var(--surface-alt);color:var(--text-primary);font-size:14px}
.mm-prefix[disabled]{cursor:default}
.mm-prefix:not([disabled]):hover{background:var(--surface-hover)}
.mm-flag{display:inline-flex;width:18px;height:12px;border-radius:2px;overflow:hidden;flex-shrink:0}
.mm-flag svg{width:100%;height:100%;display:block}
.mm-caret{width:13px;height:13px;color:var(--text-muted)}
.mm-chip{flex-shrink:0;font-size:11.5px;padding:2px 8px;border-radius:var(--radius-full);background:var(--surface-alt);border:1px solid var(--border);color:var(--text-secondary)}
/* Only rendered once a second country exists. */
.mm-country-menu{list-style:none;margin:8px 0 0;padding:4px;border:1px solid var(--border);border-radius:var(--radius-md);background:var(--surface);box-shadow:var(--shadow-lg)}
.mm-country-option{display:flex;align-items:center;gap:8px;padding:8px 10px;border-radius:var(--radius-sm);cursor:pointer;font-size:14px}
.mm-country-option:hover{background:var(--surface-hover)}
.mm-country-name{flex:1;min-width:0}
.mm-dial{color:var(--text-secondary)}
@media (max-width:600px){
  .mm-field-value,.mm-phone-row input{font-size:16px}
}

/* Transactions: the reference opens a details drawer, so it needs to look
   clickable without becoming a second button style. */
.tx-row .link-button{cursor:pointer}
.link-button{border:0;background:none;padding:0;font:inherit;color:inherit;text-align:left}
.link-button:hover{color:var(--accent);border-color:var(--accent)}

/* Rendered CV pages in the confirmation step. The SVG is page-sized, so it is
   scaled to the card rather than overflowing it. */
/* Full-page CV viewer. Level with dialogs (80) so it covers the drawer (70).
   Panning is native scrolling on .cv-viewer-stage — reimplementing momentum
   would feel wrong on iOS, and the browser already does it properly. */
.cv-viewer{position:fixed;inset:0;z-index:80;background:var(--bg);display:flex;flex-direction:column}
.cv-viewer-bar{display:flex;align-items:center;gap:8px;padding:8px 12px;border-bottom:1px solid var(--border);background:var(--surface);flex-shrink:0}
.cv-viewer-zoom{display:flex;align-items:center;gap:2px}
.cv-viewer-level{min-width:48px;text-align:center;font-size:12.5px;color:var(--text-secondary);font-variant-numeric:tabular-nums}
.cv-viewer-bar .close{margin-left:auto}
/* touch-action is the whole fix for gestures leaking to the page: pan-x pan-y
   leaves scrolling (and its momentum) to the browser while taking pinch-zoom
   away from it, so the handler below owns zoom and the page never scales. It
   also disables double-tap zoom here. `none` would have meant reimplementing
   panning. */
.cv-viewer-stage{flex:1;overflow:auto;padding:12px;-webkit-overflow-scrolling:touch;overscroll-behavior:contain;touch-action:pan-x pan-y}
/* Deliberately NOT .cv-preview-page: that rule pins svg width to 100% with
   !important so it fits its card, which would pin the zoom at one level here. */
.cv-viewer-page{margin:0 auto;width:100%}
.cv-viewer-page svg{display:block;width:100%;height:auto;background:#fff;box-shadow:var(--shadow-sm);border-radius:var(--radius-sm)}
.cv-viewer-foot{display:flex;align-items:center;justify-content:center;gap:12px;padding:8px;border-top:1px solid var(--border);background:var(--surface);flex-shrink:0}
/* The inline pages are the way in, so they have to look like it. */
.cv-preview-page{cursor:zoom-in}

/* Template picker drawer: wider than the nav drawer because it shows page
   previews, and a two-up grid so shapes can be compared at a glance. */
.side-drawer-head{display:flex;align-items:center;gap:10px;padding:14px 16px;flex-shrink:0;border-bottom:1px solid var(--border)}
.side-drawer-head .close{margin-left:auto}
.side-drawer-body{flex:1;overflow-y:auto;padding:12px}
.tpl-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:10px}
.tpl-option{display:flex;flex-direction:column;gap:6px;padding:8px;border:1px solid var(--border);border-radius:var(--radius-md);background:var(--surface);cursor:pointer;text-align:left}
.tpl-option:hover{border-color:var(--accent);background:var(--accent-light)}
.tpl-option.is-current{border-color:var(--accent);box-shadow:0 0 0 2px var(--accent-light)}
.tpl-shot{aspect-ratio:1/1.414;background:#fff;border:1px solid var(--border);border-radius:var(--radius-sm);overflow:hidden;display:block}
.tpl-shot img,.tpl-shot svg{width:100%;height:100%;object-fit:cover;object-position:top center;display:block}
.tpl-name{font-size:12px;color:var(--text-secondary);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
/* Locked previews stay legible — the point is to show what upgrading buys. */
.tpl-shot{position:relative}
.tpl-option.is-locked{cursor:default}
.tpl-option.is-locked:hover{border-color:var(--border);background:var(--surface)}
.tpl-option.is-locked .tpl-name{display:flex;align-items:center;gap:6px}
/* There was a `@media (max-width:600px){.tpl-grid{grid-template-columns:1fr 1fr}}`
   here. It restated the same two columns but dropped the `minmax(0,·)`, and a
   bare `1fr` floors the column at the item's min-content width — which for
   these cards is the preview PNG's intrinsic width, and differs per template.
   One column won, the other was squeezed, and since .tpl-shot takes its height
   from aspect-ratio the squeezed thumbnail was shorter too. Mobile only,
   because that is the only place the override applied. */

/* Rendered CV pages. Shown in full: a CV is what the employer receives, and
   confirming it inside a 60vh scroller meant approving something you had not
   actually read. */
.cv-preview{border:1px solid var(--border);border-radius:var(--radius-lg);background:var(--surface-alt);padding:12px}
.cv-preview-page + .cv-preview-page{margin-top:12px}
/* The renderer emits page-sized SVG with its own width/height attributes; those
   win over CSS width unless overridden, which is what made it overflow on a
   phone. */
.cv-preview-page svg{width:100% !important;height:auto !important;max-width:100%;display:block;background:#fff;border-radius:var(--radius-sm);box-shadow:var(--shadow-sm)}
@media (max-width:600px){
  .cv-preview{padding:8px}
}

/* Quick-apply choice cards. Full-width stacked buttons so the options read as
   a decision list rather than a form; the locked one still shows, because the
   upgrade path is the point. */
.qa-option{display:flex;flex-direction:column;gap:3px;width:100%;text-align:left;padding:14px 16px;border:1px solid var(--border);border-radius:var(--radius-lg);background:var(--surface);cursor:pointer}
.qa-option + .qa-option{margin-top:10px}
.qa-option:hover{border-color:var(--accent);background:var(--accent-light)}
.qa-option.is-locked{background:var(--surface-alt)}
.qa-option.is-locked .td-strong{color:var(--text-secondary)}

/* ---------- 8c. Quick-apply profile confirm ----------
   Read-only summary of the stored profile before it feeds the match: identity
   header, then full-width sections (summary, roles, skills, education). A
   two-column field grid put the headline — often a whole paragraph off the
   CV — in half the card; every value here gets the full width. */
.qa-profile{display:flex;flex-direction:column;gap:18px}
.qa-profile-head{display:flex;gap:14px;align-items:flex-start}
.qa-profile-id{min-width:0;flex:1}
.qa-profile-name{font-size:17px;font-weight:600;margin:0}
.qa-profile-headline{margin:2px 0 0;color:var(--text-secondary)}
.qa-profile-meta{margin:4px 0 0;overflow-wrap:anywhere}
.qa-profile-section{border-top:1px solid var(--border);padding-top:14px}
.qa-profile-section-head{display:flex;align-items:baseline;justify-content:space-between;gap:8px;margin-bottom:8px}
.qa-profile-section-head h3{margin:0;font-size:13.5px}
.qa-profile-edit-link{font-size:12.5px;color:var(--accent);white-space:nowrap;background:none;border:0;padding:0;cursor:pointer;font-family:inherit;flex-shrink:0}
.qa-profile-edit-link:hover{text-decoration:underline}
.qa-profile-head .qa-profile-edit-link{margin-left:auto}
.qa-profile-section-first{border-top:0;padding-top:0}
/* In-place edit forms. Fields sit inside .form-grid so they pair up when
   there is room; entries (roles, qualifications) are bordered so the Remove
   button reads as belonging to one entry. */
.qa-profile-form{display:flex;flex-direction:column;gap:12px;align-items:flex-start}
.qa-profile-form .form-grid,.qa-profile-form .field,.qa-profile-form .qa-profile-entries,.qa-profile-form .qa-profile-add-row,.qa-profile-form .qa-profile-chips,.qa-profile-form .qa-profile-form-foot{width:100%}
.qa-profile-form .field{margin-bottom:0}
.qa-profile-entries{display:flex;flex-direction:column;gap:10px}
.qa-profile-entry{display:flex;flex-direction:column;gap:12px;padding:12px;border:1px solid var(--border);border-radius:var(--radius-md);background:var(--surface-alt)}
.qa-profile-entry-foot{display:flex;justify-content:flex-end}
.qa-profile-add-row{display:flex;gap:8px}
.qa-profile-add-row input{flex:1;min-width:0}
.qa-profile-form-foot{display:flex;flex-direction:column;gap:8px;padding-top:4px}
.qa-profile-actions{display:flex;gap:8px;flex-wrap:wrap}
.qa-chip-remove{margin-left:6px;border:0;background:none;padding:0;color:var(--text-muted);font-size:15px;line-height:1;cursor:pointer}
.qa-chip-remove:hover{color:var(--destructive)}
.qa-profile-summary{margin:0;font-size:14px;line-height:1.55;white-space:pre-line;overflow-wrap:anywhere}
.qa-profile-summary.is-clamped{display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:4;overflow:hidden}
.qa-profile-list{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:10px}
.qa-profile-row{display:grid;grid-template-columns:minmax(0,1fr) auto;gap:2px 10px;align-items:center}
.qa-profile-row-title{font-weight:600;font-size:14px;min-width:0;overflow-wrap:anywhere}
.qa-profile-row-sub{grid-column:1;font-size:13px}
.qa-profile-chips{gap:6px}
/* A 60-character skill wraps inside the card instead of overflowing it. */
.qa-profile-chips .chip{max-width:100%;height:auto;min-height:28px;padding:4px 12px;line-height:1.3;text-align:left}

/* Rich-text editor over a markdown field (JobmadicAiOutput.createRichTextEditor).
   The surface grows with its content: a fixed height was half the reason long
   application letters appeared cut off. */
.rte{border:1px solid var(--border);border-radius:var(--radius-md);background:var(--surface);overflow:hidden}
.rte:focus-within{border-color:var(--accent);box-shadow:0 0 0 3px var(--accent-light)}
.rte-toolbar{display:flex;align-items:center;gap:2px;padding:6px 8px;border-bottom:1px solid var(--border);background:var(--surface-alt);flex-wrap:wrap}
/* Groups formatting from lists from links, so the row scans as three ideas
   rather than one undifferentiated strip of buttons. */
.rte-sep{width:1px;align-self:stretch;margin:2px 6px;background:var(--border)}
.rte-btn{display:inline-flex;align-items:center;justify-content:center;width:32px;height:32px;padding:0;border:1px solid transparent;border-radius:var(--radius-sm);background:none;color:var(--text-secondary);cursor:pointer;transition:background .12s ease,color .12s ease}
.rte-btn svg{width:17px;height:17px}
.rte-glyph{font-size:15px;line-height:1}
.rte-btn:hover{background:var(--surface-hover);color:var(--text-primary)}
.rte-btn:focus-visible{outline:2px solid var(--accent);outline-offset:-1px}
/* Active state matters: without it you cannot tell whether the caret already
   sits inside bold text. */
.rte-btn.is-active{background:var(--accent-light);color:var(--accent);border-color:var(--accent-light)}
.rte-surface{padding:16px 18px;min-height:200px;outline:none;color:var(--text-primary);font-size:14px;line-height:1.65}
.rte-surface:empty::before{content:attr(data-placeholder);color:var(--text-muted)}
.rte-surface > :first-child{margin-top:0}
.rte-surface > :last-child{margin-bottom:0}
.rte-surface p{margin:0 0 12px}
.rte-surface ul,.rte-surface ol{margin:0 0 12px;padding-left:22px}
.rte-surface li{margin:0 0 4px}
.rte-surface[contenteditable="false"]{opacity:.7}
@media (max-width:600px){
  /* Comfortable touch targets; the strip wraps rather than scrolling away. */
  .rte-btn{width:36px;height:36px}
  .rte-surface{padding:14px}
}

.radio-card.is-locked{cursor:not-allowed}
.radio-card.is-locked .body{opacity:.55;background:var(--surface-alt)}
.radio-card.is-locked .pill{margin-left:6px;vertical-align:middle}
.radio-card input:focus-visible + .body{box-shadow:0 0 0 3px var(--accent-light)}
.radio-card input:disabled + .body{opacity:.55;cursor:not-allowed}

/* Chip-style radio group — tone pickers, segmented controls. Selection is a
   native :checked state rather than a class the JS has to keep in sync, which
   is how these controls lost their selected look in the first place. */
.chip-group{display:flex;flex-wrap:wrap;gap:6px}
.chip-group label{cursor:pointer}
.chip-group input{position:absolute;opacity:0;width:0;height:0}
.chip-group .body{display:inline-flex;align-items:center;gap:5px;height:30px;padding:0 13px;
  border-radius:var(--radius-full);border:1px solid var(--border);background:var(--surface);
  font-size:12.5px;color:var(--text-secondary);transition:background .15s ease,border-color .15s ease}
.chip-group label:hover .body{background:var(--surface-hover);color:var(--text-primary)}
.chip-group input:checked + .body{border-color:var(--accent);background:var(--accent-light);
  color:var(--accent);font-weight:600}
.chip-group input:focus-visible + .body{box-shadow:0 0 0 3px var(--accent-light)}
.chip-group input:disabled + .body{opacity:.55;cursor:not-allowed}
/* Button variant. The questionnaire builds its choices in JS as <button>s, and
   there is no checkbox to hang :checked on, so selection is a class. Same rules
   as .body rather than a second chip that drifts from this one. */
.chip-group>button{display:inline-flex;align-items:center;gap:5px;height:30px;padding:0 13px;
  border-radius:var(--radius-full);border:1px solid var(--border);background:var(--surface);
  font-family:inherit;font-size:12.5px;color:var(--text-secondary);cursor:pointer;
  transition:background .15s ease,border-color .15s ease}
.chip-group>button:hover{background:var(--surface-hover);color:var(--text-primary)}
.chip-group>button.is-selected{border-color:var(--accent);background:var(--accent-light);
  color:var(--accent);font-weight:600}
.chip-group>button:focus-visible{outline:0;box-shadow:0 0 0 3px var(--accent-light)}
/* Segmented variant: equal widths, square-ish, sits in a tray. */
.chip-group.is-segmented{gap:2px;background:var(--surface-hover);border-radius:var(--radius-md);padding:3px}
.chip-group.is-segmented label{flex:1}
.chip-group.is-segmented .body{width:100%;justify-content:center;border:0;background:transparent;border-radius:6px}
.chip-group.is-segmented input:checked + .body{background:var(--surface);color:var(--text-primary);
  box-shadow:var(--shadow-xs)}
@media (prefers-reduced-motion:reduce){.chip-group .body{transition:none}}

/* ---------- 8b. Bare inputs (inline search fields) ----------
   These sit inside a bordered wrapper that already owns the height, the padding
   and the focus ring, so the input itself draws nothing.

   `height:100%`, not `height:auto`: auto sizes the box to exactly one line box
   (18.9px), leaving zero slack inside a 36–44px wrapper, so ascenders and
   descenders touch the edges and the caret — drawn at the full line height —
   gets clipped. Filling the wrapper gives the glyphs ~12px of clearance, and
   an input centres its own value vertically, so no padding maths is needed.
   `align-self:stretch` would not do: the hero location input's parent is the
   JS-injected `.combobox`, a block, where align-self has no effect. */
.sb-search input,.topbar-search input,.search-field input{
  border:0;outline:0;width:100%;min-width:0;height:auto;align-self:stretch;padding:0;
  background:transparent;box-shadow:none;font-size:13.5px}
/* Whatever sits between the wrapper and the input — the topbar's <form>, the
   JS-injected .combobox — has to pass the height down. Made flex containers so
   the input stretches inside them; a `height:100%` chain would silently fall
   back to a one-line box the moment another wrapper appeared. Icons and buttons
   declare explicit sizes, so stretch skips them. */
.sb-search form,.topbar-search form,.search-field form,.search-field .combobox{
  display:flex;flex:1;min-width:0;align-self:stretch}
.sb-search input:focus,.topbar-search input:focus,.search-field input:focus{border:0;box-shadow:none}
.sb-search input::placeholder,.topbar-search input::placeholder,.search-field input::placeholder{color:var(--text-muted)}
.sb-search:focus-within,.topbar-search:focus-within{border-color:var(--accent);box-shadow:0 0 0 3px var(--accent-light)}

/* type=search paints a clear button in Chrome/Safari and nothing in Firefox, so
   the affordance is browser-dependent chrome the design never accounted for. */
input[type=search]::-webkit-search-cancel-button,
input[type=search]::-webkit-search-decoration{-webkit-appearance:none;appearance:none}

/* File drop target. `.is-active` is toggled on dragenter/dragover. */
.dropzone{border:2px dashed var(--border-strong);border-radius:var(--radius-lg);background:var(--surface-alt);padding:28px 20px;text-align:center;transition:border-color .15s,background .15s}
.dropzone.is-active{border-color:var(--accent);background:var(--accent-light)}
.dropzone .feature-icon{margin:0 auto}
@media (prefers-reduced-motion:reduce){.dropzone{transition:none}}

/* ---------- 9. Cards ---------- */
.card{background:var(--surface);border:1px solid var(--border);border-radius:var(--radius-lg)}
/* `:where()` holds this at zero specificity so a page that authors its own gap
   with `.mt-*` (declared later, in §24) actually wins. */
:where(.card) + .card{margin-top:16px}
/* Grid/stack children already get their spacing from the container's `gap`;
   without this reset the rule above also fires between them, pushing every card
   but the first down 16px and making same-row cards different heights. */
.stats > .card,.grid-2 > .card,.grid-3 > .card,.grid-4 > .card,.stack > .card,
.plan-grid > .card{margin-top:0}
/* `.card + .card` is the only flow-rhythm rule in this file, so any *other*
   block following a card sits flush against its border. Same 16px, same zero
   specificity. `.card-foot` is excluded on purpose — it brings its own
   14px padding-top. */
:where(.card) + .btn-group,
:where(.card) + .wrap-flex,
:where(.card) + .row,
:where(.card) + .row-between,
:where(.card) + .toolbar,
:where(.card) + .grid-2,
:where(.card) + .grid-3,
:where(.card) + .grid-4,
:where(.card) + [data-when]{margin-top:16px}
/* Flex column so .card-foot pins to the bottom of a stretched grid item and
   footers line up across a row. `details.card` is excluded — flex would
   disturb the <summary> disclosure widget. */
.card:not(details){display:flex;flex-direction:column}
.card:not(details) > .card-body{flex:1}
.card-head{padding:16px 18px;border-bottom:1px solid var(--border);display:flex;align-items:center;justify-content:space-between;gap:12px;flex-wrap:wrap}
.card-head h2{font-size:14.5px;font-weight:600}
.card-head p{font-size:12.5px;color:var(--text-secondary);margin-top:2px}
.card-body{padding:18px;min-width:0}
.card-foot{padding:14px 18px;border-top:1px solid var(--border);display:flex;align-items:center;justify-content:flex-end;gap:8px;flex-wrap:wrap}
/* <summary> as a card header. `display:flex` (inherited from .card-head)
   suppresses the native disclosure marker in Chromium, so draw the chevron. */
summary.card-head{cursor:pointer;list-style:none;font-size:14.5px;font-weight:600}
summary.card-head::-webkit-details-marker{display:none}
summary.card-head::after{content:"";width:7px;height:7px;border-right:2px solid var(--text-muted);border-bottom:2px solid var(--text-muted);transform:rotate(45deg);margin-left:auto;flex-shrink:0;transition:transform .15s ease}
details[open] > summary.card-head::after{transform:rotate(-135deg)}
summary.card-head:hover{background:var(--surface-hover)}
@media (prefers-reduced-motion:reduce){summary.card-head::after{transition:none}}
.card-link{font-size:12.5px;font-weight:600;color:var(--accent)}
a.card:hover{border-color:var(--border-strong)}

/* ---------- 10. Page header / breadcrumb ---------- */
.page-head{display:flex;align-items:flex-start;justify-content:space-between;gap:16px;flex-wrap:wrap;margin-bottom:22px}
/* Flex items default to a min-width of their content, so a long unbroken page
   title pushes the header — and the page — wider than the viewport. Let both
   columns shrink, and break the title rather than truncating it: a page title
   is the one string the reader most needs in full. */
.page-head > *{min-width:0}
.page-head h1{overflow-wrap:anywhere}
.page-head .sub{color:var(--text-secondary);font-size:14px;margin-top:4px}
.breadcrumb{display:flex;align-items:center;gap:6px;font-size:12.5px;color:var(--text-muted);margin-bottom:10px;flex-wrap:wrap}
.breadcrumb a{color:var(--text-secondary)}
.breadcrumb a:hover{color:var(--accent)}
.breadcrumb .sep{color:var(--border-strong)}
.back-link{display:inline-flex;align-items:center;gap:5px;font-size:13px;font-weight:600;color:var(--accent);margin-bottom:18px}

/* ---------- 11. Stats ---------- */
.stats{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:14px}
/* A stat row is always followed by something — a filter bar, a table card, a
   chart. Most pages hand-patched the gap with an `mt-3` on that next sibling,
   so the ones that forgot (revenue shares, revenue payouts) had their filters
   sitting flush against the cards. Zero-specificity via :where(), so any page
   that still sets an explicit `mt-*` keeps winning and nothing double-spaces. */
:where(.stats) + :where(.filter-bar,.toolbar,.card,.table-wrap,.grid-2,.grid-3,.grid-4,form,section){margin-top:16px}
.stats-3{grid-template-columns:repeat(3,minmax(0,1fr))}
.stats-5{grid-template-columns:repeat(5,minmax(0,1fr))}
.stats-6{grid-template-columns:repeat(6,minmax(0,1fr))}
.stat-card{background:var(--surface);border:1px solid var(--border);border-radius:var(--radius-lg);padding:18px;display:flex;flex-direction:column}
.stat-top{display:flex;align-items:center;justify-content:space-between;gap:8px}
.stat-label{font-size:12.5px;color:var(--text-secondary);font-weight:500}
.stat-top svg{width:16px;height:16px;color:var(--text-muted)}
.stat-value{font-size:26px;font-weight:700;margin-top:8px;letter-spacing:-.01em}
.stat-change{font-size:12px;margin-top:6px;display:flex;align-items:center;gap:3px}
.stat-change svg{width:11px;height:11px}
.stat-up{color:var(--success)} .stat-down{color:var(--destructive)}
.stat-link{margin-top:auto;padding-top:8px;font-size:12px;color:var(--accent);font-weight:600;display:inline-block;align-self:flex-start}

/* ---------- 12. Tables ---------- */
.table-wrap{overflow-x:auto}
/* overflow-x:auto also clips vertically, so a row's action menu was cut off
   at the table edge. While a menu inside the table is open, let it overflow. */
.table-wrap:has(.dropdown-content:not([hidden])){overflow:visible}
/* .table-stack: on phones each row becomes a card with labelled cells
   (td[data-label]) rather than a table that scrolls sideways. */
@media (max-width:640px){
  table.table-stack,table.table-stack tbody{display:block}
  table.table-stack thead{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0)}
  table.table-stack tr{display:grid;grid-template-columns:minmax(0,1fr) auto;gap:6px 12px;padding:12px 0;border-top:1px solid var(--border)}
  table.table-stack td{display:block;padding:0;border-top:0;min-width:0}
  table.table-stack td:first-child{grid-column:1 / -1}
  table.table-stack td[data-label]{display:flex;align-items:center;justify-content:space-between;gap:8px;grid-column:1 / -1;font-size:13px}
  table.table-stack td[data-label]::before{content:attr(data-label);font-size:11px;font-weight:600;text-transform:uppercase;letter-spacing:.03em;color:var(--text-muted)}
  table.table-stack td.table-stack-actions{grid-column:1 / -1}
  table.table-stack td.table-stack-actions .row-actions{justify-content:flex-start !important}
}
table{width:100%;border-collapse:collapse}
th{text-align:left;font-size:11px;font-weight:600;text-transform:uppercase;letter-spacing:.03em;color:var(--text-muted);padding:14px 12px 10px;white-space:nowrap}
td{font-size:13.5px;padding:12px;border-top:1px solid var(--border);vertical-align:middle}
tbody tr:hover{background:var(--surface-hover)}
th:first-child,td:first-child{padding-left:0}
th:last-child,td:last-child{padding-right:0}
/* The flush first/last cells above assume the parent supplies the inset.
   A table that is the card's own child has no .card-body to do that, so
   give it the same 18px the card-head/card-body use. */
.card > .table-wrap{padding:0 18px 4px;min-width:0}
.td-strong{font-weight:600}
.td-muted{color:var(--text-secondary)}
.row-actions{display:flex;align-items:center;gap:4px;justify-content:flex-end}

/* ---------- 13. Badges / pills / tags ---------- */
.pill{display:inline-flex;align-items:center;gap:5px;height:22px;padding:0 9px;border-radius:var(--radius-full);font-size:11.5px;font-weight:600;white-space:nowrap}
.pill-success{background:var(--success-bg);color:var(--success)}
.pill-warning{background:var(--warning-bg);color:var(--warning)}
.pill-destructive{background:var(--destructive-bg);color:var(--destructive)}
.pill-info{background:var(--accent-light);color:var(--accent)}
.pill-muted{background:var(--surface-hover);color:var(--text-secondary)}
.tag{display:inline-flex;align-items:center;font-size:11.5px;padding:3px 8px;border-radius:var(--radius-sm);background:var(--surface-hover);color:var(--text-secondary);border:1px solid var(--border)}
.chip{display:inline-flex;align-items:center;height:28px;padding:0 12px;border-radius:var(--radius-full);background:var(--surface-hover);border:1px solid var(--border);font-size:12.5px;color:var(--text-secondary)}
a.chip:hover,.chip.selectable:hover{background:var(--accent-light);color:var(--accent);border-color:var(--accent-border)}
.chip.is-active{background:var(--accent);color:#fff;border-color:var(--accent)}
.key{font-family:var(--mono);font-size:11.5px;color:var(--text-secondary);background:var(--surface-hover);border:1px solid var(--border);border-radius:var(--radius-sm);padding:2px 7px}

/* ---------- 14. Alerts (flash) ---------- */
.alert{display:flex;align-items:flex-start;gap:10px;padding:12px 14px;border-radius:var(--radius-md);border:1px solid;font-size:13.5px;margin-bottom:16px}
.alert svg{width:17px;height:17px;flex-shrink:0;margin-top:1px}
.alert .close{margin-left:auto;border:0;background:transparent;color:inherit;opacity:.6;padding:0;width:20px;height:20px;display:flex;align-items:center;justify-content:center}
.alert .close:hover{opacity:1}
.alert-success{background:var(--success-bg);border-color:var(--success-border);color:var(--success-text)}
.alert-destructive{background:var(--destructive-bg);border-color:var(--destructive-border);color:var(--destructive-text)}
.alert-warning{background:var(--warning-bg);border-color:var(--warning-border);color:var(--warning-text)}
.alert-info{background:var(--info-bg);border-color:var(--info-border);color:var(--info-text)}

/* ---------- 15. Empty / loading / skeleton ---------- */
.empty-state{text-align:center;padding:44px 20px}
.empty-state svg{width:40px;height:40px;color:var(--text-muted);margin:0 auto 12px;opacity:.7}
.empty-state .title{font-size:14.5px;font-weight:600}
.empty-state .desc{font-size:13px;color:var(--text-secondary);margin-top:5px;max-width:380px;margin-left:auto;margin-right:auto}
.empty-state .btn{margin-top:16px}
.spinner{width:18px;height:18px;border:2px solid var(--border-strong);border-top-color:var(--accent);border-radius:50%;animation:spin .7s linear infinite;flex-shrink:0}
.spinner-lg{width:32px;height:32px;border-width:3px}
@keyframes spin{to{transform:rotate(360deg)}}
.loading-block{display:flex;flex-direction:column;align-items:center;gap:12px;padding:44px 20px;color:var(--text-secondary);font-size:13.5px}
.skeleton{background:linear-gradient(90deg,var(--surface-hover) 25%,var(--skeleton-shine) 50%,var(--surface-hover) 75%);background-size:200% 100%;animation:shimmer 1.4s infinite;border-radius:var(--radius-sm)}
@keyframes shimmer{0%{background-position:200% 0}100%{background-position:-200% 0}}
@media (prefers-reduced-motion:reduce){.spinner,.skeleton{animation:none}}
/* Images (posters, share cards, previews) shimmer in their own shape until they arrive:
   <img class="img-skel" style="aspect-ratio:auto 1080/1080" onload="this.classList.add('is-loaded')">. */
img.img-skel{display:block;max-width:100%;height:auto;background:linear-gradient(90deg,var(--surface-hover) 25%,var(--skeleton-shine) 50%,var(--surface-hover) 75%);background-size:200% 100%;animation:shimmer 1.4s infinite}
img.img-skel.is-loaded{background:none;animation:none}
img.img-skel.is-failed{background:var(--surface-hover);animation:none}
/* A preview being redrawn: the last image dims under a spinner. */
.img-updating{position:relative}
.img-updating > .spinner{position:absolute;top:50%;left:50%;margin:-16px 0 0 -16px;display:none}
.img-updating.is-updating > img{opacity:.5;transition:opacity .15s}
.img-updating.is-updating > .spinner{display:block}
@media (prefers-reduced-motion:reduce){img.img-skel{animation:none}}

/* ---------- 16. Tabs ---------- */
.tabs{display:flex;gap:2px;border-bottom:1px solid var(--border);margin-bottom:20px;overflow-x:auto}
.tab{padding:10px 14px;font-size:13.5px;font-weight:600;color:var(--text-secondary);border-bottom:2px solid transparent;white-space:nowrap;background:transparent;border-top:0;border-left:0;border-right:0}
.tab:hover{color:var(--text-primary)}
.tab.is-active{color:var(--accent);border-bottom-color:var(--accent)}
.tab .count{margin-left:6px;font-size:11px;color:var(--text-muted);background:var(--surface-hover);border-radius:var(--radius-full);padding:1px 6px}
.tab[aria-disabled="true"]{opacity:.5;cursor:not-allowed}
/* One panel per tab group; the inactive ones carry the `hidden` attribute. */
.tab-panel{display:block}

/* ---------- 17. Progress / meter ---------- */
.progress{height:6px;background:var(--surface-hover);border-radius:var(--radius-full);overflow:hidden}
.progress-bar{height:100%;background:var(--accent);border-radius:var(--radius-full)}
.progress-bar.is-success{background:var(--success)}
.progress-bar.is-warning{background:var(--warning)}
.progress-bar.is-destructive{background:var(--destructive)}

/* ---------- 18. Pagination ---------- */
.pagination{display:flex;align-items:center;justify-content:space-between;gap:12px;padding-top:16px;margin-top:4px;border-top:1px solid var(--border);flex-wrap:wrap}
.pagination .info{font-size:12.5px;color:var(--text-muted)}
.pagination .pages{display:flex;gap:4px}
.page-btn{min-width:32px;height:32px;padding:0 8px;border:1px solid var(--border);border-radius:var(--radius-sm);background:var(--surface);font-size:12.5px;font-weight:600;color:var(--text-secondary);display:inline-flex;align-items:center;justify-content:center}
.page-btn:hover{background:var(--surface-hover)}
.page-btn.is-active{background:var(--accent);border-color:var(--accent);color:#fff}
.page-btn[aria-disabled="true"]{opacity:.5;pointer-events:none}
/* placed after .pagination's own margin-top:4px above so it wins on source order */
:where(.card) + .pagination{margin-top:16px}

/* ---------- 19. Dropdown ---------- */
.dropdown{position:relative}
.dropdown-content{position:absolute;right:0;top:calc(100% + 6px);min-width:190px;background:var(--surface);border:1px solid var(--border);border-radius:var(--radius-md);box-shadow:var(--shadow-md);padding:5px;z-index:60;text-align:left}
.dropdown-item{display:flex;align-items:center;gap:9px;padding:8px 10px;border-radius:var(--radius-sm);font-size:13.5px;color:var(--text-secondary);width:100%;border:0;background:transparent;text-align:left}
.dropdown-item:hover{background:var(--surface-hover);color:var(--text-primary)}
.dropdown-item svg{width:15px;height:15px;color:var(--text-muted)}
.dropdown-item.is-destructive{color:var(--destructive)}
.btn.text-destructive{color:var(--destructive)}
.btn.text-destructive:hover{background:var(--destructive-bg)}
/* A titled section inside a card (e.g. the job page's Social media tab) */
.side-section-title{font-size:13.5px;font-weight:700;margin:0 0 2px;color:var(--text-primary)}
/* Admin job images: pick the share image */
.share-pick{display:flex;flex-direction:column;gap:4px;cursor:pointer;position:relative}
.share-pick input{position:absolute;top:6px;left:6px;z-index:1;width:18px;height:18px}
.share-pick img{width:100%;object-fit:cover;border-radius:var(--radius-md);border:2px solid var(--border)}
.share-pick input:checked ~ img{border-color:var(--accent)}
.share-pill{color:var(--accent);font-weight:600}
.side-subsection{border-top:1px solid var(--border);padding-top:12px}
.job-code-row{display:flex;align-items:center;gap:8px;flex-wrap:wrap}
.job-code{font-family:var(--font-mono,ui-monospace,monospace);font-weight:700;letter-spacing:.12em;font-size:15px;padding:2px 8px;border:1px dashed var(--accent);border-radius:var(--radius-sm,6px);color:var(--accent)}
.table .num{text-align:right;font-variant-numeric:tabular-nums}
/* View / Edit switch for the post text */
.seg-toggle{display:inline-flex;align-self:flex-start;border:1px solid var(--border);border-radius:var(--radius-md);padding:2px;background:var(--surface-alt);gap:2px}
.seg-toggle button{border:0;background:transparent;padding:4px 12px;font-size:12.5px;font-weight:600;color:var(--text-secondary);border-radius:calc(var(--radius-md) - 2px)}
.seg-toggle button.is-on{background:var(--surface);color:var(--text-primary);box-shadow:0 1px 2px rgba(15,23,42,.08)}
.social-preview{margin:0;padding:12px 14px;border-radius:var(--radius-md);background:var(--surface-dark);color:var(--text-on-dark);font-family:var(--mono);font-size:12.5px;line-height:1.55;white-space:pre-wrap;word-break:break-word;max-height:420px;overflow:auto}
.social-text{width:100%;min-height:240px;resize:vertical;white-space:pre-wrap;font-size:12.5px;line-height:1.55}
/* Admin → Config → Footer Social Links: one line per link, stacked on phones. */
.social-platform{padding-bottom:12px;border-bottom:1px solid var(--border)}
.social-platform:last-of-type{border-bottom:0}
.social-rows:empty{display:none}
.social-link-row{display:grid;grid-template-columns:auto minmax(0,2fr) minmax(0,1.3fr) minmax(0,1.2fr) auto;gap:8px;align-items:center}
.social-link-row input[type=url],.social-link-row input[type=text]{width:100%;min-width:0}
.social-link-on{gap:6px}
@media (max-width:640px){.social-link-row{grid-template-columns:1fr auto;padding:10px 0;border-top:1px dashed var(--border)}.social-link-row input[type=url],.social-link-row input[type=text]{grid-column:1/-1}.social-link-row .social-link-on{grid-row:1;grid-column:1}.social-link-row [data-social-remove]{grid-row:1;grid-column:2}}
.dropdown-item.is-destructive svg{color:var(--destructive)}
.dropdown-item.is-destructive:hover{background:var(--destructive-bg);color:var(--destructive)}
/* Grouped action menus: left-aligned, titled groups, item hints, paired toggles */
.dropdown-content.is-left{left:0;right:auto}
.dropdown-menu-wide{min-width:280px}
.dropdown-group-title{margin:0;padding:8px 10px 4px;font-size:11px;font-weight:600;text-transform:uppercase;letter-spacing:.05em;color:var(--text-muted)}
.dropdown-item-text{display:flex;flex-direction:column;min-width:0;line-height:1.3}
.dropdown-item-hint{font-size:11.5px;color:var(--text-muted);font-weight:400}
.dropdown-item:hover .dropdown-item-hint{color:var(--text-secondary)}
.dropdown-pair{display:grid;grid-template-columns:1fr 1fr;gap:2px}
.dropdown-content .dropdown-item:focus-visible{outline:2px solid var(--accent);outline-offset:-2px}
.btn-chevron{width:14px;height:14px;margin-left:2px}
.dropdown-item.is-current{background:var(--accent-light);color:var(--accent)}
.dropdown-item.is-current svg{color:var(--accent)}
.dropdown-item.is-destructive:hover{background:var(--destructive-bg)}
.dropdown-sep{height:1px;background:var(--border);margin:5px 0}
.dropdown-label{padding:8px 10px;font-size:12.5px}
.dropdown-label .n{font-weight:600}
.dropdown-label .e{color:var(--text-muted);font-size:12px}

/* ---------- 19b. Combobox popup ----------
   The panel spans its anchor rather than hanging off a right edge, and carries a
   keyboard-navigable active state separate from :hover — :hover is useless for
   arrow-key navigation and sticks after a tap on touch. */
.combobox{position:relative}
/* A combobox has to behave like the input it replaced wherever it is a flex
   item. The wrapper carries no width of its own, so the `width:100%` input
   inside collapses to its intrinsic size — which is why the questionnaire's
   company and institution questions rendered a stub of a text box next to
   full-width ones. */
.search-field .combobox,.row > .combobox{flex:1;min-width:0}

/* Which ancestor the panel measures against is the whole trick to its width.
   `.combobox` is the default anchor, so the panel matches the one field. Inside
   a search or filter row we make the wrapper `static`, and the absolutely
   positioned panel resolves against the row instead — spanning keyword input,
   location input and button together. Reverted below 768px, where those rows
   stack and the row's bottom edge is under the submit button. */
.search-bar,.filter-bar{position:relative}
.search-bar .combobox,.filter-bar .combobox{position:static}

.dropdown-content.is-combobox{
  left:0;right:0;min-width:0;padding:6px;
  border-radius:var(--radius-lg);box-shadow:var(--shadow-lg);
  top:calc(100% + 8px)}
.cb-list{max-height:min(340px,60vh);overflow-y:auto;scrollbar-width:thin;
  scrollbar-color:var(--border-strong) transparent}
.cb-list::-webkit-scrollbar{width:8px}
.cb-list::-webkit-scrollbar-track{background:transparent}
.cb-list::-webkit-scrollbar-thumb{background:var(--border-strong);border-radius:99px;
  border:2px solid var(--surface)}
.cb-list::-webkit-scrollbar-thumb:hover{background:var(--text-muted)}

.is-combobox .dropdown-item{min-height:40px;padding:9px 10px;border-radius:var(--radius-md);
  color:var(--text-primary);gap:10px}
.is-combobox .dropdown-item.is-active{background:var(--accent-light);color:var(--accent)}
.is-combobox .dropdown-item.is-active .cb-icon{color:var(--accent)}
.cb-icon{display:flex;flex-shrink:0;color:var(--text-muted)}
.cb-icon svg{width:16px;height:16px}
/* The matched run of the query. The UA default is a yellow block highlight. */
.is-combobox mark{background:transparent;color:var(--accent);font-weight:700;padding:0}
.dropdown-item.is-active mark{color:inherit}

/* Group headings stay pinned while the list scrolls, so you never lose track of
   whether you are looking at countries or cities. */
.is-combobox .dropdown-label{position:sticky;top:0;z-index:1;background:var(--surface);
  padding:8px 10px 5px;font-size:11px;font-weight:700;letter-spacing:.06em;
  text-transform:uppercase;color:var(--text-muted)}

.cb-empty{padding:14px 10px;font-size:13px;color:var(--text-muted);text-align:center}
.cb-foot{display:flex;align-items:center;gap:6px;padding:8px 10px 4px;margin-top:4px;
  border-top:1px solid var(--border);font-size:11.5px;color:var(--text-muted)}
.cb-foot .kbd{font-size:10px}
/* A keyboard hint is noise where there is no keyboard, and clutter in a panel
   this narrow. Width is the reliable half: a desktop browser reports
   `hover:hover` even when its window is phone-sized. */
@media (hover:none),(max-width:768px){.cb-foot{display:none}}

.dropdown-item .sub{display:block;font-size:12px;color:var(--text-muted)}

/* ---------- 20. Dialog / modal ---------- */
.dialog-overlay{position:fixed;inset:0;background:rgba(15,23,42,.5);z-index:80;display:flex;align-items:center;justify-content:center;padding:20px}
.dialog{background:var(--surface);border-radius:var(--radius-lg);box-shadow:var(--shadow-lg);width:100%;max-width:440px;max-height:90vh;overflow-y:auto}
.dialog-lg{max-width:680px}
.dialog-head{padding:20px 22px 0}
.dialog-title{font-size:17px;font-weight:700}
.dialog-desc{font-size:13.5px;color:var(--text-secondary);margin-top:6px}
.dialog-body{padding:18px 22px}
.dialog-foot{padding:16px 22px 20px;display:flex;gap:8px;justify-content:flex-end;flex-wrap:wrap}
.dialog-close{position:absolute;top:14px;right:14px;width:30px;height:30px;border:0;background:transparent;border-radius:var(--radius-sm);color:var(--text-muted);display:flex;align-items:center;justify-content:center}
.dialog-close:hover{background:var(--surface-hover);color:var(--text-primary)}

/* ---------- 21. Toasts ---------- */
.toast-container{position:fixed;right:20px;bottom:20px;z-index:90;display:flex;flex-direction:column;gap:10px;max-width:360px}
.toast{display:flex;align-items:flex-start;gap:10px;background:var(--surface);border:1px solid var(--border);border-left-width:3px;border-radius:var(--radius-md);box-shadow:var(--shadow-md);padding:12px 14px;font-size:13.5px;animation:toastIn .18s ease}
@keyframes toastIn{from{opacity:0;transform:translateY(6px)}to{opacity:1;transform:none}}
.toast svg{width:17px;height:17px;flex-shrink:0;margin-top:1px}
.toast .close{margin-left:auto;border:0;background:transparent;color:var(--text-muted);width:20px;height:20px;display:flex;align-items:center;justify-content:center}
.toast-success{border-left-color:var(--success)} .toast-success svg{color:var(--success)}
.toast-error{border-left-color:var(--destructive)} .toast-error svg{color:var(--destructive)}
.toast-info{border-left-color:var(--accent)} .toast-info svg{color:var(--accent)}

/* ---------- 22. Stepper (wizards) ---------- */
.stepper{display:flex;align-items:center;gap:0;margin-bottom:26px;list-style:none;padding:0}
.step-node{display:flex;align-items:center;gap:9px;flex-shrink:0}
.step-dot{width:30px;height:30px;border-radius:var(--radius-full);border:2px solid var(--border-strong);color:var(--text-muted);display:flex;align-items:center;justify-content:center;font-size:12.5px;font-weight:700;background:var(--surface)}
.step-node.is-active .step-dot{border-color:var(--accent);background:var(--accent);color:#fff}
.step-node.is-done .step-dot{border-color:var(--success);background:var(--success);color:#fff}
.step-label{font-size:13px;font-weight:600;color:var(--text-secondary)}
.step-node.is-active .step-label{color:var(--text-primary)}
.step-line{display:block;flex:1;height:2px;background:var(--border);margin:0 12px;min-width:20px}
.step-line.is-done{background:var(--success)}

/* ---------- 23. Timeline ---------- */
.timeline{position:relative;padding-left:24px}
.timeline::before{content:"";position:absolute;left:5px;top:4px;bottom:4px;width:1px;background:var(--border)}
.tl-item{position:relative;padding-bottom:18px}
.tl-item:last-child{padding-bottom:0}
.tl-dot{position:absolute;left:-24px;top:3px;width:11px;height:11px;border-radius:50%;background:var(--surface);border:2px solid var(--accent)}
.tl-item .when{font-size:12px;color:var(--text-muted);margin-top:2px}

/* ---------- 24. Layout utilities ---------- */
.grid-2{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:16px}
.grid-3{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:16px}
.grid-4{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:16px}
.grid-sidebar{display:grid;grid-template-columns:minmax(0,1fr) 320px;gap:20px;align-items:start}
.split-pane{display:grid;grid-template-columns:360px minmax(0,1fr);gap:20px;align-items:start}
/* Selectable row in a picker column (email lists, saved searches). */
.pick{display:block;border:1px solid var(--border);border-radius:var(--radius-md);padding:11px 12px;font-size:13.5px}
.pick + .pick{margin-top:8px}
.pick:hover{background:var(--surface-hover)}
.pick.is-active{border-color:var(--accent);background:var(--accent-light)}
.row{display:flex;align-items:center;gap:10px}
.row-between{display:flex;align-items:center;justify-content:space-between;gap:12px;flex-wrap:wrap}
.stack{display:flex;flex-direction:column;gap:12px}
/* Flex and grid items default to a min size of their content, so one wide child
   — a code block, a long identifier, a table — makes the whole column wider than
   its track and pushes the page sideways. These containers always yield instead. */
.stack > *,.row > *,.row-between > *,.grid-2 > *,.grid-3 > *,.grid-4 > *,
.stats > *,.grid-sidebar > *,.split-pane > *,.filter-bar > *{min-width:0}
.wrap-flex{display:flex;gap:8px;flex-wrap:wrap}
.mt-0{margin-top:0}.mt-1{margin-top:6px}.mt-2{margin-top:12px}.mt-3{margin-top:18px}.mt-4{margin-top:24px}
.ml-auto{margin-left:auto}
.mb-0{margin-bottom:0}.mb-1{margin-bottom:6px}.mb-2{margin-bottom:12px}.mb-3{margin-bottom:18px}.mb-4{margin-bottom:24px}
.filter-bar{display:grid;grid-template-columns:minmax(220px,1.5fr) repeat(auto-fit,minmax(150px,1fr));gap:8px;margin-bottom:16px}
.filter-bar .btn{grid-column:-2/-1}
.toolbar{display:flex;align-items:center;gap:8px;flex-wrap:wrap;margin-bottom:16px}

/* ---------- 25. Public site chrome ---------- */
.pub-header{display:flex;align-items:center;gap:32px;padding:0 24px;height:64px;border-bottom:1px solid var(--border);background:var(--surface);position:sticky;top:0;z-index:40}
.pub-logo{display:flex;align-items:center;gap:9px;font-size:17px;font-weight:700;color:var(--text-primary);flex-shrink:0}
/* The logo (partials/brand-logo.ejs): pin in the accent colour, wordmark in the text colour, so it follows the theme */
.brand-logo{display:block;flex-shrink:0;color:var(--text-primary);height:auto}
.brand-logo-pin{fill:var(--accent)}
.brand-logo-word{fill:currentColor}
.pub-logo .mark{width:30px;height:30px;border-radius:8px;background:var(--accent);display:flex;align-items:center;justify-content:center;flex-shrink:0}
.pub-logo .mark .brand-logo{height:20px;width:auto}
.pub-nav{display:flex;align-items:center;gap:24px;flex:1}
.pub-nav a{font-size:14px;font-weight:500;color:var(--text-secondary);display:flex;align-items:center;gap:4px}
.pub-nav a:hover{color:var(--text-primary)}
.pub-actions{display:flex;align-items:center;gap:10px;flex-shrink:0}
/* margin-left:auto because at <=1024 the nav and actions this button replaces
   are display:none, leaving it packed against the logo with the whole right
   half of the header empty. */
.mobile-menu-btn{display:none;width:38px;height:38px;margin-left:auto;align-items:center;justify-content:center;border:1px solid var(--border);border-radius:var(--radius-md);background:var(--surface)}

/* Side drawer. Fixed, so opening it never moves the page — the old in-flow
   panel pushed the hero and everything under it down. `.nav-drawer` is the
   public nav; `.side-drawer` is the same slide-in used elsewhere, so there is
   one drawer behaviour rather than a second one that drifts. */
.nav-drawer,.side-drawer{position:fixed;inset:0 0 0 auto;width:min(320px,86vw);z-index:70;
  background:var(--surface);border-left:1px solid var(--border);box-shadow:var(--shadow-lg);
  display:flex;flex-direction:column;transform:translateX(100%);transition:transform .22s ease}
.nav-drawer.is-open,.side-drawer.is-open{transform:translateX(0)}
/* After the shared rule, not before it: the two set the same property at the
   same specificity, so declaring this earlier meant the 320px won and the
   template drawer was never the width its previews were sized for. */
.side-drawer{width:min(420px,92vw)}
.nav-drawer-head{display:flex;align-items:center;gap:10px;padding:14px 16px;flex-shrink:0;
  border-bottom:1px solid var(--border)}
.nav-drawer-head .dropdown-label{padding:0;min-width:0}
/* `display:block` because the drawer writes these as spans, which sat the email
   on the same line as the name. Scoped rather than put on `.dropdown-label .n`
   globally: the notifications header uses the same two classes to keep a label
   and its count side by side on purpose. */
.nav-drawer-head .dropdown-label .n,.nav-drawer-head .dropdown-label .e{
  display:block;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.nav-drawer-head .close{margin-left:auto}
.nav-drawer-body{flex:1;overflow-y:auto;padding:6px 10px 14px}
.nav-drawer-foot{display:flex;align-items:center;gap:10px;padding:12px 16px;flex-shrink:0;
  border-top:1px solid var(--border)}
.nav-drawer-foot form{flex:1;margin:0}
.nav-drawer-foot .btn{flex:1}
/* Sits above the sticky .pub-header (z-40) and below the drawer (z-70). Plain
   .backdrop is z-40, level with the header, so it would render underneath it. */
.backdrop.is-nav{z-index:60;background:rgba(15,23,42,.45);opacity:0;transition:opacity .22s ease}
.backdrop.is-nav.show{opacity:1}
@media (prefers-reduced-motion:reduce){.nav-drawer,.backdrop.is-nav{transition:none}}
.wrap{max-width:1180px;margin:0 auto;padding:0 24px}
.wrap-sm{max-width:760px;margin:0 auto;padding:0 24px}
.section{padding:56px 0;border-top:1px solid var(--border)}
.section-head{display:flex;align-items:flex-end;justify-content:space-between;margin-bottom:28px;gap:16px;flex-wrap:wrap}
.section-head h2{font-size:24px;font-weight:700;letter-spacing:-.01em}
.section-head p{margin-top:4px;color:var(--text-secondary)}
.section-head.centered{flex-direction:column;align-items:center;text-align:center}
.hero{padding:64px 0 48px;text-align:center}
.hero h1{font-size:44px;font-weight:700;letter-spacing:-.02em;line-height:1.15}
.hero h1 span{color:var(--accent)}
.hero p{margin-top:14px;font-size:16px;color:var(--text-secondary);max-width:560px;margin-left:auto;margin-right:auto}
.search-bar{margin:32px auto 0;max-width:640px;display:flex;gap:8px;flex-wrap:wrap;background:var(--surface);border:1px solid var(--border);border-radius:var(--radius-lg);padding:8px;box-shadow:var(--shadow-xs)}
.search-field{flex:1;min-width:180px;display:flex;align-items:center;gap:8px;padding:0 12px;height:44px}
.search-divider{width:1px;background:var(--border);margin:6px 0}
.search-bar:focus-within{border-color:var(--accent);box-shadow:0 0 0 3px var(--accent-light)}
.search-bar .btn{height:44px}
.auth-shell{min-height:100vh;display:flex;flex-direction:column}
.auth-center{flex:1;display:flex;align-items:center;justify-content:center;padding:20px}
.auth-card{width:100%;max-width:420px;background:var(--surface);border:1px solid var(--border);border-radius:var(--radius-lg);padding:32px;box-shadow:var(--shadow-sm)}
.oauth-btn{display:flex;align-items:center;justify-content:center;gap:10px;height:42px;width:100%;border:1px solid var(--border-strong);border-radius:var(--radius-md);background:var(--surface);font-size:14px;font-weight:600}
.oauth-btn:hover{background:var(--surface-hover)}
.oauth-btn svg{width:18px;height:18px}
/* Sign-in providers sit side by side (two per row on phones), not stacked. */
.oauth-row{display:grid;grid-template-columns:repeat(auto-fit,minmax(130px,1fr));gap:10px}
.oauth-row .oauth-btn{min-width:0;white-space:nowrap}
.divider{display:flex;align-items:center;gap:12px;margin:22px 0;color:var(--text-muted);font-size:12.5px}
/* Profile wizard review: optional sections found on the CV, folded away */
.review-more{border:1px solid var(--border);border-radius:var(--radius-md);padding:10px 14px;background:var(--surface)}
.review-more>summary{cursor:pointer;font-weight:600;font-size:14px;display:flex;align-items:center;gap:8px}
.review-more-list{margin:4px 0 0;padding-left:18px;font-size:13px;color:var(--text-secondary)}
.review-more-list li{margin:2px 0}
.divider::before,.divider::after{content:"";flex:1;height:1px;background:var(--border)}
.pub-footer{border-top:1px solid var(--border);padding:48px 0 28px}
.footer-grid{display:grid;grid-template-columns:1.4fr 1fr 1fr 1fr;gap:32px}
.footer-grid h4{font-size:12.5px;font-weight:600;margin-bottom:12px}
.footer-grid ul{list-style:none}
.footer-grid li{margin-bottom:8px}
.footer-grid a{font-size:13px;color:var(--text-secondary)}
.footer-grid a:hover{color:var(--accent)}
.footer-bottom{margin-top:36px;padding-top:20px;border-top:1px solid var(--border);font-size:12.5px;color:var(--text-muted);text-align:center}
.cta-band{background:var(--surface-dark);border-radius:var(--radius-lg);padding:48px;text-align:center;color:var(--text-on-dark);margin:0 24px}
.cta-band h2{font-size:28px;font-weight:700}
.cta-band p{margin-top:8px;color:var(--text-on-dark-muted)}
.cta-band .btn-outline{background:transparent;border-color:var(--border-on-dark);color:var(--text-on-dark)}
.cta-band .btn-outline:hover{background:var(--surface-on-dark-hover)}

/* Pricing tiers. NOT `.plan-card` — that name is taken by the sidebar's plan
   badge.

   `auto-fit` sizes the row to the number of plans on its own: four plans fill
   one row, three make three wider columns, a fifth wraps. Do NOT reintroduce
   `repeat(var(--plan-cols), …)` — a custom property that resolves to anything
   but an integer makes the whole declaration invalid at computed-value time,
   which falls back to `grid-template-columns:none` and drops every card into a
   single full-width column. A preceding fallback declaration does not save it,
   because that failure happens after parsing. */
.plan-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(250px,1fr));gap:16px}
.plan-grid > *{min-width:0}
/* `.card:not(details) > .card-body{flex:1}` already stretches the body; making
   it a column and letting the CTA take the slack is what pins the buttons to a
   common baseline regardless of how many features a tier lists. Scoped to
   `.plan-tier` because `.card-body` is used on essentially every page. */
.plan-tier .card-body{display:flex;flex-direction:column;height:100%}
.plan-tier .plan-cta{margin-top:auto;padding-top:18px}
.plan-tier.is-featured{border-color:var(--accent);box-shadow:var(--shadow-sm)}
.plan-legend{text-align:center;margin-top:14px}

/* ---------- 25b. Admin test bar ----------
   A floating dev tool, not product chrome. Modelled on the `.proto-bar` in
   .dev/designs/v2-design, but built from --surface-dark / --text-on-dark so it
   inverts with the theme like `.dark-card` does, instead of the prototype's
   literal greys. Rendered for admins on every page. */
.admin-bar{position:fixed;left:50%;transform:translateX(-50%);bottom:16px;z-index:45;
  display:flex;align-items:center;gap:6px;max-width:calc(100vw - 24px);
  background:var(--surface-dark);color:var(--text-on-dark);
  border:1px solid var(--border-on-dark);border-radius:var(--radius-full);
  padding:6px 8px;box-shadow:var(--shadow-lg);font-size:12px}
/* Sits above page content but BELOW every real overlay — the nav drawer (70),
   dialogs (80) and toasts (90). It is an ambient dev tool; it must never
   obscure product UI. The prototype's bar used 95, which floated it over the
   mobile drawer. */
.admin-bar .grp{display:flex;align-items:center;gap:4px;padding:0 4px;min-width:0;overflow-x:auto}
.admin-bar .lbl{color:var(--text-on-dark-muted);font-size:10.5px;text-transform:uppercase;
  letter-spacing:.06em;font-weight:700;white-space:nowrap}
.admin-bar a,.admin-bar button{border:0;background:transparent;color:var(--text-on-dark-muted);
  font-size:12px;font-weight:600;padding:5px 9px;border-radius:var(--radius-full);white-space:nowrap}
.admin-bar a:hover,.admin-bar button:hover{color:var(--text-on-dark);background:var(--surface-on-dark-hover)}
.admin-bar a.on{background:var(--accent);color:#fff}
.admin-bar .div{width:1px;height:20px;background:var(--border-on-dark);flex-shrink:0}
.admin-bar .tuck{width:26px;height:26px;padding:0;display:flex;align-items:center;
  justify-content:center;flex-shrink:0}
.admin-bar .tuck svg{width:14px;height:14px;transition:transform .15s ease}
/* Tucked: everything but the handle collapses away, leaving a small pill that
   still names the plan currently being tested. */
.admin-bar[data-tucked="1"] .grp,
.admin-bar[data-tucked="1"] .div,
.admin-bar[data-tucked="1"] .bar-link{display:none}
.admin-bar[data-tucked="1"] .tuck svg{transform:rotate(180deg)}
.admin-bar .handle{display:none;font-weight:700;padding:5px 4px 5px 9px;white-space:nowrap}
.admin-bar[data-tucked="1"] .handle{display:block}
@media (prefers-reduced-motion:reduce){.admin-bar .tuck svg{transition:none}}
/* Compact (designer, CV editor): a small pill centred over the canvas that
   expands on hover or keyboard focus, so the full bar never sits on top of
   the block palette, the inspector or the editor's add button. */
.admin-bar[data-compact="1"]{left:50%;right:auto;transform:translateX(-50%);bottom:10px;opacity:.92}
.admin-bar[data-compact="1"]:not(:hover):not(:focus-within) .grp,
.admin-bar[data-compact="1"]:not(:hover):not(:focus-within) .div,
.admin-bar[data-compact="1"]:not(:hover):not(:focus-within) .bar-link,
.admin-bar[data-compact="1"]:not(:hover):not(:focus-within) .tuck{display:none}
.admin-bar[data-compact="1"]:not(:hover):not(:focus-within) .handle{display:block;padding-right:9px}
.admin-bar[data-compact="1"]:hover,.admin-bar[data-compact="1"]:focus-within{opacity:1}
body:has(.admin-bar[data-compact="1"]){padding-bottom:0}
/* Below 600px the plan chips overflowed their container by up to 162px and
   scrolled horizontally inside the pill. The bar becomes a small stacked panel
   instead: chips wrap onto as many rows as they need and nothing scrolls. */
@media (max-width:600px){
  .admin-bar{left:8px;right:8px;transform:none;flex-wrap:wrap;
    border-radius:var(--radius-lg);padding:8px 10px;row-gap:8px}
  .admin-bar .grp{flex-wrap:wrap;overflow-x:visible;width:100%;padding:0;row-gap:6px}
  .admin-bar .lbl{width:100%}
  .admin-bar .div{display:none}
  .admin-bar .bar-link{margin-left:auto}
  /* The tuck control stays reachable in the top-right of the wrapped panel. */
  .admin-bar .tuck{position:absolute;top:6px;right:8px}
  .admin-bar[data-tucked="1"]{flex-wrap:nowrap;border-radius:var(--radius-full);padding:6px 8px}
  .admin-bar[data-tucked="1"] .tuck{position:static}
}

/* Password reveal. The wrapper is created by app.js around every
   input[type=password], so the control is identical on all 32 of them rather
   than re-implemented per template. The input keeps its own width; only right
   padding is reserved for the button. */
.password-field{position:relative;display:block}
.password-field input{padding-right:40px}
.password-toggle{position:absolute;top:50%;right:6px;transform:translateY(-50%);
  width:28px;height:28px;display:flex;align-items:center;justify-content:center;
  border:0;background:transparent;color:var(--text-muted);border-radius:var(--radius-sm)}
.password-toggle:hover{color:var(--text-primary);background:var(--surface-hover)}
.password-toggle svg{width:16px;height:16px}

/* TOTP enrolment QR. The SVG is rendered server-side (src/utils/qr.js); the
   white plate is fixed on purpose — a QR must stay dark-on-light to scan, so
   this is one of the few places a literal colour is correct. */
.qr-frame{display:inline-block;background:#fff;padding:12px;border-radius:var(--radius-md);
  border:1px solid var(--border)}
.qr-frame svg{display:block;width:180px;height:180px}

/* job cards + marketing blocks */
.job-card{border:1px solid var(--border);border-radius:var(--radius-lg);padding:18px;background:var(--surface);display:flex;flex-direction:column}
.job-card:hover{border-color:var(--border-strong)}
.job-logo{width:36px;height:36px;border-radius:8px;background:var(--accent-light);color:var(--accent);display:flex;align-items:center;justify-content:center;font-weight:700;font-size:13px;flex-shrink:0}
/* A real logo: white tile with a hairline so transparent and dark logos read in both themes */
.job-logo.has-logo{background:#fff;border:1px solid var(--border);overflow:hidden;padding:3px}
.job-logo.has-logo img{width:100%;height:100%;object-fit:contain;border-radius:calc(8px - 3px);display:block}
/* Job page: posted / apply-by / re-advertised row under the title */
.job-dates{display:flex;flex-wrap:wrap;gap:8px 20px;margin-top:14px;padding-top:12px;border-top:1px solid var(--border)}
.job-date{display:flex;align-items:center;gap:8px;min-width:0}
.job-date svg{width:18px;height:18px;flex-shrink:0;color:var(--text-muted)}
.job-date>div{display:flex;flex-direction:column;line-height:1.25;min-width:0}
.job-date-label{font-size:11px;text-transform:uppercase;letter-spacing:.04em;color:var(--text-muted)}
.job-date-value{font-size:13px;font-weight:600;color:var(--text-primary)}
.job-date-hint{font-weight:400;color:var(--text-secondary)}
.job-date-hint::before{content:'\00B7\00A0';color:var(--text-muted)}
.job-date.is-soon svg,.job-date.is-soon .job-date-hint{color:var(--warning)}
.job-date.is-closed svg,.job-date.is-closed .job-date-hint{color:var(--destructive)}
/* Similar jobs: titles wrap (two lines at most), the company stays on one */
.related-job{display:flex;align-items:flex-start;gap:10px;margin-top:12px;color:inherit;text-decoration:none}
.related-job:first-child{margin-top:0}
.related-job-text{display:flex;flex-direction:column;gap:2px;min-width:0;flex:1}
.related-job-title{font-size:13px;font-weight:600;color:var(--text-primary);line-height:1.3;overflow-wrap:anywhere;display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:2;overflow:hidden}
.related-job:hover .related-job-title{color:var(--accent)}
.related-job-company{font-size:12px;color:var(--text-secondary);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
/* Company page */
.company-hero{display:flex;gap:18px;align-items:flex-start}
.company-hero-logo{width:72px;height:72px;font-size:24px;border-radius:14px}
.company-hero-logo img{width:100%;height:100%;border-radius:inherit;object-fit:cover}
.company-hero-stats{display:flex;flex-wrap:wrap;gap:6px 18px;margin-top:10px;font-size:13px;color:var(--text-secondary)}
.company-hero-stats strong{color:var(--text-primary)}
.company-facts{display:grid;grid-template-columns:repeat(auto-fill,minmax(180px,1fr));gap:12px 20px;margin:0;padding-top:12px;border-top:1px solid var(--border)}
.company-facts dt{font-size:11px;text-transform:uppercase;letter-spacing:.04em;color:var(--text-muted)}
.company-facts dd{margin:2px 0 0;font-size:14px;font-weight:500;color:var(--text-primary);overflow-wrap:anywhere}
.company-contact-list{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:12px}
.company-contact-list li{display:flex;gap:10px;align-items:flex-start;min-width:0}
.company-contact-list svg{width:18px;height:18px;flex-shrink:0;margin-top:2px;color:var(--text-muted)}
.company-contact-list li>div{display:flex;flex-direction:column;min-width:0;font-size:14px;overflow-wrap:anywhere}
.company-contact-label{font-size:11px;text-transform:uppercase;letter-spacing:.04em;color:var(--text-muted)}
.company-team{border:1px dashed var(--border-strong);border-radius:var(--radius-md);background:var(--surface-alt);padding:12px 14px;display:flex;flex-direction:column;gap:10px}
.company-team-head{display:flex;align-items:center;gap:6px;font-size:12px;color:var(--text-secondary);margin:0}
.company-team-head svg{width:14px;height:14px;flex-shrink:0}
.company-unpublished-row{gap:12px;padding:8px 0;border-bottom:1px solid var(--border);color:inherit;text-decoration:none}
.company-unpublished-row:last-child{border-bottom:0}
@media (max-width:640px){.company-hero{gap:12px}.company-hero-logo{width:56px;height:56px;font-size:19px}}
/* Card regions. In grid mode they stack in source order, which is exactly how
   the card read before they were named. */
.jc-head{display:flex;align-items:flex-start;gap:10px}
.jc-id{min-width:0}
.jc-aside{min-width:0;margin-top:auto}
/* Posted / closes footer: one row under a hairline, pinned to the card's foot */
.jc-meta{display:flex;flex-wrap:wrap;align-items:center;justify-content:space-between;gap:6px 12px;margin-top:14px;padding-top:10px;border-top:1px solid var(--border);font-size:12px;color:var(--text-secondary)}
.jc-meta-item{display:inline-flex;align-items:center;gap:5px;white-space:nowrap}
.jc-meta-item svg{width:14px;height:14px;flex-shrink:0;color:var(--text-muted)}
.jc-meta-hint{margin-left:6px;padding:1px 7px;border-radius:var(--radius-full);font-size:11px;font-weight:600;background:var(--warning-bg);color:var(--warning)}
.jc-deadline.is-soon,.jc-deadline.is-soon svg{color:var(--warning)}
.jc-deadline.is-closed,.jc-deadline.is-closed svg{color:var(--text-muted)}

/* Results container for the public job listings and the home page's Recent
   Jobs. NOT `.grid-3` — that is a global utility used by dozens of pages
   (including the "More filters" block on the jobs page) and by two
   `:where(.card) + .grid-3` adjacency rules. */
.job-results{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:16px}
.job-results > *{min-width:0}

/* List view, driven off `data-jobs-view` on <html>, which the switcher partial
   sets from localStorage before the cards below it are parsed — so both
   layouts render from one DOM with no reload and no flash.

   EVERY rule here is scoped `.job-results > .job-card`. Unscoped, they reached
   the `.job-card`s the home page renders in a plain `.grid-3`: the aside took
   145px of a 367px card and squeezed the title column to 118px, which read as
   the title truncating far too early. A card outside a `.job-results` must
   never be re-laid-out. */
:root[data-jobs-view="list"] .job-results{grid-template-columns:minmax(0,1fr);gap:10px}
:root[data-jobs-view="list"] .job-results > .job-card{
  display:grid;grid-template-columns:minmax(0,1fr) auto;
  grid-template-areas:"head aside" "tags aside";
  align-items:start;column-gap:20px}
:root[data-jobs-view="list"] .job-results > .job-card .jc-head{grid-area:head}
:root[data-jobs-view="list"] .job-results > .job-card .jc-tags{grid-area:tags}
:root[data-jobs-view="list"] .job-results > .job-card .jc-aside{grid-area:aside;text-align:right;align-self:center}
:root[data-jobs-view="list"] .job-results > .job-card .jc-aside p{margin-top:0;white-space:nowrap}
:root[data-jobs-view="list"] .job-results > .job-card .jc-aside{margin-top:0}
:root[data-jobs-view="list"] .job-results > .job-card .jc-meta{flex-direction:column;align-items:flex-end;gap:4px;margin-top:6px;padding-top:0;border-top:0}

/* The segmented list/grid control. */
.view-switch{flex-shrink:0}
.view-switch .body{gap:6px}
.view-switch svg{width:14px;height:14px}
.feature-icon{width:40px;height:40px;border-radius:10px;background:var(--accent-light);color:var(--accent);display:flex;align-items:center;justify-content:center;margin-bottom:14px}
.step-num{width:36px;height:36px;border-radius:var(--radius-full);background:var(--surface-dark);color:var(--text-on-dark);display:flex;align-items:center;justify-content:center;font-weight:700;margin:0 auto 14px}

/* ---------- 26. Long-form prose ----------
   Legal pages, admin documentation and blog posts render authored copy that
   the component classes don't cover. Scoped to .prose so it never leaks into
   app chrome; headings keep the global scale, only rhythm is added here. */
.prose{color:var(--text-secondary);font-size:14px;line-height:1.7;max-width:72ch;min-width:0}
/* Inside an editor the preview mirrors the editing column, so the reading
   measure above would leave half the card empty. Published prose keeps it. */
.prose.editor-preview{max-width:none}
/* Job descriptions fill their card: the card already sets the measure, and
   the descriptions bring their own headings, so the first one sits flush. */
.prose.prose-wide{max-width:none}
.prose.prose-wide > :first-child{margin-top:0}
.prose > * + *{margin-top:12px}
.prose h2{color:var(--text-primary);margin-top:28px}
.prose h3{color:var(--text-primary);margin-top:20px}
.prose strong{color:var(--text-primary);font-weight:600}
.prose a{color:var(--accent);font-weight:500}
.prose a:hover{text-decoration:underline}
.prose ul,.prose ol{padding-left:20px}
.prose ul{list-style:disc}
.prose ol{list-style:decimal}
.prose li + li{margin-top:6px}
.prose li::marker{color:var(--text-muted)}
.prose ul.plain{list-style:none;padding-left:0}
.prose code{font-family:var(--mono);font-size:12.5px;background:var(--surface-hover);border:1px solid var(--border);border-radius:var(--radius-sm);padding:1px 5px}
.prose pre{background:var(--surface-dark);color:var(--text-on-dark);border-radius:var(--radius-md);padding:14px 16px;overflow-x:auto}
.prose pre code{background:transparent;border:0;color:inherit;padding:0}
.prose blockquote{border-left:3px solid var(--border-strong);padding-left:14px;color:var(--text-muted)}
/* Rendered markdown reaches these; only h2/h3 were covered before, so a post
   using h1 or h4 fell back to the UA sizes. */
.prose h1{color:var(--text-primary);font-size:23px;margin-top:28px}
.prose h4,.prose h5,.prose h6{color:var(--text-primary);margin-top:16px}
.prose img{max-width:100%;height:auto;border-radius:var(--radius-md)}
.prose th,.prose td{border:1px solid var(--border);padding:8px 10px;vertical-align:top}
.prose th{background:var(--surface-hover);color:var(--text-primary);font-size:13px;
  text-transform:none;letter-spacing:0;white-space:normal}
/* A borderless heading-sized field — the post title behaves like the document
   title, not like a form control. */
.title-input{width:100%;border:0;background:transparent;padding:0;height:auto;
  font-size:23px;font-weight:700;letter-spacing:-.01em;color:var(--text-primary)}
.title-input:focus{box-shadow:none;border:0}
.title-input::placeholder{color:var(--text-muted)}
/* Authored prose can contain a table or a long identifier far wider than the
   column. Each must scroll or wrap inside .prose — never widen the page. */
.prose table{margin-top:12px;display:block;width:fit-content;max-width:100%;overflow-x:auto}
.prose pre{max-width:100%}
.prose code{overflow-wrap:anywhere}
.prose a{overflow-wrap:anywhere}
/* Markdown tables carry real header text, not the app's uppercase column labels. */
.prose th{text-transform:none;font-size:12.5px;color:var(--text-secondary)}
.prose img{border-radius:var(--radius-md)}
.prose hr{border:0;border-top:1px solid var(--border);margin:24px 0}

/* ---------- 26b. Letter surface ----------
   A cover letter is a document, not app chrome, so it gets a serif and a
   generous measure. The stack is local: the three cover-letter styles each
   pulled a Google Fonts <link> for this, three network requests for one face. */
.letter-surface{background:var(--surface);padding:48px 40px;font-family:var(--serif);
  font-size:15px;line-height:1.75;color:var(--text-primary)}
.letter-surface .prose{max-width:none;color:inherit;font-size:inherit;line-height:inherit}
.letter-placeholder{font-family:var(--font);font-size:13px;color:var(--text-muted);
  text-align:center;padding:64px 0}
/* Paragraph Studio: outline | letter | rewrite panel. Collapses to one column
   on narrow screens — it used to depend on a `.grid` class that never existed,
   so the three panes stacked even on desktop. */
.pst-studio{display:grid;grid-template-columns:220px minmax(0,1fr) 280px;gap:14px;align-items:start}
@media (max-width:1200px){.pst-studio{grid-template-columns:minmax(0,1fr)}}
/* A selected paragraph inside the letter. Selection used to be `ring-1
   ring-brand`, which does not exist — clicking a paragraph did nothing visible. */
.para{position:relative;margin:0 -12px 16px;padding:4px 12px;border-radius:var(--radius-md);
  cursor:pointer;white-space:pre-line}
.para:hover{background:var(--surface-hover)}
.para.is-selected{background:var(--accent-light);outline:1px solid var(--accent-border)}
.para .role{position:absolute;top:-9px;left:12px;padding:1px 6px;border-radius:var(--radius-sm);
  background:var(--accent);color:#fff;font-family:var(--mono);font-size:9px;
  text-transform:uppercase;letter-spacing:.06em}
.quality{display:inline-flex;gap:3px}
.quality i{width:6px;height:6px;border-radius:50%;background:var(--border-strong)}
.quality i.on{background:var(--accent)}
@media (max-width:768px){.letter-surface{padding:28px 20px}}

/* ---------- 26c. Interview companion ----------
   Shared by all three interview styles, which is why they convert as a set:
   the style picker puts them side by side, so a component that only exists in
   one of them reads as a bug. */

/* A small tinted square carrying a section's icon. Each output card had this
   inline as `flex h-6 w-6 items-center justify-center rounded-md bg-*-light`. */
.icon-badge{width:24px;height:24px;flex-shrink:0;display:flex;align-items:center;
  justify-content:center;border-radius:var(--radius-sm);
  background:var(--accent-light);color:var(--accent)}
.icon-badge svg{width:14px;height:14px}
.icon-badge.is-warning{background:var(--warning-bg);color:var(--warning)}
.icon-badge.is-success{background:var(--success-bg);color:var(--success)}

/* Checklist rows. `.check` is the app's generic checkbox row and must stay
   neutral, so striking out a completed item is opt-in via `.is-task` — the
   done state used to be four Tailwind classes applied from JS, so ticking an
   item changed nothing on screen. */
.check.is-task{align-items:center;cursor:pointer;padding:3px 0}
.check.is-task input:checked + span{text-decoration:line-through;color:var(--text-muted)}

/* Chat transcript (conversational coach). Bubbles alternate sides; the whole
   transcript is built in JS, so these are the only styles it can reach. */
.chat-log{display:flex;flex-direction:column;gap:12px;padding:18px;overflow-y:auto;flex:1;min-height:0}
.chat-msg{display:flex;gap:9px;max-width:82%}
.chat-msg.is-me{align-self:flex-end;flex-direction:row-reverse}
.chat-msg .bubble{padding:9px 13px;border-radius:var(--radius-lg);font-size:13.5px;
  line-height:1.55;background:var(--surface-hover);color:var(--text-primary);min-width:0}
.chat-msg.is-me .bubble{background:var(--accent);color:#fff}
.chat-msg .bubble .prose{color:inherit;font-size:inherit;max-width:none}
.chat-meta{font-size:11px;color:var(--text-muted);margin-top:4px}
.chat-msg.is-me .chat-meta{text-align:right}
.chat-compose{border-top:1px solid var(--border);padding:12px 14px;display:flex;gap:8px;
  align-items:flex-end;flex-shrink:0}
.chat-compose textarea{min-height:44px;max-height:150px}

/* The three-dot typing indicator. `.cc-dot` was referenced but never defined,
   so the indicator was invisible. */
.typing{display:inline-flex;gap:4px;align-items:center;padding:11px 13px}
.typing i{width:6px;height:6px;border-radius:50%;background:var(--text-muted);
  animation:typing 1.2s ease-in-out infinite}
.typing i:nth-child(2){animation-delay:.15s}
.typing i:nth-child(3){animation-delay:.3s}
@keyframes typing{0%,60%,100%{opacity:.3;transform:translateY(0)}30%{opacity:1;transform:translateY(-3px)}}
@media (prefers-reduced-motion:reduce){.typing i{animation:none;opacity:.6}}

/* Practice studio: setup | question deck | practice panel. */
.ps-studio{display:grid;grid-template-columns:272px minmax(0,1fr) minmax(0,1fr);
  gap:14px;align-items:start}
@media (max-width:1200px){.ps-studio{grid-template-columns:minmax(0,1fr)}}
.ps-pane{display:flex;flex-direction:column;overflow:hidden;min-height:480px}
.ps-pane-head{padding:12px 14px;border-bottom:1px solid var(--border);
  display:flex;align-items:center;gap:8px;flex-shrink:0}
.ps-pane-head h3{font-size:13.5px;font-weight:600}
.ps-scroll{overflow-y:auto;padding:10px;flex:1;min-height:0}
.ps-body{display:flex;flex-direction:column;gap:16px;padding:18px;flex:1;min-height:0;overflow-y:auto}
/* A deck row's number chip. Three states — done, current, pending — that used
   to be `bg-success text-success-foreground` etc., none of which existed. */
.deck-num{width:28px;height:28px;flex-shrink:0;display:flex;align-items:center;
  justify-content:center;border-radius:var(--radius-sm);font-family:var(--mono);
  font-size:11.5px;font-weight:600;background:var(--surface-hover);color:var(--text-secondary)}
.deck-num.is-done{background:var(--success-bg);color:var(--success)}
.pick.is-active .deck-num{background:var(--accent);color:#fff}
/* The circle timer's readout. The ring itself is an inline SVG whose stroke JS
   swaps between accent/warning/destructive as the target time approaches. */
.timer-ring{flex-shrink:0}
.timer-ring text{font-family:var(--mono);font-weight:600}
.beats{display:flex;flex-direction:column;gap:6px;font-size:13.5px}
.beats li{display:flex;gap:8px}
.beats .bullet{font-family:var(--mono);color:var(--text-muted);flex-shrink:0}
.inset-panel{border:1px solid var(--border);border-radius:var(--radius-md);
  background:var(--surface-hover);padding:14px}
.overline{font-size:11px;font-weight:600;text-transform:uppercase;letter-spacing:.07em;
  color:var(--text-muted)}
.tile{background:var(--surface-hover);border-radius:var(--radius-md);padding:10px;text-align:center}
.tile .v{font-size:18px;font-weight:600;letter-spacing:-.01em}

/* Conversational coach: chat column + a fixed captured-wins rail. */
.chat-card{display:flex;flex-direction:column;overflow:hidden;min-height:600px}
.chat-bar{padding:9px 14px;border-bottom:1px solid var(--border);display:flex;
  align-items:center;gap:8px;flex-shrink:0}
.chat-setup{padding:12px 14px;border-bottom:1px solid var(--border);
  background:var(--surface-hover);flex-shrink:0}
.composer-box{border:1px solid var(--border-strong);border-radius:var(--radius-md);
  padding:10px;background:var(--surface)}
.composer-box textarea{border:0;box-shadow:none;padding:0;min-height:44px;background:transparent}
.composer-box textarea:focus{box-shadow:none}
.composer-head{display:flex;align-items:center;gap:8px;padding-bottom:9px;
  margin-bottom:9px;border-bottom:1px solid var(--border)}
/* The live-transcription indicator's pulsing dot. */
.rec-dot{width:8px;height:8px;border-radius:50%;background:var(--destructive);
  flex-shrink:0;animation:pulse 1.4s ease-in-out infinite}
@keyframes pulse{0%,100%{opacity:1}50%{opacity:.25}}
@media (prefers-reduced-motion:reduce){.rec-dot{animation:none}}

/* ---------- 26d. Questionnaire builder frame ----------
   The frame, the surface and the floating toolbars are ours. The nodes drawn
   *inside* the surface come from public/js/questionnaire-builder.js and use a
   `qb-*` vocabulary that was never defined in any committed stylesheet — a
   pre-existing gap, recorded in the conversion checklist and in
   KNOWN_UNSTYLED_PREFIXES, not something this migration introduced. */
.qb-frame{position:relative;height:640px;overflow:hidden;cursor:grab;
  border:1px solid var(--border);border-radius:var(--radius-lg);background:var(--surface-hover)}
.qb-frame > #qb-surface{position:absolute;transform-origin:0 0}
.qb-frame #qb-edges,.qb-frame #qb-nodes{position:absolute;inset:0}
.qb-tools{position:absolute;bottom:12px;display:flex;gap:6px;z-index:2}
.qb-tools.is-left{left:12px}
.qb-tools.is-right{right:12px}
.qb-tools .btn{box-shadow:var(--shadow-sm);background:var(--surface)}
.qb-tools .btn-accent{background:var(--accent)}
.sticky-aside{position:sticky;top:16px}
@media (max-width:1024px){.sticky-aside{position:static}}

/* ---------- 26e. Accordion ----------
   A <details> disclosure. The chevron rotation is CSS driven off [open];
   admin used Tailwind's `group-open:rotate-90` / `rotate-180`, neither of
   which exists here, so every collapsible header's chevron sat frozen. */
.accordion{border:1px solid var(--border);border-radius:var(--radius-lg);
  background:var(--surface);overflow:hidden}
.accordion + .accordion{margin-top:10px}
.accordion > summary{display:flex;align-items:center;gap:10px;padding:12px 16px;
  cursor:pointer;list-style:none;font-size:13.5px}
.accordion > summary::-webkit-details-marker{display:none}
.accordion > summary:hover{background:var(--surface-hover)}
.accordion > summary .chevron{width:15px;height:15px;flex-shrink:0;color:var(--text-muted);
  transition:transform .15s ease}
.accordion[open] > summary .chevron{transform:rotate(90deg)}
.accordion[open] > summary{border-bottom:1px solid var(--border)}
.accordion-body{padding:0}
@media (prefers-reduced-motion:reduce){.accordion > summary .chevron{transition:none}}

/* JS-driven collapsibles (admin config). These are plain divs toggled by
   `hidden`, not <details>, so the chevron needs its own state class. It used to
   be Tailwind's `rotate-180`, which exists nowhere — every chevron on the
   config page sat frozen pointing down whether the card was open or shut. */
.card-head.collapsible{cursor:pointer;user-select:none;flex-wrap:nowrap}
.card-head.collapsible:hover{background:var(--surface-hover)}
.chevron{width:15px;height:15px;flex-shrink:0;color:var(--text-muted);
  transition:transform .15s ease}
.chevron.is-open{transform:rotate(180deg)}
@media (prefers-reduced-motion:reduce){.chevron{transition:none}}
.section-toggle{display:flex;align-items:center;justify-content:space-between;gap:12px;
  width:100%;padding:14px 18px;text-align:left;background:transparent;border:0}
.section-toggle:hover{background:var(--surface-hover)}
.section-body{padding:2px 18px 18px}
.section-list > * + *{border-top:1px solid var(--border)}

/* Header strip inside a bordered group (font family, flag group). */
.font-family-head{padding:12px 16px;border-bottom:1px solid var(--border);
  background:var(--surface-hover)}

/* One feature flag: description on the left, switch on the right. */
.flag-row{display:flex;align-items:center;justify-content:space-between;gap:16px;
  padding:14px 16px}
.flag-row + .flag-row{border-top:1px solid var(--border)}

/* Bulk-selection bar. Sticks to the top of the scroll area so the chosen
   action stays reachable while working down a long table. */
.bulk-bar{position:sticky;top:0;z-index:10;display:flex;align-items:center;
  justify-content:space-between;gap:12px;flex-wrap:wrap;padding:10px 14px;
  border:1px solid var(--accent-border);border-radius:var(--radius-md);
  background:var(--accent-light);margin-bottom:10px}
.bulk-bar select,.bulk-bar input{height:32px;font-size:12.5px;width:auto}

/* ---------- 27. CV thumbnail ----------
   The box. Stored thumbnails are `<svg width="240" height="340" viewBox="0 0 595 842">`
   (generateCvThumbnail, src/utils/cv-db.js). That `width` presentation attribute makes the
   SVG's width non-auto, so `align-items:stretch` could never stretch it — it sat at 240px in
   a 365px card and was then squashed 340→150 as a shrinkable flex item. The render now fills
   the width and is cropped by overflow, the same way `.tpl-preview` already did it. */
.cv-thumb{height:180px;flex-shrink:0;overflow:hidden;background:var(--surface-hover);
  border-bottom:1px solid var(--border);display:flex;align-items:flex-start;justify-content:center}
/* width:100% maps the 595pt page width onto the card in every case, so a one-page
   (595×842) and a multi-page (595×1704) CV render at the SAME scale and show the same
   region of page 1. Anchoring to flex-start is what makes that true — centring would pull a
   tall SVG's midpoint into view and show page 2's header. */
.cv-thumb > svg,.cv-thumb > img{width:100%;height:auto;display:block;flex-shrink:0}
/* Grey-bar placeholder, used only when no thumbnail_svg is stored. */
.cv-thumb.is-placeholder{flex-direction:column;align-items:stretch;padding:14px;gap:6px}
.cv-thumb .ln{height:6px;background:var(--border-strong);border-radius:3px;flex-shrink:0}
.cv-thumb .ln.hd{width:55%;background:var(--accent);height:8px}
.cv-thumb .ln.w1{width:90%}.cv-thumb .ln.w2{width:75%}.cv-thumb .ln.w3{width:82%}.cv-thumb .ln.w4{width:60%}
/* Inline chip (the "CV used" row on an application). */
.cv-thumb.is-chip{width:48px;height:64px;border:1px solid var(--border);
  border-radius:var(--radius-sm)}
/* The chip's fallback icon stays centred while a real render fills the box, so the
   centring lives on the icon rather than on the container. */
.cv-thumb .doc-icon{margin:auto;width:22px;height:22px;color:var(--text-muted);flex-shrink:0}

/* Rendered template preview (admin template gallery). Holds the A4 page ratio
   so the tile does not reflow while the SVG or PNG loads; the child preview is
   whatever the renderer emits, so it is sized from here rather than inline. */
.tpl-preview{position:relative;aspect-ratio:595/842;background:var(--surface-hover);border-bottom:1px solid var(--border);overflow:hidden;display:flex;align-items:flex-start;justify-content:center}
.tpl-preview > svg,.tpl-preview > img{width:100%;height:auto}
.tpl-preview .placeholder{margin:auto;font-size:12px;color:var(--text-muted)}
/* Actions sit in a strip along the bottom of the thumbnail, shown on hover or
   keyboard focus, so a locked (Pro) template is previewed at full clarity —
   no grey wash — with only a small lock badge to say it needs an upgrade.
   Touch screens have no hover: locked cards keep the strip visible, and a
   selected card shows its Preview button. */
.tpl-preview .overlay,.tpl-shot .overlay{position:absolute;left:0;right:0;bottom:0;display:flex;flex-wrap:wrap;gap:6px;align-items:center;justify-content:center;padding:26px 8px 10px;background:linear-gradient(to top,rgba(15,23,42,.78),rgba(15,23,42,0));opacity:0;transition:opacity .15s}
.tpl-preview:hover .overlay,.tpl-preview:focus-within .overlay,
.tpl-shot:hover .overlay,.tpl-shot:focus-within .overlay{opacity:1}
.tpl-preview .overlay .btn-outline,.tpl-shot .overlay .btn-outline{background:var(--surface);color:var(--text-primary)}
@media (hover:none){
  .tpl-preview.is-locked .overlay,.tpl-option.is-locked .overlay,
  .radio-card input:checked + .body .tpl-preview .overlay{opacity:1}
}
.tpl-lock-badge{position:absolute;top:8px;right:8px;z-index:1;display:inline-flex;align-items:center;gap:4px;padding:3px 8px 3px 7px;border-radius:var(--radius-full);background:rgba(15,23,42,.85);color:#fff;font-size:11px;font-weight:700;line-height:1.3;letter-spacing:.02em}
.tpl-lock-badge svg{width:11px;height:11px}
.radio-card.tpl-card-locked{cursor:default}
.radio-card.tpl-card-locked input:disabled + .body{opacity:1;cursor:default}
/* Lazy thumbnails shimmer until the image arrives, so a card is never blank. */
.tpl-preview:has(> img):not(.is-loaded),.tpl-shot:not(.is-loaded){background:linear-gradient(90deg,var(--surface-hover) 25%,var(--skeleton-shine) 50%,var(--surface-hover) 75%);background-size:200% 100%;animation:shimmer 1.4s infinite}
@media (prefers-reduced-motion:reduce){.tpl-preview:has(> img):not(.is-loaded),.tpl-shot:not(.is-loaded){animation:none}}
/* Large preview dialog (views/partials/template-preview-dialog.ejs). */
.tpl-lightbox-panel{max-width:760px}
.tpl-lightbox-head{display:flex;align-items:center;justify-content:space-between;gap:8px}
.tpl-lightbox-body{padding-top:12px;padding-bottom:4px}
.tpl-lightbox-frame{width:min(100%,calc(68vh * 595 / 842));aspect-ratio:595/842;margin:0 auto;border:1px solid var(--border);border-radius:var(--radius-md);overflow:hidden;background:#fff}
.tpl-lightbox-frame img{display:block;width:100%;height:auto}
.tpl-lightbox-note{margin:0 auto 0 0;align-self:center}
@media (prefers-reduced-motion:reduce){.tpl-preview .overlay{transition:none}}

/* ---------- 28. CV document surface (editor + preview) ----------
   The CV is a *document*, not app chrome, so the type scale and colours below
   are the renderer's own (2-rc / MINIMAL_STYLES) rather than the design tokens above — the
   page is authored at 595px so they map ~1:1 to the SVG's pt units.
   The editing overlays that sit on top of it DO use the design tokens. */
.cv-canvas{position:relative;background:var(--surface-alt);border:1px solid var(--border);border-radius:var(--radius-lg);padding:28px 20px;overflow-x:auto}
.cv-pages{display:flex;flex-direction:column;align-items:center;gap:14px}
.cv-page{position:relative;width:595px;max-width:100%;min-height:842px;background:#fff;border:1px solid rgba(15,23,42,.06);border-radius:8px;box-shadow:var(--shadow-md);padding:40px 50px}
.cv-page-num{position:absolute;left:0;right:0;bottom:16px;text-align:center;font-family:"Trebuchet MS",var(--font);font-size:9px;color:#5b6770}

/* document typography */
.cv-doc{font-family:"Trebuchet MS",var(--font)}
.cv-name{font-family:Georgia,"Times New Roman",serif;font-size:28px;line-height:32px;font-weight:600;color:#1f2428;letter-spacing:.3px}
.cv-headline{font-size:12px;line-height:16px;font-weight:500;color:#5b6770;letter-spacing:.6px;margin-top:4px}
.cv-contact{font-size:9px;line-height:14px;color:#5b6770;margin-top:6px}
.cv-section{position:relative;font-size:9px;line-height:14px;font-weight:600;color:#5b6770;letter-spacing:2px;text-transform:uppercase;margin-top:16px}
.cv-rule{height:1px;width:90%;background:#d4dce2;margin-top:5px;margin-bottom:12px}
.cv-body{font-size:11px;line-height:16px;color:#1f2428}
.cv-role{font-size:11px;line-height:16px;font-weight:600;color:#1f2428}
.cv-muted{font-size:9px;line-height:14px;color:#5b6770}
.cv-entry + .cv-entry{margin-top:10px}
.cv-list{list-style:none;margin-top:4px}
.cv-list li{position:relative;padding-left:12px;font-size:11px;line-height:16px;color:#1f2428}
.cv-list li + li{margin-top:4px}
.cv-list li::before{content:"";position:absolute;left:2.5px;top:6px;width:4px;height:4px;border-radius:50%;background:#1f2428}

/* editing overlays — design tokens */
.cv-block{position:relative}
.cv-block.is-selected{outline:1px solid var(--accent);outline-offset:6px;border-radius:6px;background:var(--accent-light)}
.cv-block-actions{position:absolute;top:-16px;right:-16px;display:flex;gap:6px;padding:6px;background:var(--surface);border:1px solid var(--accent-border);border-radius:var(--radius-md);box-shadow:var(--shadow-md);z-index:5}
.cv-act-add{width:24px;height:24px;flex-shrink:0;border:1px solid var(--accent-border);background:var(--accent-light);color:var(--accent);border-radius:var(--radius-sm);font-size:14px;font-weight:700;display:flex;align-items:center;justify-content:center}
.cv-act-add:hover{background:var(--accent);color:#fff}
.cv-act-del{padding:4px 8px;border:1px solid var(--destructive-border);background:var(--destructive-bg);color:var(--destructive);border-radius:var(--radius-sm);font-size:12px;font-weight:600}
.cv-act-del:hover{background:var(--destructive);color:#fff}
.cv-section-add{position:absolute;right:0;top:-3px;width:19px;height:19px;border-radius:var(--radius-full);border:1px solid var(--accent-border);background:var(--accent-light);color:var(--cv-ov-accent, var(--accent));font-size:12px;font-weight:700;line-height:1;display:flex;align-items:center;justify-content:center;box-shadow:var(--cv-ov-shadow, var(--shadow-sm))}
.cv-section-add:hover{background:var(--cv-ov-accent, var(--accent));color:#fff}
.cv-fab{position:fixed;right:24px;bottom:24px;width:46px;height:46px;border:0;border-radius:var(--radius-full);background:var(--accent);color:#fff;font-size:22px;font-weight:700;line-height:1;display:flex;align-items:center;justify-content:center;box-shadow:var(--shadow-md);z-index:10}
.cv-fab:hover{background:var(--accent-hover)}
.cv-gear{position:fixed;right:0;top:50%;transform:translateY(-50%);width:36px;height:36px;border:1px solid var(--border);border-right:0;border-radius:var(--radius-md) 0 0 var(--radius-md);background:var(--surface-hover);color:var(--text-secondary);display:flex;align-items:center;justify-content:center;box-shadow:var(--shadow-sm);z-index:10}
.cv-gear:hover{background:var(--surface);color:var(--text-primary)}
.cv-status{display:inline-flex;align-items:center;gap:5px;font-size:12px;color:var(--success);font-weight:600}
.cv-status .dot-ok{position:static;width:7px;height:7px;border-radius:50%;background:var(--success);border:0}
.cv-status.is-busy{color:var(--text-secondary)}
.cv-status.is-busy .dot-ok{background:var(--warning)}
.cv-status.is-error{color:var(--destructive)}
.cv-status.is-error .dot-ok{background:var(--destructive)}

/* ---------- 28b. CV editor chrome ----------
   The editor is full-bleed: its toolbar and canvas cancel `.content`'s padding
   so the A4 page sits on an edge-to-edge surface. Moved here from an inline
   <style> in dashboard/cv-editor.ejs — the page sizes below are A4 geometry
   (595 × 842 pt), not theme, so they are literal rather than tokenised. */
.cv-edit-bar{display:flex;align-items:center;justify-content:space-between;gap:12px;flex-wrap:wrap;
  margin:-28px -24px 0;padding:12px 24px;border-bottom:1px solid var(--border);background:var(--surface)}
.cv-edit-canvas{position:relative;isolation:isolate;margin:0 -24px -72px;height:calc(100vh - var(--topbar-h) - 62px);overflow:auto;
  background:var(--surface-alt);padding:32px 16px;display:flex;flex-direction:column;align-items:center}
/* The CV title, edited in place in the toolbar. */
.cv-edit-name{border:0;background:transparent;padding:0;height:auto;width:auto;max-width:320px;
  font-size:15px;font-weight:600;color:var(--text-primary)}
.cv-edit-name:focus{border:0;box-shadow:none;outline:0;border-bottom:1px solid var(--accent)}

#cv-preview-container{position:relative;width:min(640px,calc(100vw - 2rem));min-height:842px}
.cv-render-pages{display:flex;flex-direction:column;gap:14px;align-items:center;width:100%}
.cv-render-page{position:relative;width:min(595px,calc(100vw - 2rem));background:#fff;border-radius:8px;
  border:1px solid rgba(15,23,42,.06);box-shadow:0 8px 20px rgba(15,23,42,.08);overflow:hidden}
.cv-render-page svg{display:block;width:100%;height:auto;background:#fff}

/* Admin tools drawer, opened by the .cv-gear tab. */
.cv-admin{position:fixed;right:0;top:50%;transform:translate(100%,-50%);z-index:9;width:224px;
  background:var(--surface);border:1px solid var(--border);border-right:0;
  border-radius:var(--radius-lg) 0 0 var(--radius-lg);box-shadow:var(--shadow-lg);transition:transform .2s ease}
.cv-admin.is-open{transform:translate(0,-50%)}
@media (prefers-reduced-motion:reduce){.cv-admin{transition:none}}

/* ---------- 28c. Template designer ----------
   Full-height workbench inside the app shell: toolbar, structure/add rail,
   engine-rendered artboard with a selection overlay, and an inspector rail.
   The artboard reuses .cv-render-pages/.cv-render-page from the CV editor. */
.designer{display:flex;flex-direction:column;margin:-28px -24px -72px;height:calc(100vh - var(--topbar-h));background:var(--surface-alt)}
.designer-toolbar{display:flex;align-items:center;gap:10px;flex-wrap:wrap;padding:8px 16px;border-bottom:1px solid var(--border);background:var(--surface);flex-shrink:0}
.designer-toolbar-title{display:flex;flex-direction:column;line-height:1.2;min-width:120px}
.designer-toolbar-title strong{font-size:14px}
.designer-toolbar-group{display:flex;align-items:center;gap:6px;flex-wrap:wrap}
.designer-toolbar-group:last-child{margin-left:auto}
.designer-toolbar-field{display:flex;align-items:center;gap:6px}
.designer-toolbar-sep{width:1px;height:22px;background:var(--border);margin:0 4px}
.designer-toolbar .icon-btn{width:32px;height:32px;font-size:15px}
.designer-toolbar .icon-btn:disabled{opacity:.35}
.input-sm{height:32px;padding:0 8px;font-size:13px;width:auto;min-width:0}
.designer-toolbar select.input-sm{padding-right:30px}
/* Room to scroll the last page clear of the compact admin pill. */
body:has(.admin-bar[data-compact="1"]) .designer-artboard-scroll{padding-bottom:64px}
.designer-status{font-size:12px;color:var(--text-muted);min-width:80px;text-align:right}
.designer-status.is-ok{color:var(--success)}.designer-status.is-error{color:var(--destructive)}
.designer-main{display:grid;grid-template-columns:280px minmax(0,1fr) 320px;flex:1;min-height:0}
.designer-rail{display:flex;flex-direction:column;min-height:0;background:var(--surface);border-right:1px solid var(--border)}
.designer-rail-right{border-right:0;border-left:1px solid var(--border)}
.designer-rail-tabs{margin:0;padding:0 6px;flex-shrink:0}
.designer-rail-tabs .tab{padding:10px 8px;font-size:12.5px}
/* Five inspector tabs share a 280–320px rail: compact them and let them wrap
   rather than scroll, so "Issues" is never cut off. */
.designer-rail-right .designer-rail-tabs{flex-wrap:wrap;overflow:visible;column-gap:0}
.designer-rail-right .designer-rail-tabs .tab{padding:10px 7px;font-size:12px;display:inline-flex;align-items:center;gap:4px}
.designer-rail-right .designer-rail-tabs .designer-issues-count:empty{display:none}
.designer-rail-body{flex:1;overflow:auto;padding:10px 12px;min-height:0}
.designer-issues-count{display:inline-block;min-width:16px;padding:0 5px;border-radius:var(--radius-full);font-size:10.5px;line-height:16px;text-align:center}
.designer-issues-count.is-warn{background:var(--warning-bg);color:var(--warning)}
.designer-issues-count.is-error{background:var(--destructive-bg);color:var(--destructive)}
/* Structure tree */
.designer-tree{font-size:12.5px}
.tree-node{display:flex;align-items:center;gap:6px;height:26px;padding-right:6px;border-radius:var(--radius-sm);cursor:pointer;white-space:nowrap;overflow:hidden;user-select:none}
.tree-node:hover{background:var(--surface-hover)}
.tree-node.is-selected{background:var(--accent-light);color:var(--accent)}
.tree-node.is-hidden .tree-type,.tree-node.is-hidden .tree-text{opacity:.45;text-decoration:line-through}
.tree-node.is-drop-before{box-shadow:inset 0 2px 0 var(--accent)}
.tree-node.is-drop-after{box-shadow:inset 0 -2px 0 var(--accent)}
.tree-node.is-drop-inside{outline:2px dashed var(--accent);outline-offset:-2px}
.tree-node.is-dragging{opacity:.5}
.tree-caret{width:16px;height:16px;border:0;background:transparent;color:var(--text-muted);font-size:14px;line-height:1;flex-shrink:0;transition:transform .12s ease;cursor:pointer}
.tree-caret.is-open{transform:rotate(90deg)}
.tree-caret.is-leaf{opacity:.4;pointer-events:none}
.tree-type{font-family:var(--mono);font-size:11px;color:var(--text-secondary);flex-shrink:0}
.tree-node.is-selected .tree-type{color:var(--accent)}
.tree-text{color:var(--text-muted);overflow:hidden;text-overflow:ellipsis}
.tree-eye{margin-left:auto;width:20px;height:20px;border:0;background:transparent;color:var(--text-muted);font-size:9px;cursor:pointer;opacity:0;flex-shrink:0}
.tree-node:hover .tree-eye,.tree-node.is-hidden .tree-eye{opacity:1}
.tree-slot{font-size:10.5px;text-transform:uppercase;letter-spacing:.05em;color:var(--text-muted);padding:4px 0 2px}
/* Palette */
.palette-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:6px;margin-bottom:8px}
.palette-list{display:flex;flex-direction:column;gap:6px;margin-bottom:8px}
.palette-row{display:flex;gap:6px;align-items:center}
.palette-row .palette-item{flex:1}
.palette-row select{width:96px;height:30px;font-size:12px;padding:0 6px}
.palette-item{height:32px;padding:0 10px;border:1px solid var(--border);border-radius:var(--radius-md);background:var(--surface);font-size:12.5px;font-weight:500;color:var(--text-primary);text-align:left;cursor:pointer}
.palette-item:hover{border-color:var(--accent-border);background:var(--accent-light);color:var(--accent)}
/* Inspector */
.designer-inspector{font-size:12.5px}
.inspector-title{display:flex;align-items:center;gap:8px;margin-bottom:8px}
.inspector-title code{font-size:11px;color:var(--text-muted)}
.inspector-group{font-size:10.5px;font-weight:700;text-transform:uppercase;letter-spacing:.06em;color:var(--text-muted);margin:14px 0 6px;padding-bottom:4px;border-bottom:1px solid var(--border)}
.inspector-field{display:flex;flex-direction:column;gap:3px;margin-bottom:8px}
.inspector-field>span{font-size:11.5px;font-weight:600;color:var(--text-secondary)}
.inspector-field input,.inspector-field select,.inspector-field textarea{height:30px;padding:0 8px;font-size:12.5px}
.inspector-field textarea{height:auto;padding:6px 8px;font-family:var(--mono);font-size:11.5px;line-height:1.4;resize:vertical}
.inspector-field .help{display:block;font-size:11px;color:var(--text-muted);line-height:1.35}
.inspector-field .help.is-error{color:var(--destructive);font-weight:600}
.inspector-field input[aria-invalid="true"]{border-color:var(--destructive)}
.inspector-field.is-check{flex-direction:row;align-items:center;gap:8px;flex-wrap:wrap}
.inspector-field.is-check input{width:15px;height:15px;margin:0}
.inspector-field.is-check .help{flex-basis:100%}
.inspector-field.is-check>span{flex:1;min-width:0}
.inspector-note{display:flex;flex-direction:column;align-items:flex-start;gap:6px;margin:0 0 8px;padding:8px 10px;border-radius:var(--radius-md);background:var(--warning-bg);color:var(--warning);font-size:11.5px;line-height:1.4}
.inspector-color{display:flex;gap:6px;align-items:center}
.inspector-color input[type=color]{width:30px;height:30px;padding:2px;border-radius:var(--radius-sm);flex-shrink:0}
.inspector-color input[type=text]{flex:1;min-width:0}
.inspector-actions{display:flex;gap:6px;margin-top:12px}
.inspector-slot{display:flex;align-items:center;gap:4px;margin-bottom:4px}
.inspector-slot code{flex:1;font-size:11.5px}
.style-table{display:flex;flex-direction:column;gap:4px}
.style-row{border:1px solid var(--border);border-radius:var(--radius-md);padding:0 8px}
.style-row summary{cursor:pointer;padding:6px 0;font-size:12px}
.style-row-body{padding:4px 0 6px}
/* Artboard */
.designer-canvas{display:flex;flex-direction:column;min-width:0;min-height:0;position:relative}
.designer-error{margin:10px 16px 0;flex-shrink:0}
.designer-artboard-scroll{flex:1;overflow:auto;padding:24px}
.designer-artboard-stage{position:relative;width:max-content;min-width:100%;display:flex;justify-content:center}
/* `zoom` (not transform) so the scaled page also scales its layout box: a
   fitted page never leaves a horizontal scrollbar behind. The overlay reads
   getBoundingClientRect, which reports the zoomed geometry. */
.designer-artboard{--artboard-zoom:1;zoom:var(--artboard-zoom);transition:opacity .12s ease}
.designer-artboard.is-rendering{opacity:.6}
.designer-artboard .cv-render-page{width:595px;max-width:none;cursor:crosshair;border-radius:4px}
.designer-artboard .cv-render-page[data-page-size=Letter]{width:612px}
.designer-artboard .cv-render-page[data-page-size=Legal]{width:612px}
.designer-artboard [data-block-id]{cursor:pointer}
.artboard-overlay{position:absolute;inset:0;pointer-events:none}
.overlay-box{position:absolute;border:1px solid var(--accent);pointer-events:none;box-sizing:border-box}
.overlay-box.is-hover{border-style:dashed;opacity:.6}
.overlay-box.is-selected{border-width:2px;box-shadow:0 0 0 3px var(--accent-light)}
.overlay-label.is-grip{pointer-events:auto;cursor:move;user-select:none;touch-action:none}
.overlay-handle{position:absolute;right:-5px;top:50%;width:9px;height:18px;margin-top:-9px;background:var(--accent);border-radius:2px;cursor:ew-resize;pointer-events:auto}
.overlay-label{position:absolute;left:-1px;top:-19px;padding:1px 6px;background:var(--accent);color:#fff;font-size:10.5px;line-height:16px;border-radius:3px 3px 0 0;white-space:nowrap;font-family:var(--mono)}
/* Issues */
.designer-diagnostics{display:flex;flex-direction:column;gap:6px}
.diag-item{display:flex;flex-direction:column;gap:2px;padding:8px 10px;border:1px solid var(--border);border-left-width:3px;border-radius:var(--radius-md);background:var(--surface);text-align:left;cursor:pointer;font-size:12px}
.diag-item strong{font-size:12.5px}
.diag-item span{color:var(--text-muted);font-size:11px}
.diag-item small{color:var(--text-secondary);line-height:1.35}
.diag-item.is-warn{border-left-color:var(--warning)}
.diag-item.is-error{border-left-color:var(--destructive)}
.diag-item:hover{background:var(--surface-hover)}
.diag-item.is-static{cursor:default}
.designer-json{width:100%;font-family:var(--mono);font-size:12px;line-height:1.45;padding:10px;min-height:360px;resize:vertical}
@media (max-width:1200px){
  .designer-main{grid-template-columns:240px minmax(0,1fr) 280px}
}
/* At 1024 the admin sidebar is off-canvas, so three narrower columns still
   fit; stacking there put the inspector below every rendered page. */
@media (max-width:1024px){
  .designer-main{grid-template-columns:220px minmax(0,1fr) 260px}
  .designer-artboard-scroll{padding:16px}
}
@media (max-width:860px){
  .designer{height:auto;min-height:calc(100vh - var(--topbar-h))}
  .designer-main{grid-template-columns:1fr;grid-auto-rows:auto}
  .designer-rail{max-height:38vh;border-right:0;border-bottom:1px solid var(--border)}
  .designer-rail-right{border-left:0;border-top:1px solid var(--border)}
  /* The canvas scrolls inside itself so the inspector stays one scroll away. */
  .designer-canvas{height:70vh}
  .designer-artboard-scroll{padding:12px}
}
/* Same bleed as .content's padding, which only narrows at 560px. */
@media (max-width:560px){
  .designer{margin:-20px -16px -84px}
}

/* ---------- 28d. CV editor overlays and drawers ----------
   Everything the editor draws over the rendered document: the hover
   highlight, the in-place text editor, entry/list/skill action bars, the
   "+" beside section headings, the missing-data button, and the Templates /
   Customise / Sections drawers. Document typography stays the renderer's. */
/* Shared look for everything drawn over the CV: one accent, one radius,
   one ring/glow, one bar surface (light and dark themes). */
:root{
  --cv-ov-accent:var(--accent);
  --cv-ov-tint:color-mix(in srgb,var(--accent) 9%,transparent);
  --cv-ov-tint-strong:color-mix(in srgb,var(--accent) 30%,transparent);
  --cv-ov-line:color-mix(in srgb,var(--accent) 45%,transparent);
  --cv-ov-radius:6px;
  --cv-ov-radius-sm:4px;
  --cv-ov-outline:1px solid var(--cv-ov-accent);
  --cv-ov-glow:0 0 0 4px color-mix(in srgb,var(--accent) 12%,transparent),0 6px 20px -6px color-mix(in srgb,var(--accent) 45%,transparent);
  --cv-ov-bar-bg:var(--surface);
  --cv-ov-bar-border:var(--border);
  --cv-ov-shadow:0 1px 2px rgba(15,23,42,.06),0 4px 12px -2px rgba(15,23,42,.12);
}
.cv-inline-highlight{position:fixed;z-index:900;border:1px solid var(--cv-ov-line);border-radius:var(--cv-ov-radius-sm);background:var(--cv-ov-tint);pointer-events:none}
.cv-inline-editor{position:fixed;inset:0;z-index:1000}
/* The editor sits exactly on the text it replaces: no padding, border or
   fill (overlay-text.js sets font, size, colour, line height and position
   from the SVG text, which is hidden while editing). */
.cv-inline-editor-input,.cv-inline-editor-input:focus{position:absolute;box-sizing:content-box;margin:0;padding:0;border:0;border-radius:2px;min-width:0;min-height:0;resize:none;overflow:hidden;background:transparent;color:inherit;caret-color:var(--cv-ov-accent);outline:var(--cv-ov-outline);outline-offset:3px;box-shadow:var(--cv-ov-glow);white-space:pre-wrap;overflow-wrap:break-word;word-break:normal;tab-size:4;appearance:none;-webkit-appearance:none;scrollbar-width:none}
.cv-inline-editor-input::-webkit-scrollbar{display:none}
.cv-inline-editor-input::selection{background:var(--cv-ov-tint-strong);color:inherit}
.cv-inline-editor-input::placeholder{color:inherit;opacity:.38}
/* While the editor is open, hover frames and bars stay out of its way. */
body:has(.cv-inline-editor:not([hidden])) :is(.cv-item-frame,.cv-list-frame,.cv-level-actions,.cv-skill-actions,.cv-inline-highlight){visibility:hidden}
.cv-inline-editor-mirror{position:fixed;left:-9999px;top:0;visibility:hidden;white-space:pre;padding:0;margin:0;border:0;pointer-events:none}
.cv-inline-label{position:absolute;display:inline-flex;align-items:center;gap:6px;max-width:min(360px,calc(100vw - 12px));padding:2px 7px;border:1px solid var(--cv-ov-bar-border);border-radius:var(--cv-ov-radius);background:var(--cv-ov-bar-bg);box-shadow:var(--cv-ov-shadow);font:500 11px/16px var(--font-sans,Inter,system-ui,sans-serif);color:var(--text-primary);white-space:nowrap;letter-spacing:0;text-transform:none;cursor:default;user-select:none;-webkit-user-select:none}
.cv-inline-label::before{content:"";width:6px;height:6px;border-radius:50%;background:var(--cv-ov-accent);flex-shrink:0}
.cv-inline-label-name{overflow:hidden;text-overflow:ellipsis}
.cv-inline-label-hint{color:var(--text-secondary);font-weight:400}
.cv-page-badge{margin-left:4px}
/* Entry / list frames follow the hovered element (fixed, viewport coordinates). */
.cv-item-frame{position:absolute;z-index:5;border:1px dotted var(--destructive-border);border-radius:var(--cv-ov-radius);background:rgba(220,38,38,.03);pointer-events:none}
.cv-list-frame{position:absolute;z-index:6;border:1px dashed var(--cv-ov-line);border-radius:var(--cv-ov-radius);background:var(--cv-ov-tint);pointer-events:none}
.cv-item-frame .cv-block-actions,.cv-list-frame .cv-block-actions{position:absolute;top:-8px;right:auto;left:calc(100% + 8px);white-space:nowrap;display:flex;gap:6px;padding:5px;background:var(--cv-ov-bar-bg);border:1px solid var(--cv-ov-bar-border);border-radius:calc(var(--cv-ov-radius) + 2px);box-shadow:var(--cv-ov-shadow);pointer-events:auto;font-size:12px;line-height:1}
.cv-item-frame.is-left .cv-block-actions,.cv-list-frame.is-left .cv-block-actions{left:auto;right:calc(100% + 8px)}
.cv-item-frame.is-left .cv-act-menu{right:auto;left:0}
.cv-item-frame.is-flipped .cv-block-actions,.cv-list-frame.is-flipped .cv-block-actions{left:auto;right:0;top:auto;bottom:calc(100% + 6px)}
.cv-item-frame.is-flipped .cv-act-menu{top:auto;bottom:34px}
.cv-block-actions .cv-act-add,.cv-block-actions .cv-act-move{height:24px;min-width:24px;padding:0 6px;border:1px solid var(--accent-border);background:var(--accent-light);color:var(--cv-ov-accent);border-radius:var(--cv-ov-radius);font-weight:700;font-size:13px;cursor:pointer;display:inline-flex;align-items:center;justify-content:center}
.cv-block-actions .cv-act-move:disabled{opacity:.35;cursor:default}
.cv-block-actions .cv-act-del{height:24px;padding:0 8px;border:1px solid var(--destructive-border);background:var(--destructive-bg);color:var(--destructive);border-radius:var(--cv-ov-radius);font-size:12px;cursor:pointer}
.cv-act-menu{position:absolute;top:34px;right:0;display:flex;flex-direction:column;gap:4px;min-width:190px;padding:6px;background:var(--surface);border:1px solid var(--border);border-radius:8px;box-shadow:var(--shadow-md);pointer-events:auto;z-index:1}
.cv-act-menu-item{border:1px solid var(--border);background:var(--surface-alt);color:var(--text-primary);border-radius:6px;padding:6px 8px;text-align:left;cursor:pointer;font-size:12px;white-space:nowrap}
.cv-act-menu-item:hover{background:var(--accent-light);border-color:var(--accent-border);color:var(--accent)}
.cv-level-actions,.cv-skill-actions{position:absolute;right:auto;bottom:auto;width:max-content;z-index:7;display:flex;gap:4px;padding:5px;background:var(--cv-ov-bar-bg);border:1px solid var(--cv-ov-bar-border);border-radius:calc(var(--cv-ov-radius) + 2px);box-shadow:var(--cv-ov-shadow);font-size:12px;line-height:1}
.cv-skills-menu{position:absolute;z-index:8;top:auto;right:auto}
.cv-section-add-layer{position:absolute;left:0;top:0;width:0;height:0;overflow:visible;pointer-events:none;z-index:4}
.cv-section-add-layer .cv-section-add{position:absolute;top:auto;right:auto;pointer-events:auto;cursor:pointer}
/* SVG overlay mode (editor_overlay_mode = svg): the same chrome drawn inside
   each page's SVG (public/js/cv-editor/svg-overlay). Controls are drawn in
   screen pixels inside a scale(1/zoom) group; strokes never scale. The page
   lets the chrome paint into the gutter beside it, and the page showing
   chrome sits above its neighbours. */
#cv-preview-container[data-overlay-mode=svg] .cv-render-page{overflow:visible}
#cv-preview-container[data-overlay-mode=svg] .cv-render-page>svg{overflow:visible;border-radius:8px}
#cv-preview-container[data-overlay-mode=svg] .cv-render-page:is(:hover,:focus-within,.is-ov-active){z-index:2}
.cv-ov{font-family:var(--font-sans,Inter,system-ui,sans-serif);font-size:12px;letter-spacing:0;text-transform:none}
.cv-ov text{user-select:none;-webkit-user-select:none;pointer-events:none;font-style:normal}
.cv-ov rect,.cv-ov circle,.cv-ov path{vector-effect:non-scaling-stroke}
.cv-ov-highlight{fill:var(--cv-ov-tint);stroke:var(--cv-ov-line);stroke-width:1;pointer-events:none}
.cv-ov-frame{pointer-events:none;stroke-width:1}
.cv-ov-frame-item{fill:rgba(220,38,38,.03);stroke:var(--destructive-border);stroke-dasharray:1.5 2}
.cv-ov-frame-list{fill:var(--cv-ov-tint);stroke:var(--cv-ov-line);stroke-dasharray:4 3}
.cv-ov-bar-bg,.cv-ov-menu-bg,.cv-ov-label-bg{fill:var(--cv-ov-bar-bg);stroke:var(--cv-ov-bar-border);stroke-width:1;filter:drop-shadow(0 1px 1px rgba(15,23,42,.06)) drop-shadow(0 4px 6px rgba(15,23,42,.10))}
.cv-ov-btn{cursor:pointer;outline:none}
.cv-ov-btn-bg{fill:var(--accent-light);stroke:var(--accent-border);stroke-width:1}
.cv-ov-btn-text{fill:var(--cv-ov-accent);font-size:13px;font-weight:700}
.cv-ov-btn:hover .cv-ov-btn-bg{fill:var(--cv-ov-accent);stroke:var(--cv-ov-accent)}
.cv-ov-btn:hover .cv-ov-btn-text{fill:#fff}
.cv-ov-btn-del .cv-ov-btn-bg{fill:var(--destructive-bg);stroke:var(--destructive-border)}
.cv-ov-btn-del .cv-ov-btn-text{fill:var(--destructive);font-size:12px;font-weight:600}
.cv-ov-btn-del:hover .cv-ov-btn-bg{fill:var(--destructive);stroke:var(--destructive)}
.cv-ov-btn-item .cv-ov-btn-bg{fill:var(--surface-alt);stroke:var(--border)}
.cv-ov-btn-item .cv-ov-btn-text{fill:var(--text-primary);font-size:12px;font-weight:400}
.cv-ov-btn-item:hover .cv-ov-btn-bg,.cv-ov-btn-item:focus-visible .cv-ov-btn-bg{fill:var(--accent-light);stroke:var(--accent-border)}
.cv-ov-btn-item:hover .cv-ov-btn-text,.cv-ov-btn-item:focus-visible .cv-ov-btn-text{fill:var(--accent)}
.cv-ov-btn[aria-disabled=true]{opacity:.35;cursor:default;pointer-events:none}
.cv-ov-btn-ring,.cv-ov-adder-ring{fill:none;stroke:none}
.cv-ov-btn:focus-visible .cv-ov-btn-ring,.cv-ov-adder:focus-visible .cv-ov-adder-ring{stroke:var(--cv-ov-accent);stroke-width:2}
.cv-ov-adder{cursor:pointer;outline:none}
.cv-ov-adder-bg{fill:var(--accent-light);stroke:var(--accent-border);stroke-width:1}
.cv-ov-adder-icon{stroke:var(--cv-ov-accent);stroke-width:1.8;stroke-linecap:round}
.cv-ov-adder:hover .cv-ov-adder-bg{fill:var(--cv-ov-accent);stroke:var(--cv-ov-accent)}
.cv-ov-adder:hover .cv-ov-adder-icon{stroke:#fff}
.cv-ov-edit-ring{fill:none;stroke:var(--cv-ov-accent);stroke-width:1;pointer-events:none}
.cv-ov-edit-glow{fill:none;stroke:color-mix(in srgb,var(--accent) 16%,transparent);stroke-width:6;pointer-events:none}
.cv-ov-sel{fill:var(--cv-ov-tint-strong);pointer-events:none}
.cv-ov-compose{fill:var(--cv-ov-accent);pointer-events:none}
.cv-ov-caret{fill:var(--cv-ov-accent);pointer-events:none;animation:cv-ov-blink 1.06s steps(1,end) .5s infinite}
@keyframes cv-ov-blink{50%{opacity:0}}
.cv-ov-label{pointer-events:none}
.cv-ov-label-dot{fill:var(--cv-ov-accent)}
.cv-ov-label-name{fill:var(--text-primary);font-size:11px;font-weight:500}
.cv-ov-label-hint{fill:var(--text-secondary);font-size:11px;font-weight:400}
/* The SVG editor's keyboard: a textarea at the caret (IME windows open
   there), invisible, 16px so phones never zoom into it. */
.cv-ov-input{position:fixed;left:0;top:0;display:none;width:2px;height:16px;margin:0;padding:0;border:0;outline:0;opacity:0;font-size:16px;line-height:16px;resize:none;overflow:hidden;white-space:pre;background:transparent;color:transparent;caret-color:transparent;pointer-events:none;z-index:1}
.cv-ov-input.is-open{display:block}
/* While a field is edited in place, hover frames and bars stay out of its way. */
body:has(.cv-ov-input.is-open) .cv-ov :is(.cv-ov-highlight,.cv-ov-frames,.cv-ov-bars,.cv-ov-menus){visibility:hidden}
@media (prefers-reduced-motion:reduce){.cv-ov-caret{animation:none}}
/* Missing-data button (bottom right) and its menu. */
.cv-fab-wrap{position:fixed;right:24px;bottom:24px;z-index:56}
.cv-fab-wrap .cv-fab{position:static}
.cv-fab.is-empty{opacity:.4}
.cv-fab-menu{position:absolute;right:0;bottom:56px;min-width:230px;max-height:320px;overflow:auto;padding:8px;background:var(--surface);border:1px solid var(--border);border-radius:12px;box-shadow:var(--shadow-lg);display:flex;flex-direction:column;gap:6px}
.cv-fab-item{border:1px solid var(--border);background:var(--surface-alt);color:var(--text-primary);border-radius:8px;padding:7px 10px;text-align:left;cursor:pointer;font-size:12.5px}
.cv-fab-item:hover{background:var(--accent-light);border-color:var(--accent-border);color:var(--accent)}
/* Photo picker */
.cv-image-picker-dialog{max-width:940px}
.cv-image-picker-body{display:grid;grid-template-columns:minmax(240px,320px) minmax(280px,1fr);gap:14px}
.cv-image-picker-side{display:flex;flex-direction:column;gap:12px}
.cv-image-picker-stage{padding:12px;display:flex;flex-direction:column;gap:10px}
.cv-image-preview{display:flex;justify-content:center;align-items:center;min-height:300px;background:var(--surface-alt);border-radius:10px;border:1px solid var(--border)}
.cv-image-preview canvas{box-shadow:var(--shadow-md);background:#fff;cursor:grab;touch-action:none}
.cv-image-zoom{display:flex;align-items:center;gap:10px}
.cv-image-zoom input[type=range]{flex:1}
.cv-image-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(84px,1fr));gap:8px;max-height:240px;overflow:auto}
.cv-image-tile{border:1px solid var(--border);background:var(--surface);border-radius:8px;padding:4px;cursor:pointer}
.cv-image-tile img{display:block;width:100%;height:76px;object-fit:cover;border-radius:6px;background:var(--surface-alt)}
.cv-image-tile:hover{border-color:var(--accent)}
.cv-image-dropzone{border:1px dashed var(--border-strong);border-radius:10px;background:var(--surface-alt);padding:14px;text-align:center;font-size:12.5px;color:var(--text-secondary);cursor:pointer}
.cv-image-dropzone.is-over{border-color:var(--accent);background:var(--accent-light)}
.cv-image-status.is-error{color:var(--destructive)}
/* Sections drawer */
.cv-section-list{display:flex;flex-direction:column;gap:6px}
.cv-section-item{display:flex;align-items:center;gap:6px;padding:6px 8px;border:1px solid var(--border);border-radius:var(--radius-md);background:var(--surface)}
.cv-section-item.is-hidden .cv-section-name{color:var(--text-muted);text-decoration:line-through}
.cv-section-name{flex:1;font-size:13.5px;font-weight:500}
.cv-section-item .icon-btn{width:30px;height:30px}
.cv-section-name{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.cv-section-rename{flex:1;min-width:0;font-size:13.5px;padding:4px 6px;border:1px solid var(--accent);border-radius:6px;background:var(--surface);color:var(--text-primary)}
.cv-section-item .cv-section-rename-btn,.cv-section-item .cv-section-reset{color:var(--text-muted)}
.cv-custom-sections{display:flex;flex-direction:column;gap:6px;margin:-2px 0 4px 18px;padding-left:10px;border-left:2px solid var(--border)}
.cv-section-sub{background:var(--surface-hover)}
.cv-section-add-custom{align-self:flex-start}
.segmented{display:flex;gap:2px;margin-top:8px;background:var(--surface-hover);border-radius:var(--radius-md);padding:3px}
.segmented-btn{flex:1;min-height:32px;border:0;background:transparent;border-radius:6px;font:inherit;font-size:13px;color:var(--text-secondary);cursor:pointer}
.segmented-btn.is-active{background:var(--surface);color:var(--text-primary);font-weight:600;box-shadow:0 1px 2px rgb(0 0 0 / .08)}
.segmented-btn:focus-visible{outline:2px solid var(--accent);outline-offset:1px}
/* Customise drawer */
.cv-customise-group{margin-bottom:18px}
.swatch-row{display:flex;flex-wrap:wrap;gap:8px;margin-top:8px}
.swatch{width:30px;height:30px;border-radius:var(--radius-full);border:2px solid var(--surface);box-shadow:0 0 0 1px var(--border);cursor:pointer;padding:0}
.swatch.is-active{box-shadow:0 0 0 2px var(--accent)}
.swatch.is-default{background:repeating-linear-gradient(45deg,#fff 0 4px,#e2e8f0 4px 8px)}
.swatch-custom{width:30px;height:30px;border-radius:var(--radius-full)}
/* Template drawer categories */
.tpl-category{font-size:11px;font-weight:700;text-transform:uppercase;letter-spacing:.06em;color:var(--text-muted);margin:14px 0 6px}
.tpl-category:first-child{margin-top:0}
.tpl-name .pill{margin-left:auto;height:18px;font-size:10.5px}
/* Preview page: pages stack the same way as the editor */
.cv-preview-pages{width:100%;display:flex;justify-content:center}
.cv-preview-hint{display:none}
@media (max-width:768px){
  .qa-profile-head{gap:10px}
  .qa-profile-row{grid-template-columns:minmax(0,1fr)}
  .qa-profile-row .pill{justify-self:start}
  .cv-preview-hint{display:block}
  .cv-edit-bar .btn-sm{padding:0 10px}
  .cv-image-picker-body{grid-template-columns:1fr}
}

/* ---------- 28e. CV editor: toolbar, entry sheet, phone editing ----------
   The status keeps a fixed box (is-idle only hides it) so the CV name never
   reflows while "Saving…" shows. The secondary actions are display:contents
   on wide screens and fold into a "More" menu on phones. */
.cv-edit-bar > .row{flex-wrap:nowrap}
.cv-edit-name{flex:1 1 auto;min-width:8ch}
.cv-status{flex:0 0 86px;white-space:nowrap}
.cv-status.is-idle{visibility:hidden}
.cv-edit-actions{align-items:center;justify-content:flex-end;position:relative}
.cv-edit-more{display:contents}
.cv-edit-more-btn{display:none}
#btn-template{max-width:200px}
#btn-template .truncate{min-width:0}
.cv-customise-warning{margin-top:8px;padding:6px 9px;border-radius:var(--radius-md);background:var(--warning-bg);color:var(--warning-text,#92400e);border:1px solid var(--warning-border);font-size:12px;line-height:1.4;font-weight:500}
/* Entry sheet: a compact dialog on wide screens, a bottom sheet on phones. */
.cv-sheet{z-index:85}
.cv-sheet-panel{max-width:460px}
.cv-sheet-head{display:flex;align-items:center;justify-content:space-between;gap:8px}
.cv-sheet-grip{display:none}
.cv-sheet-body .field:last-child{margin-bottom:0}
.cv-sheet-body textarea{min-height:88px;resize:vertical}
.cv-sheet-foot{align-items:center}
.cv-sheet-extra{display:flex;gap:6px;margin-right:auto;flex-wrap:wrap}
/* Phone zoom control, shown by main.js only at ≤640px. */
.cv-zoom{position:fixed;left:12px;bottom:calc(16px + env(safe-area-inset-bottom,0px));z-index:56;display:flex;align-items:center;gap:2px;padding:4px;background:var(--surface);border:1px solid var(--border);border-radius:var(--radius-full);box-shadow:var(--shadow-md)}
.cv-zoom .icon-btn{width:36px;height:36px;font-size:18px;border-radius:var(--radius-full)}
.cv-zoom-level{min-width:52px;height:36px;border:0;background:transparent;color:var(--text-secondary);font-size:12.5px;font-weight:600;font-variant-numeric:tabular-nums;border-radius:var(--radius-full)}
.cv-zoom-level:hover{background:var(--surface-hover)}
@media (max-width:640px){
  .cv-edit-bar{row-gap:8px}
  .cv-edit-bar > .row{width:100%}
  .cv-edit-actions{width:100%;flex-wrap:nowrap;gap:6px}
  .cv-edit-actions #btn-template{flex:1 1 auto;min-width:0;max-width:none}
  .cv-edit-more-btn{display:inline-flex}
  .cv-edit-more{display:none;position:absolute;right:0;top:calc(100% + 6px);z-index:60;flex-direction:column;align-items:stretch;gap:4px;min-width:190px;padding:6px;background:var(--surface);border:1px solid var(--border);border-radius:var(--radius-lg);box-shadow:var(--shadow-lg)}
  .cv-edit-more.is-open{display:flex}
  .cv-edit-more .btn{justify-content:flex-start;height:40px;width:100%}
  /* Fit-to-width page; zoom scales the layout box so the canvas pans. */
  .cv-edit-canvas{align-items:flex-start;touch-action:pan-x pan-y;height:calc(100vh - var(--topbar-h) - 108px)}
  #cv-preview-container{margin-inline:auto;zoom:var(--cv-zoom,1);min-height:0}
  .cv-sheet{align-items:flex-end;padding:0}
  .cv-sheet-panel{max-width:none;width:100%;max-height:88vh;border-radius:16px 16px 0 0;padding-bottom:env(safe-area-inset-bottom,0px);transform:translateY(12px);transition:transform .18s ease}
  .cv-sheet.is-open .cv-sheet-panel{transform:none}
  .cv-sheet-grip{display:block;width:40px;height:4px;border-radius:2px;background:var(--border-strong);margin:8px auto 0}
  .cv-sheet-head{padding-top:10px}
  .cv-sheet-body input,.cv-sheet-body select,.cv-sheet-body textarea{font-size:16px}
  .cv-sheet-foot .btn{height:40px}
  body.has-cv-sheet .cv-fab-wrap,body.has-cv-sheet .cv-zoom{display:none}
}
@media (prefers-reduced-motion:reduce){.cv-sheet-panel{transition:none}}

/* Editor/designer state and layout hooks referenced from scripts and views. */
.text-center{text-align:center}
.cv-customise{display:flex;flex-direction:column}
.cv-image-picker .dialog{margin:auto}
.cv-list-actions{white-space:nowrap}
#cv-preview-container.is-editing [data-edit-bind]{cursor:pointer}
/* Placeholders in empty fields (editor renders only, template-engine.js
   placeholderFor): dimmed while editing, hidden with Edit off and anywhere
   else a page is shown. They keep their space either way. */
[data-edit-placeholder]{visibility:hidden}
#cv-preview-container.is-editing [data-edit-placeholder]{visibility:visible;fill-opacity:.38}
.designer-rail-left{min-width:0}

/* ---------- 29. Responsive ---------- */
@media (max-width:1200px){
  .stats-5,.stats-6{grid-template-columns:repeat(3,minmax(0,1fr))}
}
@media (max-width:1024px){
  .sidebar{transform:translateX(-100%)}
  .sidebar.open{transform:translateX(0)}
  .main{margin-left:0}
  .hamburger{display:flex}
  .stats,.grid-4,.grid-3,.job-results,.plan-grid{grid-template-columns:repeat(2,minmax(0,1fr))}
  .grid-2,.grid-sidebar,.split-pane{grid-template-columns:minmax(0,1fr)}
  .user-chip .name{display:none}
  .pub-nav{display:none}
  /* The mobile panel carries these, so showing them here too overflows the bar. */
  .pub-actions{display:none}
  .mobile-menu-btn{display:flex}
  .footer-grid{grid-template-columns:repeat(2,minmax(0,1fr))}
  .topbar-search{display:none}
}
@media (max-width:768px){
  .grid-3,.job-results,.plan-grid{grid-template-columns:minmax(0,1fr)}
  /* Below this width the right-hand column would squeeze the salary, so the
     list row folds into a single column like the grid card. */
  :root[data-jobs-view="list"] .job-results > .job-card{grid-template-columns:minmax(0,1fr);
    grid-template-areas:"head" "tags" "aside"}
  :root[data-jobs-view="list"] .job-results > .job-card .jc-aside{text-align:left;align-self:start}
  :root[data-jobs-view="list"] .job-results > .job-card .jc-meta{flex-direction:row;align-items:center;justify-content:space-between;margin-top:12px;padding-top:10px;border-top:1px solid var(--border)}
  :root[data-jobs-view="list"] .job-results > .job-card .jc-aside p{white-space:normal}
  :root[data-jobs-view="list"] .job-results > .job-card .jc-aside p:first-child{margin-top:12px}
  .hero h1{font-size:32px}
  .filter-bar{grid-template-columns:minmax(0,1fr)}
  .cta-band{margin:0 16px;padding:32px 20px}
  .footer-grid{grid-template-columns:minmax(0,1fr)}
  .wrap,.wrap-sm{padding:0 16px}
  /* The hero pill dissolves on mobile: two bare inputs sharing one card read as
     a single control. Each field becomes a standalone input instead, matching
     how every other input in the system looks. (The previous adjacent-sibling
     hairline never applied — .search-divider sits between the two fields.) */
  .search-bar{flex-direction:column;background:transparent;border:0;box-shadow:none;padding:0;gap:10px}
  .search-bar:focus-within{border:0;box-shadow:none}
  /* flex:none matters: the base rule sets flex:1, whose flex-basis:0 wins over
     height on the main axis once the bar becomes a column. */
  .search-field{flex:none;width:100%;min-width:0;height:48px;background:var(--surface);
                border:1px solid var(--border-strong);border-radius:var(--radius-md)}
  .search-field:focus-within{border-color:var(--accent);box-shadow:0 0 0 3px var(--accent-light)}
  .search-divider{display:none}
  .search-bar .btn{width:100%;height:48px}
  /* Stacked, the row's bottom edge sits below the submit button, so the panel
     goes back to hanging off its own field. It anchors to `.search-field`, not
     to the `.combobox` wrapper — the wrapper starts after the leading pin icon,
     which would inset the panel from the field's left edge. */
  .search-field{position:relative}
  .filter-bar .combobox{position:relative}
  .page-head{flex-direction:column;align-items:stretch}
  .stepper{overflow-x:auto}
  .step-label{display:none}
  .cv-canvas{padding:16px 12px}
  .cv-page{padding:24px 20px}
}
@media (max-width:560px){
  .stats,.grid-4,.grid-3,.stats-3,.stats-5,.stats-6{grid-template-columns:minmax(0,1fr)}
  .content{padding:20px 16px 84px}
  /* Keep the full-bleed editor flush with the narrower mobile content padding. */
  .cv-edit-bar{margin:-20px -16px 0;padding:12px 16px}
  .cv-edit-canvas{margin:0 -16px -84px;padding:20px 12px}
  .topbar{padding:0 16px}
  .toast-container{left:16px;right:16px;max-width:none}
  .hero{padding:40px 0 32px}
  .chip{height:34px;padding:0 14px}
  .cv-fab{bottom:104px}
}

/* One-time secret reveal (new API key): high-contrast monospace, one-click select. */
.secret-reveal{display:flex;align-items:center;gap:12px;padding:12px 12px 12px 16px;border:1px solid var(--border-strong);border-radius:var(--radius-md);background:var(--surface)}
.secret-reveal-value{flex:1;min-width:0;padding:0;background:none;border:0;font-family:var(--mono);font-size:15px;font-weight:600;line-height:1.5;color:var(--text-primary);letter-spacing:.02em;overflow-wrap:anywhere;user-select:all;-webkit-user-select:all}
.secret-reveal-copy{flex-shrink:0;min-width:96px}
@media (max-width:640px){.secret-reveal{flex-direction:column;align-items:stretch}.secret-reveal-copy{width:100%}}
/* One-off purchases: product cards on /dashboard/purchases and the quick-apply pay-once offer. */
.offer-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(220px,1fr));gap:14px}
.offer-card{display:flex;flex-direction:column;gap:6px;padding:16px;border:1px solid var(--border);border-radius:var(--radius-lg);background:var(--surface)}
.offer-card.is-highlighted{border-color:var(--accent);box-shadow:0 0 0 3px color-mix(in srgb,var(--accent) 18%,transparent)}
.offer-card-head{display:flex;align-items:baseline;justify-content:space-between;gap:10px}
.offer-card-head h3{font-size:15px;margin:0}
.offer-price{font-size:18px;font-weight:700;letter-spacing:-.01em;white-space:nowrap}
.offer-actions{display:flex;flex-wrap:wrap;gap:8px;margin-top:auto;padding-top:8px}
.qa-pay-once{border:1px solid var(--border);border-radius:var(--radius-md);padding:14px;background:var(--surface)}
.qa-pay-once + .qa-pay-once{margin-top:10px}
.qa-pay-once-head{display:flex;align-items:baseline;justify-content:space-between;gap:10px}
/* Credit counters are three short numbers: keep them in one row on phones. */
@media (max-width:640px){
  .stats.credit-stats{grid-template-columns:repeat(3,minmax(0,1fr));gap:8px}
  .credit-stats .stat-card{padding:12px}
  .credit-stats .stat-value{font-size:22px;margin-top:4px}
  .credit-stats .stat-label,.credit-stats .text-xs{font-size:11.5px}
}
/* /jobs list controls: view switch, "Still open", posted window and sort in one
   row; selects are full-width by default, so they are sized to their text. */
.jobs-list-controls{flex-wrap:nowrap;justify-content:flex-end;align-items:center;gap:10px}
.jobs-list-controls select{width:auto;min-width:140px}
.jobs-open-toggle{white-space:nowrap;font-size:13px;margin:0}
@media (max-width:760px){
  .jobs-list-controls{display:grid;grid-template-columns:1fr 1fr;width:100%;gap:8px}
  .jobs-list-controls select{width:100%;min-width:0}
  .jobs-open-toggle{justify-self:end}
}
.jc-deadline{color:var(--text-secondary);font-weight:500}
