/* Shared by project-request.html and project-dashboard.html.
 *
 * Linked AFTER assets/css/theme.min.css and user.min.css, BEFORE each page's own <style>, which
 * is the whole reason nothing in here needs !important - the cascade already puts us last.
 *
 * project-hero.css survives untouched for admin-project-requests.html, its only consumer.
 *
 * Everything is scoped to the two page wrappers. This file loads on two pages out of ~800 static
 * ones, and an unscoped token block would still reach the shared navbar and footer on both.
 *
 * Colour carries meaning here rather than being suppressed: hue names the KIND of thing (money
 * is green, a deadline is amber, a stop is red, the product itself is indigo) and every hue has a
 * matching soft tint for the surface behind it, so a coloured icon never sits on bare white.
 */

.pr-wrap, .pd-wrap, #pd-edit .modal-content, #pd-plan .modal-content {

  /* ---- ground and ink (light) ------------------------------------------------------------ */
  --paper:       #ffffff;              /* cards - the things you read */
  --paper-2:     #f7f8fd;              /* the page ground and recessed strips */
  --ink:         #16143a;              /* 15.8:1  headings, values, the answer */
  --ink-2:       #5b5a75;              /*  7.0:1  prose, secondary values */
  --ink-3:       #6b6a86;              /*  5.21:1 on --paper, 4.91:1 on --paper-2 */
  --rule:        #eceaf6;              /* decorative hairline only - 1.2:1, never a boundary */
  --rule-strong: #dcd9ee;              /* decorative, heavier. NOT a control boundary: 1.38:1 */
  /* WCAG 1.4.11 wants 3:1 for the edge of a control you have to find. --rule-strong was being
     used for that and is nowhere near it, so boundaries get their own token. */
  --edge:        #8b86a9;              /*  3.45:1 on --paper, 3.26:1 on --paper-2 */

  /* One accent, plus the second stop its gradients run to. */
  --accent:      #5546d6;              /*  6.49:1 on --paper */
  --accent-2:    #7c4ee0;              /*  5.22:1 against white - it is a gradient END STOP and
                                          a hover fill, so white sits on it and it must clear
                                          4.5:1. #8b5cf6 was 4.23:1 and failed. */
  --accent-soft: #efebfe;
  --grad:        linear-gradient(135deg, #5546d6 0%, #7c4ee0 100%);

  /* Semantic hues. Each is a text-safe value plus the tint that sits behind it. */
  /* Ratios below are computed against --paper / --paper-2 / their own soft tint, in that order.
     The four previous values were asserted rather than measured and three of them failed AA:
     #0f9d6e was 3.46:1, #b7791f 3.64:1, #dc3545 4.27:1 on --paper-2. */
  --pos:  #0a7d57;  --pos-soft:  #e6f7f1;   /* 5.14 / 4.85 / 4.64  money in, approved, paid */
  --neg:  #c62828;  --neg-soft:  #fdecee;   /* 5.62 / 5.30 / 4.93  rejected, overdue, owing */
  --warn: #8f5d13;  --warn-soft: #fdf3e3;   /* 5.61 / 5.29 / 5.10  priority, awaiting */
  --info: #1d4ed8;  --info-soft: #eaf1fe;   /* 6.70 / 6.32 / 5.91  new, informational */
  /* Text that sits ON a saturated fill. White in light; in dark the hues are LIFTED for
     legibility on a dark ground, which makes them pale - white on them is 1.8-2.4:1, while the
     dark ink is 7.4-10:1. One token so a fill cannot be recoloured without its text following. */
  --on-hue: #ffffff;

  /* ---- type ------------------------------------------------------------------------------
     One family: the theme's own sans. Labels are the sans at 700 uppercase rather than a second
     typeface, so the page reads as one voice and nothing depends on a font that may not load. */
  --f-display: inherit;
  --t-display: clamp(1.8rem, 1.2rem + 2vw, 2.5rem);  /* 1.15 / -.02em / 800 */
  --t-title:   1.35rem;                              /* 1.25 / 800 */
  --t-lead:    1rem;                                 /* 1.65 */
  --t-body:    .9375rem;                             /* 1.6 */
  --t-small:   .875rem;                              /* 1.5 */
  --t-micro:   .72rem;                               /* 1.4 / 700 / uppercase / .08em */

  /* ---- space: eight steps, nothing between ------------------------------------------------ */
  --s1: 4px;  --s2: 8px;  --s3: 12px; --s4: 16px;
  --s5: 24px; --s6: 40px; --s7: 64px; --s8: 96px;
  --measure: 34rem;                    /* ~66ch - every paragraph and message body */
  --radius:   12px;                    /* cards and panels */
  --radius-sm: 8px;                    /* fields, chips, tiles */
  --radius-btn: 0;                     /* buttons are square across the site */
  --ease: cubic-bezier(.2, 0, 0, 1);
  --row-pad: 18px;                     /* list rows; overridden for ADMIN */
  --shadow: 0 1px 2px rgba(22, 20, 58, .04), 0 8px 24px rgba(22, 20, 58, .06);
}

