/* The customer portal, in trificta.com.au's clothes.
 *
 * The staff console is dark because it is a tool somebody sits in front of all
 * day. The portal is the opposite: a customer arrives from a link on the
 * marketing site, having never seen it, and the two pages either look like one
 * company or they look like a redirect somewhere strange. On a page asking for
 * an email address that difference is worth real money.
 *
 * Every value below is lifted from https://trificta.com.au/css/style.css rather
 * than eyeballed, including the notes their stylesheet carries about which
 * orange may be used for what. That is why --orange-text exists: #F47521 is
 * 2.83:1 on white, which is fine for fills and rules and unusable for body
 * text. Getting that wrong here would fail our own contrast gate.
 *
 * Inter is SERVED FROM HERE, not hotlinked from their domain: the CSP is
 * 'self', and a font that only arrives when another origin is reachable is a
 * font that sometimes does not arrive. These are the same three woff2 files
 * trificta.com.au ships, 14KB each, so the two sites set type identically
 * rather than approximately.
 *
 * font-display: swap, deliberately, and not the site's `optional`. Optional
 * means a slow first load silently keeps the fallback and never repaints,
 * which is fine for a marketing page seen once and wrong for a portal somebody
 * opens every day: they would get two different typefaces on alternate visits.
 */

@font-face {
  font-family: 'Inter'; font-style: normal; font-weight: 400;
  font-display: swap;
  src: url('/staff/static/fonts/Inter-Regular.woff2') format('woff2');
}
@font-face {
  font-family: 'Inter'; font-style: normal; font-weight: 600;
  font-display: swap;
  src: url('/staff/static/fonts/Inter-SemiBold.woff2') format('woff2');
}
@font-face {
  font-family: 'Inter'; font-style: normal; font-weight: 700;
  font-display: swap;
  src: url('/staff/static/fonts/Inter-Bold.woff2') format('woff2');
}

:root {
  --t-orange:       #F47521;
  --t-orange-text:  #A9550F;   /* AA on white AND on the 10% tint */
  --t-orange-dark:  #d4611a;
  --t-orange-light: #f9a870;
  --t-dark:         #1a1a1a;
  --t-dark-mid:     #2d2d2d;
  --t-mid:          #555555;
  --t-light-gray:   #f5f5f5;
  --t-border:       #e0e0e0;
  --t-white:        #ffffff;
  --t-radius:       4px;
  --t-radius-lg:    8px;
  --t-shadow:       0 2px 16px rgba(0,0,0,0.10);
  --t-shadow-lg:    0 8px 32px rgba(0,0,0,0.14);
  --t-font:         'Inter', 'Segoe UI', Arial, sans-serif;
}

/* Everything below is scoped to body.portal, so loading this alongside the
 * staff stylesheet cannot repaint the console. */
body.portal {
  background: var(--t-white);
  color: var(--t-dark);
  font-family: var(--t-font);
  font-size: 15px;
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
}

body.portal a { color: var(--t-orange-text); }
body.portal a:hover { color: var(--t-orange-dark); }

body.portal .pshell { max-width: 1200px; margin: 0 auto; padding: 0 24px; }

/* The page title and the one action beside it, on the same line. The action
   used to be at the bottom of an empty state, which meant the way to raise a
   ticket appeared only when you had none. */
body.portal .phead2 {
  display: flex; align-items: center; gap: 16px; flex-wrap: wrap;
  margin: 0 0 20px;
}
body.portal .phead2 h1 {
  font-size: 26px; font-weight: 600; letter-spacing: -0.02em; margin: 0;
}
body.portal .phead2 .btn { width: auto; margin-left: auto; }

/* ---------- the header, as the site draws it ---------- */

body.portal .phead {
  display: flex; align-items: center; gap: 14px;
  /* A MINIMUM, not a height. A reseller has enough header buttons to wrap onto
     a second row, and a fixed 72px left that row sitting on the line below. */
  min-height: 72px; padding: 16px 0; row-gap: 10px;
  border-bottom: 1px solid var(--t-border);
  margin-bottom: 32px;
}
body.portal .pbrand {
  display: flex; align-items: center; gap: 10px;
  font-weight: 600; font-size: 17px; color: var(--t-dark); text-decoration: none;
  letter-spacing: -0.01em;
}
body.portal .pbrand:hover { color: var(--t-dark); }
body.portal .plogo { height: 26px; width: auto; display: block; }
body.portal .pbrand span { color: var(--t-mid); font-weight: 500; }
body.portal .phead .dim { color: var(--t-mid); font-size: 13.5px; }

/* Header links as quiet buttons, the site's shape. */
body.portal .phead .ddbtn {
  display: inline-flex; align-items: center; min-height: 38px;
  padding: 0 14px; border: 1px solid var(--t-border); border-radius: var(--t-radius);
  background: var(--t-white); color: var(--t-dark); text-decoration: none;
  font-size: 14px; font-weight: 500;
}
body.portal .phead .ddbtn:hover {
  border-color: var(--t-orange); color: var(--t-orange-text); background: var(--t-light-gray);
}
/* WHERE YOU ARE. The same faint orange the selected ticket filter uses, so one
   idea has one colour across the portal: #fff7f0 is the 10% tint of the brand
   orange, and --t-orange-text is the one orange that passes AA on it.
   Weight as well as colour, because a tint alone is a difference some screens
   and some eyes lose, and aria-current says it to a screen reader either way. */
