/* Photographers Dashboard (داشبورد عکاسان) — in-app section styles.
 *
 * Scoped under #photographersView so nothing here can leak into another view,
 * matching the modular-CSS convention (static/css/design_studio.css).
 *
 * RTL: every directional value uses a LOGICAL property (inline-start/end,
 * margin-inline, text-align:start). The app flips <html dir> to rtl for Farsi,
 * and physical left/right values would silently keep the layout LTR — the
 * classic "RTL looks almost right but the badges are on the wrong side" bug
 * (see /memories/repo/landing-rtl-proof.md for the same lesson on the landing
 * page).
 */

#photographersView {
  --pg-radius: 12px;
  --pg-line: var(--border, #e5e7eb);
  /* One step darker than --border, and darker than the theme's own --border-2
     (#cbd5e1) for form controls. Reported: "the border doesn't look like it's
     there" — on a white card, a #cbd5e1 outline at 1px is barely visible, and a
     field the eye cannot find is a field people miss. This is the strongest
     border the palette allows without reading as an error state. */
  --pg-line-strong: #94a3b8;
  --pg-muted: var(--text-3, #6b7280);
  --pg-soft: var(--surface-2, #f8fafc);
  display: block;
  padding-bottom: 32px;
}

/* The same strong border for the wizard and the confirm dialogs, which live
   outside #photographersView in the DOM (they are appended to <body>) and would
   otherwise not inherit the token above. */
#pgWizardVeil,
.pg-veil {
  --pg-line-strong: #94a3b8;
  --pg-line: var(--border, #e5e7eb);
  --pg-muted: var(--text-3, #6b7280);
  --pg-soft: var(--surface-2, #f8fafc);
}

/* ---- header -------------------------------------------------------------- */
#photographersView .pg-head {
  display: flex;
  flex-wrap: wrap;
  gap: 12px;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 18px;
}
#photographersView .pg-head h2 {
  margin: 0;
  font-size: 22px;
  display: flex;
  align-items: center;
  gap: 8px;
}
#photographersView .pg-sub {
  color: var(--text-2);
  margin: 4px 0 0;
  font-size: 13px;
  max-width: 62ch;
}
#photographersView .pg-head-actions { display: flex; gap: 8px; flex-wrap: wrap; }

/* ---- buttons ------------------------------------------------------------- */
/* Buttons in this section are the APP'S OWN .btn-primary / .btn-secondary /
   .btn-mini / .btn-pill system (static/design.css), applied directly in the JS.
   There is deliberately no second button vocabulary here: an earlier version
   defined its own .pg-btn, which drifted from the theme's colours, radius and
   padding — exactly the "these buttons don't match" problem. The two classes
   below are the only additions, and both are genuinely new states rather than
   restatements of a theme button. */

/* Destructive action, small (Revoke link, Delete, Upgrade-when-capped).
   Mirrors .st-mk-del in design.css: outlined red that fills on hover, so it
   reads as destructive without shouting next to a primary button. */
#photographersView .pg-danger,
.pg-veil .pg-danger {
  background: var(--surface, #fff);
  color: var(--danger, #dc2626);
  border: 1px solid var(--danger, #dc2626);
  font-weight: 700;
}
#photographersView .pg-danger:hover:not(:disabled),
.pg-veil .pg-danger:hover:not(:disabled) {
  background: var(--danger, #dc2626);
  color: #fff;
}
#photographersView .pg-danger:disabled { opacity: .5; cursor: not-allowed; }

/* The large destructive button inside a confirm dialog ("Revoke link"). */
.pg-veil .pg-danger-solid {
  background: var(--danger, #dc2626);
  border: 1px solid var(--danger, #dc2626);
  color: #fff;
}
.pg-veil .pg-danger-solid:hover:not(:disabled) { filter: brightness(1.08); }

/* ---- plan / quota strip -------------------------------------------------- */
#photographersView .pg-plan {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  align-items: center;
  background: var(--pg-soft);
  border: 1px solid var(--pg-line);
  border-radius: var(--pg-radius);
  padding: 10px 14px;
  margin-bottom: 16px;
  font-size: 12.5px;
}
#photographersView .pg-plan .pg-chip {
  border: 1px solid var(--pg-line);
  background: var(--surface, #fff);
  border-radius: 999px;
  padding: 3px 10px;
  white-space: nowrap;
}
#photographersView .pg-plan .pg-chip.warn {
  border-color: var(--warning, #f39c12);
  color: var(--warning, #b9770e);
}
/* A chip that states a FACT about the plan rather than a meter. Used for the
   commission rate, which is neither a limit being consumed nor a warning, and
   would read as either if it borrowed one of those two looks. */
#photographersView .pg-plan .pg-chip.info {
  border-color: var(--brand-100, #e0e7ff);
  background: var(--brand-50, #eef2ff);
  color: var(--brand-700, #4338ca);
}
/* The commission upsell. Outlined like .pg-danger but in the brand colour: it
   leads somewhere (the Upgrade page) rather than destroying something, and it
   sits NEXT to the red capped-out Upgrade button, so the two must not look
   alike. */
#photographersView .pg-lift {
  background: var(--brand-50, #eef2ff);
  color: var(--brand-700, #4338ca);
  border: 1px solid var(--brand-200, #c7d2fe);
  font-weight: 700;
}
#photographersView .pg-lift:hover:not(:disabled) {
  background: var(--brand-600, #4f46e5);
  border-color: var(--brand-600, #4f46e5);
  color: #fff;
}
#photographersView .pg-plan .pg-spacer { flex: 1; }

/* ---- "My applications" ---------------------------------------------------
   One card per company this photographer applied to. It sits ABOVE the KPIs
   because it is the answer to the question that brought them here ("did that
   company get back to me?"), and the rest of the dashboard is about galleries. */
#photographersView .pg-apps { margin-bottom: 18px; }
#photographersView .pg-apps-head {
  display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap;
  margin-bottom: 8px;
}
#photographersView .pg-apps-head h3 { margin: 0; font-size: 14px; font-weight: 800; }
#photographersView .pg-apps-head span { font-size: 12px; color: var(--pg-muted); }
#photographersView .pg-apps-grid {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
  gap: 10px;
}
#photographersView .pg-app {
  display: flex; align-items: flex-start; gap: 10px;
  border: 1px solid var(--pg-line); border-radius: var(--pg-radius);
  padding: 10px 12px; background: var(--surface, #fff);
}
#photographersView .pg-app-ok { border-color: #a7f3d0; background: #f6fefb; }
#photographersView .pg-app-bad { border-color: #fecaca; background: #fffafa; }
#photographersView .pg-app-logo {
  width: 42px; height: 42px; flex: 0 0 auto; border-radius: 10px;
  object-fit: contain; background: #fff; border: 1px solid var(--pg-line);
  padding: 3px;
}
#photographersView .pg-app-logo-ph {
  display: flex; align-items: center; justify-content: center;
  font-size: 13px; font-weight: 800; color: #6366f1; background: #eef2ff;
  padding: 0; text-transform: uppercase;
}
#photographersView .pg-app-body { min-width: 0; flex: 1 1 auto; }
#photographersView .pg-app-name {
  font-size: 13px; font-weight: 800; line-height: 1.35;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
#photographersView .pg-app-meta { font-size: 11.5px; color: var(--pg-muted); margin-top: 1px; }
#photographersView .pg-app-pill {
  display: inline-block; margin-top: 6px; border-radius: 999px;
  font-size: 11px; font-weight: 800; padding: 2px 8px;
}
#photographersView .pg-app-pill.wait { background: #fff7ed; color: #b45309; border: 1px solid #fed7aa; }
#photographersView .pg-app-pill.ok { background: #ecfdf5; color: #047857; border: 1px solid #a7f3d0; }
#photographersView .pg-app-pill.bad { background: #fef2f2; color: #b91c1c; border: 1px solid #fecaca; }
#photographersView .pg-app-note {
  margin-top: 6px; font-size: 12px; line-height: 1.55; color: #334155;
  border-inline-start: 3px solid #fecaca; padding-inline-start: 8px;
}
#photographersView .pg-app-link {
  align-self: center; flex: 0 0 auto; font-size: 12px; font-weight: 700;
  color: var(--brand-600, #4f46e5); text-decoration: none; white-space: nowrap;
}
#photographersView .pg-app-link:hover { text-decoration: underline; }

/* ---- KPI grid ------------------------------------------------------------ */
#photographersView .pg-kpis {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
  gap: 12px;
  margin-bottom: 18px;
}
#photographersView .pg-kpi {
  border: 1px solid var(--pg-line);
  border-radius: var(--pg-radius);
  padding: 12px 14px;
  background: var(--surface, #fff);
}
#photographersView .pg-kpi .v {
  font-size: 20px;
  font-weight: 700;
  line-height: 1.25;
}
#photographersView .pg-kpi .l {
  font-size: 12px;
  color: var(--pg-muted);
  margin-top: 2px;
}

/* ---- toolbar (filters / sort / search) ---------------------------------- */
/* One themed bar. Before this the tab strip, the status chips and the sort/search
   controls floated on the page background with nothing binding them together, so
   they read as three unrelated fragments instead of one filter panel. */
#photographersView .pg-toolbar {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  align-items: center;
  margin-bottom: 12px;
  padding: 10px 12px;
  border: 1px solid var(--border, #e2e8f0);
  border-radius: 14px;
  background: linear-gradient(180deg, var(--brand-50, #eef2ff) 0%, var(--surface, #fff) 100%);
  box-shadow: var(--shadow-sm, 0 1px 2px rgba(15, 23, 42, .06));
}
#photographersView .pg-tabs {
  display: flex;
  gap: 4px;
  flex-wrap: wrap;
  padding: 3px;
  border-radius: 999px;
  background: rgba(79, 70, 229, .07);
}
#photographersView .pg-tab {
  border: 1px solid transparent;
  background: transparent;
  border-radius: 999px;
  padding: 5px 12px;
  font-size: 12.5px;
  cursor: pointer;
  font-family: inherit;
  color: var(--text-2, #475569);
}
#photographersView .pg-tab:hover { background: rgba(79, 70, 229, .10); }
#photographersView .pg-tab.on {
  background: var(--brand-600, #4f46e5);
  border-color: var(--brand-600, #4f46e5);
  color: #fff;
  box-shadow: var(--shadow-sm, 0 1px 2px rgba(15, 23, 42, .08));
}
#photographersView .pg-input,
#photographersView .pg-select {
  border: 1px solid var(--pg-line);
  border-radius: 9px;
  padding: 7px 10px;
  font-size: 13px;
  font-family: inherit;
  background: var(--surface, #fff);
  color: inherit;
  min-width: 0;
}
#photographersView .pg-input.grow { flex: 1; min-width: 160px; }

/* ---- job list ------------------------------------------------------------ */
#photographersView .pg-jobs { display: flex; flex-direction: column; gap: 10px; }
#photographersView .pg-job {
  border: 1px solid var(--pg-line);
  border-radius: var(--pg-radius);
  padding: 12px 14px;
  background: var(--surface, #fff);
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  align-items: center;
}
#photographersView .pg-job .who { min-width: 150px; flex: 1; }
#photographersView .pg-job .who b { font-size: 15px; }
#photographersView .pg-job .meta {
  font-size: 12px;
  color: var(--pg-muted);
  margin-top: 2px;
  display: flex;
  gap: 10px;
  flex-wrap: wrap;
}
#photographersView .pg-job .acts { display: flex; gap: 6px; flex-wrap: wrap; }
/* The gallery's auto-deletion DATE, in red — the deadline reads as a deadline.
   The relative forms were dropped from the job lists (asked 2026-10-01). */
#photographersView .pg-exp { color: var(--danger, #dc2626); font-weight: 600; }

/* ---- status badge -------------------------------------------------------- */
#photographersView .pg-badge {
  font-size: 11.5px;
  border-radius: 999px;
  padding: 2px 9px;
  border: 1px solid transparent;
  white-space: nowrap;
}
#photographersView .pg-badge.active { background: #e8f7ee; color: #1e7e46; border-color: #b7e4c7; }
#photographersView .pg-badge.completed { background: #e7efff; color: #1c4ea8; border-color: #bcd0f7; }
#photographersView .pg-badge.expired { background: #fdeaea; color: #a32323; border-color: #f5c2c2; }
#photographersView .pg-badge.draft { background: #f2f2f4; color: #5b6470; border-color: #dcdfe4; }

/* ---- a gallery still being filled, after the wizard has closed ------------ */
/* The wizard hands the photographer their screen back immediately and the photos
   keep uploading, so the detail view has to say what is still happening — an
   empty grid with no explanation reads as a failed upload. */
#photographersView .pg-bg-note {
  display: flex;
  align-items: center;
  gap: 9px;
  border: 1px solid #bcd0f7;
  background: #f2f8fe;
  color: #1c4ea8;
  border-radius: var(--pg-radius);
  padding: 10px 13px;
  margin-bottom: 14px;
  font-size: 13px;
}
#photographersView .pg-bg-spin {
  flex: 0 0 auto;
  width: 14px;
  height: 14px;
  border: 2px solid #b6d8f5;
  border-top-color: var(--brand, #1b74bc);
  border-radius: 50%;
  animation: pg-bg-spin 0.8s linear infinite;
}
@keyframes pg-bg-spin { to { transform: rotate(360deg); } }
/* Respect a reduced-motion preference: the sentence already says what is
   happening, so the spinner is decoration, not information. */
@media (prefers-reduced-motion: reduce) {
  #photographersView .pg-bg-spin { animation: none; }
}

/* ---- how the gallery is branded for the client --------------------------- */
/* Sits between the status badge and the actions, on the row: the branding is a
   fact about the gallery, not an action on it, so it reads alongside the status
   rather than among the buttons. */
#photographersView .pg-wm {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: 11.5px;
  border-radius: 999px;
  padding: 2px 4px 2px 9px;
  border: 1px solid var(--pg-line, #dcdfe4);
  background: #f7f8fa;
  white-space: nowrap;
}
#photographersView .pg-wm .pg-wm-k { color: var(--pg-muted); }
#photographersView .pg-wm b { font-weight: 600; color: var(--text, #0f172a); }
/* `white_label` is the only mode that renders a CLEAN file, so it is the only one
   that can hand a client a usable image before payment. That is worth seeing from
   the list at a glance, hence the accent rather than another grey pill. */
#photographersView .pg-wm.clean {
  background: #eef7ff;
  border-color: #b6d8f5;
}
#photographersView .pg-wm.clean b { color: #12558f; }
#photographersView .pg-wm-sel {
  border: 0;
  background: transparent;
  font: inherit;
  font-weight: 600;
  color: var(--text, #0f172a);
  padding: 1px 2px;
  max-width: 190px;
}
#photographersView .pg-wm-sel:focus { outline: 2px solid var(--brand, #1b74bc); border-radius: 6px; }

/* The per-gallery protection switch, next to the branding pill.
   A white-label plan's clean preview is what it pays for — and also the one file a
   client can screenshot and deliver as final work. So the two states are styled as
   a SAFETY state and a RISK state, not as on/off: "on" is calm and secondary
   (the safe state needs no attention), "off" carries the warning colour, because
   that is the state where a leak costs the photographer money. */
#photographersView .pg-wm-prot {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  font: inherit;
  font-size: 11.5px;
  border-radius: 999px;
  padding: 3px 10px;
  cursor: pointer;
  white-space: nowrap;
  border: 1px solid var(--pg-line, #dcdfe4);
  background: #fdecec;
  border-color: #f0b4b4;
  color: #a32323;
}
#photographersView .pg-wm-prot:hover { filter: brightness(0.97); }
#photographersView .pg-wm-prot:focus { outline: 2px solid var(--brand, #1b74bc); outline-offset: 1px; }
#photographersView .pg-wm-prot.on {
  background: #edf8ee;
  border-color: #b7e0bd;
  color: #1d6b32;
}
/* WHOSE identity is printed across the previews. Shown on the switch itself,
   because "protected" and "protected FOR THIS CLIENT" are different promises and
   only the second one is traceable. */
#photographersView .pg-wm-mark {
  font-style: normal;
  opacity: 0.75;
  border-inline-start: 1px solid currentColor;
  padding-inline-start: 6px;
  margin-inline-start: 2px;
  max-width: 170px;
  overflow: hidden;
  text-overflow: ellipsis;
}
/* Stated, not offered: a gallery that is not white-label is watermarked whatever
   this switch would say, so it is a fact about the row rather than a button. */
