/* ═══════════════════════════════════════════════════════════════════════
   CV SHEET — the printed document's typography. ONE source of truth.

   Loaded three ways, always this exact file:
     • the SPA           <link> in index.html  → the live preview
     • the PDF renderer  server/assets/cv-kit  → headless Chromium
     • the test suite    read straight off disk

   `tests/verify.py` asserts the SPA copy and the renderer copy are
   byte-identical, so the preview and the download can never drift apart.

   ── Two rules ──────────────────────────────────────────────────────────
   1. NO hard-coded sizes. Every font-size and every vertical gap is
      `calc(N × var(--cv-scale))` (or × var(--cv-gap)). The auto-fit engine
      (cv-fit.js) owns those two variables; a literal `10pt` here would be
      invisible to it and would break the fit for that one element only.
   2. NO identity fonts (Thmanyah / brand colours / rounded corners). This is
      an ATS document, not a marketing page — see CLAUDE.md D11.

   ── Fonts ──────────────────────────────────────────────────────────────
   Self-hosted and STATIC, deliberately:
     • Carlito is metric-compatible with Calibri, so English CVs keep the
       exact look and the exact line breaks they had when the sheet said
       `font-family: Calibri`.
     • IBM Plex Sans Arabic covers the Arabic. Calibri's Arabic cannot be
       used: the renderer runs on Linux where it does not exist, and a
       silently substituted font would put the preview and the PDF back out
       of sync.
     • Static, never variable: Chromium/Skia cannot subset a variable font
       instance and falls back to Type3 glyph outlines with NO ToUnicode map,
       which makes the PDF's text unextractable. Measured — see PLAN.md §3.
   ═══════════════════════════════════════════════════════════════════════ */

@font-face {
  font-family: 'CV Latin';
  src: url('/fonts/Carlito-Regular.ttf') format('truetype');
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: 'CV Latin';
  src: url('/fonts/Carlito-Bold.ttf') format('truetype');
  font-weight: 700;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: 'CV Arabic';
  src: url('/fonts/IBMPlexSansArabic-Regular.ttf') format('truetype');
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: 'CV Arabic';
  src: url('/fonts/IBMPlexSansArabic-Bold.ttf') format('truetype');
  font-weight: 700;
  font-style: normal;
  font-display: swap;
}

.cv-preview {
  /* ── the two knobs the auto-fit engine turns ── */
  --cv-scale: 1;    /* typography scale; base font = 10pt × this      */
  --cv-gap: 0px;    /* extra space added to each section gap           */

  --cv-pad: 54px;   /* sheet margin — 14.29mm = 40.5pt                 */
  --cv-fs: calc(10pt * var(--cv-scale));

  font-family: 'CV Latin', 'CV Arabic', sans-serif;
  font-size: var(--cv-fs);
  line-height: 1.4;

  /* Optional ligatures OFF — an ATS requirement, not a taste call.
     A ligature is one glyph standing for several characters, so it needs a
     multi-character ToUnicode entry (`<04B1> <06280631>` for بر). Extractors
     disagree about those: poppler resolves them, pypdf drops some. Turning
     `liga`/`clig`/`dlig` off means "بر", "شر", "تى", "fi" … are drawn as
     separate glyphs with clean 1:1 mappings that every extractor agrees on.
     `rlig` stays ON: lam-alef (لا) is a REQUIRED Arabic ligature and killing
     it would break the script visually. `calt` stays on for the same reason —
     Arabic joining depends on contextual shaping. */
  font-variant-ligatures: no-common-ligatures no-discretionary-ligatures;
  font-feature-settings: 'liga' 0, 'clig' 0, 'dlig' 0;
  color: #000000;
  background: #ffffff;
  /* 210mm, not 794px: the renderer's @page is A4 in mm, and a 0.3px
     mismatch between the measured width and the printed width is enough to
     reflow one line onto a page that has already been sized. */
  width: 210mm;
  padding: var(--cv-pad);
  box-sizing: border-box;
}

/* The first item on a page never carries a top gap — the fit engine's
   per-page padding owns that space. */