body.portal .phead .ddbtn.on,
body.portal .phead .ddbtn.on:hover {
  border-color: var(--t-orange); background: #fff7f0;
  color: var(--t-orange-text); font-weight: 600;
}

/* ---------- the sign-in card ---------- */

body.portal .pcard {
  max-width: 440px; margin: 6vh auto 40px;
  background: var(--t-white);
  border: 1px solid var(--t-border); border-radius: var(--t-radius-lg);
  box-shadow: var(--t-shadow); padding: 36px 32px;
}
body.portal .pcard h1 {
  font-size: 24px; font-weight: 600; letter-spacing: -0.02em;
  margin: 0 0 6px; color: var(--t-dark);
}
body.portal .pcard p { color: var(--t-mid); margin: 0 0 18px; }
body.portal .pcard p.dim { font-size: 13.5px; }

body.portal .field { margin-bottom: 16px; }
body.portal .field label {
  display: block; font-size: 13px; font-weight: 600; color: var(--t-dark);
  margin-bottom: 6px;
}
/* Every text-like input, however it is spelled.
   This used to list [type=email], [type=password] and [type=text], and an
   <input> with NO type attribute matches none of them: the attribute selector
   needs the attribute to be present, not merely defaulted. The search box on
   the tickets page had no type, fell through to the dark staff stylesheet, and
   rendered as a black hole with an unreadable placeholder in it. Excluding the
   few that are not text boxes is the safer way round. */
body.portal input:not([type=checkbox]):not([type=radio]):not([type=file]):not([type=submit]):not([type=hidden]),
body.portal textarea,
body.portal select {
  /* 16px, NOT 15. Under 16 iOS Safari zooms the page in on focus and does not
     zoom back out, so a customer finishes raising a fault report on a page
     whose edges are off screen, usually while standing at the pump station
     they are reporting.
     There WAS a 16px override in the small-screen media query below and it
     never applied: it is written `body.portal input`, and this rule's five
     :not() exclusions outweigh it, so the base 15px won inside the breakpoint
     too. Set outright rather than fixed there, because one pixel does not
     warrant a breakpoint at all. */
  width: 100%; padding: 11px 13px; font-size: 16px; font-family: inherit;
  color: var(--t-dark); background: var(--t-white);
  border: 1px solid var(--t-border); border-radius: var(--t-radius);
}
/* THE RING HAS TO BE VISIBLE, which means it cannot be drawn in the orange
   this file's own header rules out for anything carrying meaning: #F47521 is
   2.83:1 on white and the 18% glow under it composites to about 1.06:1, so the
   whole indicator was a hairline nobody could see. --t-orange-text is 5.04:1.
   The glow stays as the soft edge; the border is what says "here". */
body.portal input:focus, body.portal textarea:focus, body.portal select:focus,
body.portal input:focus-visible, body.portal textarea:focus-visible,
body.portal select:focus-visible {
  outline: 2px solid var(--t-orange-text); outline-offset: 1px;
  border-color: var(--t-orange-text);
  box-shadow: 0 0 0 3px rgba(244,117,33,0.18);
}
/* Every other focusable thing: links, buttons, the details summaries that open
   a row editor. There was no rule at all, so they fell back to whatever the
   browser draws over a coloured background, which on the buttons is nothing. */
body.portal a:focus-visible,
body.portal button:focus-visible,
body.portal summary:focus-visible,
body.portal .btn:focus-visible,
body.portal .ddbtn:focus-visible {
  outline: 2px solid var(--t-orange-text); outline-offset: 2px;
  border-radius: var(--t-radius);
}

body.portal button, body.portal .btn {
  display: inline-flex; align-items: center; justify-content: center;
  width: 100%; min-height: 46px; padding: 0 18px;
  background: var(--t-orange); border: 1px solid var(--t-orange);
  /* Near-black on brand orange, which is how the site draws its buttons and
     the only way this passes AA: white on #F47521 is 2.83:1. */
  color: #1a1a1a;
  border-radius: var(--t-radius); font-size: 15px; font-weight: 600;
  font-family: inherit; cursor: pointer; text-decoration: none;
}
body.portal button:hover, body.portal .btn:hover {
  background: var(--t-orange-dark); border-color: var(--t-orange-dark); color: #1a1a1a;
}
body.portal button.sec, body.portal .btn.sec {
  background: var(--t-white); border-color: var(--t-border); color: var(--t-dark);
}
body.portal button.sec:hover, body.portal .btn.sec:hover {
  border-color: var(--t-orange); background: var(--t-light-gray); color: var(--t-dark);
}

/* The two ways in, separated by a rule with a word on it rather than by a
   heading: they are alternatives, not steps. */
body.portal .orrule {
  display: flex; align-items: center; gap: 12px;
  margin: 22px 0 18px; color: var(--t-mid); font-size: 12.5px;
  text-transform: uppercase; letter-spacing: .08em;
}
body.portal .orrule::before, body.portal .orrule::after {
  content: ''; flex: 1; height: 1px; background: var(--t-border);
}