#photographersView .pg-wm-on {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  font-size: 11.5px;
  border-radius: 999px;
  padding: 3px 10px;
  white-space: nowrap;
  border: 1px solid #e2e5ea;
  background: #f4f6f8;
  color: var(--pg-muted, #64748b);
}

/* ---- one-time secret box (link + PIN) ----------------------------------- */
#photographersView .pg-secret {
  border: 1px solid var(--brand, #1b74bc);
  border-radius: var(--pg-radius);
  background: #f2f8fe;
  padding: 14px;
  margin-bottom: 14px;
}
body.is-fa #photographersView .pg-secret { background: #f2f8fe; }
#photographersView .pg-secret h4 { margin: 0 0 6px; font-size: 14px; }
#photographersView .pg-secret .warn {
  font-size: 12px;
  color: #a32323;
  margin-bottom: 10px;
}
#photographersView .pg-secret .row {
  display: flex;
  gap: 8px;
  align-items: center;
  margin-bottom: 8px;
  flex-wrap: wrap;
}
#photographersView .pg-secret .pg-lbl { min-width: 64px; font-weight: 600; font-size: 12.5px; }
#photographersView .pg-secret .pg-hint { font-size: 12px; color: var(--pg-muted); }
#photographersView .pg-secret-acts {
  display: flex;
  gap: 8px;
  flex-wrap: wrap;
  align-items: center;
  margin-top: 12px;
  padding-top: 10px;
  border-top: 1px solid var(--pg-line);
}
#photographersView .pg-secret code {
  flex: 1;
  min-width: 180px;
  background: #fff;
  border: 1px solid var(--pg-line);
  border-radius: 8px;
  padding: 8px 10px;
  font-size: 13px;
  direction: ltr;
  text-align: left;
  overflow-wrap: anywhere;
}
#photographersView .pg-secret .pin {
  font-size: 20px;
  letter-spacing: 6px;
  font-weight: 700;
  direction: ltr;
}

