/*
 * One stylesheet, cached after the first page.
 *
 * The look is the Central Bank portal's, deliberately. People move between
 * the two all day: they read a contract there and reconcile it here, and a
 * second visual language to learn is a second chance to misread a figure.
 * Colours, radii, the sidebar, the state pills and the table are measured
 * from screenshots of the portal itself rather than guessed at.
 *
 * One downloaded font: Almarai, which Mouad asked for on 13 September. It is
 * a Libyan-legible Arabic face and it does what the system stack cannot —
 * look the same on the manager's Mac, the clerk's Windows machine and the
 * phone in between.
 *
 * The reasoning that kept webfonts out until now has not gone away, so the
 * cost is paid down rather than ignored: `display=swap` renders every word in
 * the system stack on the first paint and swaps when Almarai arrives, a
 * `preconnect` starts the handshake with the font host before the CSS asks
 * for it, and the system stack is still the fallback — if the font never
 * loads the screen is the one we had last week, not an empty one.
 *
 * **It is served from this system, not from Google.** Six woff2 files, 151 KB
 * in total, sitting in `web/static/web/fonts/` beside this stylesheet. A link
 * to Google would have meant every screen in every Libyan office waiting on a
 * handshake with somebody else's server before a word could change shape —
 * the wrong dependency for a system that has to work on a bad morning, and
 * one more thing to be blocked or slow. Almarai is under the SIL Open Font
 * License, which permits exactly this; the licence travels with the files.
 *
 * Each face is declared with a `unicode-range`, so an Arabic page fetches the
 * Arabic file and the Latin one only because the figures are Latin digits —
 * 49 KB for the weight the whole screen is set in — and the heavier weights
 * arrive only where something is actually bold.
 *
 * One theme, light, for the same reason. The portal has no dark mode, and a
 * dark copy of it would not look like it.
 */


/* ---------- Almarai ----------
 *
 * Served from here rather than from a font host; see the note at the top.
 * Three weights, because that is what the interface uses once the browser has
 * matched: 400 for text, 700 for anything the design asks for 600 or 650 —
 * Almarai has no 600, and CSS weight matching picks 700 for it rather than
 * smearing the 400 into a synthetic bold — and 800 for the headline figures.
 */