body.portal .banner {
  border: 1px solid var(--t-border); border-left: 3px solid var(--t-orange);
  background: var(--t-light-gray); border-radius: var(--t-radius);
  padding: 12px 14px; margin: 0 0 18px; font-size: 14px; color: var(--t-dark);
}
body.portal .banner.bad { border-left-color: #c92f2f; }
body.portal .banner.ok  { border-left-color: #2e7d32; }

/* ---------- content pages ---------- */

body.portal .panel {
  background: var(--t-white); border: 1px solid var(--t-border);
  border-radius: var(--t-radius-lg); margin-bottom: 22px; overflow: hidden;
}
body.portal .panel .hd {
  display: flex; align-items: center; gap: 10px;
  padding: 14px 18px; border-bottom: 1px solid var(--t-border);
  background: var(--t-light-gray); font-weight: 600; font-size: 14.5px;
}
body.portal .panel .bd { padding: 18px; }
body.portal table { width: 100%; border-collapse: collapse; font-size: 14px; }
body.portal th {
  text-align: left; padding: 10px 14px; font-size: 12px; font-weight: 600;
  text-transform: uppercase; letter-spacing: .05em; color: var(--t-mid);
  border-bottom: 1px solid var(--t-border);
}
body.portal td { padding: 12px 14px; border-bottom: 1px solid var(--t-border); }

/* People: a row carries full-size buttons, so its cells centre on them rather
   than leaving the text at the top. The licence key permission sits beside its
   toggle, not in a block under it. */
body.portal table.people td { vertical-align: middle; }
body.portal tr:last-child td { border-bottom: none; }
body.portal .dim { color: var(--t-mid); }
body.portal .empty { padding: 22px 18px; color: var(--t-mid); }

/* The tag is defined once, further down, as plain words in a cell. An earlier
   pill treatment used to be declared here and then overwritten property for
   property by that one, which is a rule that reads as live and is not. */

body.portal .pfoot {
  border-top: 1px solid var(--t-border); margin-top: 40px; padding: 20px 0 40px;
  color: var(--t-mid); font-size: 13px;
}

@media (max-width: 640px) {
  body.portal .pshell { padding: 0 16px; }
  body.portal .pcard { margin-top: 3vh; padding: 26px 20px; border-radius: var(--t-radius); }
  body.portal .phead { height: auto; padding: 14px 0; flex-wrap: wrap; gap: 8px; }
  /* 16px or iOS zooms the page in on focus and never zooms back out. */
  body.portal .tablewrap { overflow-x: auto; }
}


/* ---------- the customer's four figures ---------- */

/* Four numbers, each a link to the list behind it. Laid out as equal columns
   that collapse to two on a phone, which is where most of these are read. */
body.portal .statrow {
  display: grid; grid-template-columns: repeat(4, 1fr); gap: 12px;
  margin: 0 0 22px;
}
body.portal .stat {
  display: block; padding: 16px 18px; text-decoration: none;
  border: 1px solid var(--t-border); border-radius: var(--t-radius-lg);
  background: var(--t-white); color: var(--t-dark);
}
body.portal .stat:hover { border-color: var(--t-orange); color: var(--t-dark); }
body.portal .stat b {
  display: block; font-size: 28px; font-weight: 600; line-height: 1.1;
  letter-spacing: -0.02em;
}
body.portal .stat span {
  display: block; margin-top: 4px; font-size: 12.5px; color: var(--t-mid);
}
/* The one that is being shown, and the one that wants something from you. */
body.portal .stat.on { border-color: var(--t-orange); background: #fff7f0; }
body.portal .stat.act b { color: var(--t-orange-text); }

/* ---------- priority, drawn as a scale rather than a pill ---------- */

/* Five coloured pills down a table column is bunting, and it is the first
   thing that makes an interface look generated. A rung on a scale is a short
   bar and a label: the colour carries the severity, the text stays plain, and
   the column can be read down without reading a word. */
body.portal .prio {
  display: inline-flex; align-items: center; gap: 7px;
  font-weight: 600; font-size: 12.5px; white-space: nowrap;
}
body.portal .prio::before {
  content: ''; width: 3px; height: 14px; border-radius: 2px;
  background: var(--t-border); flex: 0 0 3px;
}
body.portal .p-p1::before { background: #c92f2f; }
body.portal .p-p2::before { background: var(--t-orange); }
body.portal .p-p3::before { background: #c9a227; }
body.portal .p-p4::before { background: #9aa0a6; }
body.portal .p-p5::before { background: var(--t-border); }
body.portal .p-p1 { color: #a52121; }
body.portal .p-p4, body.portal .p-p5 { color: var(--t-mid); }

/* The tag pill goes everywhere else too. Status reads as words in a table
   cell; it does not need a badge around it to be understood. */
body.portal .tag {
  display: inline; padding: 0; border: none; background: none;
  border-radius: 0; font-size: inherit; font-weight: 500; color: var(--t-mid);
}

/* ---------- filters and paging ---------- */

body.portal .ticketfilter {
  display: flex; align-items: flex-end; gap: 12px; flex-wrap: wrap;
  margin: 0 0 18px;
}
body.portal .ticketfilter .field { margin: 0; flex: 1 1 220px; max-width: 340px; }
body.portal .ticketfilter button { width: auto; flex: 0 0 auto; min-height: 43px; }
/* EVERY CONTROL THE SAME HEIGHT, or the labels do not line up.
   align-items:flex-end lines up the BOTTOM of each field, and an input and a
   select do not render the same height from the same padding: the select is a
   little taller, so its bottom edge matched while its label sat lower than the
   one beside it. Two labels a few pixels apart reads as a mistake even when
   nobody can say what is wrong. Matching the button keeps the whole row on one
   baseline. */
body.portal .ticketfilter input,
body.portal .ticketfilter select { height: 43px; }
/* The export link belongs to the row above it rather than floating between the
   filters and the results. */
body.portal .ticketfilter + .exportline { margin-top: 0; }

/* The list filter is the ticket filter's row with a way out of it. Clear sits
   on the same baseline as the button rather than under it, and only appears
   when there is something to clear. */
body.portal .listfilter .clearfilter {
  flex: 0 0 auto; align-self: center; font-size: 13.5px; padding-bottom: 2px;
}
/* What the filter did, said in the one place somebody looks when a row they
   expected is not there. Sits tight under the row it explains. */
body.portal .filtercount { margin: -10px 0 16px; font-size: 13px; }
body.portal .scopenote { margin: -8px 0 18px; font-size: 13px; }

body.portal .pager {
  display: flex; align-items: center; gap: 16px; flex-wrap: wrap;
  margin: 16px 0 40px; font-size: 14px;
}
body.portal .pagerlinks { margin-left: auto; display: flex; gap: 14px; }

/* The priority explainer under the urgency picker: what each rung MEANS, so
   the choice is informed rather than guessed. */
body.portal .priohelp {
  margin: 10px 0 0; padding: 12px 14px; font-size: 12.5px; line-height: 1.5;
  background: var(--t-light-gray); border: 1px solid var(--t-border);
  border-radius: var(--t-radius);
}
body.portal .priohelp div + div { margin-top: 6px; }
body.portal .priohelp b { color: var(--t-dark); }

@media (max-width: 640px) {
  body.portal .statrow { grid-template-columns: 1fr 1fr; gap: 8px; }
  body.portal .stat { padding: 12px 13px; }
  body.portal .stat b { font-size: 22px; }
  body.portal .phead2 h1 { font-size: 21px; }
  body.portal .phead2 .btn { width: 100%; margin-left: 0; }
}

/* Sign out is a form so it cannot be fired by a GET, but it has to sit in the
   header looking like the links beside it. */
body.portal .signout { margin: 0; display: inline-flex; }
body.portal .signout button.ddbtn {
  width: auto; min-height: 38px; padding: 0 14px;
  background: var(--t-white); border: 1px solid var(--t-border);
  color: var(--t-dark); font-size: 14px; font-weight: 500;
}
body.portal .signout button.ddbtn:hover {
  border-color: var(--t-orange); background: var(--t-light-gray); color: var(--t-dark);
}

/* The customer's own controls on a ticket: urgency and open/closed, side by
   side above the reply box rather than buried under it. */
body.portal .ticketactions {
  display: flex; gap: 14px; flex-wrap: wrap; align-items: flex-end;
  margin: 0 0 20px; padding: 14px 16px;
  background: var(--t-light-gray); border: 1px solid var(--t-border);
  border-radius: var(--t-radius-lg);
}
body.portal .inlineform {
  display: flex; gap: 8px; align-items: center; margin: 0; flex-wrap: wrap;
}
body.portal .inlineform label { font-size: 13px; font-weight: 600; margin: 0; }
body.portal .inlineform select { width: auto; min-width: 190px; }
body.portal .inlineform button { width: auto; min-height: 40px; }
@media (max-width: 640px) {
  body.portal .ticketactions { flex-direction: column; align-items: stretch; }
  body.portal .inlineform { width: 100%; }
  body.portal .inlineform select, body.portal .inlineform button { flex: 1; }
}

/* A checkbox and its words on one line, hit target included. */
body.portal .checkline {
  display: flex; align-items: center; gap: 10px; margin: 0 0 16px;
  font-size: 14.5px; cursor: pointer;
}
body.portal .checkline input { width: 18px; height: 18px; margin: 0; }
/* A checkbox's words read as a sentence. app.css draws every label as a small
   capitalised field heading, and a checkline inherited it, so "Can sign in"
   came out as grey capitals beside its own box. */
body.portal label.checkline {
  font-size: 14px; font-weight: 400; color: var(--t-dark);
  text-transform: none; letter-spacing: normal; margin-bottom: 8px;
}

/* What was promised on this ticket, above the controls rather than buried. */
body.portal .slanote {
  margin: 0 0 14px; font-size: 13.5px;
}
body.portal .slanote b { color: var(--t-dark); }

/* The one money figure that asks for something to happen. */
body.portal .overdue { color: #a52121; }
body.portal .exportline { margin: -6px 0 18px; font-size: 13.5px; }

/* ===========================================================================
   Phones
   ===========================================================================

   The portal is read on a phone more than anywhere else: somebody standing at
   a pump station wanting to know what we said. Everything below is about that
   one situation.

   The big change is that data tables STOP BEING TABLES. A five-column table on
   a 390px screen has two honest options, and both are bad: squeeze the columns
   until nothing is readable, or scroll sideways, which hides the columns that
   matter behind a gesture nobody discovers. Each row becomes a card instead,
   with the column heading printed beside its value, so nothing is hidden and
   nothing needs a horizontal swipe.
   ========================================================================= */

@media (max-width: 640px) {

  /* Nothing may push the page sideways. One overflowing element drags the
     whole document with it and every other page then scrolls too. */
  body.portal { overflow-x: hidden; }

  /* --- tables become cards --- */

  body.portal table.stack,
  body.portal table.stack tbody,
  body.portal table.stack tr,
  body.portal table.stack td { display: block; width: 100%; }

  /* The headings move INTO the rows, so they are not lost, just relocated. */
  body.portal table.stack thead { display: none; }

  body.portal table.stack tr {
    border: 1px solid var(--t-border); border-radius: var(--t-radius-lg);
    margin: 0 0 10px; padding: 4px 0; background: var(--t-white);
  }
  body.portal table.stack tr:last-child { margin-bottom: 0; }

  body.portal table.stack td {
    border: none; padding: 7px 14px;
    display: flex; align-items: baseline; gap: 12px;
    white-space: normal;
  }
  body.portal table.stack td::before {
    content: attr(data-label);
    flex: 0 0 92px; color: var(--t-mid);
    font-size: 11px; font-weight: 600; text-transform: uppercase;
    letter-spacing: .04em;
  }
  /* The subject is the thing being scanned for, so it gets the whole width
     and a size that says so, with no label in front of it. */
  body.portal table.stack td.main {
    display: block; padding-top: 2px; padding-bottom: 2px;
    font-size: 15.5px; font-weight: 600; line-height: 1.35;
  }
  body.portal table.stack td.main::before { content: none; }
  body.portal table.stack td.main a { color: var(--t-dark); text-decoration: none; }
  /* The reference sits above it, small, as a caption rather than a row. */
  body.portal table.stack td.mono {
    padding-bottom: 0; font-size: 12px; color: var(--t-mid);
  }
  body.portal table.stack td.mono::before { content: none; }

  /* The panel that holds them loses its box: two nested borders around every
     row is a lot of lines on a small screen. */
  body.portal .panel:has(table.stack) { border: none; background: none; }
  body.portal .panel:has(table.stack) .hd {
    background: none; border: none; padding: 0 2px 10px;
  }
  body.portal .panel:has(table.stack) .tablewrap { overflow: visible; }

  /* --- header --- */

  /* Logo on its own line, actions wrapping under it, so nothing is cut off and
     nothing needs a sideways scroll to reach. */
  body.portal .phead {
    height: auto; padding: 12px 0; gap: 8px;
    flex-wrap: wrap; margin-bottom: 20px;
  }
  body.portal .pbrand { flex: 1 0 100%; }
  body.portal .phead .dim { flex: 1 0 100%; order: 3; font-size: 12.5px; }
  body.portal .navmenu { position: static; }
  body.portal .navmenubox { position: static; min-width: 0; box-shadow: none; margin-top: 6px; }
  body.portal .phead .ddbtn, body.portal .signout button.ddbtn {
    /* 44px, which is the figure both Apple and Google publish for a target
       somebody is expected to hit deliberately. */
    min-height: 44px; padding: 0 12px; font-size: 13.5px;
  }

  /* --- everything you can tap --- */

  body.portal button, body.portal .btn { min-height: 48px; }
  body.portal .inlineform button { min-height: 48px; }
  body.portal a { text-decoration-thickness: from-font; }

  /* --- forms --- */

  /* Fields already go to 16px to stop iOS zooming the page in on focus and
     never zooming back out. Labels get room; a textarea gets enough height to
     write a sentence in without scrolling inside it. */
  body.portal .field { margin-bottom: 18px; }
  body.portal textarea { min-height: 140px; }
  body.portal .priohelp { font-size: 12px; }

  /* The urgency picker's option text is long by design, and a select on a
     phone renders it in a native list where the length is fine. */
  body.portal select { text-overflow: ellipsis; }

  /* --- paging --- */

  body.portal .pager { flex-direction: column; align-items: stretch; gap: 10px; }
  body.portal .pagerlinks { margin-left: 0; justify-content: space-between; }
  body.portal .pagerlinks a {
    min-height: 44px; display: inline-flex; align-items: center;
    padding: 0 14px; border: 1px solid var(--t-border);
    border-radius: var(--t-radius); text-decoration: none;
  }
}

/* Very narrow phones. The label column in a stacked row costs 92px of a 320px
   screen, so it goes above the value instead of beside it. */
@media (max-width: 380px) {
  body.portal table.stack td { display: block; }
  body.portal table.stack td::before { display: block; margin-bottom: 2px; }
}

/* The detail behind a purchase order, folded away. */
body.portal .polines { margin: 6px 0 14px; }
body.portal .polines summary {
  cursor: pointer; font-size: 13.5px; color: var(--t-orange-text);
  padding: 8px 0; min-height: 44px; display: flex; align-items: center;
}
body.portal .polines table { margin-top: 6px; }

/* ---------- dark-console leftovers ----------
   THE PORTAL LOADS app.css AND THEN THIS FILE. Every component the portal
   renders but does not restyle keeps the staff console's colours, and the
   console is a dark theme: pale text chosen for near-black, translucent tints
   that assume a dark backdrop. On white they range from low contrast to
   invisible.

   Two of these were reported from the live portal: the success toast was pale
   green text on a near-white tint, and a table row went black under the cursor
   and swallowed its own text. The rest are the same fault found by listing the
   classes the portal templates render, then subtracting the ones this file
   already styles. */

/* The toast. app.css sets color:#9ff0c4 on a 14% green tint, which is pale
   text on a dark panel there and pale text on white here. */
body.portal .flash {
  background: var(--t-white);
  border: 1px solid var(--t-border);
  border-left: 3px solid var(--t-border);
  color: var(--t-dark);
  box-shadow: 0 10px 28px rgba(0, 0, 0, .14);
}
body.portal .flash.ok  { border-left-color: #2e7d32; }
body.portal .flash.bad { border-left-color: #c92f2f; }
/* The close control inherits the console's dim grey, which is a mid grey meant
   for a dark panel. */
body.portal .flashx { color: #6b6b6b; }
body.portal .flashx:hover { color: var(--t-dark); }

/* Row hover. app.css uses --panel2 (#1c2027), a near-black panel tint. */
body.portal tbody tr:hover { background: #f0f0f0; }

/* Severity tags. sev-ok is #3fbf7f, which is 2.1:1 on white; sev-over is white
   on a red fill and survives, but is restated so the pair stays together and
   somebody changing one sees the other. */
body.portal .tag.sev-ok,
body.portal .sev-ok { color: #1b6b45; border-color: #1b6b45; }
body.portal .tag.sev-warn,
body.portal .sev-warn { color: #8a5a00; border-color: #8a5a00; }
body.portal .tag.sev-over,
body.portal .sev-over { color: #ffffff; background: #c92f2f; border-color: #c92f2f; }

/* Pagination. The disabled state is --dim at 45% opacity, which on white is
   somewhere around 1.6:1 and reads as an empty gap rather than a control. */
body.portal .pager a { background: var(--t-white); color: var(--t-dark); }
body.portal .pager a:hover { border-color: var(--t-orange); color: var(--t-orange-text); }
body.portal .pager .off { color: #6b6b6b; opacity: 1; }

/* The summary bar's track is a near-black panel colour. */
body.portal .sumbar { background: #ececec; }

/* Timeline meta lines on a ticket: --dim is a light grey for a dark ground. */
body.portal .event .meta,
body.portal .meta { color: #5a5a5a; }

/* Numeric cells pick up the console's dim colour in a couple of places. */
body.portal .num { color: var(--t-dark); }

/* The brand orange is 2.83:1 on white, so it is a border and an accent here,
   never body text. This is the one that would be easiest to reintroduce. */
/* .subnav is a staff console component that no portal page uses; it went with
   a copied block. The literal was a second orange beside the token that names
   the safe one, which is how a third one gets added next time. */
body.portal a.on { color: var(--t-orange-text); }

/* Deal end-customer suggestions (portal_deals.html, app.js dealSuggest). A list
   under the name field: the reseller's own customers first, then names from the
   public business register. Keyboard: arrows move, Enter picks, Escape closes. */
body.portal .suggest-host { position: relative; }
body.portal ul.suggest {
  position: absolute; z-index: 20; left: 0; right: 0; margin: 2px 0 0; padding: 4px 0;
  list-style: none; background: var(--t-white); border: 1px solid var(--t-border);
  border-radius: var(--t-radius); box-shadow: var(--t-shadow); max-height: 320px; overflow-y: auto;
}
body.portal ul.suggest li { padding: 7px 12px; cursor: pointer; line-height: 1.35; }
body.portal ul.suggest li[aria-selected="true"], body.portal ul.suggest li:hover { background: var(--t-light-gray); }
body.portal ul.suggest li .sg-name { color: var(--t-dark); }
body.portal ul.suggest li .sg-meta { display: block; font-size: 12px; color: var(--t-mid); }
body.portal ul.suggest li.sg-head {
  cursor: default; padding: 6px 12px 2px; font-size: 11px; letter-spacing: .06em;
  text-transform: uppercase; color: var(--t-mid); background: none;
}

/* A checkbox field: the box beside its label, and the explanation beneath. */
body.portal .field.check { display: flex; flex-wrap: wrap; align-items: center; gap: 4px 8px; }
/* The box at its own size. app.css makes every .field input full width, and a
   checkbox that wide draws its box in the middle of the row with the label
   pushed onto the line under it. */
body.portal .field.check input[type=checkbox] { width: 18px; height: 18px; margin: 0; flex: none; }
body.portal .field.check label { margin: 0; }
body.portal .field.check > .dim { flex-basis: 100%; }

/* ---------- a release's change log on the Software page ---------- */

/* The version row and its notes read as one item: no rule between them. */
body.portal #software-list tr.hasnotes td { border-bottom: none; }
body.portal .relnotesrow td { padding-top: 0; }
body.portal .relnotes summary {
  cursor: pointer; list-style-position: inside;
  padding: 8px 12px; border: 1px solid var(--t-border); border-radius: var(--t-radius);
  background: var(--t-light-gray); font-size: 13.5px; font-weight: 500; color: var(--t-dark);
}
body.portal .relnotes summary:hover { border-color: var(--t-orange); }
body.portal .relnotes summary:focus-visible { outline: 2px solid var(--t-orange-text); outline-offset: 2px; }
body.portal .relnotes[open] summary { border-bottom-left-radius: 0; border-bottom-right-radius: 0; }
body.portal .relnotesbody {
  border: 1px solid var(--t-border); border-top: none; border-radius: 0 0 var(--t-radius) var(--t-radius);
  padding: 4px 16px 12px; font-size: 14px; line-height: 1.55; max-width: 100%;
}
body.portal .relnotesbody h2 { font-size: 15px; margin: 16px 0 6px; }
body.portal .relnotesbody h3 { font-size: 14px; margin: 12px 0 4px; }
body.portal .relnotesbody ul, body.portal .relnotesbody ol { padding-left: 20px; margin: 6px 0; }
body.portal .relnotesbody li { margin: 3px 0; }
body.portal .relnotesbody code { font-size: 12.5px; background: var(--t-light-gray); padding: 1px 4px; border-radius: 3px; }
body.portal .relnotesbody table { font-size: 13px; }

/* ---------- removing somebody on People ---------- */

/* Quieter than the button beside it: it is the rarer, harder-to-undo action,
   and the confirmation says so. */
body.portal .removeperson { margin: 6px 0 0; }
body.portal .removeperson button.linkish {
  width: auto; min-height: 0; padding: 0; background: none; border: none;
  color: var(--t-mid); font-size: 12.5px; font-weight: 500; text-decoration: underline;
}
body.portal .removeperson button.linkish:hover { color: #b3261e; }
body.portal .confirmbox:not(.keep) #confirm-yes { background: #b3261e; border-color: #b3261e; color: #fff; }

/* The primary contact's editor for one person, opened in place. */
body.portal td.personactions { min-width: 150px; }
body.portal .personedit summary {
  cursor: pointer; display: inline-block; padding: 5px 12px; font-size: 13px; font-weight: 500;
  border: 1px solid var(--t-border); border-radius: var(--t-radius); background: var(--t-white);
}
body.portal .personedit summary:hover { border-color: var(--t-orange); }
body.portal .personedit[open] summary { background: var(--t-light-gray); }
body.portal .personedit form {
  margin: 8px 0 0; padding: 10px 12px; min-width: 230px;
  border: 1px solid var(--t-border); border-radius: var(--t-radius); background: var(--t-light-gray);
}
body.portal .personedit .checkline { margin: 0 0 8px; font-size: 13.5px; }
body.portal .personedit button { width: auto; min-height: 36px; padding: 0 16px; font-size: 13.5px; }

/* ---------- fixes found reviewing the reseller portal ---------- */

/* A ticket's report and its replies keep the lines they were typed with.
   app.css wraps ".event .body", a body INSIDE an event, and the portal marks
   one element as both, so every report ran together as a single paragraph. */
body.portal .event.body { white-space: pre-wrap; }

/* The file picker in the site's light style. The portal's input rule excludes
   file inputs, so they kept app.css's dark field: a black bar on a white page. */
body.portal input[type=file] {
  width: 100%; padding: 8px; font-size: 13.5px; color: var(--t-dark);
  background: var(--t-white); border: 1px dashed var(--t-border); border-radius: var(--t-radius);
}
body.portal input[type=file]::file-selector-button {
  margin-right: 12px; padding: 6px 12px; font: inherit; font-weight: 500; cursor: pointer;
  color: var(--t-dark); background: var(--t-light-gray); border: 1px solid var(--t-border); border-radius: var(--t-radius);
}

/* A checkbox field's words read as a sentence, as a checkline's do. */
body.portal .field.check label {
  font-size: 14px; font-weight: 500; color: var(--t-dark);
  text-transform: none; letter-spacing: normal;
}

/* One customer's keys under its own name, on a reseller's Licence keys page. */
body.portal h2.keysfor { margin: 26px 0 8px; font-size: 16px; }

/* ---------- headings, the account menu and a ticket's reference ---------- */

/* A section heading on a page, as on Licences and Deals ("Ask for a licence",
   "Register a deal") and over each version's manuals. app.css draws h2 as a
   small capitalised label for the console's panels, which read here as a
   caption stuck to the table above it. */
body.portal h2 {
  font-size: 17px; font-weight: 600; letter-spacing: -0.01em; text-transform: none;
  color: var(--t-dark); margin: 30px 0 12px;
}
body.portal h2 + p.dim { margin-top: -6px; }

/* The account's own settings, in one menu. */
body.portal .navmenu { position: relative; display: inline-block; }
body.portal .navmenu > summary { list-style: none; cursor: pointer; }
body.portal .navmenu > summary::-webkit-details-marker { display: none; }
body.portal .navmenu > summary::after { content: ' \25BE'; }
body.portal .navmenubox {
  position: absolute; right: 0; top: calc(100% + 6px); z-index: 60; min-width: 190px;
  display: flex; flex-direction: column; padding: 6px;
  background: var(--t-white); border: 1px solid var(--t-border);
  border-radius: var(--t-radius); box-shadow: var(--t-shadow);
}
body.portal .navmenubox a {
  padding: 9px 12px; border-radius: var(--t-radius); color: var(--t-dark);
  text-decoration: none; font-size: 14px;
}
body.portal .navmenubox a:hover { background: var(--t-light-gray); color: var(--t-orange-text); }

/* A ticket's reference above its subject, which is the heading. */
body.portal p.ticketref { margin: 0 0 4px; display: flex; align-items: center; gap: 8px; font-size: 13px; }

/* A row's own actions: an editor opened in place, and the quieter actions
   beside it. */
body.portal td.dealactions, body.portal td.personactions { min-width: 150px; }
body.portal .dealedit summary, body.portal .personedit summary {
  cursor: pointer; display: inline-block; padding: 5px 12px; font-size: 13px; font-weight: 500;
  border: 1px solid var(--t-border); border-radius: var(--t-radius); background: var(--t-white);
}
body.portal .dealedit summary:hover { border-color: var(--t-orange); }
body.portal .dealedit[open] summary { background: var(--t-light-gray); }
body.portal .dealedit form {
  margin: 8px 0 0; padding: 10px 12px; min-width: 250px;
  border: 1px solid var(--t-border); border-radius: var(--t-radius); background: var(--t-light-gray);
}
body.portal .dlab {
  display: block; margin: 8px 0 3px; font-size: 12px; font-weight: 600;
  text-transform: none; letter-spacing: normal; color: var(--t-mid);
}
body.portal .dealedit input, body.portal .dealedit textarea,
body.portal .personedit input { font-size: 13.5px; }
body.portal .dealedit button, body.portal form.outcome button { width: auto; min-height: 36px; padding: 0 16px; font-size: 13.5px; }
body.portal form.outcome { margin: 0 0 6px; }

/* ---------- the confirmation dialog ---------- */

/* app.css draws it for the dark console; on the portal it takes the site's
   light card, and its buttons sit side by side at their own width rather than
   stretching like a form's. */
body.portal .modal { background: rgba(26, 26, 26, 0.45); }
body.portal .modalbox {
  background: var(--t-white); color: var(--t-dark);
  border: 1px solid var(--t-border); border-radius: var(--t-radius-lg);
  box-shadow: var(--t-shadow-lg);
}
body.portal .modalhd { border-bottom-color: var(--t-border); }
body.portal .modalhd button { width: auto; min-height: 0; color: var(--t-mid); background: none; border: none; }
body.portal .modalbd { color: var(--t-dark-mid); }
body.portal .modalft { border-top-color: var(--t-border); }
body.portal .modalft button { width: auto; min-height: 40px; }
body.portal .confirmbox.keep #confirm-yes {
  background: var(--t-orange); border-color: var(--t-orange); color: var(--t-dark);
}

/* ---------- the phone, last word ---------- */

/* LAST IN THE FILE ON PURPOSE. Each selector below matches its base rule
   exactly, and a media query adds no specificity, so the only thing that
   decides the winner is which comes later. Written higher up, where the rest
   of the 640px rules live, every one of these lost to the rule it was written
   to override and changed nothing. */
@media (max-width: 640px) {
  /* THE FOUR HARDEST ACTIONS TO UNDO WERE THE SMALLEST TARGETS: Remove from
     account, Withdraw, Lost and Cancel rendered about 19px tall, because
     .removeperson button.linkish (0,3,2) beat the 48px floor at (0,1,2). */
  body.portal .removeperson button.linkish,
  body.portal form.outcome button,
  body.portal .dealedit button,
  body.portal .personedit button,
  body.portal .personedit summary,
  body.portal .dealedit summary,
  body.portal .dealedit summary, body.portal .personedit summary,
  body.portal .listfilter .clearfilter,
  body.portal .modalhd button {
    min-height: 44px; display: inline-flex; align-items: center;
  }
  /* A row editor is a form in the last cell of a table that scrolls sideways,
     so at 400px it opened off the right edge. The tables stack into cards on a
     phone now, so there is no sideways to open into and the form takes the
     width of the card. */
  body.portal .dealedit form,
  body.portal .personedit form { min-width: 0; width: 100%; }
}

/* Download key: the primary action on the Licence keys page, and it had no
   rule at all, so it rendered as a 12.5px link with a hit area to match. Given
   the shape of a quiet button, like every other action that does something. */
body.portal a.keydl {
  display: inline-flex; align-items: center; min-height: 34px;
  padding: 0 12px; border: 1px solid var(--t-border); border-radius: var(--t-radius);
  font-size: 13px; font-weight: 500; text-decoration: none;
  color: var(--t-orange-text); background: var(--t-white);
}
body.portal a.keydl:hover { border-color: var(--t-orange); background: var(--t-light-gray); }

/* The manuals list. Also had no rule, so it fell back to browser bullets in a
   panel where nothing else has them. */
body.portal ul.manuals { margin: 0; padding: 0; list-style: none; }
body.portal ul.manuals li { padding: 5px 0; border-top: 1px solid var(--t-border); }
body.portal ul.manuals li:first-child { border-top: none; }

@media (max-width: 640px) {
  body.portal a.keydl { min-height: 44px; }
  body.portal ul.manuals li { padding: 10px 0; }
}