/* ---- photos grid (photographer's own view) ------------------------------ */

/* Thumbnails of the files chosen for upload.
   Shown before anything is sent, so the wrong session cannot be committed — and in
   the job detail they appear the instant the files are picked, because the upload
   starts then too. */
.pg-picked {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(76px, 1fr));
  gap: 8px;
  margin-top: 12px;
  max-height: 260px;
  overflow-y: auto;
}
#pgWizardVeil .pg-picked { margin-top: 12px; }
.pg-picked-item {
  position: relative;
  aspect-ratio: 1 / 1;
  border: 1px solid var(--pg-line);
  border-radius: 9px;
  overflow: hidden;
  background: var(--pg-soft);
}
.pg-picked-item img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}
#pgWizardVeil .pg-picked-x {
  position: absolute;
  top: 3px;
  inset-inline-end: 3px;
  width: 22px;
  height: 22px;
  border: 0;
  border-radius: 50%;
  background: rgba(15, 23, 42, .72);
  color: #fff;
  font-size: 11px;
  line-height: 1;
  cursor: pointer;
  padding: 0;
  display: flex;
  align-items: center;
  justify-content: center;
}
#pgWizardVeil .pg-picked-x:hover { background: var(--danger, #dc2626); }

/* While an upload is in flight the drop zone itself carries the state, so the
   feedback sits where the click happened — and a second click is discouraged by
   the cursor, not by a disabled attribute the zone does not have. */