@font-face {
  font-family: Almarai;
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("fonts/almarai-arabic-400-normal.woff2") format("woff2");
  unicode-range: U+0600-06FF, U+0750-077F, U+0870-088E, U+0890-0891, U+0898-08E1, U+08E3-08FF, U+FB50-FDFF, U+FE70-FEFF, U+10E60-10E7E, U+1EE00-1EEFF;
}
@font-face {
  font-family: Almarai;
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("fonts/almarai-latin-400-normal.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: Almarai;
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url("fonts/almarai-arabic-700-normal.woff2") format("woff2");
  unicode-range: U+0600-06FF, U+0750-077F, U+0870-088E, U+0890-0891, U+0898-08E1, U+08E3-08FF, U+FB50-FDFF, U+FE70-FEFF, U+10E60-10E7E, U+1EE00-1EEFF;
}
@font-face {
  font-family: Almarai;
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url("fonts/almarai-latin-700-normal.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: Almarai;
  font-style: normal;
  font-weight: 800;
  font-display: swap;
  src: url("fonts/almarai-arabic-800-normal.woff2") format("woff2");
  unicode-range: U+0600-06FF, U+0750-077F, U+0870-088E, U+0890-0891, U+0898-08E1, U+08E3-08FF, U+FB50-FDFF, U+FE70-FEFF, U+10E60-10E7E, U+1EE00-1EEFF;
}
@font-face {
  font-family: Almarai;
  font-style: normal;
  font-weight: 800;
  font-display: swap;
  src: url("fonts/almarai-latin-800-normal.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

:root {
  /* ---------------------------------------------------------------------
     The Central Bank's own portal, matched.

     Mouad, 14 Sep, with the address: https://fx.cbl.gov.ly/ — "make it
     similar to this."

     The reason is not decoration. The people who use this system spend part
     of every morning in that portal, downloading the export this system
     reads. Two screens about the same money, an hour apart, should not look
     like two different trades. So the values below are that portal's, taken
     off the page rather than guessed at: it is built on Tailwind, so the
     greys are Tailwind's grey scale and the green is green-600, and the
     corner it rounds every box and button by is 6px.

     Its typeface is Almarai, which is already what this system uses.
     --------------------------------------------------------------------- */

  --ground:      #F9FAFB;   /* gray-50  */
  --surface:     #FFFFFF;
  --surface-2:   #F9FAFB;
  --border:      #E5E7EB;   /* gray-200 */
  --border-firm: #D1D5DB;   /* gray-300 — what the portal draws a field in */
  --ink:         #111827;   /* gray-900 */
  --ink-2:       #4B5563;   /* gray-600 */
  --ink-3:       #6B7280;   /* gray-500 */

  --accent:      #16A34A;   /* green-600, the portal's button */
  /* The same green one step darker, for green that is *text*. White on
     green-600, and green-600 on white, both measure 3.30:1 — under the 4.5
     the guidelines ask of body text, and a bank's name in a table at 13.6px
     is body text. green-700 measures 5.02:1 and, side by side, reads as the
     same green. Fills stay green-600, so the system still looks like the
     portal; only the letters are darkened. Measured 14 Sep, every visible
     run of text on every screen. */
  --accent-text: #15803D;
  --accent-2:    #15803D;   /* green-700 */
  --accent-deep: #166534;   /* green-800 */
  --accent-soft: #F0FDF4;   /* green-50  */
  --accent-line: #BBF7D0;   /* green-200 */
  --brass:       #B45309;   /* amber-700 — the bank-balance half of a bar */

  --good:        #15803D;   /* green-700 */
  --warn:        #D97706;   /* amber-600 */
  --warn-soft:   #FFFBEB;   /* amber-50  */
  --crit:        #DC2626;   /* red-600   */
  --crit-soft:   #FEF2F2;   /* red-50    */
  --info:        #2563EB;   /* blue-600  */

  /* Tailwind's shadow-sm, which is what the portal puts under a raised box:
     barely there, and the same under everything. */
  --shadow: 0 1px 2px 0 rgba(0,0,0,.05);
  --shadow-card: 0 1px 2px 0 rgba(0,0,0,.05);
  --radius: 6px;
  --radius-sm: 6px;
  --side: 17.5rem;

  /* Almarai, at Mouad's request, 13 Sep: "use the almaraai font" — and, as it
     turned out the next day, the portal's own typeface too.
     The system stack behind it is not decoration: `font-display: swap` draws
     every word in it on the first paint and changes to Almarai when the file
     arrives, so a slow morning costs a font and never a blank screen. */
  --sans: Almarai, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
  --mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;
}

* { box-sizing: border-box; }

body {
  margin: 0;
  background: var(--ground);
  color: var(--ink);
  font-family: var(--sans);
  font-size: 15px;
  line-height: 1.55;
  -webkit-font-smoothing: antialiased;
}

a { color: var(--accent-text); }
:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: 2px; }

.num {
  font-variant-numeric: tabular-nums;
  font-feature-settings: "tnum";
}

/* Arabic customer names sit beside Latin references; let each run render in
   its own direction rather than forcing one on both. */
.bidi { unicode-bidi: plaintext; }

/* ---------- sign in ---------- */

/* Plain white, like the portal's own sign-in. The green wash that used to be
   here was the one place in the system with a colour behind the words, and
   the portal has nothing of the kind: a mark, a heading, two fields, a green
   button. */
.signin-page {
  min-height: 100vh;
  display: grid;
  place-items: center;
  padding: 2rem 1.25rem;
  background: var(--surface);
}

.signin {
  width: 100%;
  max-width: 23rem;
  display: flex;
  flex-direction: column;
  gap: 1.5rem;
}

.signin-brand { display: flex; flex-direction: column; gap: .4rem; text-align: center; }
.signin-mark {
  align-self: center;
  width: 2.9rem; height: 2.9rem;
  display: grid; place-items: center;
  border-radius: 50%;
  background: var(--accent);
  color: #fff;
  margin-bottom: .35rem;
}
.glyph { display: block; }
/* The portal sets its heading at 24px and 700, with the line underneath it in
   grey — a good deal larger than this was, and it is the first thing anybody
   reads in the morning. */
.signin-brand h1 { font-size: 1.5rem; font-weight: 800; margin: 0; letter-spacing: -.01em; }
.signin-brand p { margin: 0; color: var(--ink-3); font-size: .9rem; }

.card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: var(--shadow-card);
}

/* No card round the sign-in fields. The portal puts them straight on the
   page, and a white card on a white page is a border drawn for no reason. */
.signin .card { border: 0; box-shadow: none; background: none; }
.signin form { padding: 0; display: flex; flex-direction: column; gap: 1.25rem; }
.signin .btn { width: 100%; justify-content: center; margin-top: .25rem; }

/* A field, as the portal draws one: the label above it in the text colour
   rather than a grey, the box white with a grey-300 hairline, 8px by 12px of
   padding inside, and the corner rounded by 6. Focus is a 2px green line laid
   just inside the edge — it does not push the box about, and it is the same
   green as the button, so the eye learns one colour for "this is live". */
.field { display: flex; flex-direction: column; gap: .35rem; }
.field label { font-size: .85rem; font-weight: 700; color: var(--ink); }
.field input {
  font: inherit;
  padding: .5rem .75rem;
  min-height: 2.625rem;          /* 42px, the portal's field */
  border: 1px solid var(--border-firm);
  border-radius: var(--radius-sm);
  background: var(--surface);
  color: var(--ink);
}
.field input:focus {
  border-color: var(--accent);
  outline: 2px solid var(--accent);
  outline-offset: -2px;
  box-shadow: none;
}

.btn {
  font: inherit;
  /* Almarai is drawn at 300, 400, 700 and 800 — there is no 600 in it, so a
     600 here is the browser picking 700 and pretending otherwise. Said
     plainly instead. */
  font-weight: 700;
  padding: .5rem 1rem;
  min-height: 2.625rem;
  border-radius: var(--radius-sm);
  border: 1px solid transparent;
  background: var(--accent);
  color: #fff;
  cursor: pointer;
}
.btn:hover { background: var(--accent-2); }
.btn:active { background: var(--accent-deep); }
.btn[disabled], .btn[aria-busy="true"] { opacity: .65; cursor: progress; }
.btn-quiet {
  background: var(--surface);
  color: var(--ink-2);
  border-color: var(--border-firm);
}
.btn-quiet:hover { background: var(--surface-2); }

/* The button that applies a filter, at the end of a row of boxes.

   It used to be `btn-quiet` with the padding and font size shrunk by an inline
   style, which made it the same pale rectangle as the reset link next to it
   and smaller than the fields it belonged to. Two identical grey buttons, and
   nothing saying which one was the action. */
.btn-apply {
  font-size: 1rem;
  padding: .7rem 1.8rem;
  min-height: 2.75rem;
  box-shadow: var(--shadow-card);
}
.btn-reset {
  font-size: .9rem;
  padding: .7rem 1.1rem;
  min-height: 2.75rem;
  text-decoration: none;
  display: inline-flex; align-items: center;
}

.form-error {
  background: var(--crit-soft);
  border: 1px solid var(--crit);
  border-radius: var(--radius);
  padding: .6rem .75rem;
  font-size: .85rem;
  color: var(--crit);
}

/*
 * A question, not a refusal: the typed figure disagrees with the one the
 * Central Bank published, and the person has to say which they mean. Amber
 * rather than red, because nothing has gone wrong.
 */
.confirm {
  background: var(--warn-soft);
  border: 1px solid var(--warn);
  border-radius: var(--radius);
  padding: .8rem .9rem;
  display: flex; flex-wrap: wrap; align-items: center; gap: .6rem;
}
.confirm p {
  margin: 0 0 .2rem; flex-basis: 100%;
  font-size: .85rem; line-height: 1.55; color: var(--ink-2);
}
.btn-warn { background: var(--warn); }
.btn-warn:hover, .btn-warn:active { background: #8C5215; }
.crit-text { color: var(--crit); }

.signin-foot { text-align: center; font-size: .78rem; color: var(--ink-3); }

/* ---------- shell ---------- */

/*
 * The portal's frame: a fixed panel on the reading side holding the mark, the
 * navigation and the person signed in, and beside it a white bar carrying the
 * name of the page. `inset-inline-start` puts the panel on the right in Arabic
 * and on the left in English without a second rule.
 */

body.app { padding-inline-start: var(--side); min-height: 100vh; }

.side {
  position: fixed;
  inset-block: 0;
  inset-inline-start: 0;
  width: var(--side);
  background: var(--surface);
  border-inline-end: 1px solid var(--border);
  display: flex;
  flex-direction: column;
  z-index: 20;
  overflow-y: auto;
}

.side-mark {
  display: flex; align-items: center; gap: .6rem;
  padding: 1.15rem 1.35rem;
  text-decoration: none; color: var(--ink);
  border-bottom: 1px solid var(--border);
}
.side-mark .mark {
  width: 1.9rem; height: 1.9rem; border-radius: 50%; flex: none;
  background: var(--accent-2); color: #fff;
  display: grid; place-items: center;
}
.side-mark .wordmark { display: flex; flex-direction: column; line-height: 1.15; }
.side-mark .wordmark b { font-size: .9rem; font-weight: 650; letter-spacing: -.01em; }
.side-mark .wordmark span { font-size: .68rem; color: var(--ink-3); letter-spacing: .02em; }

.side nav { display: flex; flex-direction: column; gap: .1rem; padding: .8rem .7rem; }
.side nav a {
  display: flex; align-items: center; gap: .7rem;
  padding: .6rem .75rem;
  border-radius: var(--radius-sm);
  color: var(--ink-2); text-decoration: none;
  font-size: .9rem;
}
.side nav a svg { flex: none; color: var(--ink-3); }
.side nav a:hover { background: var(--surface-2); color: var(--ink); }
.side nav a.here { background: var(--accent-soft); color: var(--accent-2); font-weight: 600; }
.side nav a.here svg { color: var(--accent-2); }

.side-foot { margin-top: auto; border-top: 1px solid var(--border); padding: .9rem .8rem; }
.side-who { display: flex; align-items: center; gap: .7rem; padding: .3rem .55rem .7rem; }
.side-who svg { flex: none; color: var(--ink-3); }
.side-who .lines { min-width: 0; }
.side-who b { display: block; font-size: .85rem; font-weight: 600; }
.side-who span { display: block; font-size: .74rem; color: var(--ink-3);
                 overflow: hidden; text-overflow: ellipsis; }

.side-act { display: flex; align-items: center; gap: .35rem; }
.side-act form { flex: none; }
.side-out {
  display: flex; align-items: center; gap: .6rem;
  width: 100%; font: inherit; font-size: .88rem; font-weight: 600;
  padding: .55rem .75rem; border: 0; border-radius: var(--radius-sm);
  background: none; color: var(--crit); cursor: pointer; text-align: start;
}
.side-out:hover { background: var(--crit-soft); }
.side-lang {
  font: inherit; font-size: .78rem; font-weight: 600;
  padding: .45rem .7rem; border-radius: var(--radius-sm);
  border: 1px solid var(--border); background: var(--surface);
  color: var(--ink-2); cursor: pointer;
}
.side-lang:hover { background: var(--surface-2); }

.pagebar {
  background: var(--surface);
  border-bottom: 1px solid var(--border);
  padding: .95rem 1.6rem;
  display: flex; align-items: center; gap: 1rem;
  position: sticky; top: 0; z-index: 10;
}
.pagebar h2 { margin: 0; font-size: 1.05rem; font-weight: 650; letter-spacing: -.01em; }
.pagebar .back {
  margin-inline-start: auto;
  width: 2.1rem; height: 2.1rem; flex: none;
  display: grid; place-items: center;
  border-radius: 50%;
  border: 1px solid var(--border);
  background: var(--surface);
  color: var(--ink-2);
  text-decoration: none;
}
.pagebar .back:hover { background: var(--surface-2); }
.pagebar .back svg { transform: scaleX(1); }
[dir="ltr"] .pagebar .back svg { transform: scaleX(-1); }

@media (max-width: 60rem) {
  body.app { padding-inline-start: 0; }
  .side {
    position: static; width: auto; flex-direction: column;
    border-inline-end: 0; border-bottom: 1px solid var(--border);
  }
  .side nav { flex-direction: row; flex-wrap: wrap; }
  .side-foot { margin-top: 0; }
}

.wrap { max-width: 80rem; margin: 0 auto; padding: 1.4rem 1.6rem 4rem; }

.page-head {
  display: flex; align-items: flex-end; justify-content: space-between;
  gap: 1rem; flex-wrap: wrap; margin-bottom: 1.25rem;
}
.page-head h1 { font-size: 1.35rem; margin: 0 0 .15rem; letter-spacing: -.015em; }
.page-head .sub { color: var(--ink-3); font-size: .85rem; margin: 0; }
.page-head .sub.viewing {
  color: var(--warn); font-weight: 600; margin-bottom: .1rem;
}

.range { display: flex; align-items: flex-end; gap: .5rem; flex-wrap: wrap; }
.range .field { gap: .2rem; }
.range label { font-size: .7rem; text-transform: uppercase; letter-spacing: .06em; color: var(--ink-3); }
.range input, .range select { font: inherit; font-size: .85rem; padding: .4rem .5rem;
  border: 1px solid var(--border-firm); border-radius: var(--radius);
  background: var(--surface); color: var(--ink); }

section { margin-bottom: 1.75rem; }
h2.section {
  font-size: .78rem; text-transform: uppercase; letter-spacing: .08em;
  color: var(--ink-3); margin: 0 0 .6rem; font-weight: 650;
}

/* ---------- alerts ---------- */

.alerts { display: flex; flex-direction: column; gap: .5rem; }
/* No coloured bar down the leading edge — Mouad, 13 Sep: "remove the border
   right on alert" — but the hairline all the way round stays, at his word:
   "keep the border on alert 1px". Severity is carried by the tint of the
   card, the colour of the heading and the ▲ ● ■ before it; the border's job
   is to say where the card ends, which is a different job. */
.alert {
  display: flex; gap: .75rem; align-items: flex-start;
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--radius); padding: .75rem .9rem;
}
.alert .body { flex: 1; min-width: 0; }
.alert strong { display: block; font-size: .92rem; }
.alert p { margin: .15rem 0 0; font-size: .85rem; color: var(--ink-2); }
.alert .sig { font-size: .95rem; line-height: 1.3; }
.alert.critical { background: var(--crit-soft); }
.alert.critical .sig, .alert.critical strong { color: var(--crit); }
.alert.warning { background: var(--warn-soft); }
.alert.warning .sig, .alert.warning strong { color: var(--warn); }
.alert.note .sig { color: var(--info); }
.alert a { font-size: .83rem; font-weight: 600; white-space: nowrap; }

/* ---------- figures ---------- */

.tiles { display: grid; grid-template-columns: repeat(auto-fit, minmax(11rem, 1fr)); gap: .75rem; }
.tile {
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--radius); padding: .9rem 1rem;
  display: flex; flex-direction: column; gap: .15rem;
}
.tile .label {
  font-size: .72rem; text-transform: uppercase; letter-spacing: .06em;
  color: var(--ink-3); font-weight: 600;
}
.tile .value { font-size: 1.5rem; font-weight: 650; letter-spacing: -.02em; line-height: 1.15; }
.tile .unit { font-size: .78rem; color: var(--ink-3); font-weight: 500; }
.tile .foot { font-size: .78rem; color: var(--ink-3); margin-top: .1rem; }
.tile.hero { background: var(--accent-soft); border-color: var(--accent); }
.tile.hero .value { color: var(--accent-text); font-size: 1.85rem; }
.tile.hero .label { color: var(--accent-text); }

/* ---------- the band of figures above a filtered list ---------- */

.band { margin-bottom: 1.6rem; }
.band-head {
  display: flex; flex-wrap: wrap; align-items: baseline;
  gap: .5rem 1rem; margin-bottom: .6rem;
}

/* The figures answer a question, and this says which question. Without it a
   filtered total reads as the company's total. */
.applied { margin: 0; display: flex; flex-wrap: wrap; gap: .35rem; align-items: center; }
.chip-filter {
  font-size: .75rem; color: var(--ink-2);
  background: var(--surface-2); border: 1px solid var(--border);
  border-radius: 999px; padding: .2rem .6rem;
}
.chip-filter bdi { color: var(--ink); font-weight: 650; }
.applied a { font-size: .75rem; color: var(--ink-3); }

/* Bigger than the dashboard's, because here it is the first thing on the
   page rather than one section among several. */
.tiles.headline .tile.hero .value { font-size: 2.4rem; }
.tiles.headline .tile .value { font-size: 1.7rem; }

/* Up to three banks, so boxes rather than a list you have to open. Nothing
   ticked is every bank, which is what an untouched filter should mean. */
.label-as-legend {
  font-size: .7rem; text-transform: uppercase; letter-spacing: .06em;
  color: var(--ink-3);
}
.boxes { display: flex; flex-wrap: wrap; gap: .3rem; }
.box {
  display: inline-flex; align-items: center; gap: .4rem;
  font-size: .85rem; height: 2.2rem; padding: 0 .6rem;
  border: 1px solid var(--border-firm); border-radius: var(--radius);
  background: var(--surface); cursor: pointer; white-space: nowrap;
}
.box:has(input:checked) { border-color: var(--accent); background: var(--accent-soft); color: var(--accent-text); font-weight: 650; }
.box input { margin: 0; accent-color: var(--accent); }

/* ---------- tables ---------- */

.table-wrap {
  overflow-x: auto; background: var(--surface);
  border: 1px solid var(--border); border-radius: var(--radius);
}
table { border-collapse: collapse; width: 100%; font-size: .85rem; }
th, td { padding: .75rem .9rem; text-align: start; border-bottom: 1px solid var(--border); white-space: nowrap; }
tbody tr:last-child td { border-bottom: 0; }
tbody tr:hover td { background: var(--surface-2); }
thead th {
  background: var(--surface-2); color: var(--ink-2);
  font-size: .7rem; text-transform: uppercase; letter-spacing: .05em; font-weight: 650;
}
tbody tr:last-child td { border-bottom: none; }
tbody tr:hover { background: var(--surface-2); }
td.n, th.n { text-align: end; font-variant-numeric: tabular-nums; }
td.strong { font-weight: 650; }
.muted { color: var(--ink-3); }
.pos { color: var(--good); }
.neg { color: var(--crit); }
.money { font-variant-numeric: tabular-nums; }

/* a bar drawn in the cell itself — no library, no request */
/*
 * What one request leaves you.
 *
 * The figure leads and the bar follows it, measured against the best-paying
 * bank rather than against a total — so the longest bar is always the bank
 * worth having, and no arithmetic can push one past the end of its track. A
 * bank that lost money gets no bar: a loss is said, in red, not drawn.
 */
.yield { display: flex; align-items: center; gap: .6rem; }
.yield .figure { font-size: 1.05rem; font-weight: 650; min-width: 4.2rem; text-align: end; }
.yield .track {
  flex: 1; min-width: 3rem; height: 8px;
  border-radius: 4px; background: var(--surface-2); overflow: hidden;
}
.yield .track > span { display: block; height: 100%; background: var(--accent); border-radius: 4px; }
.yield.down .figure { color: var(--crit); }
.yield.down .track { background: repeating-linear-gradient(
    135deg, var(--crit-soft), var(--crit-soft) 4px, transparent 4px, transparent 8px); }

.bar { display: block; height: 6px; border-radius: 3px; background: var(--surface-2); overflow: hidden; min-width: 4rem; }
.bar > span { display: block; height: 100%; background: var(--accent); border-radius: 3px; }
.bar.two > span.b { background: var(--brass); }

/* Two parts of one whole, side by side, so the split is the picture. */
.bar.duo { display: flex; }
.bar.duo > span { border-radius: 0; }
.bar.duo > span.a { background: var(--accent); }
.bar.duo > span.b { background: var(--brass); }
.bar.duo > span:first-child { border-start-start-radius: 3px; border-end-start-radius: 3px; }
.bar.duo > span:last-child { border-start-end-radius: 3px; border-end-end-radius: 3px; }

/* A colour beside a label, so identity is never colour alone. */
.key { display: inline-block; width: .55rem; height: .55rem; border-radius: 2px; margin-inline-end: .35rem; }
.key.a { background: var(--accent); }
.key.b { background: var(--brass); }

.pair { display: flex; flex-direction: column; gap: 3px; }
.pair .k { font-size: .68rem; color: var(--ink-3); letter-spacing: .04em; text-transform: uppercase; }
/* A count with its share written underneath, inside a numeric cell. Without
   this the flex column left the figure at the start of the cell while every
   other number in the table hugged the column's edge, and a column of figures
   that does not line up is harder to read down. */
td.n .pair { align-items: start; }

/* Whose figures you are looking at, in the frame, for a platform
   administrator. It sits directly under the mark because that is where the
   answer to "where am I" belongs, and because on every screen but the
   dashboard there used to be no answer and no way to change it. */
.side-company {
  display: flex; flex-direction: column; gap: .3rem;
  padding: .9rem 1rem 0;
}
.side-company label {
  font-size: .68rem; letter-spacing: .06em; text-transform: uppercase;
  color: var(--ink-3);
}
.side-company select {
  font: inherit; font-size: .85rem;
  padding: .45rem .6rem; min-height: 2.4rem;
  border: 1px solid var(--border-firm); border-radius: var(--radius);
  background: var(--surface); color: var(--ink);
  width: 100%;
}
.side-company select:focus {
  border-color: var(--accent); outline: 2px solid var(--accent); outline-offset: -2px;
}

/* The actions at the right of a page heading: an export beside whatever the
   page's own action is. They sit together so neither reads as the other's
   afterthought, and they wrap under the heading on a phone. */
.head-actions { display: flex; gap: .6rem; flex-wrap: wrap; align-items: center; }

/* ---------- tabs ----------
 *
 * Mouad, 14 Sep: "i feel its very random at الشركة والمصارف / make taps up for
 * banks." One bank, one panel, chosen from a row of tabs — so the same bank
 * stops appearing three times in three places down four metres of page.
 *
 * Drawn the way the portal draws a tab strip: no boxes, no pills, a single
 * hairline along the bottom of the row with the chosen tab's own line sitting
 * on top of it in green. The line is a border rather than a background,
 * because a filled tab competes with the buttons underneath it, and on this
 * page the buttons are the things that do something.
 *
 * Links, not script: the choice is in the address, so it survives a save, a
 * reload, the back button and a link somebody sends.
 */
.tabs {
  display: flex; flex-wrap: wrap; gap: .25rem;
  border-bottom: 1px solid var(--border);
  margin-bottom: 1.25rem;
}
.tabs .tab {
  display: inline-flex; align-items: center; gap: .4rem;
  padding: .65rem .9rem;
  min-height: 2.75rem;
  margin-bottom: -1px;               /* sit the tab's line on the row's line */
  border-bottom: 2px solid transparent;
  color: var(--ink-2);
  font-weight: 700; font-size: .9rem;
  text-decoration: none;
  white-space: nowrap;
}
.tabs .tab:hover { color: var(--ink); border-bottom-color: var(--border-firm); }
.tabs .tab.on {
  color: var(--accent-text);
  border-bottom-color: var(--accent);
}
/* The one that adds rather than chooses. Quieter, and pushed to the far end
   so it is never mistaken for one of the banks. */
.tabs .tab.add { color: var(--ink-3); font-weight: 400; margin-inline-start: auto; }
.tabs .tab.add.on { color: var(--accent-text); }
.tabs .tab .chip { font-size: .62rem; padding: .05rem .4rem; }

/* The panel behind one tab: what this bank is and how it charges side by
   side, and its balances across the foot of both. `.cols` used to lay these
   out on its own and left a hole where the tallest panel wrapped. */
.bank-grid {
  display: grid; gap: 1rem; align-items: start;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  margin-bottom: 1.5rem;
}
.bank-grid > .wide { grid-column: 1 / -1; }
@media (max-width: 60rem) { .bank-grid { grid-template-columns: 1fr; } }

/* A short list of label-and-value lines inside a panel — how a bank charges,
   what it holds. Plain rows, not a table: there are two of them. */
.mini-list { list-style: none; margin: 0; padding: 0; }

/* A state, written the way the portal writes one: a pale pill, never bare
   coloured text. Green is the resting state there, so green is the default. */
.chip {
  display: inline-block; font-size: .72rem; font-weight: 600;
  padding: .15rem .6rem; border-radius: 999px;
  border: 1px solid var(--accent-line);
  background: var(--accent-soft);
  color: var(--accent-2);
  white-space: nowrap;
}
.chip.cash { color: var(--brass); border-color: #EADFC4; background: #FDF8EE; }
.chip.noncash { color: var(--info); border-color: #CFE0EA; background: #F1F7FA; }
.chip.ok { color: var(--accent-2); }
/* red-700 rather than the red-600 everything else uses: the chip is 11.5px on
   a pink ground, where red-600 measures 4.41 and the guidelines want 4.5.
   Nobody would have seen the difference; the measurement did. */
.chip.bad { color: #B91C1C; border-color: #FECACA; background: var(--crit-soft); }

.empty {
  background: var(--surface); border: 1px dashed var(--border-firm);
  border-radius: var(--radius); padding: 2rem 1.25rem; text-align: center;
  color: var(--ink-3);
}
.empty strong { display: block; color: var(--ink); margin-bottom: .3rem; }

.split { display: grid; grid-template-columns: 1.6fr 1fr; gap: 1rem; align-items: start; }

/* A grid or flex child does not shrink below its own content unless it is
   told to: its default `min-width` is `auto`, not zero. That is why the
   sideways scroller inside one silently stops scrolling and pushes the whole
   page instead — on a phone the dashboard sat 482px wider than the screen and
   every heading was drawn off the edge, while the scroller it was supposed to
   be inside did nothing. */
.split > *, .cols > *, .stack > * { min-width: 0; }
.table-wrap { min-width: 0; }
@media (max-width: 60rem) { .split { grid-template-columns: 1fr; } }

.note-line { font-size: .8rem; color: var(--ink-3); margin: .5rem 0 0; }

@media (prefers-reduced-motion: reduce) { * { transition: none !important; animation: none !important; } }


/* ---------- upload, front and centre ---------- */

.upload-lead {
  display: grid;
  grid-template-columns: 1.7fr 1fr;
  gap: 1rem;
  align-items: start;
  margin-bottom: 2rem;
}
@media (max-width: 62rem) { .upload-lead { grid-template-columns: 1fr; } }

.upload-card { padding: 1.4rem 1.5rem; border-color: var(--border-firm); }
.upload-head h1 { font-size: 1.3rem; margin: 0 0 .25rem; letter-spacing: -.015em; }
.upload-head p { margin: 0 0 1.1rem; color: var(--ink-2); font-size: .9rem; }

@media (max-width: 48rem) { .upload-grid { grid-template-columns: 1fr; } }

.upload-grid select, .upload-grid input[type=file] {
  font: inherit; font-size: .9rem; width: 100%;
  padding: .55rem .6rem;
  border: 1px solid var(--border-firm); border-radius: var(--radius);
  background: var(--ground); color: var(--ink);
}
.upload-grid input[type=file] { padding: .45rem .5rem; }
.upload-grid button { white-space: nowrap; }

.upload-note {
  margin: 1rem 0 0; font-size: .8rem; color: var(--ink-3);
  border-top: 1px dashed var(--border-firm); padding-top: .7rem;
}
.field-error { margin: .25rem 0 0; font-size: .8rem; color: var(--crit); font-weight: 600; }

.upload-history h2.section { margin-top: 0; }
.feed { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: .4rem; }
.feed li {
  display: flex; align-items: center; gap: .6rem;
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--radius); padding: .5rem .7rem; font-size: .82rem;
}
.feed-when { color: var(--ink-3); font-size: .74rem; white-space: nowrap; }
.feed-what { flex: 1; min-width: 0; display: flex; flex-direction: column; }
.feed-what .k { font-size: .7rem; }


.mono { font-family: var(--mono); font-size: .78rem; }
.warn-text { color: var(--warn); }
.crumb { margin: 0 0 .2rem; font-size: .8rem; color: var(--ink-3); }
.crumb a { color: var(--ink-3); }


tfoot tr.total td { background: var(--surface-2); border-top: 2px solid var(--border-firm); }

.inline-form { display: flex; gap: .6rem; align-items: end; flex-wrap: wrap; }
.inline-form .field { flex: 1 1 8rem; }
.inline-form input {
  font: inherit; font-size: .9rem; width: 100%;
  padding: .5rem .6rem; border: 1px solid var(--border-firm);
  border-radius: var(--radius); background: var(--ground); color: var(--ink);
}

.mini-row {
  display: flex; justify-content: space-between; align-items: baseline;
  gap: .5rem; padding: .35rem 0; border-bottom: 1px solid var(--border);
  font-size: .87rem;
}
.mini-row:last-of-type { border-bottom: none; }


/* ---------- forms beyond sign-in ---------- */

.field .hint { font-size: .76rem; color: var(--ink-3); line-height: 1.4; }
.field .field-error { font-size: .78rem; color: var(--crit); font-weight: 600; }
.field.has-error input, .field.has-error select { border-color: var(--crit); }
.field select, .field textarea {
  font: inherit; padding: .5rem .75rem;
  min-height: 2.625rem;
  border: 1px solid var(--border-firm); border-radius: var(--radius);
  /* White, like every other box on the portal. These were grey, which read as
     "switched off" next to the white text fields beside them. */
  background: var(--surface); color: var(--ink);
}
.field select:focus, .field textarea:focus {
  border-color: var(--accent); outline: 2px solid var(--accent); outline-offset: -2px;
}
.field input[type="file"] { padding: .5rem; background: var(--surface); }
.field input[type="checkbox"] { width: auto; }

.alert.good { background: var(--accent-soft); }
.alert.good .sig, .alert.good strong { color: var(--good); }

/* The upload panel — the reason people open this page, so it looks like it. */
.upload-panel {
  background: var(--surface);
  border: 1px solid var(--accent);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  padding: 1.25rem 1.35rem;
  margin-bottom: 1.75rem;
}
.upload-panel h2 {
  margin: 0 0 .2rem; font-size: 1.05rem; letter-spacing: -.01em;
  display: flex; align-items: center; gap: .5rem;
}
.upload-panel h2 .mark {
  width: 1.4rem; height: 1.4rem; border-radius: 50%;
  background: var(--accent); color: #fff;
  display: grid; place-items: center; flex: none;
}
.upload-panel > p { margin: 0 0 1rem; color: var(--ink-3); font-size: .85rem; }
.upload-grid {
  display: grid; grid-template-columns: minmax(14rem, 1.2fr) minmax(14rem, 1.4fr) auto;
  gap: 1rem; align-items: start;
}
.upload-grid .btn { margin-top: 1.35rem; white-space: nowrap; }
@media (max-width: 52rem) {
  .upload-grid { grid-template-columns: 1fr; }
  .upload-grid .btn { margin-top: 0; }
}

/* ---------- one box you type into and pick out of ----------
   The contract on the upload panel. The <select> is still the real control
   and is still in the page; the script hides it and puts .combo-box in front.
   Without the script `.combo-on` is never set, nothing here applies, and the
   dropdown looks as it always did. */

.field[data-combo] { position: relative; }
.field.combo-on > select { display: none; }

.combo-box {
  font: inherit; font-size: .9rem; width: 100%;
  padding: .55rem .6rem;
  border: 1px solid var(--border-firm); border-radius: var(--radius);
  background: var(--ground); color: var(--ink);
}
.combo-box:focus {
  border-color: var(--accent);
  outline: 2px solid var(--accent); outline-offset: 0;
  background: var(--surface);
}

.combo-list {
  position: absolute; z-index: 40;
  inset-inline-start: 0; inset-inline-end: 0; top: 100%;
  margin: .25rem 0 0; padding: .25rem;
  list-style: none;
  max-height: 16rem; overflow-y: auto; -webkit-overflow-scrolling: touch;
  background: var(--surface);
  border: 1px solid var(--border-firm); border-radius: var(--radius);
  box-shadow: 0 4px 12px -2px rgba(0,0,0,.12), var(--shadow);
}
.combo-option {
  padding: .5rem .55rem; border-radius: 4px;
  font-size: .85rem; line-height: 1.35; cursor: pointer;
}
.combo-option:hover { background: var(--ground); }
.combo-option.on {
  background: var(--accent-soft); color: var(--accent-deep); font-weight: 600;
}
.combo-empty { padding: .6rem .55rem; font-size: .82rem; color: var(--ink-3); }

/* Said to a screen reader, shown to nobody. */
.combo-say {
  position: absolute; width: 1px; height: 1px;
  margin: -1px; padding: 0; overflow: hidden;
  clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap;
}

/* A finger is not a mouse pointer. */
@media (max-width: 52rem) {
  .combo-option { padding: .7rem .6rem; font-size: .88rem; }
  .combo-list { max-height: 14rem; }
}

/* ---------- filters, one row above the table ---------- */

.filters {
  display: flex; gap: .5rem; align-items: flex-end; flex-wrap: wrap;
  margin-bottom: .75rem;
}
.filters .field { gap: .2rem; align-self: flex-end; }
.filters label {
  font-size: .7rem; text-transform: uppercase; letter-spacing: .06em;
  color: var(--ink-3);
}
/* input[type=...] rather than a bare element selector, so nothing later
   quietly outranks these and stretches a control to the row's height. */
.filters input[type=search],
.filters input[type=date],
.filters input[type=text],
.filters select {
  font: inherit; font-size: .85rem; padding: .4rem .5rem;
  border: 1px solid var(--border-firm); border-radius: var(--radius);
  background: var(--surface); color: var(--ink);
  flex: 0 0 auto; height: 2.2rem;
}
.filters .grow { flex: 1 1 14rem; }
.filters .grow input[type=search] { width: 100%; }

.pager {
  display: flex; justify-content: space-between; align-items: center;
  gap: 1rem; margin-top: .75rem; font-size: .85rem; color: var(--ink-3);
}
.pager .links { display: flex; gap: .4rem; }
.pager a, .pager span.current {
  padding: .3rem .6rem; border-radius: var(--radius);
  border: 1px solid var(--border-firm); text-decoration: none; color: var(--ink-2);
}
.pager span.current { background: var(--accent); color: #fff; border-color: var(--accent); }
.pager a:hover { background: var(--surface-2); }

/* A section the page has switched off. Some panels are laid out with
   flex, which would otherwise win against the attribute. */
[hidden] { display: none !important; }

/* phone.js appends this cell to every wide row. On a laptop it is an
   invented fourteenth column and must never be drawn; the phone block below
   switches it on. Hidden here rather than only added on a phone, because a
   window dragged from narrow to wide would otherwise keep it. */
td.more-cell { display: none; }

.panel {
  background: var(--surface); border: 1px solid var(--border);
  border-radius: 12px; padding: 1.2rem 1.3rem;
  box-shadow: var(--shadow-card);
}
.panel h3 { margin: 0 0 .2rem; font-size: 1rem; }
.panel > p.lead { margin: 0 0 1rem; color: var(--ink-3); font-size: .85rem; }
.panel form { display: flex; flex-direction: column; gap: .8rem; }
.panel form .btn { align-self: flex-start; }

.stack { display: flex; flex-direction: column; gap: 1rem; }
.cols { display: grid; grid-template-columns: repeat(auto-fit, minmax(20rem, 1fr)); gap: 1rem; align-items: start; }

.row-actions a { font-size: .8rem; font-weight: 600; text-decoration: none; }

/* A button that has to sit beside a link and not look like a second kind of
   thing. Switching a bank off is a change to stored data, so it is a POST and
   therefore a button, whatever it looks like. */
.linklike {
  font: inherit; font-size: .8rem; font-weight: 600;
  background: none; border: 0; padding: 0;
  color: var(--accent-text); cursor: pointer;
}
.linklike:hover { text-decoration: underline; }
.row-actions form { margin-inline-start: .6rem; }
.row-actions a + a { margin-inline-start: .6rem; }

/* Removal sits under a rule at the foot of the edit panel: reachable on
   purpose, never on the way to something else. */
.panel-foot {
  margin-top: 1rem; padding-top: .8rem;
  border-top: 1px solid var(--border);
  display: flex; flex-wrap: wrap; gap: .5rem; align-items: baseline;
}
.panel-foot form { display: flex; gap: .5rem; align-items: baseline; flex-wrap: wrap; }
.linklike.danger { color: var(--crit); }


/* ---------- the phone ----------
 *
 * A company owner checks the day's profit standing in the office, on a phone,
 * not sitting at the laptop where the file was uploaded. Below this width the
 * layout stops pretending to be a smaller laptop.
 *
 * The breakpoint is 46rem and it is also written in phone.js, which cannot ask
 * a stylesheet what its own breakpoints are. Change one, change the other.
 */

@media (max-width: 46rem) {

  .wrap { padding: 1rem 1rem 3rem; }
  .page-head { flex-direction: column; align-items: flex-start; gap: .6rem; }

  /* A table with no headings to borrow — see phone.js. It cannot be labelled,
     but it can be allowed to wrap, and that alone stops it dragging the whole
     page sideways. The dashboard's payment-mix table is the one that did. */
  table[data-wrap] th, table[data-wrap] td { white-space: normal; }

  /* Each row becomes a card, each figure carries its column heading. */
  table.as-cards,
  table.as-cards tbody,
  table.as-cards tr,
  table.as-cards td { display: block; width: auto; }

  table.as-cards thead { display: none; }

  table.as-cards tr {
    padding: .55rem .9rem;
    border-bottom: 1px solid var(--border);
  }
  table.as-cards tr:last-child { border-bottom: 0; }
  table.as-cards tbody tr:hover { background: transparent; }

  table.as-cards td {
    display: grid;
    grid-template-columns: minmax(5.5rem, 38%) 1fr;
    gap: .75rem;
    align-items: baseline;
    padding: .28rem 0;
    border: 0;
    white-space: normal;
  }

  table.as-cards td::before {
    content: attr(data-label);
    color: var(--ink-3);
    font-size: .68rem; text-transform: uppercase; letter-spacing: .05em;
    font-weight: 650; line-height: 1.5;
  }

  /* The column was the alignment; the label is now. A number pushed to the
     far edge of a narrow card is further from its own heading than from the
     one below it. */
  table.as-cards td.n { text-align: start; }

  /* Marked by phone.js: a heading with nothing under it is six lines of
     labels against six dashes. */
  table.as-cards td[data-empty] { display: none; }

  /* A row that spans the width is a summary or an empty-state line, not a
     record — no heading belongs beside it. */
  table.as-cards td[data-span] { grid-template-columns: none; }
  table.as-cards td[data-span]::before { content: none; }

  /* Anything you have to hit with a thumb. 2.75rem is 44px, which is the
     smallest target Apple and Google both consider reliable; several of these
     were 15px, which is a target you miss and then mistrust. */
  table.as-cards td a,
  .row-actions a,
  .pager a, .pager span.current {
    display: inline-flex; align-items: center;
    min-height: 2.75rem; padding-inline: .2rem;
  }
  .side nav a { min-height: 2.75rem; display: flex; align-items: center; }

  /* One control per line, full width, thumb-sized. A row of five small boxes
     that wraps unpredictably is worse than a short column of five clear ones. */
  .filters { flex-direction: column; align-items: stretch; gap: .6rem; }
  .filters .field { width: 100%; align-self: stretch; }
  /* `.grow` is `flex: 1 1 14rem`, which grows along the main axis — and the
     main axis is now the vertical one. Left alone it made the search box
     fourteen rems TALL, with a hand's width of empty white under it. */
  .filters .grow { flex: 0 0 auto; }
  .boxes { flex-direction: column; align-items: stretch; }
  .box { height: 2.75rem; justify-content: flex-start; }
  .filters input[type=search],
  .filters input[type=date],
  .filters input[type=text],
  .filters select { width: 100%; height: 2.75rem; font-size: .95rem; }
  .filters .btn { width: 100%; min-height: 2.75rem; justify-content: center; }

  /* iOS zooms the whole page in when a focused field is under 16px, and then
     leaves it zoomed. Every typing target is held at 1rem for that reason. */
  input, select, textarea { font-size: 1rem; }

  .pager { flex-direction: column; align-items: stretch; gap: .6rem; }
  .pager .links { justify-content: center; }

  /* Everything a thumb has to land on, not only the things inside a table.
     The sign-out button was 39px, the language switch 36, the date fields 38,
     and a link out of a section 15 — all of them targets you miss once and
     then stop trusting. Checkboxes and radios are left alone: the browser
     draws those at a fixed size and stretching the box moves the tick away
     from the label it belongs to. */
  .btn, button { min-height: 2.75rem; }
  input:not([type=checkbox]):not([type=radio]):not([type=file]),
  select, textarea { min-height: 2.75rem; }
  /* A link a thumb is meant to hit gets the same 44px as a button. `.feed-row`
     is here because of the checklist on the welcome screen: its "do this step"
     link was 15px tall on a phone — the one link on that page whose whole job
     is to be pressed. Found on 14 Sep by measuring every link on every screen
     at 390px rather than by looking at them. */
  .note-line a, .alert a, .feed-row a {
    display: inline-flex; align-items: center; min-height: 2.75rem;
  }

  /* The date range above the dashboard, one control to a line like the
     filters below it. */
  .range { flex-direction: column; align-items: stretch; width: 100%; }
  .range .field, .range .btn { width: 100%; }
  .range .btn { justify-content: center; }

  /* The fields behind the tap. Held out of the document rather than merely
     made transparent, so a card is short until somebody asks for the rest. */
  table.as-cards.has-extra td[data-extra] { display: none; }
  table.as-cards.has-extra tr[data-open] td[data-extra] { display: grid; }

  table.as-cards td.more-cell {
    display: block;
    grid-template-columns: none;
    padding: .1rem 0 0;
  }
  table.as-cards td.more-cell::before { content: none; }

  .more-btn {
    font: inherit; font-size: .78rem; font-weight: 650;
    color: var(--accent-text); background: none; border: 0;
    padding: .6rem .1rem; min-height: 2.75rem;
    cursor: pointer; text-align: start;
  }
  .more-btn:hover { text-decoration: underline; }

  /* The opened part sits under a rule so it reads as the same record
     continued, not as the next one starting. */
  tr[data-open] td[data-extra]:first-of-type { border-top: 1px solid var(--border); padding-top: .5rem; }
}
