:root {
  /* ---- Colour ----
     Every pair that carries text clears WCAG AA (4.5:1). --placeholder is the
     one that had to change: the old #9aa19b sat at 2.64:1, and placeholder text
     here is not decoration — it's the Georgian example text that shows a
     first-timer what counts, which makes it the most important copy in the app.
     #6e756f is 4.73:1 on --surface and 4.57:1 on --surface-sunken, while
     staying far lighter than --ink-soft (8.04:1) so it still reads as "not
     filled in yet". */
  --paper: #eff1ec;
  --surface: #ffffff;
  --surface-sunken: #fbfbfa;
  --ink: #1e2321;
  --ink-soft: #4b524d;
  --placeholder: #6e756f;
  --accent: #2f6f5e;
  --accent-strong: #275c4e;
  --accent-soft: #e4ede9;
  --on-accent: #ffffff;
  --danger: #a34b3f;
  --danger-soft: #f7e9e7;
  /* Amber, and only ever for an expiry warning. "Expires in 30 days" used to
     be drawn on --accent-soft — a seafarer whose medical runs out next month
     was being shown that in the colour of good news. Deliberately not a second
     accent: nothing else on the page may use it.
     --warning is 6.21:1 on white and 5.41:1 on --warning-soft. */
  --warning: #8a5500;
  --warning-soft: #fbeed6;

  /* Two weights of edge. Cards keep the lightest — a card is identified by
     its fill and shadow, so its border is decoration and can stay soft. An
     input's border is the only thing saying where the input is, so it needs
     to clear WCAG 1.4.11 (3:1 non-text contrast). The old value, #c3c7bd, sat
     at 1.72:1. #8f968b is the lightest value that passes, at 3.04:1. There
     used to be a third weight for hover (--line-strongest, 2.30:1 — also
     failing); hover now promotes straight to --accent instead, the way the
     secondary buttons already did. */
  --line: #d7dad2;
  --line-strong: #8f968b;

  /* ---- Semantic aliases ----
     The base values above say what a colour *is*; these say what it is *for*.
     Rules below reference the alias wherever the design system does, so a
     consumer can retheme by role rather than by hunting for every literal. */
  --bg-app: var(--paper);
  --surface-card: var(--surface);
  --surface-entry: var(--surface-sunken);
  --text-primary: var(--ink);
  --text-secondary: var(--ink-soft);
  --text-placeholder: var(--placeholder);
  --text-accent: var(--accent);
  --border-card: var(--line);
  --border-input: var(--line-strong);
  /* Hover promotes to the accent — the same move the secondary buttons make.
     A third grey existed only to be a hover state and never cleared 3:1. */
  --border-input-hover: var(--accent);

  --shadow-card: 0 1px 3px rgba(0, 0, 0, 0.05);
  --shadow-paper: 0 8px 30px rgba(0, 0, 0, 0.08);
  --shadow-btn: 0 2px 8px rgba(30, 35, 33, 0.16);
  --focus-ring: 0 0 0 4px rgba(47, 111, 94, 0.12);
  --focus-ring-danger: 0 0 0 4px rgba(163, 75, 63, 0.12);

  /* ---- Type scale ----
     Six steps. It was eight, and five of them sat between 12 and 16px —
     12/13/14/15/16 don't read as levels, they read as per-element tuning that
     never got cleaned up. Each step here is a visible jump.

     --text-base is 16px because that is also the threshold under which iOS
     Safari zooms the page in on focus. Every input is base, so the old
     phone-only "bump inputs to 16px" override is gone: they were never
     supposed to be smaller than this in the first place. */
  --text-xs: 12px;   /* hints, section counts, the CV's own small print */
  --text-sm: 14px;   /* field labels, secondary controls, help text */
  --text-base: 16px; /* inputs, buttons, body copy */
  --text-lg: 18px;   /* step titles */
  --text-2xl: 28px;  /* the logo, and the name on the CV */

  /* ---- Document scale ----
     The document does not borrow the interface's scale — it is a printed
     artefact skimmed by a stranger in about twenty seconds, not a UI read at
     arm's length, and 1.5 leading (right for the interface) sets Georgian
     Mkhedruli too tight to skim: its ascenders and descenders are tall. */
  --doc-name: 30px;   /* the one thing read first */
  --doc-entry: 15px;  /* an entry title */
  --doc-body: 14px;   /* description and summary copy */
  --doc-meta: 13px;   /* contact line, institution, sub-lines, dates */
  --doc-label: 11px;  /* mono section titles, table headers */
  --doc-table: 12px;

  --doc-lead: 1.62;
  --doc-lead-tight: 1.28; /* names and entry titles */

  /* ~62 Georgian characters — what the eye takes in one sweep. */
  --doc-measure: 66ch;
  /* Dates sit in their own right-aligned tabular column. A floor, not a fixed
     width: it was fixed at 104px, measured against "2021 – 2025", and the
     seafarer profile writes "2019-09-01 – 2023-07-01". */
  --doc-dates: 104px;

  /* Section rhythm. Wider than the UI's, because white space is the only
     device a one-colour document has to separate one part from another. */
  --doc-section-gap: 26px;
  --doc-entry-gap: 16px;

  /* Below these SHEET widths (never window widths) the document stacks. All
     sit under A4's ~673px of printed content, so print resolves to wide.

     TRAP: a CSS @container condition cannot take a var(), so the queries
     below spell these out as literals. These three tokens are the written
     record of what those numbers mean — change one and you must change its
     query by hand. */
  --doc-stack-table: 560px;
  --doc-stack-details: 600px;
  --doc-stack-entry: 380px;

  --leading-body: 1.5;
  --leading-note: 1.6;
  --tracking-label: 0.05em;   /* step numbers, preview caption */
  --tracking-section: 0.08em; /* CV section titles */
  --weight-regular: 400;
  --weight-medium: 500;
  --weight-semibold: 600;

  /* ---- Type aliases ---- */
  --type-logo: var(--weight-semibold) var(--text-2xl)/1.2 var(--font-display);
  --type-step-title: var(--weight-semibold) var(--text-lg)/1.3 var(--font-display);
  --type-body: var(--weight-regular) var(--text-base)/var(--leading-body) var(--font-body);
  --type-label: var(--weight-medium) var(--text-sm)/1.4 var(--font-body);
  --type-hint: var(--weight-regular) var(--text-xs)/var(--leading-note) var(--font-body);
  --type-mono-label: var(--weight-medium) var(--text-xs)/1.2 var(--font-mono);

  /* Functional measurements, deliberately not on the spacing scale: these are
     ergonomics, not rhythm, and forcing them through it would hide that. */
  --touch-min: 44px;      /* thumb target floor */
  --control-height: 48px; /* inputs, buttons, add-entry */
  --fixed-bar-clearance: 96px; /* room under the phone download bar */
  --measure-note: 46ch;   /* the storage note's max width */
  --shell-max: 1440px;
  --paper-pad-block: 48px;
  --paper-pad-inline: 44px;

  --border-hairline: 1px solid var(--line);

  /* ---- Spacing ----
     Was 22 distinct values. The functional measurements are tokenised above
     rather than folded in here: they're ergonomics, not design rhythm, and
     putting them on this scale would hide that. */
  --space-1: 4px;
  --space-2: 8px;
  --space-3: 12px;
  --space-4: 16px;
  --space-5: 20px;
  --space-6: 24px;
  --space-7: 32px;
  --space-8: 40px;
  --space-9: 48px;
  --space-10: 64px;

  --radius-sm: 4px;
  --radius-md: 8px;
  --radius-lg: 12px;  /* inputs, buttons, entry cards */
  --radius-xl: 16px;  /* the section cards */
  --radius-pill: 999px;

  --transition: 150ms ease;

  --font-display: 'Noto Serif Georgian', 'Georgia', serif;
  --font-body: 'Noto Sans Georgian', 'Segoe UI', sans-serif;
  --font-mono: 'JetBrains Mono', monospace;
}

