/* ==========================================================================
   portfolio-reviewer — design system

   Implements DESIGN.md from austinjoseph617/design-system, which is the single
   source of truth. Every value below is read from that document, never from
   another app's stylesheet: two implementations agreeing by coincidence is the
   drift the shared document exists to prevent.

   A value this file needs and the document does not give is a defect in the
   document. Add it there first (§9), then use it here.

   Section numbers in the comments below cite DESIGN.md.
   ========================================================================== */

:root {
  /* --- §2 Surfaces ---------------------------------------------------- */
  --bg:            #F5F5F7;               /* app canvas */
  --surface:       #FFFFFF;               /* cards */
  --fill:          rgba(120,120,128,.08); /* input fills, row hover */
  --fill-hover:    rgba(120,120,128,.12);

  /* --- §2 Text -------------------------------------------------------- */
  --label:         #1D1D1F;               /* primary; never pure black */
  --label-2:       rgba(60,60,67,.78);    /* supporting text */
  --label-3:       rgba(60,60,67,.30);    /* placeholders, disabled */

  /* --- §2 Lines & accent ---------------------------------------------- */
  --separator:     rgba(60,60,67,.18);
  --border:        rgba(60,60,67,.22);
  --accent:        #1D1D1F;               /* graphite, not a hue */
  --accent-hover:  #000000;
  --on-accent:     #FFFFFF;
  --focus-ring:    rgba(29,29,31,.28);

  /* --- §2 Semantic — the only permitted hues -------------------------- */
  --red:           #D70015;               /* destructive, errors */
  --red-bg:        #FDECEB;
  --green:         #248A3D;               /* affirmative */
  --green-bg:      #E8F5EA;
  --amber:         #B25000;               /* warning */
  --amber-bg:      #FDF0E3;

  /* --- §2 Tier hues — vivid, fills only, never text ------------------- */
  /* None clears 4.5:1 on white (2.06, 2.02, 3.65), so a tier's name is set
     in --label beside the hue rather than in it. Graphite on any of the three
     clears AA at 4.61:1 worst case, which is what a marker relies on. */
  --tier-incomplete:   #FF9F0A;           /* 0-65   Incomplete        */
  --tier-ready:        #30D158;           /* 66-100 Ready             */
  /* The bands before 2026-10-25. Kept, not deleted: a report written then has
     its band frozen on it and draws its own gauge, and `var(--tier-strong)`
     resolving to nothing would paint a transparent stop with no error. */
  --tier-strong:       #30D158;           /* 66-82  Strong   (legacy) */
  --tier-exceptional:  #0A84FF;           /* 83-100 Exceptional (legacy) */

  /* --- §3 Typography -------------------------------------------------- */
  --font: -apple-system, BlinkMacSystemFont, "SF Pro Text", "Segoe UI",
          Helvetica, Arial, sans-serif;

  /* Each step is three tokens, not one (§3). */
  --t-title1-size: 28px; --t-title1-weight: 700; --t-title1-track: -.022em;
  --t-title2-size: 22px; --t-title2-weight: 700; --t-title2-track: -.021em;
  --t-title3-size: 19px; --t-title3-weight: 600; --t-title3-track: -.015em;
  --t-headline-size: 17px; --t-headline-weight: 600; --t-headline-track: -.011em;
  --t-body-size: 15px;   --t-body-weight: 400;   --t-body-track: -.006em;
  --t-callout-size: 14px; --t-callout-weight: 400; --t-callout-track: -.003em;
  --t-footnote-size: 13px; --t-footnote-weight: 400; --t-footnote-track: 0;
  --t-caption-size: 12px; --t-caption-weight: 400; --t-caption-track: 0;
  --t-caption2-size: 11px; --t-caption2-weight: 500; --t-caption2-track: .006em;

  /* --- §4 Spacing — 8pt grid, 4pt subdivisions ------------------------ */
  --s-1: 4px; --s-2: 8px; --s-3: 12px; --s-4: 16px;
  --s-5: 24px; --s-6: 32px; --s-7: 48px;

  /* --- §4 Radii ------------------------------------------------------- */
  --r-sm: 6px; --r-md: 8px; --r-lg: 12px; --r-xl: 18px; --r-full: 999px;

  /* --- §4 Elevation — depth is a whisper ------------------------------ */
  --shadow-sm: 0 1px 2px rgba(0,0,0,.04), 0 0 0 .5px rgba(0,0,0,.03);
  --shadow-md: 0 4px 12px rgba(0,0,0,.06), 0 1px 3px rgba(0,0,0,.04);

  /* --- §4 Layout constants -------------------------------------------- */
  --row-h: 44px;                          /* minimum list row (§4b) */
}

