/* The client portal.
 *
 * The frame is not in here. A portal page loads dashboard.css and
 * dashboard-shell.css the same way an employee dashboard page does, and gets
 * the sidebar, the section header, the stat tiles (.dash-stats), the tables
 * (.quotes-table) and the form controls (.jf-group) from them. This file adds
 * only the things a client report has and the staff dashboard does not.
 *
 * Which means: before adding a rule here, check whether the dashboard already
 * has the component. Two nearly-matching cards is the thing this file exists
 * to avoid.
 *
 * Radii are 8px and surfaces are var(--bg-card) on var(--border), because that
 * is what every card in dashboard.css uses.
 */

.portal-page { background: var(--bg-primary); }

/* Stat tiles.
 *
 * .dash-stats is a four-column grid (two on a phone), because the dashboard's
 * own sections carry four figures. A client report carries three, which would
 * leave an empty column on a desktop and one tile alone on a phone's second
 * row, so the portal has three columns at every width. Scoped to .portal-page
 * so the staff dashboard keeps the grid it was built for. */
.portal-page .dash-stats { grid-template-columns: repeat(3, minmax(0, 1fr)); }

/* Sections ---------------------------------------------------------------- */

.pt-section { margin-top: 28px; }
.pt-section h2 { font-size: 1.15rem; margin: 0; }
.pt-sub {
  color: var(--text-secondary);
  font-size: 0.85rem;
  margin: 0.2rem 0 12px;
  max-width: 70ch;
}
.pt-panel {
  background: var(--bg-card);
  border: 1px solid var(--border);
  border-radius: 8px;
  padding: 17px;
}
.pt-summary { margin: 0; }

/* A heading with no standfirst under it still needs the standfirst gap. On the
 * heading rather than on what follows it, because under a type filter the
 * first entry below a heading is often hidden and an adjacent-sibling rule
 * would land the margin on the hidden one. */
.pt-section h2:not(:has(+ .pt-sub)) { margin-bottom: 12px; }

/* The assurance line.
 *
 * Above the incidents on purpose: being there every night is the thing being
 * bought, and it is what makes a quiet week read as a good week rather than as
 * an empty report. The green edge is the one colour in here that is not a
 * theme token, because it means "covered" rather than "accent". */
/* The same shape as .pt-attention, the banner it stands in for: the text takes
   the row so Download PDF sits at the right, and the same gap to the figures
   below. */