* {
  box-sizing: border-box;
}

/* The browser's `[hidden] { display: none }` is an ordinary declaration, so
   any rule of ours that sets display beats it. A retired expiry badge did
   exactly that — `display: inline-block` meant every document that was *not*
   expiring still painted an empty pill, because hiding it set an attribute
   the stylesheet was overriding. `.field-note { display: flex }` would do the
   same today, and a closed <details> whose contents are `display: grid` is
   the same failure again. Stated once, with the weight to hold. */
[hidden] {
  display: none !important;
}

body {
  margin: 0;
  background: var(--paper);
  color: var(--ink);
  font-family: var(--font-body);
  line-height: var(--leading-body);
}

button, input, textarea, select {
  font-family: inherit;
  font-size: var(--text-base);
}

:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

/* Colour and border only — never size or position, so nothing shifts under the
   pointer. Until now no transitions existed at all, which meant the
   reduced-motion rule below was switching off motion that was never there. */
button,
input,
textarea,
select,
.skip-link {
  transition: background-color var(--transition), border-color var(--transition),
    color var(--transition), opacity var(--transition);
}

@media (prefers-reduced-motion: reduce) {
  * { transition: none !important; animation: none !important; }
}

/* Was 1280, which left 160px of a 1440px screen unused while both panels were
   short of room — and giving the preview 58% of a 1280px cap is what started
   clipping values in the form. A wider cap serves both: the sheet gets closer
   to a real page, and the form gets its field width back. */
.app-shell {
  max-width: var(--shell-max);
  margin: 0 auto;
  padding: 0 var(--space-6) var(--space-10);
}

.app-header {
  padding: var(--space-7) 0 var(--space-5);
  border-bottom: 1px solid var(--line);
  margin-bottom: var(--space-7);
}

/* Logo and reset on one row, tagline full width beneath — the same structure
   works on a 320px phone and a 1440px desktop. */
/* The title takes the free space and the controls stay a cluster on the right.
   With space-between and two children, 1440px stranded them at opposite edges
   with a void between, and at tablet width the switcher's label wrapped to two
   lines against the heading. */
.app-header-row {
  display: flex;
  align-items: center;
  gap: var(--space-4);
}

.app-header-row .logo {
  flex: 1;
  min-width: 0;
}

.logo {
  font-family: var(--font-display);
  /* The spec asked for a 32px page title. There is no page title — this is the
     only candidate — and a round was spent shrinking this header because at
     390px it ate a quarter of the screen before the first field. 28px is the
     step that gives the weight without undoing that. */
  font-size: var(--text-2xl);
  font-weight: 700;
  color: var(--ink);
  margin: 0; /* it's the page's h1 now, so drop the UA heading margin */
}

/* Out of the way until it's tabbed to. Without it, reaching the download
   button means tabbing through every field in the form. */
.skip-link {
  position: absolute;
  left: -9999px;
  top: 0;
  padding: var(--space-3) var(--space-4);
  background: var(--accent);
  color: var(--on-accent);
  border-radius: 0 0 var(--radius-md) 0;
  z-index: 20;
}

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

.tagline {
  margin: var(--space-2) 0 0;
  color: var(--ink-soft);
  font-size: var(--text-base);
}

/* 42/58, not 50/50: the preview is the artefact the user is actually making,
   and the form is the means. The form column still lands around 500px at
   1280px wide, which the container query below turns into two comfortable
   fields. */
.app-main {
  display: grid;
  grid-template-columns: minmax(0, 42fr) minmax(0, 58fr);
  gap: var(--space-8);
  align-items: start;
}



/* ---- Form panel ---- */

.form-panel {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
}

/* ---- Steps ----
   One open at a time, at every width. All six (seven for the seafarer CV)
   used to sit open at once — 24 fields, or over fifty — as one endless
   column, which was the biggest thing between a first-timer and a finished
   CV. Closed, a step is one row: number, name, count. So six rows fit on one
   screen and you can see what you have not done.

   A real <button> + is-open class rather than <details>: the accordion is
   controlled (main.js holds which step is open), and `open` is an attribute
   no parent can coordinate across siblings. */
.step {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-xl);
  box-shadow: var(--shadow-card);
  /* Field grids ask the STEP's width, not the window's: at 900px the window
     is roomy while the column is 414px. */
  container-type: inline-size;
}

.step.is-open {
  border-color: var(--line-strong);
}

/* align-items: flex-start, not center: below ~340px the title wraps to two
   lines and a vertically-centred count sat wedged against the second line.
   Aligned to the top they read as two columns of a row instead. */
.step-header {
  display: flex;
  align-items: flex-start;
  gap: var(--space-3);
  width: 100%;
  min-height: var(--control-height);
  padding: var(--space-4) var(--space-5);
  border: none;
  background: none;
  font: inherit;
  color: inherit;
  cursor: pointer;
  text-align: left;
  border-radius: var(--radius-xl);
}

.step-header:hover {
  background: var(--surface-sunken);
}

.step.is-open .step-header {
  border-radius: var(--radius-xl) var(--radius-xl) 0 0;
}

.step-title {
  font-family: var(--font-display);
  font-size: var(--text-lg);
  font-weight: 600;
}

.step-chevron {
  flex: none;
  width: 12px;
  height: 8px;
  background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 8'%3E%3Cpath d='M1 1.5 6 6.5 11 1.5' fill='none' stroke='%234b524d' stroke-width='1.75' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E") no-repeat center;
  transition: transform var(--transition);
}