.cv-preview > *:first-child {
  margin-top: 0;
}

/* The Arabic family must come first for RTL sheets or Carlito's Latin
   metrics would drive the line height of Arabic text. */
.cv-preview[dir='rtl'] {
  font-family: 'CV Arabic', 'CV Latin', sans-serif;
}

.cv-preview * {
  font-family: inherit;
  color: #000000;
  background: transparent;
}

/* ── Header block ─────────────────────────────────────────── */
.cv-name {
  font-size: calc(16pt * var(--cv-scale));
  font-weight: bold;
  text-align: center;
  margin-bottom: calc(2px * var(--cv-scale));
}

.cv-contact-line {
  font-size: calc(9pt * var(--cv-scale));
  text-align: center;
  margin-bottom: calc(2px * var(--cv-scale));
}

.cv-contact-line a {
  color: #000000;
  text-decoration: none;
}

/* Each contact item is ONE unbreakable token.
 *
 * Without this the browser breaks a LinkedIn URL at its hyphens, and the break
 * is real: it lands in the PDF's text layer, so an ATS reads
 * `linkedin.com/in/ahmed-` and files a candidate with a dead link. Measured on
 * three of five shipped fixtures before this rule existed — invisible on the
 * page, because the two halves still sit next to each other on screen.
 *
 * Scoped to the CHILDREN, never the line: `nowrap` on the container would
 * push a four-item contact line off the sheet. The line still wraps freely at
 * the `|` separators, which is exactly where a break is harmless.
 */
.cv-contact-line > span,
.cv-contact-line > a {
  white-space: nowrap;
}

.cv-professional-title {
  font-size: calc(11pt * var(--cv-scale));
  font-weight: bold;
  text-align: center;
  margin-bottom: calc(10px * var(--cv-scale));
}

.cv-header-block {
  text-align: center;
  margin-bottom: calc(10px * var(--cv-scale));
}

/* Latin-only runs (email, phone, LinkedIn, URLs) inside an RTL sheet.
   Without the isolation the bidi algorithm reorders the space-separated
   groups of a phone number — "+966 50 123 4567" prints as
   "4567 123 50 966+" — because the spaces between European numbers take
   the paragraph's RTL direction. `isolate` pins the whole run LTR. */
.cv-ltr {
  direction: ltr;
  unicode-bidi: isolate;
}

/* ── Section headings ─────────────────────────────────────── */
.cv-section-heading {
  font-size: calc(11pt * var(--cv-scale));
  font-weight: bold;
  text-transform: uppercase;
  color: #000000;
  border-bottom: 1px solid #000000;
  padding-bottom: calc(2px * var(--cv-scale));
  margin-top: calc(14px * var(--cv-scale) + var(--cv-gap));
  margin-bottom: calc(6px * var(--cv-scale));
  letter-spacing: 0.03em;
}

/* Arabic is a connected script: `letter-spacing` breaks the joins and
   `uppercase` is meaningless for it. */
.cv-preview[dir='rtl'] .cv-section-heading,
.cv-preview[dir='rtl'] .cv-section-heading * {
  letter-spacing: normal;
  text-transform: none;
}

/* ── Entries ──────────────────────────────────────────────── */
.cv-entry {
  margin-bottom: calc(8px * var(--cv-scale));
}

.cv-entry-header {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  font-weight: bold;
  font-size: var(--cv-fs);
  margin-top: calc(8px * var(--cv-scale));
  margin-bottom: 0;
}

.cv-job-title {
  font-weight: bold;
  font-size: var(--cv-fs);
  margin-bottom: calc(3px * var(--cv-scale));
}

/* List dots are real DOM text, not a native ::marker — a ::marker lands on
   the far LEFT even in an RTL sheet. */
.cv-bullets {
  list-style: none;
  padding-inline-start: calc(6px * var(--cv-scale));
  margin: calc(2px * var(--cv-scale)) 0 calc(6px * var(--cv-scale)) 0;
}

.cv-bullets li {
  font-size: var(--cv-fs);
  margin-bottom: calc(2px * var(--cv-scale));
  display: flex;
  align-items: baseline;
  gap: calc(7px * var(--cv-scale));
}