.pt-assure {
  display: flex;
  gap: 12px 16px;
  align-items: center;
  flex-wrap: wrap;
  background: var(--bg-card);
  border: 1px solid var(--border);
  border-left: 3px solid #16a34a;
  border-radius: 0 8px 8px 0;
  padding: 16px 18px;
  margin-top: 18px;
  margin-bottom: 20px;
}
.pt-tick { color: #16a34a; font-size: 1.25rem; line-height: 1; flex: 0 0 auto; }
.pt-assure p { margin: 0; font-size: 0.92rem; flex: 1 1 280px; min-width: 0; }

/* Moving between reports ---------------------------------------------------
 *
 * Previous, the picker, next, on one line above the report. The arrows keep
 * their words on a wide screen and drop them on a phone, where the picker
 * between them already says which week this is. */
.pt-period {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  flex-wrap: wrap;
  background: var(--bg-card);
  border: 1px solid var(--border);
  border-radius: 8px;
  padding: 8px;
}
.pt-step {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 8px 12px;
  border-radius: 6px;
  font-size: 0.85rem;
  font-weight: 600;
  color: var(--accent-text);
  text-decoration: none;
  white-space: nowrap;
}
.pt-step:hover { background: var(--bg-secondary); }
.pt-step:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.pt-step.is-off { color: var(--text-secondary); opacity: 0.45; }
.pt-pick { display: flex; align-items: center; gap: 8px; flex: 1 1 auto; justify-content: center; min-width: 0; }
.pt-pick-select {
  min-width: 0;
  max-width: 100%;
  padding: 8px 12px;
  border: 1px solid var(--border);
  border-radius: 6px;
  background: var(--bg-card);
  color: var(--text-primary);
  font: inherit;
  font-size: 0.92rem;
  font-weight: 600;
  cursor: pointer;
}
.pt-pick-select:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
/* The Go button is for when script is off. portal-report.js hides it. */
.pt-pick-go[hidden] { display: none; }
.pt-period-range { display: flex; flex-direction: column; padding: 4px 8px; }
.pt-period-range span { font-size: 0.8rem; color: var(--text-secondary); }
.pt-period-links { display: flex; gap: 8px; flex-wrap: wrap; }
/* The switcher sits where the first banner used to, so the banner under it
   keeps a gap rather than touching it. */
.pt-period + .pt-attention, .pt-period + .pt-assure { margin-top: 14px; }

@media (max-width: 600px) {
  /* Arrows either side of the picker on one line, the picker taking what is
     left, rather than three rows stacked. */
  .pt-period:has(.pt-pick) { flex-wrap: nowrap; gap: 4px; }
  .pt-pick { flex: 1 1 0; }
  .pt-pick-select { width: 100%; font-size: 0.85rem; padding: 8px; }
  .pt-step-label { display: none; }
  .pt-step { padding: 10px 8px; }
}

/* When we were there ------------------------------------------------------
 *
 * The hours, not a count of visits. "311 patrol visits" is a number nobody can
 * answer; an unbroken band from 9pm to 5am is the thing being bought.
 */
.pt-hours { display: flex; align-items: flex-end; gap: 5px; height: 150px; }
.pt-hour {
  flex: 1;
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  align-items: center;
  height: 100%;
  min-width: 0;
}
.pt-hour-fill { width: 100%; background: var(--accent); border-radius: 3px 3px 0 0; display: block; }
.pt-hour-label {
  font-size: 0.6rem;
  color: var(--text-secondary);
  margin-top: 6px;
  white-space: nowrap;
}

/* Week by week ------------------------------------------------------------ */

.pt-trend { display: flex; align-items: flex-end; gap: 10px; height: 150px; }
.pt-trend-col {
  flex: 1;
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  align-items: center;
  height: 100%;
  min-width: 0;
}
.pt-trend-n {
  font-family: 'Rajdhani', sans-serif;
  font-size: 0.95rem;
  font-weight: 700;
  color: var(--text-secondary);
  margin-bottom: 4px;
  font-variant-numeric: tabular-nums;
}
.pt-trend-fill {
  width: 100%;
  background: var(--border);
  border-radius: 3px 3px 0 0;
  display: block;
}
/* The week being read, against the ones either side of it. */
.pt-trend-col.current .pt-trend-fill { background: var(--accent); }
.pt-trend-col.current .pt-trend-n { color: var(--accent-text); }
.pt-trend-label {
  font-size: 0.62rem;
  color: var(--text-secondary);
  margin-top: 6px;
  white-space: nowrap;
}

/* Needs your attention ---------------------------------------------------- */

.pt-attention {
  display: flex;
  align-items: center;
  gap: 16px;
  flex-wrap: wrap;
  background: var(--bg-card);
  border: 1px solid var(--border);
  border-left: 3px solid #d97706;
  border-radius: 0 8px 8px 0;
  padding: 16px 18px;
  margin-bottom: 20px;
}
.pt-attention > div { flex: 1 1 320px; min-width: 0; }
.pt-attention-n {
  flex: 0 0 auto;
  font-family: 'Rajdhani', sans-serif;
  font-size: 2.1rem;
  font-weight: 700;
  line-height: 1;
  color: #d97706;
  font-variant-numeric: tabular-nums;
}
.pt-attention p { margin: 0.2rem 0 0; font-size: 0.88rem; color: var(--text-secondary); }

/* Happening more than once ------------------------------------------------ */

.pt-repeats { list-style: none; margin: 0; padding: 0; }
.pt-repeats li + li { margin-top: 11px; }
.pt-repeats strong { display: block; font-size: 0.92rem; }
.pt-repeats span { display: block; font-size: 0.8rem; color: var(--text-secondary); }

/* Where we patrolled ------------------------------------------------------ */

.pt-sector + .pt-sector { margin-top: 11px; }
.pt-sector-top { display: flex; justify-content: space-between; align-items: baseline; gap: 10px; }
.pt-sector-n {
  font-family: 'Rajdhani', sans-serif;
  font-size: 1rem;
  color: var(--accent-text);
  font-variant-numeric: tabular-nums;
}
.pt-sector-bar {
  height: 6px;
  background: var(--bg-secondary);
  border-radius: 3px;
  overflow: hidden;
  margin-top: 0.35rem;
}
.pt-sector-bar i { display: block; height: 100%; background: var(--accent); }

/* The full log, closed by default ----------------------------------------- */

.pt-details {
  background: var(--bg-card);
  border: 1px solid var(--border);
  border-radius: 8px;
}
.pt-details > summary {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px;
  flex-wrap: wrap;
  padding: 14px 17px;
  cursor: pointer;
  list-style: none;
}
.pt-details > summary::-webkit-details-marker { display: none; }
.pt-details > summary::after {
  content: "Show";
  font-size: 0.78rem;
  font-weight: 600;
  color: var(--accent-text);
}
.pt-details[open] > summary::after { content: "Hide"; }
.pt-details > summary:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
.pt-details-title { font-family: 'Rajdhani', sans-serif; font-size: 1.05rem; font-weight: 700; }
.pt-details-n { font-size: 0.8rem; color: var(--text-secondary); margin-right: auto; }
.pt-details .pt-log { border: 0; border-top: 1px solid var(--border); border-radius: 0 0 8px 8px; }

/* Patrol notes: the checks a guard wrote something on. Inside .pt-details,
   capped like the patrol log so a busy month does not run for pages. */
.pt-pnotes {
  list-style: none;
  margin: 0;
  padding: 0;
  border-top: 1px solid var(--border);
  max-height: 440px;
  overflow-y: auto;
}
.pt-pnotes li { padding: 11px 17px; }
.pt-pnotes li + li { border-top: 1px solid var(--border); }
.pt-pnote-head { display: flex; justify-content: space-between; align-items: baseline; gap: 4px 12px; flex-wrap: wrap; }
.pt-pnote-head strong { font-size: 0.9rem; }
.pt-pnote-head span { font-size: 0.78rem; color: var(--text-secondary); }
.pt-pnotes p { margin: 0.3rem 0 0; font-size: 0.86rem; }

/* Themes ------------------------------------------------------------------ */

/* Each row is a link that filters the observations below, so it needs the hit
   area and the states a control has. The markup inside is all spans, which is
   what keeps an anchor wrapping it valid. */
.pt-theme {
  display: block;
  padding: 11px 12px;
  border: 1px solid transparent;
  border-radius: 6px;
  color: inherit;
  text-decoration: none;
  cursor: pointer;
  transition: background-color 0.15s, border-color 0.15s;
}
.pt-theme:hover { background: var(--bg-secondary); }
.pt-theme:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.pt-theme.active { background: var(--bg-secondary); border-color: var(--accent); }

.pt-theme + .pt-theme { margin-top: 4px; }
.pt-theme-top { display: flex; justify-content: space-between; align-items: baseline; gap: 10px; }
.pt-theme-n {
  font-family: 'Rajdhani', sans-serif;
  font-size: 1.05rem;
  color: var(--accent-text);
  font-variant-numeric: tabular-nums;
}
.pt-theme-bar {
  display: block;
  height: 6px;
  background: var(--bg-secondary);
  border-radius: 3px;
  overflow: hidden;
  margin: 0.35rem 0 0.25rem;
}
.pt-theme-bar i { display: block; height: 100%; background: var(--accent); }
.pt-theme-cats { display: block; font-size: 0.75rem; color: var(--text-secondary); }

/* Clearing the filter, beside the count it belongs to. */
.pt-obs-all { margin-left: 0.5rem; color: var(--accent-text); font-weight: 600; white-space: nowrap; }

/* Observations ------------------------------------------------------------ */

.pt-ob {
  background: var(--bg-card);
  border: 1px solid var(--border);
  border-radius: 8px;
  padding: 15px;
  margin-bottom: 11px;
}
.pt-ob-head {
  display: flex;
  justify-content: space-between;
  gap: 10px;
  align-items: flex-start;
  flex-wrap: wrap;
}
.pt-ob-head h3 { font-size: 1rem; margin: 0; }
.pt-ob-meta { margin: 0.2rem 0 0; font-size: 0.78rem; color: var(--text-secondary); }
.pt-ob-id { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 0.73rem; }
.pt-chip {
  flex: 0 0 auto;
  font-size: 0.66rem;
  text-transform: uppercase;
  letter-spacing: 0.5px;
  border: 1px solid var(--border);
  border-radius: 4px;
  padding: 0.12rem 0.45rem;
  color: var(--text-secondary);
}
/* Flagged by our own people as worth a look. Sorted to the top of its list as
   well as marked, because a flag below three routine entries is not a flag. */
.pt-ob.is-flagged { border-left: 3px solid #d97706; }
.pt-chip-flag { border-color: #d97706; color: #d97706; font-weight: 700; }

.pt-ob-note { margin: 0.65rem 0 0; font-size: 0.9rem; }
/* A remark left on a checkpoint rather than an account of an incident. */
.pt-ob-note.is-remark { color: var(--text-secondary); font-style: italic; }
.pt-ob-foot { margin-top: 0.75rem; display: flex; gap: 10px; align-items: center; flex-wrap: wrap; }
.pt-ask {
  background: none;
  border: 1px solid var(--border);
  color: var(--text-secondary);
  border-radius: 6px;
  padding: 0.32rem 0.7rem;
  font-family: 'Barlow', sans-serif;
  font-size: 0.78rem;
  font-weight: 600;
  cursor: pointer;
}
.pt-ask:hover { border-color: var(--accent); color: var(--accent-text); }
.pt-ev { font-size: 0.75rem; color: var(--text-secondary); }

/* Patrol log.
 *
 * The table itself is .quotes-table from dashboard.css. This only caps its
 * height, because a three-week view runs to several hundred checkpoint rows
 * and the sections below it should still be reachable. */
.pt-log { max-height: 440px; overflow-y: auto; }

/* Property list and empty states ------------------------------------------ */

.pt-empty { padding: 32px 0; color: var(--text-secondary); max-width: 60ch; }
.pt-empty p { margin: 0 0 0.6rem; }

.pt-site-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
  gap: 12px;
}
.pt-site-card {
  background: var(--bg-card);
  border: 1px solid var(--border);
  border-radius: 8px;
  padding: 18px;
  text-decoration: none;
  color: var(--text-primary);
  display: flex;
  flex-direction: column;
  gap: 10px;
  transition: border-color 0.15s;
}
.pt-site-card:hover { border-color: var(--accent); }
.pt-site-card h2 { font-size: 1.05rem; margin: 0; }
.pt-site-go { font-size: 0.82rem; color: var(--accent-text); font-weight: 600; }

/* Call to action ----------------------------------------------------------- */

.pt-cta {
  background: var(--bg-card);
  border: 1px solid var(--accent);
  border-radius: 8px;
  padding: 22px;
  text-align: center;
  margin-top: 30px;
}
.pt-cta h2 { margin: 0 0 0.25rem; font-size: 1.15rem; }
.pt-cta p { color: var(--text-secondary); margin: 0 auto 14px; max-width: 56ch; }

/* Sign in.
 *
 * The page itself is built on login.css and the public header, so it matches
 * the employee sign-in instead of being a second card that does the same job
 * differently. This is the only rule login.css has no equivalent for: the note
 * telling somebody to ring the office, which sits between the button and the
 * back link. */
.login-help {
  margin: 1.25rem 0 0;
  padding-top: 1.25rem;
  border-top: 1px solid var(--border);
  font-size: 0.82rem;
  line-height: 1.5;
  color: var(--text-secondary);
  text-align: center;
}
.login-help a { color: var(--accent-text); font-weight: 600; white-space: nowrap; }

@media (max-width: 600px) {
  .pt-ob-head { flex-direction: column; }
  /* Nine hour labels will not fit side by side on a phone; the hover title
     and the shape of the band still carry it. */
  .pt-hour-label { display: none; }
  .pt-trend-label { font-size: 0.55rem; }
}

/* The period, on a phone.
 *
 * dashboard-shell.css hides .dash-main-desc below 900px, on the reasoning that
 * a section's description is the first thing to give up for space. Here that
 * line is not a description: it is which week this report covers, and without
 * it the page does not say what it is showing. So the portal keeps it. */
@media (max-width: 900px) {
  .portal-page .dash-main-desc {
    display: block;
    font-size: 0.8rem;
  }
}

/* Client services ---------------------------------------------------------
 *
 * The request form, moved in from the public site. The controls themselves are
 * the dashboard's (.jf-row, .jf-group), so what follows is only the page
 * around them.
 */

/* The phone callout above the form. Its border is the one warm colour in the
   portal, because it is the one block on the page saying "not this form". */
.pt-now {
  display: flex;
  align-items: center;
  gap: 18px;
  flex-wrap: wrap;
  background: var(--bg-card);
  border: 1px solid var(--border);
  border-left: 3px solid #d97706;
  border-radius: 0 8px 8px 0;
  padding: 16px 18px;
  margin-bottom: 22px;
}
.pt-now-body { flex: 1 1 320px; min-width: 0; }
.pt-now-label {
  display: block;
  font-size: 0.68rem;
  text-transform: uppercase;
  letter-spacing: 1.2px;
  font-weight: 700;
  color: #d97706;
  margin-bottom: 0.3rem;
}
.pt-now p { margin: 0; font-size: 0.9rem; }
.pt-now-call { flex: 0 0 auto; white-space: nowrap; }

/* Banners: the outcome of a request, and anything wrong with the form. Green
   and red are the two statuses this page has, on the same left edge the
   assurance line uses so the page has one shape for "here is a result". */
.pt-alert {
  border: 1px solid var(--border);
  border-left-width: 3px;
  border-radius: 0 8px 8px 0;
  padding: 15px 17px;
  margin-bottom: 20px;
  background: var(--bg-card);
}
.pt-alert h2 { margin: 0 0 0.3rem; font-size: 1.05rem; }
.pt-alert p { margin: 0; font-size: 0.9rem; }
.pt-alert.is-ok    { border-left-color: #16a34a; }
.pt-alert.is-error { border-left-color: #ef4444; font-weight: 600; }

/* Form beside the "what you can ask for" list. One column until there is
   genuinely room for two, because the form is the page and a squeezed form is
   worse than a long one. */
.pt-svc { display: grid; gap: 24px; align-items: start; }
/* Grid children default to min-width:auto, which lets a long word or a wide
   control push the column past its track instead of wrapping. */
.pt-svc-form, .pt-svc-aside { min-width: 0; }
@media (min-width: 1240px) {
  .pt-svc { grid-template-columns: minmax(0, 1fr) 320px; }
}

/* The first section in a column sits against the block above it, which
   already has margin of its own. */
.pt-section-first { margin-top: 0; }

.pt-asks { list-style: none; margin: 0; padding: 0; }
.pt-asks li + li { margin-top: 13px; }
.pt-asks strong { display: block; font-size: 0.88rem; }
.pt-asks span { display: block; font-size: 0.78rem; color: var(--text-secondary); line-height: 1.55; }

.pt-svc-send { margin-top: 24px; }
.pt-svc-note { margin: 0.8rem 0 0; font-size: 0.8rem; color: var(--text-secondary); max-width: 60ch; }

/* The hint under the chosen request type. Ported from clients.css, which the
   portal does not load. */
.client-hints {
  margin-top: 0.75rem;
  padding-left: 0.9rem;
  border-left: 2px solid var(--accent);
}
.client-hint {
  margin: 0 0 0.5rem;
  font-size: 0.82rem;
  line-height: 1.6;
  color: var(--text-secondary);
}
.client-hints.is-single .client-hint { margin-bottom: 0; }
.client-hint:last-child { margin-bottom: 0; }

/* The entry a request was opened from, named at the top of the form so the
   reader can see the page kept hold of it. */
.pt-about-line {
  margin: 0 0 1rem;
  padding-bottom: 0.9rem;
  border-bottom: 1px solid var(--border);
  font-size: 0.85rem;
  color: var(--text-secondary);
}
.pt-about-line strong { color: var(--text-primary); }

/* "Ask about this" is a link now rather than a button opening a modal, so it
   needs the box shape a button had by default. */
a.pt-ask { display: inline-block; text-decoration: none; }

/* Your account ------------------------------------------------------------ */

.pt-account { max-width: 620px; }
.pt-details-list { margin: 0; display: grid; gap: 10px; }
.pt-details-list > div { display: grid; grid-template-columns: 110px minmax(0, 1fr); gap: 12px; }
.pt-details-list dt { font-size: 0.78rem; text-transform: uppercase; letter-spacing: 0.8px; color: var(--text-secondary); padding-top: 2px; }
.pt-details-list dd { margin: 0; font-weight: 600; overflow-wrap: anywhere; }
.pt-account form .jf-group { margin-bottom: 14px; }