.step.is-open .step-chevron {
  transform: rotate(180deg);
}

.step-body {
  padding: 0 var(--space-5) var(--space-5);
}

/* A neutral count — "4 documents", "3 / 12 filled". It tells you where your own
   gaps are without claiming any of them is required; see stepStatus() in
   render/form.js for why that distinction matters here. */
.step-status {
  margin-left: auto;
  font-size: var(--text-xs);
  color: var(--ink-soft);
  font-variant-numeric: tabular-nums;
  /* "4 / 4 filled" broken over two lines reads like a fraction gone wrong.
     The title beside it wraps instead — it has words to spare. */
  white-space: nowrap;
}

.step-status.is-empty {
  color: var(--placeholder);
}

.step-number {
  font-family: var(--font-mono);
  font-size: var(--text-sm);
  color: var(--accent);
  letter-spacing: var(--tracking-label);
}

.field-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--space-4);
}

@container (max-width: 440px) {
  .field-grid {
    /* minmax(0, …), not a bare 1fr: a bare 1fr floors the track at its
       content's min-content width, and a <select> reports the width of its
       widest option ("Certificate of Competency"). That pushed the document
       card 5px past a 320px screen. */
    grid-template-columns: minmax(0, 1fr);
  }
}

.field {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}

.field-full {
  grid-column: 1 / -1;
}

.field-label {
  font-size: var(--text-sm);
  font-weight: 500;
  color: var(--ink-soft);
}

input, textarea, select {
  min-height: 48px;
  /* 12px, not 16: the height comes from min-height, and every horizontal pixel
     spent on padding is one an email address doesn't get. */
  padding: var(--space-3);
  border: 1px solid var(--border-input);
  border-radius: var(--radius-lg);
  background: var(--surface);
  color: var(--ink);
}

input:hover, textarea:hover, select:hover {
  border-color: var(--border-input-hover);
}

/* The ring is a box-shadow so it can be soft and tinted, which an outline
   can't. But forced-colors mode throws box-shadows away, and this is the only
   focus indicator these controls have — so a transparent outline rides along
   and becomes visible exactly there. */
input:focus-visible, textarea:focus-visible, select:focus-visible {
  border-color: var(--accent);
  box-shadow: var(--focus-ring);
  outline: 2px solid transparent;
  outline-offset: 2px;
}


/* The native picker stays — on a phone the OS list beats anything we could
   build, and it is the control this user already knows. Only the *closed*
   state is restyled, so a select reads as one of our fields instead of a bare
   browser control sitting next to them. The chevron is an inline data URI, not
   an image file: nothing on this page may make a network request. */
select {
  appearance: none;
  -webkit-appearance: none;
  /* It was reporting `cursor: default` — the one control on the page that
     didn't look clickable. */
  cursor: pointer;
  padding-right: var(--space-7);
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 8'%3E%3Cpath d='M1 1.5 6 6.5 11 1.5' fill='none' stroke='%234b524d' stroke-width='1.75' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right var(--space-3) center;
  background-size: 12px 8px;
}

textarea {
  /* No drag handle: the box sizes itself to its content, so the handle only
     ever fought back — drag it taller, type one character, it snapped shut. */
  resize: none;
  min-height: 64px;
  /* Height is driven by content (see autoGrow in render/form.js), so an inner
     scrollbar would only ever hide text that the box is about to grow to fit. */
  overflow: hidden;
}

.field-invalid {
  border-color: var(--danger);
}

/* Must come after the generic focus rule above, which would otherwise paint a
   green border and a green ring on a field flagged as invalid. */
.field-invalid:focus-visible {
  border-color: var(--danger);
  box-shadow: var(--focus-ring-danger);
}

input::placeholder, textarea::placeholder {
  color: var(--placeholder);
}

/* ---- A problem, under the field it is about ----
   Not a summary above the section, which tells you something is wrong
   somewhere. Two levels, and they are the system's existing semantics: amber
   will become a problem, red already is one. No icons — a 2px rule in the
   colour, then words. */
.field-note {
  display: flex;
  gap: var(--space-2);
  align-items: flex-start;
  font-size: var(--text-xs);
  line-height: var(--leading-note);
  margin: var(--space-1) 0 0;
}

.field-note-mark {
  flex: none;
  width: 2px;
  align-self: stretch;
  border-radius: var(--radius-pill);
  background: var(--warning);
}

.field-note-text { color: var(--warning); }
.field-note.is-danger .field-note-mark { background: var(--danger); }
.field-note.is-danger .field-note-text { color: var(--danger); }
.field-note.is-quiet .field-note-mark { background: var(--line-strong); }
.field-note.is-quiet .field-note-text { color: var(--ink-soft); }

/* Guidance that does not disappear. This copy used to live in placeholders,
   where it vanished on the first keystroke and never showed what a finished
   line looks like. Same words, now permanent, and no control to discover. */
.step-guide {
  margin: 0 0 var(--space-4);
  font-size: var(--text-sm);
  line-height: var(--leading-note);
  color: var(--ink-soft);
  max-width: 62ch;
}

/* ---- Orientation ----
   Names what is still empty, each one a link. Deliberately not a progress
   bar: a percentage implies a target and makes an unfinished CV look like a
   failed one, while naming the gaps implies a choice — which is the truth,
   because nothing in this product is required. */
.ledger {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2) var(--space-3);
  align-items: baseline;
  margin: var(--space-5) 0 0;
  font-size: var(--text-xs);
  color: var(--ink-soft);
}

.ledger-label {
  font-family: var(--font-mono);
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
  color: var(--placeholder);
}

.ledger button {
  border: none;
  background: none;
  padding: 0;
  font: inherit;
  color: var(--accent);
  cursor: pointer;
  text-decoration-line: underline;
  text-decoration-color: var(--line-strong);
  text-underline-offset: 3px;
}

.ledger button:hover { text-decoration-color: var(--accent); }

/* Closing one step and opening the next is the whole path through the form,
   so it is a real control rather than something you assemble by scrolling. */
.step-advance {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  margin-top: var(--space-5);
  padding-top: var(--space-4);
  border-top: 1px solid var(--line);
}

.step-advance .spacer { margin-left: auto; }

.help-text {
  font-size: var(--text-sm);
  color: var(--ink-soft);
  margin: 0 0 var(--space-4);
}

/* An empty list used to be a bare "+ Add…" button floating under a heading,
   which reads as an unfinished screen rather than as a section you haven't got
   to yet. One line says which it is. */
.empty-state {
  margin: 0;
  padding: var(--space-4) 0 0;
  color: var(--placeholder);
  font-size: var(--text-sm);
}