[data-bs-theme="dark"] .pr-wrap,
[data-bs-theme="dark"] .pd-wrap,
[data-bs-theme="dark"] #pd-edit .modal-content,
[data-bs-theme="dark"] #pd-plan .modal-content {
  --paper:       #1a1d2e;
  --paper-2:     #14162340;
  --ink:         #f2f1f8;              /* 14.9:1 */
  --ink-2:       #b0aec6;              /*  7.4:1 */
  --ink-3:       #8f8dab;              /*  4.9:1 */
  --rule:        rgba(242, 241, 248, .10);
  --rule-strong: rgba(242, 241, 248, .20);
  --edge:        #6c6a80;              /*  3.19:1 over --paper */
  --accent:      #a99cff;              /*  8.3:1 */
  --accent-2:    #c4a8ff;
  --accent-soft: rgba(169, 156, 255, .16);
  --grad:        linear-gradient(135deg, #5a4bd0 0%, #7a5ae8 100%);  /* white: 6.26 -> 4.71 */
  /* Lifted for a dark ground: the light-mode values are 2-3:1 here. */
  --pos:  #3ddc9a;  --pos-soft:  rgba(61, 220, 154, .14);
  --neg:  #ff8a94;  --neg-soft:  rgba(255, 138, 148, .14);
  --warn: #f0b429;  --warn-soft: rgba(240, 180, 41, .14);
  --info: #7aa7ff;  --info-soft: rgba(122, 167, 255, .14);
  --shadow: 0 1px 2px rgba(0, 0, 0, .3), 0 8px 24px rgba(0, 0, 0, .28);
  --on-hue: #16143a;                   /* 7.4-10:1 on the lifted hues above */
}

/* ---- the shared primitives ---------------------------------------------------------------
   Every piece of text and every decorated element on all three surfaces is one of these.
   Naming them is what stops the next label inventing a seventh size or a twelfth hue. */

/* 1. LABEL - every metadata key, table header, kicker, byline and stage label. */
.pr-wrap .lbl, .pd-wrap .lbl,
.pd-meta dt, .pd-money-k, .pd-hl-node .k, .pd-wrap thead th,
.pd-track .lbl, .pd-msg .who, .pr-tech-count, .pd-card-hd, .pd-tab span {
  font-size: var(--t-micro); font-weight: 700;
  text-transform: uppercase; letter-spacing: .08em; color: var(--ink-3);
  line-height: 1.4; margin: 0;
}

/* 2. VALUE - every metadata value. */
.pr-wrap .val, .pd-wrap .val,
.pd-meta dd, .pd-money-v, .pd-hl-node .v {
  font-size: var(--t-small); font-weight: 700; color: var(--ink); line-height: 1.4;
}

/* 3. FIGURE - money, dates, counts, ids. Tabular so columns of money line up on the decimal
      without changing typeface. */
.pr-wrap .num, .pd-wrap .num, .pd-money-v, .pd-ring .pct {
  font-variant-numeric: tabular-nums; font-feature-settings: "tnum" 1;
}