/* There is no dark mode. §2's inverted surface is for transient chrome and is
   explicitly "not a second theme and not a dark mode" — so the app has one
   palette, and inventing a second would be inventing values (§8). */

* { box-sizing: border-box; }

body {
  margin: 0;
  padding: var(--s-6) var(--s-4) var(--s-7);
  background: var(--bg);                  /* §4b: content sits on a canvas */
  color: var(--label);
  font-family: var(--font);
  font-size: var(--t-body-size);
  font-weight: var(--t-body-weight);
  letter-spacing: var(--t-body-track);
}

/* §7 — one focus ring for the whole product. :focus-visible, so keyboard users
   see it and mouse users don't. */
:focus-visible {
  outline: 2px solid var(--focus-ring);
  outline-offset: 2px;
}

/* --------------------------------------------------------------------------
   §5 Components — only those a one-page app has an equivalent of. The rest of
   §5 (toolbar, sidebar, inspector, revision history) describes furniture this
   app does not have.
   -------------------------------------------------------------------------- */

/* .card-inset — white rounded card on the grey canvas (§4b). */
.card-inset {
  background: var(--surface);
  border: 1px solid var(--separator);
  border-radius: var(--r-lg);
  box-shadow: var(--shadow-sm);
  padding: var(--s-5);
  margin-bottom: var(--s-4);
}

/* .btn — default white with a hairline; .btn-primary is graphite filled. */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: 40px;                       /* §7: ≥40px on touch surfaces */
  padding: 0 var(--s-4);
  font-family: inherit;
  font-size: var(--t-callout-size);
  font-weight: 600;
  letter-spacing: var(--t-callout-track);
  color: var(--label);
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  cursor: pointer;
}
.btn:hover { background: var(--fill); }
.btn-primary {
  color: var(--on-accent);
  background: var(--accent);
  border-color: var(--accent);
}
.btn-primary:hover { background: var(--accent-hover); border-color: var(--accent-hover); }
/* .btn-quiet — borderless chrome (§5). Two declarations: the label colour and
   the hover fill already come from .btn, and the disabled dimming below. */
.btn-quiet { background: none; border-color: transparent; }
/* Two spellings of one state, so an inert button has one definition of how it
   looks. aria-disabled is for a button that must still answer a press in order
   to say why it will not go; see public/login/index.html. */
.btn:disabled, .btn[aria-disabled="true"] { opacity: .5; cursor: not-allowed; }
.btn:disabled:hover, .btn[aria-disabled="true"]:hover { background: var(--accent); }
/* That rule is for the filled primary. A quiet button has no fill to keep, so
   without this a disabled one turns graphite under the pointer. */
.btn-quiet:disabled:hover, .btn-quiet[aria-disabled="true"]:hover { background: none; }

/* .field — input/select/textarea. Graphite border and a soft ring on focus. */
.field {
  min-height: 40px;
  padding: 0 var(--s-3);
  font-family: inherit;
  font-size: var(--t-body-size);
  letter-spacing: var(--t-body-track);
  color: var(--label);
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-md);
}
.field::placeholder { color: var(--label-3); }
.field:focus-visible { border-color: var(--accent); }

/* .row-actions — the controls that act on the block above them, and whatever
   says what they did. Lived in settings/index.html until the pilot banner's
   feedback form needed it too - §9.4: the second consumer is what moves a
   rule here. Wrapping is the point of it: a button and its status text sit on
   one line until they cannot, and then the text takes its own rather than
   pushing the button off the edge. */
.row-actions {
  display: flex; align-items: center; gap: var(--s-3);
  margin-top: var(--s-3); flex-wrap: wrap;
}