#photographersView .pg-drop-busy {
  opacity: .6;
  pointer-events: none;
  cursor: progress;
}
#photographersView .pg-drop-busy .pg-drop-ico { animation: pg-pulse 1.1s ease-in-out infinite; }
@keyframes pg-pulse {
  0%, 100% { opacity: 1; }
  50% { opacity: .35; }
}

/* The wizard's client-mobile field borrows the shared PhoneInput component, so it
   needs the status/hint line the sign-up form shows — the same ✅ / ⚠️ and the
   same "this is what will be saved" wording, rather than a second dialect. */
#pgWizardVeil .pg-phone-line {
  display: flex;
  align-items: center;
  gap: 6px;
  min-height: 18px;
  margin-top: 4px;
}
#pgWizardVeil .pg-phone-status { font-size: 13px; }
#pgWizardVeil .pg-phone-hint {
  font-size: 12px;
  color: var(--pg-muted, #64748b);
  line-height: 1.4;
}
#pgWizardVeil .pg-phone-hint.success { color: #15803d; }
#pgWizardVeil .pg-phone-hint.error { color: #b91c1c; font-weight: 600; }
#photographersView .pg-photos {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(110px, 1fr));
  gap: 8px;
  /* `start`, not the default `stretch`: tiles are now as tall as their own photo,
     and stretching would make every tile in a row match the tallest one — which
     is exactly the "border in a size that does not change with the photo" this
     replaced. */
  align-items: start;
}
#photographersView .pg-photo {
  position: relative;
  border: 1px solid var(--pg-line);
  border-radius: 9px;
  overflow: hidden;
  /* The tile takes its height from the photo, so the border wraps the WHOLE
     picture at its own aspect ratio. Reported: "the border that wraps around the
     photo ... is stuck in a predetermined size that does not change with the
     photo size" — a fixed `aspect-ratio: 4 / 3` with a `contain`-ed image inside
     drew a 4:3 box around a portrait and letterboxed the photo within it, so the
     frame belonged to the grid rather than to the picture.
     Deliberately NO `min-height` here: one was tempting for safety, but it put a
     floor under the height and letterboxed anything wider than about 3:1 — a
     panorama came out in an 84px box around a 33px photo, which is the same bug
     in miniature. The height floor belongs to the PLACEHOLDER (`.ph` below),
     which is the only state that has no image to size itself from. */
  background: #0f172a;
}
#photographersView .pg-photo .zoom {
  display: block;
  width: 100%;
  height: 100%;
  padding: 0;
  border: 0;
  background: none;
  cursor: zoom-in;
}
/* The tile's CONTENT is wrapped in this span so the render poller can replace it
   while leaving the delete button and the grid geometry alone. It must be given an
   explicit block box: as an inline span its children's `height: 100%` resolved
   against the tile only by luck, and the moment the tile stops being the nearest
   containing block the thumbnails would collapse. */
#photographersView .pg-photo .pg-ph-slot {
  display: block;
}
#photographersView .pg-photo img {
  width: 100%;
  /* `auto`, not `100%`: the image's own proportions decide the tile's height, so
     the border hugs the photo. With the old fixed frame this had to be `100%`
     plus `object-fit: contain` to letterbox the picture inside a box that was
     never the picture's shape. */
  height: auto;
  object-fit: contain;
  display: block;
}
#photographersView .pg-photo .ph {
  display: flex;
  align-items: center;
  justify-content: center;
  /* Its own minimum, not `height: 100%`: the tile's height is now driven by the
     image, so a percentage here resolves against an indefinite height and the
     "Processing…" text would sit in a zero-height box. */
  min-height: 84px;
  font-size: 11.5px;
  color: var(--pg-muted);
  text-align: center;
  padding: 6px;
}
/* A photo that could not be processed. It used to be indistinguishable from a
   slow one — both said "Processing…" — so the failure is now stated, coloured,
   and carries its own retry. The column layout is what makes room for the button
   without the text and the button fighting for the same line in a 110px tile. */
#photographersView .pg-photo .ph-failed {
  flex-direction: column;
  gap: 6px;
  color: #fca5a5;
}
#photographersView .pg-photo .ph-failed .btn-mini {
  font-size: 11px;
  padding: 3px 8px;
}
#photographersView .pg-photo .del {
  position: absolute;
  inset-block-start: 4px;
  inset-inline-end: 4px;
  background: rgba(0, 0, 0, .6);
  color: #fff;
  border: 0;
  border-radius: 6px;
  width: 24px;
  height: 24px;
  cursor: pointer;
  font-size: 13px;
  line-height: 1;
  /* Above the view button, which now fills the tile — otherwise the ✕ would be
     unclickable and every attempt to delete would open the photo instead. */
  z-index: 2;
}

/* ---- client details card ------------------------------------------------- */
/* Hint lines ("they are uploaded when you press Create gallery", the watermarked
   -preview explanation, the missing-channel warning). Used on the view, in the
   wizard and in the modal, so the base style is shared — the two scoped rules
   further up only override the colour. */
#photographersView .pg-hint,
#pgWizardVeil .pg-hint,
.pg-veil .pg-hint {
  font-size: 12px;
  color: var(--pg-muted, #64748b);
  line-height: 1.45;
}
/* Who the gallery belongs to. The mobile and the email are LINKS (`tel:` /
   `mailto:`) rather than plain text, because the reason to look at this card is
   usually to contact the person: "did you get the code?" should be one tap, not a
   copy-and-paste into a phone app. */
#photographersView .pg-client-top {
  display: flex;
  justify-content: space-between;
  gap: 10px;
  align-items: center;
  flex-wrap: wrap;
}
#photographersView .pg-client-name {
  font-size: 15px;
  font-weight: 600;
  margin: 6px 0 5px;
}
#photographersView .pg-contact {
  display: flex;
  gap: 14px;
  flex-wrap: wrap;
  align-items: center;
  font-size: 13.5px;
}
#photographersView .pg-contact-a {
  color: var(--brand, #1b74bc);
  text-decoration: none;
  font-weight: 600;
  /* The digits stay LTR inside an RTL page (dir="ltr" in the markup), and the
     underline appears on hover so it reads as clickable without looking like body
     copy. */
  border-bottom: 1px solid transparent;
  /* A PHONE NUMBER must never break across lines — "+98912000" / "0977" is not a
     number anyone can read or dial. An EMAIL is a different animal: it is long,
     it has no spaces to break at, and `nowrap` made it overflow the card, which
     the card's own `overflow: hidden` then CUT OFF. Reported as "if email alone,
     the email is not showing" — it rendered, it was clipped: at a 430px viewport
     the address was 525px wide inside a 379px card, so 163px of it was invisible
     and the client looked like they had no contact details at all. Wrapping it is
     the fix; `min-width: 0` lets the flex item actually shrink. */
  white-space: nowrap;
  max-width: 100%;
  min-width: 0;
}
#photographersView .pg-contact-a[href^="mailto:"] {
  white-space: normal;
  overflow-wrap: anywhere;
}
#photographersView .pg-contact-a:hover { border-bottom-color: currentColor; }
#photographersView .pg-contact-none { color: var(--pg-muted); }