/* 4. TILE - the soft rounded square behind every icon. An icon on bare white reads as clip art;
      the same icon on its own tint reads as a component. `.t-*` picks the hue. */
.jt {
  display: inline-grid; place-items: center; flex: 0 0 auto;
  width: 2.1rem; height: 2.1rem; border-radius: var(--radius-sm);
  background: var(--accent-soft); color: var(--accent); font-size: .8rem;
}
.jt-lg { width: 2.75rem; height: 2.75rem; border-radius: 10px; font-size: 1rem; }
.t-pos  { background: var(--pos-soft);  color: var(--pos); }
.t-neg  { background: var(--neg-soft);  color: var(--neg); }
.t-warn { background: var(--warn-soft); color: var(--warn); }
.t-info { background: var(--info-soft); color: var(--info); }
.t-ink  { background: var(--paper-2);   color: var(--ink-2); }

/* 5. PILL - status and priority. Carries a glyph as well as a hue, because colour alone cannot
      be read by everyone and is the first thing lost to a greyscale print. */
.jp {
  display: inline-flex; align-items: center; gap: .35rem;
  padding: .22rem .6rem; border-radius: 999px;
  font-size: var(--t-micro); font-weight: 700; white-space: nowrap;
  background: var(--accent-soft); color: var(--accent);
}
.jp-pos  { background: var(--pos-soft);  color: var(--pos); }
.jp-neg  { background: var(--neg-soft);  color: var(--neg); }
.jp-warn { background: var(--warn-soft); color: var(--warn); }
.jp-info { background: var(--info-soft); color: var(--info); }
.jp-mute { background: var(--paper-2);   color: var(--ink-2); }

/* 6. LINK - inline navigation that is not a button. */
.pr-link {
  font-size: var(--t-small); font-weight: 700; color: var(--accent);
  text-decoration: none; background: none; border: 0; padding: 0; cursor: pointer;
}
.pr-link:hover { color: var(--accent); text-decoration: underline; text-underline-offset: .25em; }
.pr-link:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

/* 7. The primary action. The one gradient on each surface, so it cannot be mistaken for
      decoration - it is always the thing that submits or creates. */
/* Solid ink rather than a gradient, and a 6px corner: a flat dark block reads as the one
   committing action on the surface without competing with the coloured tiles around it.
   --ink/--paper rather than literals, so the pair inverts in dark mode instead of leaving a
   near-black button on a near-black page. White on --ink is 15.8:1. */
.jbtn {
  display: inline-flex; align-items: center; gap: .5rem;
  background: var(--ink); color: var(--paper);
  border: 1px solid var(--ink); border-radius: var(--radius-btn);
  padding: .6rem 1.1rem; font-size: var(--t-small); font-weight: 700;
  text-decoration: none; cursor: pointer; box-shadow: none;
  transition: background-color .16s var(--ease), border-color .16s var(--ease);
}
/* The brand comes back on hover, which is where movement belongs on a control you are about to
   press. White on --accent is 6.49:1. */