/* --- Page chrome (§5) -------------------------------------------------------
   The wordmark, the rule under it, and the icon controls either side. These
   lived in index.html until the settings page needed them too - §9.4: the
   second consumer is what moves a rule here. */

.wrap { max-width: 780px; margin: 0 auto; }

/* The rule closes the navigation, so it spans the page rather than the 780px
   text column - which is why <header> sits outside .wrap and carries its own.
   The report's feedback section is 1400px wide, and a 780px rule above it
   reads as an underline on the title instead of an edge to the chrome. */
header {
  padding-bottom: var(--s-4);
  border-bottom: 1px solid var(--separator);
  margin-bottom: var(--s-7);
}

/* Title left, settings right, and the home control in the left gutter. */
.home-row {
  position: relative;
  display: flex; align-items: center; justify-content: space-between;
  gap: var(--s-3);
}

/* The controls at the right of the wordmark row. A group so they stay
   together when space-between pushes them to the edge. */
.chrome-actions { display: flex; align-items: center; gap: var(--s-1); }

/* DESIGN.md 5: 32px square. 7: interactive chrome is at least 32px, and an
   icon-only control carries aria-label and title, which the anchor does. */
.btn-icon {
  display: inline-flex; align-items: center; justify-content: center;
  width: 32px; height: 32px;
  border-radius: var(--r-md); color: var(--label-2);
  /* Browser defaults, not design decisions: an <a href> arrives underlined and
     a <button> arrives with a 2px outset border, a grey fill, Arial and a
     default cursor. The header draws one of these controls as each, so without
     the reset the pair sits side by side looking like two different
     components. DESIGN.md 3: no underline in chrome. */
  text-decoration: none; cursor: pointer;
  background: none; border: 0; font: inherit;
}
.btn-icon:hover { background: var(--fill); color: var(--label); }

/* A labelled control is no longer icon-only, so the fixed square gives way to
   the text while the 32px minimum the rule actually asks for stays. A modifier
   rather than a change to the base: .btn-home is positioned absolutely in the
   gutter, and widening every .btn-icon would move it. */
.btn-icon.labelled {
  width: auto; gap: var(--s-2);
  padding: 0 var(--s-3); min-height: 32px;
  font-size: var(--t-footnote-size);
  letter-spacing: var(--t-footnote-track);
}

/* On a phone the words come off and the square comes back. Measured rather
   than guessed: the wordmark and the two labelled controls stop sharing one
   line at 447px with no home control on screen and about 475px with one, and
   below that the wordmark wrapped to two lines. 480px is the first round step
   clear of both.

   `clip-path` rather than `display: none`, and the text is kept rather than
   dropped, because that text IS the control's accessible name now that the
   `aria-label` is gone - removing it from the box tree would take the name
   with it and leave an icon nobody can identify. */
@media (max-width: 480px) {
  .btn-icon.labelled { width: 32px; padding: 0; gap: 0; }
  .btn-icon.labelled > span {
    position: absolute; width: 1px; height: 1px;
    overflow: hidden; clip-path: inset(50%); white-space: nowrap;
  }
}

/* Out of flow, in the gutter left of the text column. In flow it was a flex
   item, so appearing pushed the title sideways and opening a report made the
   heading jump. */
.btn-home {
  position: absolute; right: calc(100% + var(--s-2));
  top: 50%; transform: translateY(-50%);
}
/* Hidden until a report is on screen - at home it would be a control that does
   nothing. DESIGN.md 10: a control appearing once per page is drawn at rest,
   not revealed on hover. The title is NOT conditional; it carries the app's
   name. A page that is always somewhere to come back from omits `.when-report`
   and the control simply shows. */
.btn-home.when-report { display: none; }
header.has-home .btn-home.when-report { display: inline-flex; }

.icon { width: 20px; height: 20px; }            /* DESIGN.md 6 */
.icon-sm { width: 16px; height: 16px; }         /* DESIGN.md 6 */

/* No underline in chrome (DESIGN.md 3). */
a.home { color: inherit; text-decoration: none; }
h1 a.home:hover { color: var(--label-2); }