/* ---- the photographer's own full-preview viewer ------------------------- */
/* Scoped `.pg-veil.pg-viewer`, NOT `.pg-viewer`, and that extra class is
   load-bearing rather than stylistic. The shared dialog rule
   `.pg-veil .pg-modal` is declared LATER in this file and has the SAME
   specificity (0,2,0), so it was silently winning on the three properties that
   decide this stage's shape: `max-width` (this rule asks for min(1100px, 94vw)
   and was getting the dialog's 420px), `padding` (12px -> 20px) and
   `background` (#0b1220 -> near-white). The visible result was an 800px photo
   hanging 400px out of the right edge of a small white card — reported as "the
   image does not get wrapped around completely to the right side to fit".
   Adding the class wins the tie without touching the dialog rule every other
   modal in the app depends on. */
.pg-veil.pg-viewer { align-items: center; }
.pg-veil.pg-viewer .pg-viewer-modal {
  max-width: min(1100px, 94vw);
  width: auto;
  padding: 12px;
  background: #0b1220;
}
.pg-veil.pg-viewer #pgViewImg {
  display: block;
  /* A percentage of the MODAL's content box, never a viewport unit. `90vw`
     looked equivalent and is not: it is wider than `94vw - 24px` on every screen
     under 600px, so a phone would still have hung the photo over the card's
     edge. `min(1060px, 100%)` keeps the ceiling on a big screen and makes
     overflow impossible on a small one. The whole frame is still shown in full —
     never `cover`: the point of opening a photo is to see all of it. */
  max-width: min(1060px, 100%);
  /* Reserve the veil's 16px of padding, this modal's 12px and the bar with its
     gap, so the "Close" button cannot be pushed off the bottom of a short
     window. */
  max-height: calc(100vh - 150px);
  width: auto;
  height: auto;
  margin: 0 auto;
  object-fit: contain;
  border-radius: 10px;
}
.pg-veil.pg-viewer .pg-viewer-bar {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  margin-top: 10px;
  color: #cbd5e1;
  font-size: 13px;
}
.pg-veil.pg-viewer .pg-viewer-bar .btn-mini {
  min-width: 38px;
  font-size: 16px;
  line-height: 1;
}

/* ---- activity feed ------------------------------------------------------- */
#photographersView .pg-feed { display: flex; flex-direction: column; gap: 6px; }
#photographersView .pg-feed .item {
  display: flex;
  gap: 10px;
  align-items: baseline;
  font-size: 12.5px;
  border-bottom: 1px dashed var(--pg-line);
  padding: 6px 0;
}
#photographersView .pg-feed .item:last-child { border-bottom: 0; }
#photographersView .pg-feed .when { color: var(--pg-muted); font-size: 11.5px; white-space: nowrap; }

/* ---- drop zone (upload photos) -------------------------------------------- */
/* Mirrors the Compose card's .drop-zone (design.css): same dashed border, same
   brand hover, same 📁 affordance. Only the internal spacing of the three lines
   (icon / action / hint) is section-specific. */