.jbtn:hover { background: var(--accent); border-color: var(--accent); color: #fff; }
.jbtn:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
/* White on --ink-3 is 5.21:1 now that the token moved; it was 3.77:1 before. */
.jbtn:disabled { background: var(--ink-3); border-color: var(--ink-3); color: #fff;
  box-shadow: none; transform: none; cursor: default; }
/* The quiet half of a pair - "Back" beside "Pay". Same geometry, no fill, a real 3:1 edge. */
.jbtn-quiet { background: var(--paper); color: var(--ink-2); border-color: var(--edge); }
.jbtn-quiet:hover { background: var(--paper); border-color: var(--ink); color: var(--ink); }

/* Headings. */
.pr-wrap h1, .pd-wrap h1, .pr-sec, .pd-subject, .pd-empty h2 {
  font-family: var(--f-display); font-weight: 800; color: var(--ink);
  letter-spacing: -.02em; text-wrap: balance;
}
.pr-wrap p, .pd-wrap p { text-wrap: pretty; }

/* Breadcrumbs are metadata, so they take the metadata treatment. Shared because both surfaces
   carry one and they must not drift apart. */
.pr-crumb { font-size: var(--t-micro); font-weight: 700; text-transform: uppercase;
  letter-spacing: .08em; color: var(--ink-3); margin: 0 0 var(--s5); }
/* The one clickable thing in the breadcrumb was --ink-3, the same grey as the static text either
   side of it, so nothing said it could be pressed. Accent plus weight makes it read as a link
   without turning the whole line blue. */
.pr-crumb a { color: var(--accent); font-weight: 800; text-decoration: none; }
.pr-crumb a:hover { color: var(--accent); text-decoration: underline;
  text-underline-offset: .25em; }
.pr-crumb a:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.pr-crumb .sep { margin: 0 var(--s2); color: var(--rule-strong); }

/* Phoenix component neutralisation.
   DESCENDANT selectors, deliberately: theme.css declares these custom properties on .card, .btn
   and .table THEMSELVES (theme.min.css:6055 for the card radius), so the same override written on
   the ancestor wrapper is shadowed by the component's own declaration and never lands. */
.pr-wrap .card, .pd-wrap .card {
  --phoenix-card-bg: var(--paper);
  --phoenix-card-border-color: var(--rule);
  --phoenix-card-border-radius: var(--radius);
  --phoenix-card-box-shadow: none;
}
.pr-wrap .alert, .pd-wrap .alert {
  --phoenix-alert-border-radius: var(--radius);
  font-size: var(--t-small);
}

/* TOAST - a confirmation that does not depend on where the page is scrolled.
   Deliberately fixed rather than inline: an upload can be triggered from the composer at the
   bottom of a long thread, and an inline banner at the top confirms it somewhere the reader is
   not looking. Not scoped to the wrappers because it is appended to <body>, outside them. */
.jch-toast {
  position: fixed; z-index: 1080; left: 50%; bottom: 24px; transform: translateX(-50%);
  display: flex; align-items: center; gap: .6rem; max-width: min(92vw, 30rem);
  background: #16143a; color: #fff; border-radius: 8px;
  padding: .7rem 1rem; font-size: .875rem; font-weight: 600;
  box-shadow: 0 8px 30px rgba(22, 20, 58, .35);
  animation: jch-toast-in .22s cubic-bezier(.2, 0, 0, 1);
}
.jch-toast .fa-solid { color: #3ddc9a; }
.jch-toast.is-out { opacity: 0; transition: opacity .25s ease; }
@keyframes jch-toast-in { from { opacity: 0; transform: translate(-50%, 10px); }
                          to   { opacity: 1; transform: translate(-50%, 0); } }
@media (prefers-reduced-motion: reduce) {
  .jch-toast { animation: none; }
  .jch-toast.is-out { transition: none; }
}

/* Motion. Everything is opacity or a single axis, <= 320ms, on one curve. Nothing loops except
   the functional spinner, which lives on the page that owns it. */
@keyframes pr-rise { from { opacity: 0; transform: translateY(6px); }
                     to   { opacity: 1; transform: none; } }
@keyframes pd-fill { to { transform: scaleX(1); } }
@keyframes pd-in   { from { opacity: 0; transform: scale(.9); }
                     to   { opacity: 1; transform: none; } }
/* Movement ONLY - deliberately no opacity.
   An entrance that fades from 0 makes the content's visibility depend on the animation actually
   running, and a document whose timeline never advances (a background tab, a frame that is not
   compositing) leaves it pinned at the first frame: a blank panel where the project should be.
   Shifting 8px instead means the worst case is a panel sitting 8px low and perfectly readable.
   The same reasoning is on .pd-ring.draw - correctness must not depend on a clock. */
@keyframes pd-sect { from { transform: translateY(8px); }
                     to   { transform: none; } }

@media (prefers-reduced-motion: reduce) {
  .jbtn, .jbtn:hover { transition: none; transform: none; }
}