.cv-li-dot {
  flex-shrink: 0;
}

/* ── Body paragraphs ──────────────────────────────────────── */
.cv-text {
  font-size: var(--cv-fs);
  margin: 0;
  line-height: 1.4;
}

.cv-text-tight {
  font-size: var(--cv-fs);
  margin: 0 0 calc(1px * var(--cv-scale)) 0;
}

.cv-text-item {
  font-size: var(--cv-fs);
  margin: 0 0 calc(2px * var(--cv-scale)) 0;
}

/* ── Certificates / skills / languages ────────────────────── */
.cv-cert-line {
  display: flex;
  justify-content: space-between;
  font-size: var(--cv-fs);
  margin-bottom: calc(3px * var(--cv-scale));
}

.cv-cert-date {
  font-weight: bold;
  white-space: nowrap;
  padding-inline-start: calc(16px * var(--cv-scale));
}

.cv-skill-line {
  font-size: var(--cv-fs);
  margin-bottom: calc(3px * var(--cv-scale));
}

.cv-skill-label {
  font-weight: bold;
}

.cv-skills-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  column-gap: calc(26px * var(--cv-scale));
  align-items: start;
}

/* ═══════════════════════════════════════════════════════════════════════
   DESIGNS

   Four ways for the same CV to look. `data-cv-template` is stamped on the
   sheet root by CVPreview, and `renderVectorPdf` posts that root's outerHTML,
   so a design reaches the PDF with no server change at all.

   Three rules every variant obeys, and none of them is a style preference:

   1. NO hard-coded sizes — same rule as the rest of this file. Every size is
      `calc(N * var(--cv-scale))`, or the auto-fit engine cannot see it and the
      design silently breaks the page fit for that one element.
   2. SINGLE COLUMN, TEXT ONLY. No multi-column layout, no tables, no images,
      no inverted colour blocks. The machine-readability axis is 25 of the 100
      points this product sells, and a "nicer" design that costs a quarter of
      the score is not a design, it is a defect. White-on-dark is excluded for
      the same family of reasons: it reads to a crude ATS heuristic the way
      keyword-stuffing does, and no visual gain is worth that.
   3. `classic` OWNS NOTHING HERE. It is the sheet as defined above, so a CV
      saved before designs existed renders byte-identically today.

   What actually differs is the shape of the page: how the header presents
   itself, how a section announces itself, and how tightly the entries sit.
   ═══════════════════════════════════════════════════════════════════════ */

/* ── Compact ───────────────────────────────────────────────────────────
   The same design, breathing less. For the CV that lands on page two by
   three lines: the fit engine will not shrink below 9.5pt (D33), so when
   type cannot get smaller the spacing is the only honest place left to go. */
.cv-preview[data-cv-template='compact'] .cv-header-block,
.cv-preview[data-cv-template='compact'] .cv-professional-title {
  margin-bottom: calc(6px * var(--cv-scale));
}

.cv-preview[data-cv-template='compact'] .cv-section-heading {
  margin-top: calc(9px * var(--cv-scale) + var(--cv-gap));
  margin-bottom: calc(4px * var(--cv-scale));
}

.cv-preview[data-cv-template='compact'] .cv-entry {
  margin-bottom: calc(5px * var(--cv-scale));
}

.cv-preview[data-cv-template='compact'] .cv-entry-header {
  margin-top: calc(5px * var(--cv-scale));
}

.cv-preview[data-cv-template='compact'] .cv-bullets {
  margin: calc(1px * var(--cv-scale)) 0 calc(4px * var(--cv-scale)) 0;
}

.cv-preview[data-cv-template='compact'] .cv-bullets li {
  margin-bottom: calc(1px * var(--cv-scale));
}

.cv-preview[data-cv-template='compact'] .cv-cert-line,
.cv-preview[data-cv-template='compact'] .cv-skill-line {
  margin-bottom: calc(2px * var(--cv-scale));
}