.reorder-hint {
  margin: var(--space-3) 0 0;
}

/* Plain links, no nav bar and no banner: the guides exist for someone who has
   finished and scrolled, not for someone filling in a field. On phones the
   fixed download bar sits below this, which the shell's padding already
   clears. */
.site-footer {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-3) var(--space-6);
  max-width: var(--shell-max);
  margin: 0 auto;
  padding: var(--space-5) var(--space-6) var(--space-10);
  border-top: 1px solid var(--line);
  font-size: var(--text-sm);
}

/* Three stacked links measuring 21px tall. WCAG 2.5.8 puts the minimum target
   at 24px, and these sit at the very bottom of a phone screen where a thumb is
   least accurate. Padding rather than font-size: they are meant to be quiet. */
/* 24px clears the WCAG 2.5.8 floor and was set deliberately for that. 44px is
   what a thumb actually wants, and these sit at the very bottom of a phone
   screen where accuracy is worst — the padding costs nothing, they are still
   quiet links. */
.site-footer a {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  color: var(--ink-soft);
}

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

/* Quiet on purpose — reassurance, not an announcement. */
/* Two statements, not four wedged into one paragraph. The save indicator used
   to sit inline in the middle of this prose as an inline-flex pill, and the
   price was a display:block span inside the same <p> — a block element cutting
   a sentence in half, which is exactly why it read ragged. */
.form-notes {
  margin-top: var(--space-6);
  padding: 0 var(--space-1);
  max-width: var(--measure-note);
  font-size: var(--text-xs);
  line-height: 1.6;
  color: var(--ink-soft);
}

.storage-note {
  margin: 0;
}

.recovery-note {
  margin: 0;
  padding: var(--space-4);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  background: var(--surface);
  color: var(--ink);
}

.entry-cards {
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
}

.entry-card {
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  padding: var(--space-4);
  background: var(--surface-sunken);
}

.add-entry-btn {
  margin-top: var(--space-4);
  min-height: 48px;
  padding: var(--space-2) var(--space-4);
  border: 1px dashed var(--accent);
  border-radius: var(--radius-lg);
  background: transparent;
  color: var(--accent);
  cursor: pointer;
  font-weight: 500;
}

.add-entry-btn:hover {
  background: var(--accent-soft);
}

.entry-actions {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  margin-top: var(--space-3);
}

.move-entry-btn {
  border: 1px solid var(--line);
  border-radius: var(--radius-md);
  background: var(--surface);
  color: var(--ink-soft);
  cursor: pointer;
  /* The arrow glyphs come from a system fallback (we vendor only the georgian
     and latin subsets), so they need a little size to read as controls. */
  font-size: var(--text-base);
  font-weight: 600;
  line-height: 1;
  padding: var(--space-1) var(--space-3);
}

.move-entry-btn:hover:not(:disabled) {
  border-color: var(--accent);
  color: var(--accent);
}

.move-entry-btn:disabled {
  opacity: 0.35;
  cursor: default;
}

.remove-entry-btn {
  /* Pushed away from the reorder controls so it can't be hit by accident. */
  margin-left: auto;
  border: none;
  background: none;
  color: var(--danger);
  font-size: var(--text-sm);
  cursor: pointer;
  padding: 0;
}

/* It deletes an entry and gave no feedback at all before the click. */
.remove-entry-btn:hover {
  text-decoration: underline;
}

.skills-section .skill-input {
  width: 100%;
  margin-top: var(--space-2);
}

.skill-tags {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
  margin-top: var(--space-3);
}

.skill-tag {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  background: var(--accent-soft);
  color: var(--accent);
  border-radius: var(--radius-pill);
  padding: var(--space-2) var(--space-2) var(--space-2) var(--space-3);
  font-size: var(--text-sm);
}

.skill-tag button {
  border: none;
  background: none;
  color: var(--accent);
  cursor: pointer;
  font-size: var(--text-base);
  line-height: 1;
  padding: var(--space-1) var(--space-2);
  border-radius: var(--radius-pill);
}

/* Same gap as the delete button: it removes a skill and said nothing. */
.skill-tag button:hover {
  background: var(--surface);
  color: var(--danger);
}

/* ---- Preview panel ---- */

.preview-panel {
  position: sticky;
  top: 24px;
}

/* The label belongs to the panel it names, so it sits at the left with the
   actions pushed to the right by margin rather than by space-between spreading
   two items to opposite edges of a 1440px column. */
.preview-toolbar {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  margin-bottom: var(--space-3);
}

.preview-toolbar .toolbar-actions {
  margin-left: auto;
}

.preview-label {
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  color: var(--ink-soft);
  letter-spacing: 0.05em;
  text-transform: uppercase;
}

.toolbar-actions {
  display: flex;
  align-items: center;
  gap: var(--space-3);
}

.print-btn {
  padding: var(--space-3) var(--space-5);
  min-height: 48px;
  border: none;
  border-radius: var(--radius-lg);
  background: var(--accent);
  color: var(--on-accent);
  font-weight: 600;
  cursor: pointer;
  /* Elevation is part of the transition list below, so the lift eases in with
     the darkening rather than snapping. */
  transition: background-color var(--transition), box-shadow var(--transition),
    transform var(--transition);
}

.print-btn:hover {
  background: var(--accent-strong);
  box-shadow: var(--shadow-btn);
  transform: translateY(-1px);
}

.print-btn:active {
  transform: translateY(0);
}

.label-short {
  display: none;
}

/* Desktop keeps both panels visible, so there is nothing to toggle between. */
.view-toggle {
  display: none;
  align-items: center;
  min-height: 48px;
  padding: var(--space-2) var(--space-4);
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-lg);
  background: var(--surface);
  color: var(--ink);
  cursor: pointer;
  font-size: var(--text-base);
}

.view-toggle:hover {
  border-color: var(--accent);
  color: var(--accent);
}

/* Bordered like the other secondary controls (.view-toggle, .move-entry-btn)
   so it reads as a control rather than stray text — it had no border or
   background at all. The destructive red is kept for hover only: clear about
   what it does without competing with the download button. */
.reset-btn {
  flex: none;
  min-height: 48px;
  padding: var(--space-2) var(--space-4);
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-lg);
  background: var(--surface);
  color: var(--ink-soft);
  cursor: pointer;
  font-size: var(--text-sm);
}

.reset-btn:hover {
  border-color: var(--danger);
  color: var(--danger);
}

.preview-paper {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  box-shadow: var(--shadow-paper);
  padding: var(--space-9) 44px;
  min-height: 600px;
  /* The document's own breakpoints ask this box, never the window: at a
     900px window the preview column can be 395px of content, so a
     window-based @media query measures the wrong box entirely. */
  container-type: inline-size;
}