h1 {
  margin: 0;
  font-size: var(--t-title1-size);
  font-weight: var(--t-title1-weight);
  letter-spacing: var(--t-title1-track);
}
/* Title Case in secondary grey — never an uppercase micro-label (§3). */
h2 {
  margin: var(--s-6) 0 var(--s-2);
  font-size: var(--t-footnote-size);
  font-weight: 600;
  color: var(--label-2);
}
h3 {
  margin: 0 0 var(--s-2);
  font-size: var(--t-footnote-size);
  font-weight: 600;
  color: var(--label-2);
}
/* A sub-heading of an h3, at the same step. Without a rule it falls through to
   the browser's 15px/700 graphite with a fractional margin - off the scale, and
   louder than the heading above it (§3). */
h4 {
  margin: 0 0 var(--s-2);
  font-size: var(--t-footnote-size);
  font-weight: 600;
  color: var(--label-2);
}

@media (max-width: 860px) {
  /* No gutter left to put it in: the column is 780px inside 16px of body
     padding, so free space to the left is (viewport - 780) / 2 and the icon
     needs 40px - it runs out at exactly this breakpoint. Back into flow. */
  .btn-home { position: static; transform: none; }
  .home-row { gap: var(--s-2); }
}

/* .meter — 0–100% bar. Neutral: data never carries hue (§5). */
.meter {
  height: var(--s-2);
  background: var(--fill);
  border-radius: var(--r-full);
  overflow: hidden;
}
.meter > div {
  height: 100%;
  background: var(--label);
  border-radius: var(--r-full);
  transition: width .5s ease;
}

/* .err — a failure said in words, in the one hue that means it (§2). Both
   pages show one, which is what moved it here (§9.4). */
.err { color: var(--red); }

/* .pill — compact status. Hue only for meaning (§2). */
.pill {
  display: inline-block;
  padding: var(--s-1) var(--s-2);
  font-size: var(--t-caption-size);
  border-radius: var(--r-full);
  background: var(--fill);
  color: var(--label-2);
}
.pill.affirm { background: var(--green-bg); color: var(--green); }
.pill.warn   { background: var(--amber-bg); color: var(--amber); }

/* .notice — amber read-only banner (§5), hidden until .show exactly as the
   component table specifies. Same meaning the document gives .snapshot: what
   you are reading is not the whole record. Used here when a review covers only
   part of a page, which is a caveat on the score rather than a decoration. */
.notice { display: none; }
.notice.show {
  display: block;
  padding: var(--s-3) var(--s-4);
  margin-bottom: var(--s-4);
  background: var(--amber-bg);
  color: var(--amber);
  border-radius: var(--r-md);
  font-size: var(--t-callout-size);
  letter-spacing: var(--t-callout-track);
}
.notice strong { font-weight: 600; }
.notice .note {
  display: block;
  margin-top: var(--s-1);
  font-size: var(--t-footnote-size);
}

/* .empty-state — says why, and what to do about it (§5, §10). */
.empty-state {
  padding: var(--s-5);
  text-align: center;
  color: var(--label-2);
  font-size: var(--t-callout-size);
}


/* --- Text helpers (§3) ------------------------------------------------- */
.secondary { color: var(--label-2); }
.tertiary  { color: var(--label-3); }

/* Says what is not built yet, so a section that looks inert reads as pending
   rather than broken. Lived in settings/index.html until the how-it-works page
   needed it too - §9.4: the second consumer is what moves a rule here. */
.soon {
  margin: var(--s-2) 0 0;
  color: var(--label-3); font-size: var(--t-footnote-size);
}

/* Numbers in counts and lists align (§3). */
.tnum { font-variant-numeric: tabular-nums; }

/* A review's cost, in both lists that draw a review - §9.4: the second
   consumer is what moves a rule here.

   The width is the point. Without it the cell is as wide as its contents, so
   "~$0.808", "$0.22" and the em dash of a review with no token data each put
   the date beside them in a different place, and a list of twenty rows has no
   columns at all. 4rem holds the longest of them right-aligned at footnote
   size, which is also why the figure is .tnum: a column of money lines up on
   the decimal point or it is not a column. */
.cost { min-width: 4rem; text-align: right; }

/* §7 — every animation is wrapped. */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .01ms !important;
  }
}