/* ── Banner ────────────────────────────────────────────────────────────
   The name announced between two rules, and section headings that lead with
   weight rather than with a line under them. Nothing is inverted and nothing
   is filled: the "banner" is two borders and the space between them. */
.cv-preview[data-cv-template='banner'] .cv-header-block {
  border-top: calc(3px * var(--cv-scale)) solid #000000;
  border-bottom: 1px solid #000000;
  padding: calc(9px * var(--cv-scale)) 0;
  margin-bottom: calc(12px * var(--cv-scale));
}

.cv-preview[data-cv-template='banner'] .cv-name {
  font-size: calc(19pt * var(--cv-scale));
  letter-spacing: 0.06em;
  margin-bottom: calc(4px * var(--cv-scale));
}

/* Arabic is a connected script: letter-spacing breaks the joins. Same
   exception the base sheet already makes for section headings. */
.cv-preview[dir='rtl'][data-cv-template='banner'] .cv-name {
  letter-spacing: normal;
}

.cv-preview[data-cv-template='banner'] .cv-professional-title {
  font-size: calc(10.5pt * var(--cv-scale));
  font-weight: normal;
  margin-bottom: calc(4px * var(--cv-scale));
}

.cv-preview[data-cv-template='banner'] .cv-section-heading {
  font-size: calc(11.5pt * var(--cv-scale));
  border-bottom: none;
  border-inline-start: calc(4px * var(--cv-scale)) solid #000000;
  padding-inline-start: calc(7px * var(--cv-scale));
  padding-bottom: 0;
  margin-top: calc(15px * var(--cv-scale) + var(--cv-gap));
  margin-bottom: calc(7px * var(--cv-scale));
}

/* ── Academic ──────────────────────────────────────────────────────────
   Left-aligned (start-aligned, so it flips correctly in Arabic), roomier,
   and with the date on its own line instead of pushed to the far edge — the
   long-form register a research or teaching CV is read in. */
.cv-preview[data-cv-template='academic'] .cv-header-block,
.cv-preview[data-cv-template='academic'] .cv-name,
.cv-preview[data-cv-template='academic'] .cv-professional-title,
.cv-preview[data-cv-template='academic'] .cv-contact-line {
  text-align: start;
}

.cv-preview[data-cv-template='academic'] .cv-name {
  font-size: calc(20pt * var(--cv-scale));
  margin-bottom: calc(3px * var(--cv-scale));
}

.cv-preview[data-cv-template='academic'] .cv-professional-title {
  font-weight: normal;
  margin-bottom: calc(6px * var(--cv-scale));
}

.cv-preview[data-cv-template='academic'] .cv-header-block {
  border-bottom: 1px solid #000000;
  padding-bottom: calc(8px * var(--cv-scale));
  margin-bottom: calc(14px * var(--cv-scale));
}

.cv-preview[data-cv-template='academic'] .cv-section-heading {
  text-transform: none;
  letter-spacing: normal;
  border-bottom: none;
  font-size: calc(12pt * var(--cv-scale));
  margin-top: calc(18px * var(--cv-scale) + var(--cv-gap));
  margin-bottom: calc(5px * var(--cv-scale));
}

/* The date leaves the far edge and follows the employer on its own line.
   `display: block` on a flex row is all it takes; the children are already
   in reading order in the DOM, which is also the order an ATS reads them. */
.cv-preview[data-cv-template='academic'] .cv-entry-header,
.cv-preview[data-cv-template='academic'] .cv-cert-line {
  display: block;
}

.cv-preview[data-cv-template='academic'] .cv-cert-date {
  font-weight: normal;
  padding-inline-start: 0;
}

.cv-preview[data-cv-template='academic'] .cv-entry {
  margin-bottom: calc(11px * var(--cv-scale));
}

/* ── Pagination safety ────────────────────────────────────────
   The renderer assigns blocks to explicit page containers, so these are
   belt-and-braces: they only matter for the rare block that is taller than
   a whole page and therefore has to be split by Chromium itself. */
.cv-preview [data-pdf-block] {
  break-inside: avoid;
}
.cv-section-heading {
  break-after: avoid;
}