/* ---- Empty preview ----
   See render/empty-state.js. The sheet used to be blank below the name, which
   is the first thing every visitor sees; now it shows the shape of the document
   they are about to build. print.css drops the whole block. */
.cv-ghost {
  margin-top: var(--space-6);
}

.cv-ghost-hint {
  margin: 0 0 var(--space-6);
  color: var(--ink-soft);
  font-size: var(--doc-body);
}

.cv-ghost-section {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  margin-bottom: var(--space-6);
  padding-top: var(--space-4);
  border-top: 1px solid var(--line);
}

.cv-ghost-title {
  font-family: var(--font-mono);
  font-size: var(--doc-label);
  letter-spacing: var(--tracking-section);
  text-transform: uppercase;
  color: var(--placeholder);
}

.cv-ghost-line {
  height: 8px;
  border-radius: var(--radius-pill);
  background: var(--line);
}

.cv-ghost-line.is-short {
  width: 60%;
}

/* At full ink a placeholder name reads as content the user didn't type. */
.cv-name.is-placeholder {
  color: var(--placeholder);
}

/* Every breakpoint below asks the SHEET's width, never the window's: the
   preview column is ~395px of content inside a 924px window, so a window
   query measures the wrong box. That is why .preview-paper is a query
   container. */
.cv-document {
  font-family: var(--font-body);
  font-size: var(--doc-body);
  line-height: var(--doc-lead);
  color: var(--ink);
  max-width: var(--doc-measure);
}

/* The seafarer CV's eleven-column tables need the full sheet. */
.cv-document.is-wide {
  max-width: none;
}

.cv-name {
  font-family: var(--font-display);
  font-size: var(--doc-name);
  font-weight: 600;
  line-height: var(--doc-lead-tight);
  letter-spacing: -0.01em;
  margin: 0;
  color: var(--ink);
}

.cv-rank {
  margin: var(--space-1) 0 0;
  font-size: var(--doc-entry);
  color: var(--accent);
}

/* Each fact is its own element rather than one middot-joined run: a parser
   reads an email far more reliably that way, and a human scans it just as
   fast. The separator is drawn by CSS, so it never lands in extracted text —
   this is the single change that most improves ATS email extraction. */
.cv-contact {
  margin: var(--space-2) 0 0;
  font-size: var(--doc-meta);
  color: var(--ink-soft);
  display: flex;
  flex-wrap: wrap;
  gap: 0 var(--space-3);
}

.cv-contact span:not(:last-child)::after {
  content: ' ·';
  color: var(--line-strong);
}

/* The title sits on the baseline of a rule that runs to the measure — a
   document device rather than a UI one, and it survives print, where colour
   does not. */
.cv-section {
  margin-top: var(--doc-section-gap);
}

.cv-section-head {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  margin-bottom: var(--space-3);
}

.cv-section-title {
  font-family: var(--font-mono);
  font-size: var(--doc-label);
  letter-spacing: var(--tracking-section);
  text-transform: uppercase;
  color: var(--accent);
  margin: 0;
  white-space: nowrap;
}

.cv-section-rule {
  flex: 1;
  height: 1px;
  background: var(--line);
}

/* A section can double as navigation: clicking it opens the step that fills
   it. A link, never a mode — the document itself is never editable. */
/* Sized to the title it wraps, not to the section. See section() in
   render/preview.js for why the whole section is no longer the control. */
.cv-jump {
  display: inline-block;
  text-align: left;
  border: 1px solid transparent;
  border-radius: var(--radius-md);
  background: none;
  padding: var(--space-1) var(--space-2);
  /* Pulled left so the title still lines up with the text below it. */
  margin: 0 0 0 calc(-1 * var(--space-2));
  font: inherit;
  color: inherit;
  cursor: pointer;
  transition: background-color var(--transition), border-color var(--transition);
}

.cv-jump:hover {
  background: var(--surface-sunken);
  border-color: var(--line);
}

.cv-entry {
  margin-bottom: var(--doc-entry-gap);
}

.cv-entry:last-child {
  margin-bottom: 0;
}

/* Dates in their own right-aligned tabular column, so a reader can run down
   them without reading the roles. */
.cv-entry-row {
  display: grid;
  grid-template-columns: 1fr minmax(var(--doc-dates), max-content);
  gap: var(--space-3);
  align-items: baseline;
}

.cv-entry-title {
  font-size: var(--doc-entry);
  font-weight: 600;
  line-height: var(--doc-lead-tight);
}

.cv-entry-dates {
  font-family: var(--font-mono);
  font-size: var(--doc-meta);
  color: var(--ink-soft);
  text-align: right;
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
}

.cv-entry-sub {
  font-size: var(--doc-meta);
  color: var(--ink-soft);
  margin-top: 2px;
}

.cv-entry-desc {
  font-size: var(--doc-body);
  margin: var(--space-2) 0 0;
}

.cv-body {
  font-size: var(--doc-body);
  margin: 0;
}

@container (max-width: 380px) {
  .cv-entry-row { grid-template-columns: 1fr; }
  .cv-entry-dates { text-align: left; }
}

/* Everything below is a base rule. It has to sit ABOVE the @media blocks:
   media queries add no specificity, so a base rule placed after them wins on
   source order and silently undoes the responsive one. That had already
   happened here — `.profile-switch { margin-left: auto; margin-right: 12px }`
   was overriding the phone rule that gives the switcher its own row. */

/* ---- Maritime profile ---- */

.profile-switch {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  margin-left: auto;
  margin-right: var(--space-2);
}

.profile-switch-label {
  font-size: var(--text-xs);
  color: var(--ink-soft);
  /* Two words that mean one thing. Wrapped, they read as a stray line of grey
     text leaning on the h1. */
  white-space: nowrap;
}

.field-hint {
  font-size: var(--text-xs);
  color: var(--ink-soft);
}

.field-computed {
  background: var(--surface-sunken);
  color: var(--ink-soft);
}

/* Group headings between steps. The seafarer form is eleven steps, which is
   the right depth per step but a long undifferentiated list to look at — these
   split it into four named stretches. Text only: nothing to click, nothing to
   open, no state to get out of sync with the steps under it. */
.step-group {
  margin: var(--space-6) 0 var(--space-3);
  font-size: var(--text-sm);
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--text-secondary);
}

.step-group:first-child {
  margin-top: 0;
}

.entry-more {
  margin-top: var(--space-3);
}

.entry-more summary {
  font-size: var(--text-sm);
  color: var(--accent);
  cursor: pointer;
}

.entry-more .field-grid {
  margin-top: var(--space-3);
}