#photographersView .pg-drop,
#pgWizardVeil .pg-drop {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 2px;
  text-align: center;
  padding: 18px 14px;
  margin-top: 10px;
  /* The theme's .drop-zone dashes are 2px in --border-2 (#cbd5e1), which is the
     thing that "doesn't look like it's there" on a white wizard panel. Same
     shape, darker line. */
  border-color: var(--pg-line-strong, #94a3b8);
  color: var(--text-2, #475569);
}
#photographersView .pg-drop:hover,
#pgWizardVeil .pg-drop:hover,
#photographersView .pg-drop.dragover,
#pgWizardVeil .pg-drop.dragover {
  border-color: var(--brand-500, #6366f1);
  background: var(--brand-50, #eef2ff);
}
#photographersView .pg-drop-ico,
#pgWizardVeil .pg-drop-ico { font-size: 26px; line-height: 1.1; }
#photographersView .pg-drop-main,
#pgWizardVeil .pg-drop-main { font-weight: 600; font-size: 13.5px; }
#photographersView .pg-drop-hint,
#pgWizardVeil .pg-drop-hint { font-size: 11.5px; color: var(--pg-muted); }
#photographersView .pg-drop-count,
#pgWizardVeil .pg-drop-count {
  margin-top: 4px;
  font-size: 12px;
  font-weight: 700;
  color: var(--brand-600, #4f46e5);
}

/* ---- confirm / prompt dialogs --------------------------------------------- */
/* Used instead of window.confirm/prompt, which render as unthemed OS boxes (and
   cannot be styled at all on iOS). Keyboard-first: focus lands on the confirm
   button or the input, Enter confirms, Escape cancels. */
.pg-veil {
  position: fixed;
  inset: 0;
  background: rgba(15, 23, 42, .5);
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 95;
  padding: 16px;
}
.pg-veil .pg-modal {
  background: var(--surface, #fff);
  color: var(--text, #0f172a);
  border-radius: 16px;
  box-shadow: 0 20px 50px rgba(15, 23, 42, .28);
  width: 100%;
  max-width: 420px;
  padding: 20px;
}
.pg-veil .pg-modal h3 {
  margin: 0 0 6px;
  font-size: 16.5px;
  font-weight: 700;
  line-height: 1.35;
}
.pg-veil .pg-note {
  font-size: 13px;
  color: var(--text-2, #475569);
  margin: 0 0 14px;
  line-height: 1.5;
}
/* The irreversible-action warning, shown above the confirm button so the
   consequence is read BEFORE the click rather than discovered afterwards. */
.pg-veil .pg-warn {
  background: #fef2f2;
  border: 1px solid #fecaca;
  color: #991b1b;
  border-radius: 10px;
  padding: 10px 12px;
  font-size: 12.5px;
  line-height: 1.5;
  margin: 0 0 14px;
  font-weight: 600;
}
.pg-veil .pg-modal-acts {
  display: flex;
  gap: 8px;
  justify-content: flex-end;
  flex-wrap: wrap;
  margin-top: 4px;
}
/* Numbered steps inside a modal. Scoped to `.pg-veil` because the wizard has its
   own `.pg-steps` (a progress bar of dots) under `#pgWizardVeil`, and the two
   share only a name. */
.pg-veil .pg-steps { margin: 0 0 14px; }
.pg-veil .pg-step {
  display: flex;
  gap: 9px;
  align-items: flex-start;
  font-size: 12.5px;
  line-height: 1.5;
  color: var(--text-2, #475569);
  margin-bottom: 8px;
}
.pg-veil .pg-step:last-child { margin-bottom: 0; }
.pg-veil .pg-step-n {
  flex: 0 0 auto;
  width: 18px;
  height: 18px;
  border-radius: 50%;
  background: var(--brand, #1b74bc);
  color: #fff;
  font-size: 11px;
  font-weight: 700;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  margin-top: 1px;
}
.pg-veil .pg-modal-acts button { cursor: pointer; font-family: inherit; }
.pg-veil .pg-field { margin-bottom: 12px; }
.pg-veil .pg-field input {
  width: 100%;
  box-sizing: border-box;
  border: 1px solid var(--border, #e5e7eb);
  border-radius: 10px;
  padding: 9px 11px;
  font-size: 14px;
  font-family: inherit;
  background: var(--surface, #fff);
  color: inherit;
}
/* A destructive confirm is tinted very slightly, so the weight of the decision is
   felt before the button label is read. */
.pg-veil.danger .pg-modal { border-top: 3px solid var(--danger, #dc2626); }

/* The send confirmation: where it goes, the secrets themselves, and why the two
   messages do not arrive together. */
.pg-veil .pg-send-to {
  border: 1px solid var(--pg-line-strong, #94a3b8);
  border-radius: 10px;
  padding: 10px 12px;
  margin: 0 0 14px;
  font-size: 13.5px;
  display: flex;
  flex-direction: column;
  gap: 6px;
  word-break: break-all;
}
.pg-veil .pg-tag {
  display: inline-block;
  min-width: 52px;
  font-size: 11px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: .04em;
  color: var(--text-3, #94a3b8);
}
/* Which channel carries which secret. Two segmented pickers side by side, with
   the unavailable channel visibly inert rather than absent — hiding it makes the
   photographer hunt for a control that does not exist. */
.pg-veil .pg-chan-row {
  display: flex;
  flex-wrap: wrap;
  gap: 14px;
  margin: 0 0 14px;
}
.pg-veil .pg-chan-row .pg-field { flex: 1 1 180px; margin: 0; }
.pg-veil .pg-chan {
  display: inline-flex;
  border: 1px solid var(--pg-line-strong, #94a3b8);
  border-radius: 999px;
  overflow: hidden;
}
.pg-veil .pg-chan-opt {
  appearance: none;
  border: 0;
  background: transparent;
  color: var(--text-2, #475569);
  font: inherit;
  font-size: 13px;
  padding: 6px 14px;
  cursor: pointer;
  transition: background .15s, color .15s;
}
.pg-veil .pg-chan-opt + .pg-chan-opt { border-inline-start: 1px solid var(--pg-line-strong, #94a3b8); }
.pg-veil .pg-chan-opt:hover:not(:disabled) { background: var(--surface-2, #f1f5f9); }
.pg-veil .pg-chan-opt.on {
  background: var(--brand-600, #4f46e5);
  color: #fff;
  font-weight: 600;
}
/* No address on this channel: dimmed AND struck through, and not clickable. */
.pg-veil .pg-chan-opt:disabled {
  opacity: .45;
  cursor: not-allowed;
  text-decoration: line-through;
}
.pg-veil .pg-code {
  display: block;
  border: 1px solid var(--pg-line-strong, #94a3b8);
  border-radius: 9px;
  background: var(--surface-2, #f8fafc);
  padding: 9px 11px;
  font-size: 13px;
  /* Always LTR: a link and a numeric code are read left-to-right even on a Farsi
     page, and mirroring them makes them hard to compare against the message. */
  direction: ltr;
  text-align: left;
  overflow-wrap: anywhere;
}
.pg-veil .pg-code.pg-pin {
  font-size: 22px;
  letter-spacing: 8px;
  font-weight: 700;
  text-align: center;
}
/* Link/code on the left, its copy button on the right. The button sits inside the
   same row as the value it copies, so "copy what?" is never ambiguous. */
.pg-veil .pg-code-row {
  display: flex;
  gap: 8px;
  align-items: stretch;
}
.pg-veil .pg-code-row .pg-code {
  flex: 1;
  min-width: 0;
  /* Vertically centred against the copy button, but still LTR-left. */
  display: flex;
  align-items: center;
}
.pg-veil .pg-code-row .pg-code.pg-pin { justify-content: center; }
.pg-veil .pg-copy {
  flex: 0 0 auto;
  cursor: pointer;
  font-family: inherit;
  font-size: 15px;
  line-height: 1;
  padding: 0 12px;
}
/* The blue ground from the very first deploy, now applied to the whole secrets
   block inside the send dialog — it reads as "these values matter" without
   needing another colour introduced. */
.pg-veil .pg-modal-blue { border-top: 3px solid var(--brand, #1b74bc); }
.pg-veil .pg-secret-ground {
  border: 1px solid var(--brand, #1b74bc);
  border-radius: var(--pg-radius, 12px);
  background: #f2f8fe;
  padding: 14px;
  margin: 0 0 14px;
}
.pg-veil .pg-secret-ground .pg-field:last-of-type { margin-bottom: 6px; }
.pg-veil .pg-secret-ground .pg-hint { font-size: 12px; color: var(--text-2, #475569); }
.pg-veil .pg-explain {
  font-size: 12.5px;
  line-height: 1.5;
  color: var(--text-2, #475569);
  background: var(--brand-50, #eef2ff);
  border: 1px solid var(--brand-100, #e0e7ff);
  border-radius: 10px;
  padding: 9px 11px;
  margin: 0 0 14px;
}
/* The gateway's own error text, on the job's delivery list. */
#photographersView .pg-msg-err {
  font-size: 11.5px;
  color: var(--danger, #dc2626);
  padding: 0 0 8px 2px;
  line-height: 1.45;
}

/* ---- wizard modal -------------------------------------------------------- */
#pgWizardVeil {
  position: fixed;
  inset: 0;
  background: rgba(0, 0, 0, .45);
  display: none;
  align-items: center;
  justify-content: center;
  z-index: 80;
  padding: 16px;
}
#pgWizardVeil.open { display: flex; }
#pgWizardVeil .pg-wizard {
  background: var(--surface, #fff);
  color: var(--text-1, #111);
  border-radius: 14px;
  width: 100%;
  max-width: 620px;
  max-height: 90vh;
  overflow: auto;
  padding: 18px;
}
#pgWizardVeil .pg-steps {
  display: flex;
  gap: 6px;
  margin-bottom: 14px;
  flex-wrap: wrap;
}
#pgWizardVeil .pg-steps .s {
  flex: 1;
  min-width: 40px;
  height: 4px;
  border-radius: 999px;
  background: var(--pg-line);
}
#pgWizardVeil .pg-steps .s.on { background: var(--brand, #1b74bc); }
#pgWizardVeil h3 { margin: 0 0 4px; font-size: 17px; }
#pgWizardVeil .pg-note { font-size: 12.5px; color: var(--pg-muted); margin: 0 0 12px; }
#pgWizardVeil .pg-field { margin-bottom: 12px; }
#pgWizardVeil .pg-field label {
  display: block;
  font-size: 12.5px;
  margin-bottom: 4px;
  color: var(--text-2);
}
#pgWizardVeil .pg-field input,
#pgWizardVeil .pg-field select,
#pgWizardVeil .pg-field textarea {
  width: 100%;
  border: 1px solid var(--pg-line-strong, #94a3b8);
  border-radius: 9px;
  padding: 8px 10px;
  font-size: 13.5px;
  font-family: inherit;
  background: var(--surface, #fff);
  color: inherit;
  box-sizing: border-box;
}
#pgWizardVeil .pg-field input:focus,
#pgWizardVeil .pg-field select:focus,
#pgWizardVeil .pg-field textarea:focus {
  border-color: var(--brand, #1b74bc);
  outline: 2px solid var(--brand-50, #eef2ff);
  outline-offset: 0;
}
/* The theme's .inp-c (used by the profile form and this wizard's inputs) carries
   its own lighter border; strengthen it inside this section so both paths match. */
#photographersView .inp-c,
#pgWizardVeil .inp-c,
.pg-veil .inp-c {
  border-color: var(--pg-line-strong, #94a3b8);
}
#photographersView .inp-c:focus,
#pgWizardVeil .inp-c:focus,
.pg-veil .inp-c:focus {
  border-color: var(--brand, #1b74bc);
}
#pgWizardVeil .pg-modes { display: flex; flex-direction: column; gap: 8px; }
#pgWizardVeil .pg-mode {
  border: 1px solid var(--pg-line);
  border-radius: 9px;
  padding: 10px 12px;
  cursor: pointer;
  font-size: 13.5px;
  display: flex;
  gap: 9px;
  align-items: flex-start;
}
#pgWizardVeil .pg-mode.on { border-color: var(--brand, #1b74bc); background: #f2f8fe; }
#pgWizardVeil .pg-mode.locked { opacity: .55; cursor: not-allowed; }
#pgWizardVeil .pg-mode small { display: block; color: var(--pg-muted); font-size: 11.5px; }
/* Name + the watermark status pill on one line, so the pill never pushes the
   description onto its own row. */
#pgWizardVeil .pg-mode .pg-mode-top {
  display: flex;
  gap: 7px;
  align-items: center;
  flex-wrap: wrap;
}
#pgWizardVeil .pg-mode-badge {
  font-size: 10.5px;
  font-weight: 600;
  letter-spacing: .02em;
  padding: 1px 7px;
  border-radius: 999px;
  background: #dcfce7;
  color: #15803d;
  border: 1px solid #86efac;
  white-space: nowrap;
}
/* "Watermarked" is a caveat, not a recommendation: the client receives a marked
   proof rather than a usable photograph. The amber matches the sidebar's own
   caution marker, so the two colours mean the same thing in both places. */
#pgWizardVeil .pg-mode-badge.warn {
  background: #fef3c7;
  color: #92400e;
  border-color: #fcd34d;
}
/* The mode is shown, not chosen: a plan with a single option is a fact, not a
   radio group, so there is no pointer cursor and nothing to click. */
#pgWizardVeil .pg-mode-fixed { cursor: default; }
#pgWizardVeil .pg-mode-tick { color: #15803d; font-weight: 700; flex: none; }
#pgWizardVeil .pg-mode small + small { margin-top: 3px; }
#pgWizardVeil .pg-foot {
  display: flex;
  gap: 8px;
  justify-content: flex-end;
  margin-top: 16px;
  flex-wrap: wrap;
}
#pgWizardVeil .pg-error {
  color: var(--danger, #dc2626);
  font-size: 12.5px;
  margin-top: 8px;
  min-height: 16px;
}
#pgWizardVeil .pg-drop {
  border: 2px dashed var(--pg-line);
  border-radius: var(--pg-radius);
  padding: 20px;
  text-align: center;
  font-size: 13px;
  color: var(--pg-muted);
  cursor: pointer;
}
#pgWizardVeil .pg-drop.on { border-color: var(--brand, #1b74bc); background: #f2f8fe; }

/* ---- empty state --------------------------------------------------------- */
#photographersView .pg-empty {
  border: 1px dashed var(--pg-line);
  border-radius: var(--pg-radius);
  padding: 28px 18px;
  text-align: center;
  color: var(--pg-muted);
  font-size: 13.5px;
}

/* ---- profile logo preview ------------------------------------------------ */
#photographersView .pg-logo-prev {
  width: 92px;
  height: 92px;
  border: 1px solid var(--pg-line);
  border-radius: 10px;
  background:
    repeating-conic-gradient(#e9edf2 0% 25%, #fff 0% 50%) 50% / 16px 16px;
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
}
#photographersView .pg-logo-prev img { max-width: 84%; max-height: 84%; }

/* ---- the profile form (identity + branding) ------------------------------ */
#photographersView .pg-section-title {
  margin: 4px 0 10px;
  font-size: 13px;
  font-weight: 800;
  letter-spacing: .02em;
  text-transform: uppercase;
  color: var(--pg-muted, #64748b);
}
#photographersView .pg-section-title:not(:first-child) {
  margin-top: 22px;
  padding-top: 16px;
  border-top: 1px solid var(--pg-line, #e5e9f0);
}
/* Sub-groups inside the identity block (personal / artistic / contact). They
   are quieter than the section title because they belong to ONE save. */
#photographersView .pg-subsection-title {
  margin: 16px 0 8px;
  font-size: 12.5px;
  font-weight: 800;
  color: var(--pg-text, #334155);
}
#photographersView .pg-field { margin-bottom: 12px; }
/* A personal Bale/Eitaa id: the platform's mark sits inside the field, so the
   row names the app without a second line of label. */
#photographersView .pg-handle { display: flex; align-items: center; gap: 8px; }
#photographersView .pg-handle .inp-c { flex: 1 1 auto; min-width: 0; }
#photographersView .pg-handle-ic {
  flex: 0 0 34px; width: 34px; height: 34px; border-radius: 10px;
  object-fit: contain; background: #fff; border: 1px solid #e2e8f0; padding: 3px;
}
/* The two private documents: pick / current file / remove on one line. */
#photographersView .pg-doc-row {
  display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
}
#photographersView .pg-doc-name {
  max-width: 220px; overflow: hidden; text-overflow: ellipsis;
  white-space: nowrap; font-size: 12px; font-weight: 700; color: #4338ca;
}
#photographersView .pg-doc-x {
  width: 26px; height: 26px; border-radius: 999px; border: 1px solid #fecaca;
  background: #fef2f2; color: #dc2626; font-size: 15px; font-weight: 800;
  line-height: 1; cursor: pointer;
}
#photographersView .pg-doc-x:hover { background: #dc2626; color: #fff; border-color: #dc2626; }
#photographersView .pg-field > label {
  display: block;
  margin-bottom: 5px;
  font-size: 12.5px;
  font-weight: 700;
}
#photographersView .pg-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
  gap: 0 14px;
}
#photographersView textarea.inp-c { min-height: 64px; resize: vertical; }
#photographersView .pg-badge-prev {
  width: 84px;
  height: 84px;
  border-radius: 16px;
  object-fit: cover;
  border: 2px solid #fff;
  box-shadow: 0 8px 20px rgba(15, 23, 42, .18);
  background: #e2e8f0;
}
/* The badge photo sits at the TOP RIGHT of the profile card, above the price
   column. The button is still the only way to replace it (the photo itself is
   the preview). */
#photographersView .pg-profile-top {
  display: flex;
  align-items: flex-start;
  gap: 16px;
  margin-bottom: 12px;
}
#photographersView .pg-profile-main { flex: 1 1 auto; min-width: 0; }
#photographersView .pg-profile-photo {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
  flex: 0 0 124px;
}
#photographersView .pg-profile-photo .pg-badge-prev { display: block; }
#photographersView .pg-profile-photo .pg-badge-prev[hidden] { display: none; }
#photographersView .pg-profile-photo .pg-photo-pick { white-space: nowrap; }
@media (max-width: 560px) {
  #photographersView .pg-profile-top { flex-direction: column; }
  #photographersView .pg-profile-photo { flex-basis: auto; align-items: flex-start; }
}

/* On narrow screens the toolbar stacks and the job rows become cards. */
@media (max-width: 640px) {
  #photographersView .pg-job { flex-direction: column; align-items: stretch; }
  #photographersView .pg-job .acts { justify-content: flex-start; }
}

/* ---- Sales tab -----------------------------------------------------------
   Hand-rolled charts, deliberately: there is no charting library in this app and
   every other graph (see renderAnalytics in shell.js) is CSS. Keeping the shapes
   in one block means a real library can replace this later without touching the
   markup that produces the data.
   Every rule is scoped to #photographersView so nothing leaks into the shell. */
#photographersView .pg-sales-card {
  padding: 14px 16px;
  margin-bottom: 16px;
}
#photographersView .pg-sales-card h3 {
  margin: 0 0 12px;
  font-size: 14px;
  font-weight: 700;
}

/* A horizontal bar, used by the by-price list and inside the client table. The
   wider element is the track; .pg-bar-fill is sized inline as a percentage. */
#photographersView .pg-bar {
  flex: 1;
  height: 8px;
  background: var(--pg-soft);
  border-radius: 4px;
  overflow: hidden;
  min-width: 40px;
}
#photographersView .pg-bar-fill { height: 100%; border-radius: 4px; }

#photographersView .pg-sales-row {
  display: flex;
  align-items: center;
  gap: 10px;
  font-size: 12.5px;
  margin-bottom: 8px;
}
#photographersView .pg-sales-row-name { flex: 0 0 120px; font-weight: 600; }
#photographersView .pg-sales-row-v { flex: 0 0 auto; font-weight: 700; }
#photographersView .pg-sales-row-n {
  flex: 0 0 auto;
  color: var(--text-3);
  font-size: 11.5px;
}

/* The 12-month trend. Column heights come from inline percentages against the
   largest value in the series, so the tallest bar is always full height. */
#photographersView .pg-trend {
  display: flex;
  align-items: flex-end;
  gap: 4px;
  height: 150px;
}
#photographersView .pg-trend-col {
  flex: 1 1 0;
  min-width: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  height: 100%;
  justify-content: flex-end;
}
#photographersView .pg-trend-bar-wrap {
  width: 100%;
  flex: 1 1 auto;
  display: flex;
  align-items: flex-end;
}
#photographersView .pg-trend-bar {
  width: 100%;
  background: linear-gradient(180deg, var(--brand-500, #6366f1), var(--brand-600, #4f46e5));
  border-radius: 4px 4px 0 0;
  min-height: 0;
  transition: height .2s ease;
}
#photographersView .pg-trend-v {
  font-size: 10.5px;
  font-weight: 700;
  color: var(--text-2);
  margin-top: 4px;
  white-space: nowrap;
}
#photographersView .pg-trend-l {
  font-size: 10.5px;
  color: var(--text-3);
  white-space: nowrap;
}

/* The client table. Scrolls horizontally rather than squashing five columns on
   a phone — a money column that wraps mid-number is worse than a swipe. */
#photographersView .pg-sales-scroll { overflow-x: auto; }
#photographersView .pg-sales-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 12.5px;
}
#photographersView .pg-sales-table th,
#photographersView .pg-sales-table td {
  padding: 8px 10px;
  text-align: start;
  border-bottom: 1px solid var(--pg-line);
  white-space: nowrap;
}
#photographersView .pg-sales-table th {
  font-size: 11.5px;
  color: var(--text-3);
  text-transform: uppercase;
  letter-spacing: .02em;
  cursor: pointer;
  user-select: none;
}
#photographersView .pg-sales-table th:hover { color: var(--brand-600, #4f46e5); }
#photographersView .pg-sales-table tbody tr:last-child td { border-bottom: 0; }
#photographersView .pg-sales-name { font-weight: 600; }
#photographersView .pg-sales-sub { font-size: 11px; color: var(--text-3); font-weight: 400; }
/* The money cells carry a bar UNDER the figure, so the ranking is visible without
   reading the numbers. */
#photographersView .pg-sales-money {
  font-weight: 700;
  min-width: 130px;
}
#photographersView .pg-sales-money .pg-bar { margin-top: 5px; }

@media (max-width: 640px) {
  #photographersView .pg-trend { height: 110px; }
  #photographersView .pg-trend-v { display: none; }
  #photographersView .pg-sales-row-name { flex-basis: 84px; }
}