/* Hoisted out of .entry-more because it is not about .entry-more: the browser
   hides a closed <details>'s contents by setting them to `display: none` in
   the UA sheet, and any rule of ours that sets display — `.field-grid` above,
   for one — simply overrides it. So "More detail" opened and closed while its
   five fields stayed on screen the whole time, several thousand pixels of
   scrolling on an eight-contract history that was supposed to be hidden. Any
   <details> on this page needs the same guarantee. */
details:not([open]) > *:not(summary) {
  display: none;
}

/* Expiry is drawn by .field-note under the date, plus the border tint below.
   The badge and the section roll-up that used to carry it are gone: between
   them they said something was wrong somewhere and left the reader hunting
   for the line it meant. Border tint only here — a border cannot say what is
   wrong, so the note carries the words and the two must always agree. */
.entry-card.entry-expired {
  border-color: var(--danger);
}

/* `entry-expiring` was set on the card and then styled by nothing at all, so
   the badge was the only sign. */
.entry-card.entry-expiring {
  border-color: var(--warning);
}

.photo-field {
  display: flex;
  align-items: flex-start;
  gap: var(--space-4);
  margin-bottom: var(--space-4);
}

/* The passport-photo box and the control that fills it are the same element —
   see renderPhotoField(). Dashed while empty, because that is how every other
   "add something" affordance on this page is drawn. */
.photo-picker {
  width: 96px;
  height: 124px;
  flex: none;
  display: flex;
  align-items: center;
  justify-content: center;
  border: 1px solid var(--line);
  border-radius: var(--radius-md);
  background: var(--surface-sunken);
  overflow: hidden;
  cursor: pointer;
}

.photo-picker.is-empty {
  border: 1px dashed var(--accent);
}

.photo-picker.is-empty:hover {
  background: var(--accent-soft);
}

.photo-picker img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.photo-empty {
  font-size: var(--text-sm);
  color: var(--accent);
}

.photo-actions {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--space-2);
}

/* Visually hidden but still focusable — it is the only focusable part of this
   control, since a <label> never is. The ring goes on the box it labels. */
.photo-input {
  position: absolute;
  width: 1px;
  height: 1px;
  opacity: 0;
  pointer-events: none;
}

.photo-input:focus-visible + .photo-picker {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

.save-warning {
  margin: var(--space-3) 0 0;
  padding: var(--space-3);
  border-radius: var(--radius-md);
  background: var(--danger-soft);
  color: var(--danger);
  font-size: var(--text-sm);
}

/* ---- Document tables ----
   One DOM structure. A real <table> on desktop and in print (so thead can
   repeat across printed pages), stacked label/value cards on phones. */

/* A real <table> specifically so thead repeats on every printed page of a
   multi-page sea service history. Hairlines under rows only, never vertical
   rules; numerics right-aligned and tabular so tonnages compare down the
   column. */
.cv-table-wrap {
  max-width: 100%;
  overflow-x: auto;
}

.cv-table {
  width: 100%;
  border-collapse: collapse;
  font-size: var(--doc-table);
}

.cv-table th {
  font-family: var(--font-mono);
  font-size: var(--doc-label);
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--accent);
  text-align: left;
  padding: 0 var(--space-3) var(--space-2) 0;
  white-space: nowrap;
  font-weight: 500;
}

.cv-table td {
  padding: var(--space-2) var(--space-3) var(--space-2) 0;
  border-top: 1px solid var(--line);
  vertical-align: top;
}

.cv-table .num,
.cv-table .num-head {
  text-align: right;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

.cv-total {
  margin: var(--space-3) 0 0;
  font-size: var(--doc-meta);
  color: var(--ink-soft);
}

/* Eleven columns against a ~395px sheet: stack into label/value rows.
   print.css puts it back, because A4 is wider than this threshold. */
@container (max-width: 560px) {
  .cv-table,
  .cv-table tbody,
  .cv-table tr,
  .cv-table td { display: block; }
  .cv-table thead { display: none; }
  .cv-table tr {
    border-top: 1px solid var(--line);
    padding: var(--space-2) 0;
  }
  .cv-table td {
    border: none;
    padding: 1px 0;
    display: flex;
    gap: var(--space-2);
    font-size: var(--doc-meta);
  }
  .cv-table td::before {
    content: attr(data-label);
    flex: 0 0 38%;
    color: var(--ink-soft);
  }
  .cv-table .num { text-align: left; }
}

.cv-header-maritime {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--space-5);
}

.cv-photo {
  width: 84px;
  height: 108px;
  object-fit: cover;
  border: 1px solid var(--line);
  flex: none;
}

/* Two pairs per row on paper: a crewing manager checks fifteen facts, and a
   single column wastes half the sheet. */
.cv-details {
  display: grid;
  grid-template-columns: max-content 1fr max-content 1fr;
  gap: var(--space-1) var(--space-3);
  margin: 0;
  font-size: var(--doc-meta);
}

.cv-details dt {
  color: var(--ink-soft);
}

.cv-details dd {
  margin: 0;
}

@container (max-width: 600px) {
  .cv-details { grid-template-columns: max-content 1fr; }
}


/* ---- Screen-only layout ----
   Every width query below is scoped to `screen`, and that is load-bearing
   rather than tidiness. A printed page has a CSS width too — A4 is ~794px, or
   ~673px once @page margins are taken off — so an unscoped `(max-width: …)`
   query applies while printing. The phone layout then lands on paper: at one
   point that meant `.app-main:not(.showing-preview) .preview-paper { display:
   none }` hid the entire document and every exported PDF came out blank.
   print.css should never have to undo these; it never sees them now. */

/* A narrower gutter between the two panels once the window stops being wide.
   40px of empty space between them costs each panel 20px it can actually use. */
@media screen and (max-width: 1100px) {
  .app-main {
    gap: var(--space-6);
  }
}

/* Was 900px, which meant every tablet in portrait — iPad at 820, most Android
   at 800 — lost the live preview and got the phone's toggle instead, despite
   having room for both. At 840 the two panels come out around 380px each:
   narrow for a preview, but a narrow preview beats no preview. */
@media screen and (max-width: 840px) {
  .app-main {
    grid-template-columns: 1fr;
  }

  /* Stacked, the form would otherwise run the full 772px of a tablet, which is
     a very long way for the eye to travel between a 12px label and its field.
     Centred inside a comfortable measure instead. */
  .form-column,
  .preview-panel {
    width: 100%;
    max-width: 720px;
    margin-left: auto;
    margin-right: auto;
  }

  /* Stacked, the preview and its toolbar sit below the entire form — several
     screens of scrolling before the download button comes into view. Sticky
     can't fix it: a grid item's sticky constraint is its own grid area. Pin
     the toolbar to the viewport instead, so the button is always in reach. */
  .preview-panel {
    position: static;
  }

  .preview-toolbar {
    position: fixed;
    left: 0;
    right: 0;
    bottom: 0;
    z-index: 10;
    margin: 0;
    padding: var(--space-3) var(--space-4) calc(var(--space-3) + env(safe-area-inset-bottom));
    background: var(--surface);
    border-top: 1px solid var(--line);
  }

  /* The "შენი CV" caption labels the preview, which is now nowhere near this
     bar — dropping it leaves room for the toggle and the download button. */
  .preview-toolbar .preview-label {
    display: none;
  }

  .toolbar-actions {
    justify-content: flex-end;
  }

  .reset-btn,
  .print-btn,
  .view-toggle {
    white-space: nowrap;
  }

  /* Only one panel on screen at a time, chosen by the toggle. */
  .view-toggle {
    display: inline-flex;
  }

  .app-main.showing-preview .form-column {
    display: none;
  }

  .app-main:not(.showing-preview) .preview-paper {
    display: none;
  }

  /* Keep the fixed bar from covering the last lines of the CV. */
  .app-shell {
    padding-bottom: var(--fixed-bar-clearance);
  }

  /* The footer is a sibling of .app-shell, so the clearance above does nothing
     for it: scrolled to the bottom, the download bar sat on top of the last one
     or two guide links. Present but untappable is the round-6 bug again. */
  .site-footer {
    padding-bottom: var(--fixed-bar-clearance);
  }
}

/* ---- Phone-sized screens ----
   Deliberately at the end of the file: media queries add no specificity, so a
   block placed above the base rules loses to them on equal specificity. Kept
   separate from the 900px breakpoint so tablets keep the two-column form,
   which suits them fine. */

@media screen and (max-width: 600px) {
  /* Two columns leaves ~139px per field at 390px wide, which cuts an ordinary
     name or email off mid-word while it's being typed. */
  /* Comfortable thumb targets. Delete was 18px tall and is the one control
     nobody wants to hit by accident. */
  .move-entry-btn {
    min-width: 44px;
    min-height: 44px;
  }

  .remove-entry-btn {
    min-height: 44px;
    padding: 0 var(--space-1);
  }

  .reset-btn {
    min-height: 44px;
  }

  /* Was 42px — the one input type that missed the touch minimum. */
  select {
    min-height: 44px;
  }

  /* 36px, and it is the most-tapped control on the page: a seafarer with
     fifteen certificates presses "+ Add document" fifteen times. */
  .add-entry-btn {
    min-height: 44px;
  }

  /* Table stacking deliberately does NOT live here. It is an @container query
     against the sheet (see .cv-table above), because the binding constraint
     is the preview column — ~395px of content inside a 924px window — and a
     window query measures the wrong box entirely. That was the bug where the
     seafarer sheet overflowed its paper on desktop. */

  .photo-field {
    gap: var(--space-3);
  }

  /* Logo + switcher + reset came to 369px against a 360px screen. The overflow
     zoomed the whole page out, which slid the fixed download bar below the
     visible area — the button was there, just unreachable. The switcher takes
     its own row instead. */
  .app-header-row {
    flex-wrap: wrap;
    /* Tighter than the desktop --space-4, because at 320px the switcher and
       the reset button together came to exactly the available width and the
       button wrapped onto a third row of its own. row-gap still spaces the
       rows properly if a longer translation ever does force a wrap. */
    gap: var(--space-3);
  }

  /* The title takes the whole first row; the switcher and "ახალი CV" share the
     one below it.

     It used to try to share a row with the switcher, and the result was the
     worst thing on the phone layout. `.app-header-row .logo` carries
     `flex: 1; min-width: 0` for the desktop header — and with the row wrapping
     here, the switcher (~170px) and reset (~102px) are effectively fixed while
     the title is the only thing that can give. So it gave: at 360px the title's
     box shrank to 24px, its text stayed 116px wide, overflowed, and rendered
     *underneath* the two controls. Four lines of one character each, with
     buttons sitting on top. `min-width: 0` is what removed the floor that would
     otherwise have forced the wrap.

     `flex-basis: 100%` makes the wrap explicit rather than something the title
     loses a fight over, and `min-width: auto` restores the floor. */
  .app-header-row .logo {
    flex: 1 0 100%;
    min-width: auto;
  }

  /* Reading order is unchanged: the title is first in the markup, and these
     two carry order 2 and 3 so they follow it onto the second row. The reset
     button stays after the switcher — it wipes the draft, and the choice a
     first-time visitor actually needs should not sit below the destructive
     control.

     What buys the room on that second row is dropping the switcher's visible
     label: the select already reads "ზოგადი CV" or "მეზღვაურის CV", so the
     word above it was saying the same thing twice. */
  .profile-switch {
    /* Grows into the row so the reset button is pushed to the right edge
       without needing a margin to do it — and shrinks first if a translation
       is ever wide enough that something has to give. */
    flex: 1 1 auto;
    min-width: 0;
    margin: 0;
    order: 2;
  }

  /* Clipped, not display:none — the <label> is what gives the select its
     accessible name, and display:none takes it out of the tree entirely. */
  .profile-switch-label {
    position: absolute;
    width: 1px;
    height: 1px;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
  }

  .profile-switch select {
    flex: 1;
    min-width: 0;
  }

  .reset-btn {
    order: 3;
    flex: 0 0 auto;
    margin: 0;
  }

  .profile-switch-label {
    flex: none;
  }

  /* The paper kept its desktop 48/44px padding on a 360px screen, leaving the
     document a 255px column — a quarter of the width spent on margin. */
  .preview-paper {
    padding: var(--space-5) var(--space-4);
    min-height: 0;
  }

  /* Take some length out of the form. It ran to seven screens on a 360px
     phone, and every one of those is scrolling between a person and their CV. */
  .step-header {
    padding: var(--space-3) var(--space-4);
  }

  .step-body {
    padding: 0 var(--space-4) var(--space-4);
  }

  .app-header {
    padding: var(--space-5) 0 var(--space-4);
    margin-bottom: var(--space-5);
  }

  /* 24px each side is 13% of a 360px screen. Narrower gutters go to the
     document and the fields, which need it more than the margin does. */
  .app-shell {
    padding-left: var(--space-4);
    padding-right: var(--space-4);
  }

  /* The full "PDF-ის ჩამოტვირთვა" plus the toggle overran a 360px bar by 40px,
     and a 320px one in both states. */
  .label-long {
    display: none;
  }

  .label-short {
    display: inline;
  }

  .print-btn {
    padding: var(--space-3) var(--space-4);
  }

  /* The h1 is a descriptive phrase now, not a four-character wordmark. At 28px
     it wrapped to two lines and put 100px back into a header two rounds were
     spent shrinking. One step down keeps it on one line beside the reset
     button from 360px up. */
  .logo {
    font-size: var(--text-lg);
  }

  /* 20px of display serif, a count and a chevron do not share 358px: the title
     wrapped to two lines and left the count floating beside the gap. One step
     down still reads as a heading against 12px labels. */
  .step-title {
    font-size: var(--text-base);
  }

  /* The pitch — "no account, no AI, no waiting" — belongs to someone deciding
     whether to use this. Someone already tapping through the form has decided,
     and on a 390px screen those two lines push the first field 53px further
     down a page that is nothing but scrolling. */
  .tagline {
    display: none;
  }
}


/* ---- Paywall watermark ----
   Drawn whenever <html class="paywall"> is set and the document has not been
   marked paid. That polarity is the whole point: the watermark is the default
   and payment removes it, so any failure — a script that never runs, a state
   read that throws — leaves it on rather than off.

   Text, not a background image. Chrome's print dialog has a "Background
   graphics" checkbox that a user can simply untick, and everything drawn with
   background-image disappears with it. A pseudo-element's `content` is
   foreground text and prints either way.

   Positioned differently on screen and on paper, because "every sheet" only
   means something on paper. `fixed` resolves against the viewport, not against
   .cv-document — on screen that painted the diagonal text across the whole
   app, header and form included, and it only looked correct on a phone because
   the preview panel is hidden there. So: `absolute` here, contained by the
   document it marks, and `fixed` in print.css where repeating per sheet is the
   entire point. */
.paywall .cv-document:not([data-paid="true"]) {
  position: relative;
}

/* The mark is inset -25% so a -30deg rotation still covers the corners, which
   means it is wider than the document and has to be clipped by it — otherwise
   it pushes the page 91px sideways and the whole app scrolls horizontally.
   (`fixed` never did this, because fixed boxes are out of flow entirely.)
   
   Screen only. `overflow: hidden` on an element that spans page breaks is how
   printed content gets silently clipped, and in print the mark is fixed and
   needs no clipping anyway. Tables keep their own .cv-table-wrap scroller, so
   nothing inside loses the ability to overflow. */
@media screen {
  .paywall .cv-document:not([data-paid="true"]) {
    overflow: hidden;
  }
}

.paywall .cv-document:not([data-paid="true"])::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 2;
  /* Never intercepts a click or a text selection: the preview underneath stays
     readable and selectable, which is what makes this a sample rather than a
     punishment. */
  pointer-events: none;
  user-select: none;
  /* A tile, not one long string. The string version held a fixed number of
     lines, so it could not fill a tall box: a 2888px seafarer CV had a 480px
     band through it with no mark at all, and how much of the document got
     marked depended on how many contracts were in it.

     Screen only. Chrome's print dialog can switch background graphics off,
     which would strip this — so print.css restores the text version, which is
     foreground content and prints either way. Two mechanisms, because the two
     media have opposite failure modes. */
  background-image: url("data:image/svg+xml;utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='420' height='240'%3E%3Ctext x='0' y='150' transform='rotate(-30 0 150)' font-family='sans-serif' font-size='26' font-weight='600' letter-spacing='3' fill='%232f6f5e' fill-opacity='0.13'%3ECV.ge %C2%B7 %E1%83%92%E1%83%90%E1%83%93%E1%83%90%E1%83%A3%E1%83%AE%E1%83%93%E1%83%94%E1%83%9A%E1%83%98 %E1%83%9C%E1%83%98%E1%83%9B%E1%83%A3%E1%83%A8%E1%83%98%3C/text%3E%3C/svg%3E");
  background-repeat: repeat;
}

/* ---- Paywall gate ---- */

.gate-dialog {
  max-width: 30rem;
  padding: var(--space-6);
  border: var(--border-hairline);
  border-radius: var(--radius-xl);
  background: var(--surface-card);
  color: var(--text-primary);
  box-shadow: var(--shadow-paper);
}

.gate-dialog::backdrop {
  background: rgba(30, 35, 33, 0.4);
}

.gate-title {
  font: var(--type-step-title);
  margin: 0 0 var(--space-3);
}

.gate-body {
  font: var(--type-body);
  color: var(--text-secondary);
  margin: 0 0 var(--space-4);
}

/* Empty until something happens, so it takes no room in the resting state. */
.gate-status:empty {
  display: none;
}

.gate-status {
  font-size: var(--text-sm);
  color: var(--danger);
  margin: 0 0 var(--space-4);
}

.gate-actions {
  display: flex;
  justify-content: flex-end;
  gap: var(--space-3);
}

/* ---- Restore access ---- */

/* The input already inherits height, border and focus ring from the global
   input rule above — this just gives it room and separates it from the
   paragraph above. */
.restore-input {
  display: block;
  width: 100%;
  margin: 0 0 var(--space-4);
}

/* One slot, two possible contents, decided by payment state — see
   renderPaymentStatusNote() in main.js. Sits with the other quiet notes under
   the form, same size and weight as the price line above it. No border-top of
   its own: .price-note already draws one directly above this, and a second
   rule right underneath it would read as a doubled line rather than two
   separate statements. */
.payment-status-note {
  margin: var(--space-3) 0 0;
}

.payment-status-note[hidden] {
  display: none;
}

/* Block-level with real vertical padding, not a bare inline link. It wraps to
   two lines in Georgian and had `padding: 0`, which left a 38px target with a
   ragged shape — under the comfortable 44px minimum, on the one control a
   customer who has already paid needs to find. */
.restore-link {
  display: block;
  width: 100%;
  min-height: 44px;
  padding: var(--space-2) 0;
  background: none;
  border: none;
  color: var(--accent);
  text-align: left;
  text-decoration: underline;
  cursor: pointer;
  font: inherit;
  font-size: inherit;
}

.restore-link:hover {
  color: var(--accent-strong);
}

.copy-order-id {
  background: none;
  border: 1px solid var(--border-input);
  border-radius: var(--radius-pill);
  padding: 2px var(--space-3);
  margin-left: var(--space-2);
  font-size: var(--text-xs);
  color: var(--text-secondary);
  cursor: pointer;
}

.copy-order-id:hover {
  border-color: var(--border-input-hover);
  color: var(--ink);
}

/* The price, stated before anyone fills anything in. Quiet, factual, and in
   the same breath as the storage note — the two things a first-time visitor
   is entitled to know without asking. */
/* Commercial terms, separated by a rule rather than by illegally interrupting
   the paragraph above. Different subject, so it gets a visible boundary. */
.price-note {
  margin: var(--space-3) 0 0;
  padding-top: var(--space-3);
  border-top: 1px solid var(--line);
  color: var(--text-secondary);
}
