/* portal.css — the client portal's design system (Furetank redesign, 2 Oct 2026, feature/furetank-portal).

   One quiet palette: paper grey, white surfaces, the tenant's navy for headings and its blue for actions and links,
   and colour only where it carries meaning (a document that has LAPSED, a relief that is OVERDUE). Every brand value
   arrives per tenant from portal_brand_style() in inc/layout.php (--heading, --accent, --accent-strong, --link,
   --intro-fill, --highlight); nothing Furetank-specific is written here, so Northwind and Bluewater render in their own
   colours. No gradients, no shadows, no tinted status chips, no letter-spaced capitals (the owner's restraint rule,
   5 and 9 Sep 2026). Desktop-first: the base rules are the 1280-1679 desktop, the media blocks at the end step down to
   320 and up to 1920.

   A page's own layout belongs in /assets/page-<key>.css, which the shell links when it exists; the vocabulary every
   page should reuse is listed with markup in claude-dev/pass27/furetank/COMPONENTS.md.

   The class names below are a contract: specs and harness probes read them (.side-item, .nav-toggle, .chip-*, .pill*,
   .kcard, .fleet-strip, .cov*, .mv-*, .tbl, .group-head, .hide ...). Restyle them; never rename them. */

/* ================================================================ 1. tokens */
:root {
  /* brand fallbacks — portal_brand_style() overrides the first six per tenant */
  --heading: #0f2a47;
  --accent: #2457c5;
  --accent-strong: var(--accent);
  --link: var(--accent-strong);
  --intro-fill: var(--heading);
  --highlight: var(--accent);        /* a tenant without a highlight gets the accent rule it always had */
  --primary: var(--accent-strong);   /* read FIRST by the frozen dialog.css for its focus ring */

  /* surfaces and lines - fallback values; derived from the tenant's --heading below where color-mix() is supported */
  --surface: #ffffff;
  --bg: #f5f6f8;          /* the canvas */
  --surface-2: #f8f9fa;   /* table head band, group-head rows, row hover */
  --hover: #f5f6f8;       /* nav and tile hover */
  --selected: #f1f3f5;    /* the selected nav item, queue item */
  --line: #e5e7eb;        /* card borders, header hairline */
  --line-2: #eef0f3;      /* row dividers */
  --field-line: #7b8794;  /* input borders: 3.66:1 on white (WCAG 1.4.11; the old #e5e7eb was about 1.2:1) */
  --btn-line: #cbd2d9;    /* ghost button border (the words identify the button) */
  --on-intro: #ffffff;
  --on-intro-2: #cfd6dc;

  /* text - fixed neutrals, contrast-verified on this portal (UI audit, 17 Sep 2026). Both of the quiet tiers were under
     WCAG AA on the paper-grey ground once (#6b7280 4.47:1, #9ca3af 2.35:1) and were darkened until they passed. */
  --ink: #111827;
  --ink-2: #4b5563;
  --ink-3: #5d6470;   /* 5.51:1 on #f5f6f8 */
  --ink-4: #646b78;   /* 4.96:1 - the quietest tier, still readable */
  /* the ONE alarm colour, exactly this value: portal-responsive.spec.js recognises red by rgb(185, 28, 28) */
  --bad: #b91c1c;

  --font: 'Inter', system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;
  --mono: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;

  /* type: nothing smaller than 12px except the phone band's one label */
  --fs-h1: 28px;
  --fs-h2-lg: 22px;
  --fs-h2: 17px;
  --fs-stat: 28px;
  --fs-body: 14px;
  --fs-sub: 13px;
  --fs-label: 12px;

  /* spacing (4-based) */
  --sp-1: 4px; --sp-2: 8px; --sp-3: 12px; --sp-4: 16px; --sp-5: 20px; --sp-6: 24px; --sp-7: 32px; --sp-8: 40px; --sp-9: 48px;
  --gutter: 32px;        /* the utility bar's and the canvas's side padding, identical so their edges line up */
  --gap: 20px;           /* grid gap */
  --pad: 20px 22px;      /* card padding */
  --r-sm: 6px; --r: 8px; --r-lg: 10px;

  /* layout */
  --side-w: 248px;
  --utility-h: 64px;
  --canvas-max: 1480px;  /* the content width, excluding the gutters; the canvas is left-aligned */
  --queue-w: 320px;      /* the proposal queue */
  --mark-h: 96px;        /* the sidebar mark's height (sized by height, one axis only) */
}
@supports (color: color-mix(in srgb, red 50%, blue)) {
  :root {
    /* brand-tinted neutrals: a teal tenant's greys lean teal, Furetank's lean navy - from the tenant's own --heading */
    --bg:         color-mix(in srgb, var(--heading) 3%,  #f8f9fb);
    --surface-2:  color-mix(in srgb, var(--heading) 3%,  #ffffff);
    --hover:      color-mix(in srgb, var(--heading) 4%,  #ffffff);
    --selected:   color-mix(in srgb, var(--heading) 6%,  #ffffff);
    --line:       color-mix(in srgb, var(--heading) 12%, #ffffff);
    --line-2:     color-mix(in srgb, var(--heading) 6%,  #ffffff);
    --field-line: color-mix(in srgb, var(--heading) 58%, #ffffff);
    --btn-line:   color-mix(in srgb, var(--heading) 22%, #ffffff);
    --on-intro-2: color-mix(in srgb, #ffffff 80%, var(--intro-fill));
  }
}

/* ================================================================ 2. base */
* { box-sizing: border-box; }
html, body { margin: 0; padding: 0; }
body { font-family: var(--font); font-size: var(--fs-body); line-height: 1.5; color: var(--ink); background: var(--bg); -webkit-font-smoothing: antialiased; }
/* [hidden] MEANS HIDDEN. `.btn { display: inline-flex }` beat the browser's own [hidden] rule, so "Clear filter" showed on a
   fresh Documents page with an empty search (DESIGN_SPEC D2). */
[hidden] { display: none !important; }
a { color: var(--link); text-decoration: none; }
a:hover { text-decoration: underline; text-underline-offset: 2px; }
/* A link inside a sentence is told from the words around it by more than colour (ISSUE-328, WCAG 1.4.1). */
p a:not(.btn):not(.btn-out):not(.card-link),
.mv-what a:not(.btn), .group-head a:not(.btn), .foot a:not(.btn) { text-decoration: underline; text-underline-offset: 2px; }   /* Overview's crew movements, Documents' group heads (ISSUE-438); the footer's privacy notice (ISSUE-538) */
/* THE FOCUS RING: --accent-strong, which reaches 3:1 on white and on the canvas for every tenant (the old --accent ring was
   2.69:1 on the canvas for the default teal, D13). */
:focus-visible { outline: 2px solid var(--accent-strong); outline-offset: 2px; }
h1, h2, h3 { margin: 0; color: var(--heading); font-weight: 600; text-wrap: balance; }
h1 { font-size: var(--fs-h1); line-height: 1.2; font-weight: 700; letter-spacing: -0.01em; }
h2 { font-size: var(--fs-h2); line-height: 1.3; }
/* Sentence case, no tracking: the owner's restraint rule. Weight and colour carry the hierarchy. */
h3 { font-size: 13px; line-height: 1.4; color: var(--ink-3); font-weight: 600; }
p { margin: 0 0 .8em; }
strong, b { font-weight: 600; }
.muted { color: var(--ink-3); } .small { font-size: 12.5px; } .tnum { font-variant-numeric: tabular-nums; }
.inline { display: inline; }
.skip { position: absolute; left: -999px; top: 0; background: var(--heading); color: #fff; padding: 8px 12px; z-index: 99; border-radius: var(--r-sm); }
.skip:focus { left: 8px; top: 8px; }
/* Said to a screen reader, not drawn: the phone menu's button carries what its badge is counting. */
.sr-only { position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0; overflow: hidden; clip-path: inset(50%); white-space: nowrap; border: 0; }
/* utilities used instead of style attributes (the CSP forbids inline styles) */
.m-0 { margin: 0 !important; } .block { display: block !important; } .nowrap { white-space: nowrap; } .maxw-520 { max-width: 520px; }
.mt-6 { margin-top: 6px; } .mt-8 { margin-top: 8px; } .mt-10 { margin-top: 10px; } .mt-12 { margin-top: 12px; } .mt-14 { margin-top: 14px; } .mt-16 { margin-top: 16px; } .mt-18 { margin-top: 18px; } .mt-20 { margin-top: 20px; } .mt-24 { margin-top: 24px; }
.mb-10 { margin-bottom: 10px; } .mb-12 { margin-bottom: 12px; } .mb-16 { margin-bottom: 16px; } .mb-20 { margin-bottom: 20px; }

/* --- the one icon set (portal_icon() in inc/layout.php) --------------------------------------
   ONE tone: currentColor, so an icon is the colour of the text it sits beside and adds no accent. It never carries
   meaning alone — the words are beside it and the icon is aria-hidden. Sized here, not by its attributes. */
.ic { flex: 0 0 auto; width: 14px; height: 14px; vertical-align: -2px; color: var(--ink-3); }
.ic-nav { width: 18px; height: 18px; }
.btn .ic, .btn-out .ic { width: 16px; height: 16px; color: inherit; }
.card-link .ic, .nav-toggle .ic { color: inherit; }

/* ================================================================ 3. the shell */
/* The sidebar's white column runs the full height of the page however long it is: ::before paints the column and the
   sidebar itself sits on it, sticky, one viewport tall, its menu scrolling inside on a short screen. */
.app { display: grid; grid-template-columns: var(--side-w) minmax(0, 1fr); min-height: 100vh; }
.app::before { content: ""; grid-column: 1; grid-row: 1; background: var(--surface); border-right: 1px solid var(--line); }
.side { grid-column: 1; grid-row: 1; align-self: start; position: sticky; top: 0; height: 100vh; display: flex; flex-direction: column; min-width: 0; }
.body { grid-column: 2; grid-row: 1; min-width: 0; display: flex; flex-direction: column; }

/* THE MARK IS SIZED BY HEIGHT, IN ONE PLACE (the asset is 220x147; a width from one rule and a height from another once
   stretched it to 150x44 - probe_portal_mark.js keeps the aspect within 0.06). */
.side .logo { display: block; padding: 24px 24px 12px; }
.side .logo img { display: block; width: auto; height: var(--mark-h); max-width: 100%; object-fit: contain; }
.logo-text { font-weight: 700; font-size: 20px; line-height: 1.2; color: var(--heading); }
/* WHOSE PORTAL, THEN WHAT IT IS. The company is the identity and carries the weight; "Client portal" stays quiet beside
   it (R6 review item 2; probe_portal_mark.js wants the name at least 1.5px larger than the label). */
.side-who { padding: 0 24px 16px; border-bottom: 1px solid var(--line-2); color: var(--ink-2); font-size: 14px; font-weight: 600; line-height: 1.35; }
.side-who b { display: block; margin-top: 1px; color: var(--heading); font-size: 18px; font-weight: 700; line-height: 1.3; letter-spacing: 0; }

.side nav { flex: 1 1 auto; min-height: 0; overflow-y: auto; padding: 10px 12px 16px; }
.side-sec { font-size: 13px; font-weight: 600; color: var(--ink-3); padding: 16px 12px 6px; }
.side-item { position: relative; display: flex; align-items: center; gap: 12px; min-height: 40px; padding: 9px 12px; border-radius: var(--r-sm);
             color: var(--ink-2); font-size: 14px; font-weight: 500; line-height: 1.3; text-decoration: none; }
.side-item .ic-nav { color: var(--ink-3); }
.side-item > span:not(.badge) { min-width: 0; }
.side-item .badge { margin-left: auto; }
.side-item:hover { background: var(--hover); color: var(--ink); text-decoration: none; }
/* THE ACTIVE ITEM: the ground, the weight and the heading colour carry the state; the 3px rule in the tenant's highlight
   is identity, not information (Furetank's yellow is 1.35:1 on white, so it can never be the only sign - R5). */
.side-item.on { background: var(--selected); color: var(--heading); font-weight: 600; }
.side-item.on .ic-nav { color: var(--heading); }
.side-item.on::before { content: ""; position: absolute; left: 0; top: 9px; bottom: 9px; width: 3px; border-radius: 2px; background: var(--highlight); }
.side-foot { padding: 16px 20px 20px 24px; border-top: 1px solid var(--line-2); font-size: 12.5px; line-height: 1.4; color: var(--ink-2); }
.side-foot b { display: block; font-weight: 600; }
.side-foot span { display: block; font-size: 12px; color: var(--ink-4); }
/* THE PHONE MENU'S BUTTON EXISTS ONLY ON A PHONE. It is in the markup at every width so the shell has one shape; the
   phone block below is the only place it is shown. */
.nav-toggle { display: none; }
/* COUNT MARKS. --accent-strong, not --accent: they carry WHITE TEXT, and a mid-tone brand colour under white is not
   readable. Every count is this ordinary mark (rule 2, correction round 1): the expiry register's count mixes lapsed
   documents with ones still valid for up to 30 days, so it is not red. .badge-bad stays defined (class names are a
   contract) for a count that is ALL lapsed - none exists today (owner question 3). */
.badge { display: inline-flex; align-items: center; justify-content: center; min-width: 22px; height: 20px; padding: 0 6px; border-radius: 10px;
         background: var(--accent-strong); color: #fff; font-size: 12px; font-weight: 700; line-height: 1; font-variant-numeric: tabular-nums; flex: none; }
.badge-bad { background: var(--bad); }

/* --- the utility bar ------------------------------------------------------------------------ */
.topbar { height: var(--utility-h); display: flex; align-items: center; gap: 20px; padding: 0 var(--gutter);
          background: var(--surface); border-bottom: 1px solid var(--line); }
/* WHERE YOU ARE (correction round 1). The crumbs before the last NEVER shrink: a section link drawn as "Propos…" or a bare
   "…" said nothing. Only the last crumb - the page or record itself, whose full name is the h1 below - gives way, with an
   ellipsis and its words as a title (layout.php). The ellipsis lives on the crumb's own a/span, not on the list item: a
   link draws its focus ring outside its own box, and an li with overflow:hidden cut that ring to a sliver or to nothing
   (WCAG 2.4.7). The bar clips only as a safety net (a tenant name too long for any bar), and its 6px padding keeps every
   ring inside what it shows. */
.where { flex: 1 1 auto; min-width: 0; font-size: 13px; overflow: hidden; padding: 6px; margin: -6px; }
.where ol { list-style: none; margin: 0; padding: 0; display: flex; align-items: center; min-width: 0; white-space: nowrap; }
.where li { display: flex; align-items: baseline; flex: none; min-width: 0; color: var(--ink-3); }
.where li:last-child { flex: 0 1 auto; }
.where li > a, .where li > span { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.where li + li::before { content: "/"; flex: none; color: var(--ink-4); margin: 0 8px; }
.where li:first-child a { color: var(--heading); font-weight: 600; }
.where a { color: var(--ink-2); }
.where [aria-current="page"] { color: var(--ink-2); }
.clocks { display: flex; gap: 20px; flex: none; min-width: 0; }
.clock { display: flex; flex-direction: column; line-height: 1.25; }
.clock b { font-size: 12px; font-weight: 500; color: var(--ink-3); }
.clock time { font-size: 14px; font-weight: 600; color: var(--ink); font-variant-numeric: tabular-nums; }
.topbar-right { display: flex; align-items: center; gap: 16px; flex: none; }
.topbar-right::before { content: ""; width: 1px; height: 28px; background: var(--line); }
.user { display: flex; align-items: center; gap: 10px; color: var(--ink); text-decoration: none; min-width: 0; }
.user:hover { text-decoration: none; }
.user:hover strong { text-decoration: underline; text-underline-offset: 2px; }
.user-init { width: 34px; height: 34px; border-radius: 50%; background: var(--accent-strong); color: #fff; display: inline-flex; align-items: center;
             justify-content: center; font-weight: 600; font-size: 12.5px; flex: none; }
.user-text { display: flex; flex-direction: column; line-height: 1.3; min-width: 0; }
.user-text strong { font-size: 14px; font-weight: 600; color: var(--ink); white-space: nowrap; }
.may { font-size: 12px; font-weight: 400; color: var(--ink-3); white-space: nowrap; }
.btn-out { display: inline-flex; align-items: center; gap: 8px; min-height: 36px; padding: 0 12px; font: inherit; font-size: 13.5px; font-weight: 500;
           color: var(--ink); background: var(--surface); border: 1px solid var(--btn-line); border-radius: var(--r-sm); cursor: pointer; }
.btn-out:hover { background: var(--hover); }

/* --- the canvas ----------------------------------------------------------------------------- */
/* Left-aligned so its edge lines up with the breadcrumb above it; capped so a 1920 screen keeps a readable measure. It is
   also a size container ("main"), so a component can follow the room it actually has (the stat strip does). */
.main { width: 100%; max-width: calc(var(--canvas-max) + 2 * var(--gutter)); padding: 28px var(--gutter) 48px; container: main / inline-size; }
.foot { width: 100%; max-width: calc(var(--canvas-max) + 2 * var(--gutter)); margin-top: auto; padding: 16px var(--gutter); border-top: 1px solid var(--line-2);
        font-size: 12px; color: var(--ink-4); display: flex; justify-content: space-between; gap: 8px 16px; flex-wrap: wrap; }

/* ================================================================ 4. page head and the intro */
.page-head { display: flex; align-items: flex-end; justify-content: space-between; gap: 12px 16px; margin-bottom: 20px; flex-wrap: wrap; }
.page-head > div:first-child { min-width: 0; flex: 1 1 24rem; }
/* the description goes BELOW the title, so a long one no longer squeezes it */
.page-head p { margin: 6px 0 0; color: var(--ink-3); font-size: 14px; max-width: 72ch; }
.btn-row { display: flex; gap: 8px; flex-wrap: wrap; align-items: center; }

/* THE COMPACT BRANDED INTRO (portal_intro(); Overview only). It carries the page's h1. Compact on purpose - the guide asks
   for "compact", not the prototype's 206px hero. The thin mark is the tenant's highlight; the photo appears only when the
   tenant's brand names one, never under a gradient and never with a caption chip on it. */
.intro { display: grid; grid-template-columns: minmax(0, 1fr); min-height: 136px; margin: 0 0 20px; overflow: hidden;
         background: var(--intro-fill); color: var(--on-intro); border-radius: var(--r-lg); }
.intro.has-photo { grid-template-columns: minmax(0, 1fr) 38%; }
.intro-copy { position: relative; display: flex; flex-direction: column; justify-content: center; gap: 4px; min-width: 0; padding: 24px 28px 24px 48px; }
.intro-copy::before { content: ""; position: absolute; left: 28px; top: 50%; width: 4px; height: 48px; margin-top: -24px; border-radius: 2px; background: var(--highlight); }
/* the principal's line is set ABOVE the credit (rule 10: "Furetank" and "Client portal" prominent, "Crew management by LT
   Marine" secondary): larger, heavier and in the band's full ink, against the sub-line's 14 / 400 in the softer ink */
.intro-kicker { margin: 0; font-size: 15px; font-weight: 600; line-height: 1.35; color: var(--on-intro); }
.intro h1 { color: var(--on-intro); }
.intro-sub { margin: 2px 0 0; font-size: 14px; color: var(--on-intro-2); }
.intro-actions { margin-top: 10px; }
/* THE PRINCIPAL'S MARK (rule 9: "the principal's name and mark"), at the right of the band: brand.logo_on_dark only - the
   tenant's own mark drawn for a dark ground (Furetank: the approved furetank-logo-inverted.png). Sized by height, one axis,
   so it keeps its proportions. A tenant that names none gets none, and the band is complete without it. */
.intro.has-mark { grid-template-columns: minmax(0, 1fr) auto; }
/* with the mark beside it the photograph takes 32%, so the sub-line keeps ONE line and the band its compact height (at 38% it
   wrapped at 1280 and the band grew from 136 to 154 px); below 1200 px there is no photograph at all (the 1199 block) */
.intro.has-mark.has-photo { grid-template-columns: minmax(0, 1fr) auto 32%; }
.intro-mark { display: flex; align-items: center; min-width: 0; padding: 20px 32px 20px 16px; }
.intro-mark img { display: block; width: auto; height: 72px; max-width: 100%; object-fit: contain; }
.intro-photo { position: relative; margin: 0; min-height: 136px; }
.intro-photo img { position: absolute; inset: 0; display: block; width: 100%; height: 100%; object-fit: cover; object-position: 50% 45%; }

/* ================================================================ 5. cards, grids, the stat strip */
.grid { display: grid; gap: var(--gap); }
/* min(360px,100%) so a 320px viewport never overflows the track floor. align-items: start, so a CARD IS ITS OWN HEIGHT (a
   viewer's one-sentence panel was once stretched to 427px beside a taller card - R7 review). */
.grid-2 { grid-template-columns: repeat(auto-fit, minmax(min(360px, 100%), 1fr)); align-items: start; }
/* The overview is not three equal cards: the fleet is the anchor. minmax(0,...) so a wide table scrolls inside its own
   cell instead of forcing the grid wider (a 1fr track's minimum is its content's min-content width). */
.grid-main { grid-template-columns: minmax(0, 1.7fr) minmax(0, 1fr); align-items: start; }
@media (max-width: 1100px) { .grid-main { grid-template-columns: minmax(0, 1fr); } }
/* Two explicit, equal columns (no auto-fit third column at 1440 - D7): from 1024, or from 1280 for the "-wide" form. */
.grid-halves, .grid-halves-wide { display: grid; gap: var(--gap); grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); align-items: start; }
/* A working column beside a fixed aside (the proposal review): from 1024. */
.grid-aside { display: grid; gap: var(--gap); grid-template-columns: minmax(0, 1fr) 380px; align-items: start; }
.stack { display: flex; flex-direction: column; gap: var(--gap); min-width: 0; }

.card { background: var(--surface); border: 1px solid var(--line); border-radius: var(--r); padding: var(--pad); min-width: 0; }
/* THE CARD HEAD. Its first child stays the title's own text node (portal-overview.spec reads h2.firstChild); a count
   follows it, and the head link sits at the right. Only the head LINK is small: a name in a heading stays heading-sized
   (the old `.card h2 a { 12.5px }` shrank the candidate's name on the proposal page, D3 - so h2.block keeps its size). */
.card h2 { display: flex; align-items: baseline; flex-wrap: wrap; gap: 4px 8px; margin-bottom: 12px; }
.card h2 .small, .card h2 .muted { font-size: 13px; font-weight: 500; color: var(--ink-3); font-variant-numeric: tabular-nums; }
.card-link, .card h2 > a:not(.btn) { margin-left: auto; display: inline-flex; align-items: center; gap: 2px; font-size: 13px; font-weight: 600; color: var(--link); line-height: 1.4; }
.card-link .ic-chevron { width: 14px; height: 14px; }
.card h2.block > a { margin-left: 0; display: inline; font-size: inherit; font-weight: inherit; line-height: inherit; color: var(--heading); }
.card-sub { margin: -6px 0 12px; font-size: 13px; color: var(--ink-3); }
.card-foot { margin: 12px 0 0; padding-top: 10px; border-top: 1px solid var(--line-2); font-size: 12.5px; color: var(--ink-3); }
.card > :last-child { margin-bottom: 0; }

/* THE STAT STRIP (.fleet-strip; the class and its "7 vessels" text are read by the specs). Four labelled figures, each a
   link to the screen that lists it. A 1px gap over a line-coloured ground draws every divider, including between wrapped
   rows. Four across when the canvas has 700px, two below. Markup: a.stat > b.n + " " + span.l + span.s (the older
   `<a><b>7</b> vessels</a>` renders the same way). */
.fleet-strip { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 1px; margin: 20px 0; overflow: hidden;
               background: var(--line-2); border: 1px solid var(--line); border-radius: var(--r); }
.fleet-strip > a { display: block; min-width: 0; padding: 16px 20px; background: var(--surface); color: var(--ink); font-size: 14px; font-weight: 600; line-height: 1.35; text-decoration: none; }
.fleet-strip > a:hover { background: var(--hover); text-decoration: none; }
.fleet-strip > a:hover .l { text-decoration: underline; text-underline-offset: 2px; }
.fleet-strip > a:last-child:nth-child(odd) { grid-column: 1 / -1; }
.fleet-strip b { display: block; margin-bottom: 4px; color: var(--heading); font-size: var(--fs-stat); font-weight: 600; line-height: 1.1; font-variant-numeric: tabular-nums; }
.fleet-strip .l { font-weight: 600; color: var(--ink); }
.fleet-strip .s { display: block; margin-top: 2px; font-size: 12.5px; font-weight: 400; color: var(--ink-3); }
.fleet-strip .s-bad { color: var(--bad); font-weight: 600; }   /* overdue is the one red (R2) */
@container main (min-width: 700px) {
  .fleet-strip { grid-template-columns: repeat(4, minmax(0, 1fr)); }
  .fleet-strip > a:last-child:nth-child(odd) { grid-column: auto; }
}

/* ================================================================ 6. tables */
.tbl-wrap { overflow-x: auto; }
table.tbl { width: 100%; border-collapse: collapse; font-size: 14px; }
.tbl th { text-align: left; font-size: 12px; font-weight: 600; color: var(--ink-3); background: var(--surface-2); padding: 10px 12px;
          border-top: 1px solid var(--line); border-bottom: 1px solid var(--line); white-space: nowrap; vertical-align: bottom; }
.tbl td { padding: 12px; border-bottom: 1px solid var(--line-2); vertical-align: middle; }
.tbl tbody tr:last-child td { border-bottom: 0; }
.tbl .num { text-align: right; font-variant-numeric: tabular-nums; }
.tbl .date { white-space: nowrap; font-variant-numeric: tabular-nums; }
.tbl td > a > strong, .tbl td > strong > a { font-weight: 600; }
/* a row that leads somewhere answers the pointer */
.tbl tbody tr:not(.group-head):has(a[href]):hover > td { background-color: var(--surface-2); }
/* deliberate column widths: <colgroup><col class="w-date">... (no inline style - the CSP forbids it) */
col.w-num { width: 5.5rem; } col.w-days { width: 5rem; } col.w-short { width: 6rem; } col.w-act { width: 8.5rem; }
col.w-date { width: 9.5rem; } col.w-ctr { width: 9rem; } col.w-when { width: 11rem; } col.w-until { width: 12rem; } col.w-status { width: 13rem; }
/* ISSUE-724, 1 Oct 2026 (owner decision, "tables on phones and tablets"): A WIDE TABLE SAYS SO AND KEEPS THE SEAFARER IN
   VIEW. /assets/table_cue.js (a copy of the office's js/table_cue.js, kept identical by the deployability self-test) marks a
   table .tbl-wide only while it is wider than its box and puts one line of words above it; here the line, and the first
   column (the seafarer) and an action column held in place while the rest scrolls. The held cells are painted the card's
   own white and a hairline divides them. Below 640 px the tables are labelled lists and have nothing to scroll. */
.table-cue { margin: 0 0 6px; font-size: 12px; color: var(--ink-3); }
table.tbl-pin-start > * > tr > :first-child:not([colspan]),
table.tbl-pin-2 > * > tr > :nth-child(2):not([colspan]),
table.tbl-pin-end > * > tr > :last-child:not([colspan]) { position: sticky; z-index: 1; white-space: normal; background-color: var(--surface); }
table.tbl-pin-start > thead > tr > :first-child:not([colspan]),
table.tbl-pin-2 > thead > tr > :nth-child(2):not([colspan]),
table.tbl-pin-end > thead > tr > :last-child:not([colspan]) { background-color: var(--surface-2); }
table.tbl-pin-start > * > tr > :first-child:not([colspan]) { left: 0; }
table.tbl-pin-2 > * > tr > :nth-child(2):not([colspan]) { left: 0; }
table.tbl-pin-end > * > tr > :last-child:not([colspan]) { right: 0; }
table.tbl-pin-start > * > tr > :first-child:not([colspan])::after,
table.tbl-pin-2 > * > tr > :nth-child(2):not([colspan])::after,
table.tbl-pin-end > * > tr > :last-child:not([colspan])::after { content: ''; position: absolute; top: 0; bottom: 0; width: 0; pointer-events: none; }
table.tbl-pin-start:not(.tbl-pin-2) > * > tr > :first-child:not([colspan])::after,
table.tbl-pin-2 > * > tr > :nth-child(2):not([colspan])::after { right: 0; border-right: 1px solid var(--line); }
table.tbl-pin-end > * > tr > :last-child:not([colspan])::after { left: 0; border-left: 1px solid var(--line); }
table.tbl-wide tr.group-head .gh { display: inline-block; position: sticky; left: 12px; }   /* the vessel's name, held too */
@media print { .table-cue { display: none; } }
/* the entity cell: an avatar where the row is a person, the name as a link, a sub-line. One link colour for every entity. */
.person { display: flex; align-items: center; gap: 10px; min-width: 0; }
.person a { font-weight: 600; color: var(--link); }
.person .sub, .tbl .sub { display: block; font-size: 12.5px; color: var(--ink-3); }
/* group heads (kept: the specs and the filter scripts use tr.group-head) */
.group-head td { background: var(--surface-2); font-size: 13px; font-weight: 600; color: var(--heading); padding: 8px 12px; }
.group-head .small, .group-head .muted { font-weight: 400; color: var(--ink-3); }
.group-head .ic { color: var(--heading); margin-right: 4px; }

/* ================================================================ 7. avatars, status words */
.av { display: inline-flex; width: 32px; height: 32px; border-radius: 50%; overflow: hidden; background: var(--surface-2); border: 1px solid var(--line-2);
      align-items: center; justify-content: center; flex: none; vertical-align: middle; }
.av img { width: 100%; height: 100%; object-fit: cover; display: block; }
.av-init { font-weight: 600; font-size: 12px; color: var(--ink-2); line-height: 1; }
.av-pane { width: 56px; height: 56px; } .av-pane.av-init { font-size: 18px; }
.av-lg { width: 64px; height: 64px; } .av-lg.av-init { font-size: 20px; }
/* STATUS IS A WORD, NOT A COLOURED TABLET (R2). No backgrounds, no borders, no padding: ordinary ink for "in order", darker
   ink and weight for "attention soon" and "short", red ONLY where something has lapsed or is overdue. The class names are
   untouched, so every page and every test that names a chip still works (portal-responsive.spec: every .chip is
   transparent, and a chip in rgb(185, 28, 28) is .chip-expired or .chip-overdue). */
.chip, .pill { display: inline-block; padding: 0; border: 0; border-radius: 0; background: none; font-size: 13px; font-weight: 500; white-space: nowrap; line-height: 1.4; color: var(--ink-2); }
.chip-ok { color: var(--ink-3); font-weight: 500; }
.chip-none { color: var(--ink-3); font-weight: 400; }
.chip-warn { color: var(--ink-2); font-weight: 500; }
.chip-missing { color: var(--ink-2); font-weight: 500; }   /* a paper not on file (ISSUE-401) */
.chip-soon { color: var(--ink); font-weight: 600; }
.chip-expired, .chip-overdue { color: var(--bad); font-weight: 600; }
.pill { color: var(--ink-2); }
.pill-short { color: var(--ink); font-weight: 600; }       /* short of complement = ink + weight, no longer red (R2, D17) */
.pill-sent { color: var(--ink); font-weight: 600; }
.pill-accepted, .pill-on, .pill-off, .pill-expired { color: var(--ink-2); font-weight: 600; }
.pill-declined, .pill-withdrawn { color: var(--ink-3); font-weight: 400; }
.ref { font-family: var(--mono); font-size: 12px; color: var(--ink-3); }
.doc-list { margin: 0 0 8px; padding-left: 18px; } .doc-list li { margin: 3px 0; }

/* ================================================================ 8. forms, buttons, notices, empty states */
.field { display: flex; flex-direction: column; gap: 6px; margin-bottom: 14px; }
.field label { font-size: 13px; font-weight: 600; color: var(--ink-2); }
.field label .muted { font-weight: 400; }
.field input, .field textarea, .field select { font: inherit; font-size: 14px; min-height: 40px; padding: 9px 12px; border: 1px solid var(--field-line); border-radius: var(--r-sm); background: var(--surface); color: var(--ink); width: 100%; }
.field textarea { min-height: 84px; resize: vertical; }
.field input:focus, .field textarea:focus, .field select:focus { outline: 2px solid var(--accent-strong); outline-offset: 1px; border-color: var(--accent-strong); }
.btn { display: inline-flex; align-items: center; justify-content: center; gap: 8px; min-height: 36px; padding: 8px 16px; font: inherit; font-size: 14px; font-weight: 600; line-height: 1.25;
       border-radius: var(--r-sm); border: 1px solid var(--accent-strong); background: var(--accent-strong); color: #fff; cursor: pointer; text-decoration: none; }
.btn:hover { filter: brightness(.94); text-decoration: none; }
.btn-accent { background: var(--accent-strong); border-color: var(--accent-strong); color: #fff; }
.btn-ghost { background: var(--surface); color: var(--ink); border-color: var(--btn-line); }
.btn-ghost:hover { background: var(--hover); filter: none; }
.btn-danger { background: var(--surface); color: var(--ink); border-color: var(--btn-line); }   /* retired from use: no red buttons (R2) */
.btn-sm { min-height: 32px; padding: 5px 12px; font-size: 13px; }
.btn-dl { min-width: 7.5rem; }
.linkbtn { background: none; border: 0; padding: 0; color: var(--link); font: inherit; font-size: 13px; cursor: pointer; text-decoration: underline; text-underline-offset: 2px; }
/* NOTICES: white, one hairline and a 3px rule that says the kind - no tinted grounds (R2). role="alert" stays on the bad one. */
.notice { padding: 11px 14px; border: 1px solid var(--line); border-left: 3px solid var(--line); border-radius: var(--r); background: var(--surface); color: var(--ink); margin-bottom: 16px; font-size: 14px; }
.notice strong:first-child { font-weight: 600; }
.notice-ok { border-left-color: var(--heading); }
.notice-info { border-left-color: var(--accent-strong); }
.notice-bad { border-left-color: var(--bad); }
/* EMPTY STATES: inside a card, words where the table would start (no box in a box); standing alone on a page (a 404, the
   viewer's Activity), a card of their own. Every page keeps its sentences word for word (R8). */
.empty { padding: 20px 0; color: var(--ink-3); font-size: 14px; }
.main > .empty { background: var(--surface); border: 1px solid var(--line); border-radius: var(--r); padding: 28px; }
.empty .empty-title { display: block; margin-bottom: 4px; font-size: 15px; font-weight: 600; color: var(--ink); }

/* ================================================================ 9. filter bar, search, tabs, section index */
.filterbar { display: flex; gap: 10px; align-items: center; margin-bottom: 14px; flex-wrap: wrap; }
.filterbar input[type="search"], .search input { font: inherit; font-size: 14px; height: 40px; padding: 0 12px; border: 1px solid var(--field-line); border-radius: var(--r-sm);
                                                 background: var(--surface); color: var(--ink); min-width: 0; }
.filterbar > input[type="search"] { flex: 1 1 22rem; max-width: 30rem; }
/* the search field with its icon: <label class="search">{icon}<input type="search" id="crew-q" ...></label> */
.search { position: relative; display: flex; align-items: center; flex: 1 1 22rem; max-width: 30rem; min-width: 0; margin: 0; }
.search .ic { position: absolute; left: 12px; width: 16px; height: 16px; pointer-events: none; }
.search input, .filterbar .search input[type="search"] { width: 100%; padding-left: 36px; }
/* An empty field has nothing to clear: Chromium reserves its clear button's room even then, which cut wave twelve's ISSUE-802 placeholder
   ('...by name or vesse') at 390 px beside the 36 px icon. The button returns as soon as there is text. */
.search input[type="search"]:placeholder-shown::-webkit-search-cancel-button, .filterbar input[type="search"]:placeholder-shown::-webkit-search-cancel-button { display: none; }
.filterbar input[type="search"]:focus, .search input:focus { outline: 2px solid var(--accent-strong); outline-offset: 1px; border-color: var(--accent-strong); }
.filterbar .count { font-size: 13px; color: var(--ink-3); }
.filterbar .fb-end { margin-left: auto; }
/* FILTER TABS: links (every filter stays a URL and works without scripting), or buttons in a tablist where they only switch
   a group. The selected one by aria-current / aria-selected / .on: heading colour, weight and a 2px underline. A count is a
   plain numeral (.n) - no tinted count box (R2). */
.ftabs { display: flex; flex-wrap: wrap; gap: 0 22px; margin: 0 0 16px; padding: 0; border-bottom: 1px solid var(--line); list-style: none; }
.ftabs a, .ftabs button { display: inline-flex; align-items: center; gap: 6px; min-height: 40px; padding: 8px 2px; margin-bottom: -1px; font: inherit; font-size: 14px; font-weight: 500;
                          color: var(--ink-2); background: none; border: 0; border-bottom: 2px solid transparent; cursor: pointer; text-decoration: none; white-space: nowrap; }
.ftabs a:hover, .ftabs button:hover { color: var(--ink); text-decoration: none; }
.ftabs [aria-current="page"], .ftabs [aria-current="true"], .ftabs [aria-selected="true"], .ftabs .on { color: var(--heading); font-weight: 600; border-bottom-color: var(--accent-strong); }
.ftabs .n { font-size: 13px; font-weight: 400; color: var(--ink-3); font-variant-numeric: tabular-nums; }
.sec-index { margin: 0 0 14px; font-size: 13px; color: var(--ink-3); }
.sec-index a { font-weight: 500; }
/* Named classes, because the portal's CSP forbids a style attribute (UI sweep, 9 Sep 2026). */
.pager { margin: 0 0 10px; }
.board-spaced { margin-top: 12px; }
/* A FILTERED-OUT ROW STAYS OUT, AT EVERY WIDTH — and this needs !important, which is not decoration here. `display: none` on
   `tr.hide` has the same specificity as `.tbl tr { display: block }` in the phone block below, and that rule comes later,
   so it won: below 640 px the search counted correctly and then showed the rows it had just excluded (R7-UI-01). This is a
   STATE rule and the phone block is a LAYOUT rule; the state has to outrank it. */
tr.hide, tfoot.hide, tbody.hide, thead.hide, section.card.hide,
.tbl tr.hide, .tbl tbody.hide, .tbl tfoot.hide { display: none !important; }

/* ================================================================ 10. proposals: the board, the queue */
.board { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(290px, 100%), 1fr)); gap: 16px; align-items: start; }
.col { min-width: 0; }
.col-head { display: flex; align-items: center; justify-content: space-between; gap: 8px; background: var(--surface); border: 1px solid var(--line); border-radius: var(--r); padding: 12px 14px; margin-bottom: 10px; }
.col-head strong { font-size: 14px; color: var(--heading); }
.col-head .cnt { font-size: 13px; font-weight: 600; color: var(--ink-3); font-variant-numeric: tabular-nums; }
.col-head.hot { box-shadow: none; }   /* waiting is work, not danger: no red rule (D16) */
.kcard { position: relative; background: var(--surface); border: 1px solid var(--line); border-radius: var(--r); padding: 14px 16px; margin-bottom: 10px; }
/* the selected queue item: the ground and a 3px rule in the action colour (yellow is reserved for the sidebar - R5) */
.kcard.is-selected, .kcard[aria-current="true"] { background: var(--selected); box-shadow: inset 3px 0 0 var(--accent-strong); }
.kcard-top { display: flex; align-items: center; gap: 8px; margin-bottom: 8px; flex-wrap: wrap; }
.kcard-top .when { margin-left: auto; font-size: 12px; color: var(--ink-3); white-space: nowrap; }
.kcard .who { display: flex; align-items: center; gap: 10px; margin-bottom: 8px; }
.kcard .who strong { font-size: 14.5px; color: var(--ink); }
/* the sub-line under the name - NOT the avatar span, whose centring display:block broke ("JM", "VR" clipped - D4) */
.kcard .who > div > span, .kcard .who .sub { display: block; font-size: 12.5px; color: var(--ink-3); }
.kcard .line { font-size: 13px; color: var(--ink-2); margin: 2px 0; }
.kcard .line b { color: var(--ink); font-weight: 600; }
.kcard .actions { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; margin-top: 12px; padding-top: 12px; border-top: 1px solid var(--line-2); }
details.more > summary { list-style: none; cursor: pointer; padding: 12px 14px; border: 1px solid var(--line); border-radius: var(--r); color: var(--ink-2); font-size: 13px; font-weight: 500; text-align: center; background: var(--surface); }
details.more > summary::-webkit-details-marker { display: none; }
details.more > summary:hover { background: var(--hover); }
details.more[open] > summary { margin-bottom: 10px; }

/* ================================================================ 11. bars: contract progress, crew coverage */
/* Width comes from the pw-* utility (10% steps) — the CSP forbids style attributes. */
.ctr { display: flex; align-items: center; gap: 8px; white-space: nowrap; }
.ctr-bar { width: 56px; height: 4px; border-radius: 2px; background: var(--line); overflow: hidden; flex: none; }
.ctr-bar i { display: block; height: 100%; background: var(--ink-3); }   /* a length, not a brand ribbon */
.ctr-bar.over i { background: var(--bad); }                             /* over the tour: the one red that belongs */
.pw-0{width:0}.pw-10{width:10%}.pw-20{width:20%}.pw-30{width:30%}.pw-40{width:40%}.pw-50{width:50%}
.pw-60{width:60%}.pw-70{width:70%}.pw-80{width:80%}.pw-90{width:90%}.pw-100{width:100%}
/* GRAPHIC 1: crew coverage against the declared complement. The sentence beside it is the fact; the bar is an aid, the
   heading colour at every level - short is said in words ("· 1 short"), no longer a red fill (R2, D17). */
.cov { display: flex; align-items: center; gap: 10px; text-decoration: none; color: inherit; min-width: 0; }
.cov:hover { text-decoration: none; }
.cov:hover .cov-txt { text-decoration: underline; text-underline-offset: 2px; }
.cov-bar { flex: 0 0 84px; height: 8px; border: 1px solid var(--line); border-radius: 999px; background: var(--bg); overflow: hidden; }
.cov-fill { display: block; height: 100%; background: var(--heading); }
.cov-fill.cov-short { background: var(--heading); }
.cov-txt { font-size: 13.5px; color: var(--ink-2); white-space: nowrap; }
/* A VESSEL WITH NO DECLARED COMPLEMENT STILL LINES UP. No bar (an empty track would read as "nobody aboard"); an empty box
   of the bar's width holds its place (R6 review, item 5). */
.cov-none { display: flex; align-items: center; gap: 10px; min-width: 0; }
.cov-none::before { content: ""; flex: 0 0 84px; }
/* ISSUE-710, 30 Sep 2026: in the coverage table the header and the sentence may wrap, and the sentence drops under its bar
   when the column is narrow, so Joining stays inside the card at 1101-1330 px. */
.tbl-cov th { white-space: normal; }
.tbl-cov .cov, .tbl-cov .cov-none { flex-wrap: wrap; row-gap: 4px; }
.tbl-cov .cov-txt { white-space: normal; }

/* ================================================================ 12. profile, facts */
.profile-head { display: flex; gap: 16px; align-items: center; margin-bottom: 20px; }
.profile-head h1 { margin-bottom: 2px; }
.profile-head .muted { font-size: 14px; }
/* ONE TREATMENT FOR A SECONDARY LABEL, AND IT IS NOT LETTER-SPACED CAPITALS: the same as a table heading - 12px, 600, muted,
   sentence case (portal-presentation.spec: no transform, weight >= 600, size <= 12.5px). */
.facts { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(10rem, 100%), 1fr)); gap: 14px 24px; margin: 0; }
.facts div { min-width: 0; }
.facts dt { font-size: 12px; font-weight: 600; color: var(--ink-3); }
.facts dd { margin: 3px 0 0; font-size: 14px; font-weight: 500; color: var(--ink); font-variant-numeric: tabular-nums; }

/* ================================================================ 13. the Overview briefing, movements, documents */
/* --- a briefing row: who, why, what to do --- */
.brief + .brief { margin-top: var(--gap); }
.brief-none { color: var(--ink-3); margin: 0; }
.brief-list { list-style: none; margin: 0; padding: 0; }
.brief-list li { display: grid; grid-template-columns: minmax(0, 1.5fr) minmax(0, 1.4fr) auto; gap: 12px; align-items: center; padding: 12px 0; border-top: 1px solid var(--line-2); }
.brief-list li:first-child { border-top: 0; padding-top: 4px; }
.bl-who { display: flex; align-items: center; gap: 10px; min-width: 0; }
.bl-who a { font-weight: 600; }
.bl-who .sub { display: block; color: var(--ink-3); font-size: 12.5px; }
/* tabular figures so the dates down this column line up on their digits (R6 review, item 3) */
.bl-why { color: var(--ink-2); font-size: 13.5px; min-width: 0; font-variant-numeric: tabular-nums; }
.bl-do { justify-self: end; }
/* the same row in a narrow side column: who over why, the action at the right (.brief-list.is-narrow) */
.brief-list.is-narrow li { grid-template-columns: minmax(0, 1fr) auto; grid-template-areas: "who do" "why do"; row-gap: 4px; }
.brief-list.is-narrow .bl-who { grid-area: who; }
.brief-list.is-narrow .bl-why { grid-area: why; padding-left: 42px; }
.brief-list.is-narrow .bl-do { grid-area: do; align-self: center; }
/* --- graphic 2: the movements arranged for the next 60 days --- */
.mv-vessel { display: flex; align-items: center; gap: 6px; font-size: 13px; font-weight: 600; color: var(--heading); margin: 14px 0 4px; padding-bottom: 6px; border-bottom: 1px solid var(--line-2); }
.mv-vessel:first-child { margin-top: 0; }
.mv-vessel .ic { color: var(--heading); }
.mv-list { list-style: none; margin: 0; padding: 0; }
.mv-list li { display: grid; grid-template-columns: 6.5rem minmax(0, 1fr); gap: 10px; padding: 6px 0; align-items: baseline; }
.mv-date { color: var(--ink-2); font-size: 13px; font-variant-numeric: tabular-nums; }
.mv-what { font-size: 13.5px; min-width: 0; }
.mv-what .sub { color: var(--ink-3); font-size: 12.5px; margin-left: 6px; }
/* a direction marker that is a SHAPE, not a colour: joining points in, signing off points out */
.mv-dir { display: inline-block; width: 0; height: 0; margin-right: 6px; vertical-align: middle; border-top: 4px solid transparent; border-bottom: 4px solid transparent; }
.mv-dir.on  { border-left: 6px solid var(--heading); }
.mv-dir.off { border-right: 6px solid var(--ink-3); }
/* --- a shared file: the kind icon and the title are ONE value (on a phone each cell is a label/value grid, and a loose
   icon would have been a third column) --- */
.doc-name .dn { display: flex; align-items: flex-start; gap: 8px; min-width: 0; font-weight: 500; }
.doc-name .dn > span { min-width: 0; }
.doc-name .ic { width: 16px; height: 16px; margin-top: 2px; }

/* ================================================================ 14. sign-in (outside the shell) */
/* THE QUIET CENTRED CARD (owner, 4 Sep 2026). In the tenant's brand - a mark large enough to read, the navy heading, the
   blue button - and nothing else: no photo, no split, no hero, no highlight mark (R4). */
.login-wrap { min-height: 100vh; display: flex; align-items: center; justify-content: center; padding: 32px 16px; background: var(--bg); }
.login-card { width: 100%; max-width: 420px; background: var(--surface); border: 1px solid var(--line); border-radius: var(--r-lg); padding: 32px 32px 24px; }
.login-card .logo { display: inline-block; padding: 0 0 20px; border: 0; }
/* HEIGHT, one axis, one place (the mark was 52px and its wordmark barely legible - D11) */
.login-card .logo img { display: block; width: auto; height: 80px; max-width: 100%; object-fit: contain; }
.login-card .logo-text { font-size: 22px; }
.login-card h1 { font-size: 22px; line-height: 1.25; margin: 0 0 4px; }
.login-card .who { color: var(--ink-2); font-size: 15px; font-weight: 600; margin: 0 0 6px; }
.login-card > p.muted { color: var(--ink-3); margin: 0 0 18px; }
.login-card .btn { width: 100%; min-height: 44px; margin-top: 6px; }
.login-card .field input { min-height: 44px; }
.login-foot { text-align: center; font-size: 12px; line-height: 1.55; color: var(--ink-4); margin: 22px 0 0; }
.login-foot a { color: var(--link); }
/* the two-step screens (moved here from login.php's own <style>) */
.mfa-steps { margin: 0 0 14px; padding-left: 20px; font-size: 14px; line-height: 1.5; } .mfa-steps li { margin: 4px 0; }
.mfa-qr { display: flex; justify-content: center; margin: 4px 0 14px; } .mfa-qr svg { display: block; border: 1px solid var(--line); border-radius: var(--r-sm); }
.mfa-key { font-size: 13px; margin: 0 0 14px; }
.mfa-key code { display: inline-block; margin-top: 6px; padding: 6px 10px; border-radius: 4px; background: var(--surface-2); border: 1px solid var(--line-2); font-family: var(--mono); font-size: 14px; }
/* the one tracking left in the portal: the digits of the one-time code */
#code { font-size: 22px; letter-spacing: .25em; text-align: center; }

/* THE SPLIT SIGN-IN (owner, chat, 2 Oct 2026: "we can use any image or photo from furetank as they are our clients, if needed
   to modernize the design of their login page lets do it too"). ONLY for a tenant whose brand names a sign-in photo: login.php
   adds .login-split when portal_brand_photo($b, 'photo') validates; every other tenant keeps the quiet card above, untouched.
   The reviewed prototype's split, in the house rules: a white pane with the SAME form (the company as a sentence-case eyebrow,
   the h1, one factual sentence), and the photograph under a FLAT scrim - no gradient - with the principal's name, one factual
   line and the tenant's highlight as the one identifying rule. The form is FIRST in the document and on every screen: from 960
   px the photograph stands beside it (and stays in view while a tall two-step screen scrolls); below 960 it is a short band
   AFTER the form, so a phone reaches the fields without scrolling past a picture. Colours are the tenant's tokens only. */
.login-wrap.login-split { display: grid; grid-template-columns: minmax(0, 1fr); grid-template-rows: 1fr auto; align-items: stretch; justify-content: normal;
                          min-height: 100vh; padding: 0; background: var(--surface); }
.login-pane { display: flex; align-items: center; justify-content: center; min-width: 0; padding: 32px 24px 28px; }   /* a tall tablet: the form in the middle of the room above the band */
.login-split .login-card { display: flex; flex-direction: column; max-width: 400px; padding: 0; border: 0; border-radius: 0; background: none; }
.login-split .login-card .logo { align-self: flex-start; padding: 0 0 28px; }
.login-split .login-card .logo img { height: 80px; }
/* the eyebrow: the company, sentence case, weight and the link colour carry it - no letter-spaced capitals (R3) */
.login-split .login-card .login-eyebrow { margin: 0 0 6px; color: var(--link); font-size: 15px; font-weight: 600; line-height: 1.35; }
/* room for the logo's focus ring (2 px at a 2 px offset): in this flex column the logo has no line box under it as it has in the
   card, and the ring crossed the eyebrow (or a two-step h1) by 4 px. A top margin on what follows, so it holds where the auto
   margin from 960 px has no free height to give (a short laptop screen) */
.login-split .login-card .logo + * { margin-top: 6px; }
.login-split .login-card h1 { margin: 0 0 12px; font-size: 30px; line-height: 1.15; letter-spacing: -0.015em; }
.login-lede { margin: 0 0 22px; color: var(--ink-2); font-size: 15px; line-height: 1.55; text-wrap: pretty; }
.login-split .login-card > p.muted { margin: 0 0 16px; }
.login-split .login-foot { margin-top: 24px; text-align: left; }
/* the photograph: the img fills its box (object-fit: cover); ::after is the flat scrim. --scrim is printed by login.php: the
   tenant's primary darkened until white on it reaches 12:1, so at .72 the white words keep 4.5:1 even over a white pixel. */
/* The caption is IN THE FLOW at the foot of the box, not absolutely placed: a long principal's name then makes the band taller
   instead of climbing out of it (a 70-character name at 320 px did, by 33 px, when the caption was bottom-anchored). */
.login-photo { position: relative; display: flex; flex-direction: column; justify-content: flex-end; min-width: 0; min-height: 152px; padding: 28px 24px;
               overflow: hidden; background: var(--scrim, var(--intro-fill)); }
/* the file is a portrait-leaning crop at the source's full height (1100x1080: drawn 1:1 at 1920x1080, not upscaled). Below 960 the
   band shows the deck (70%: no lettering, no hull name to cut); the 960 block frames the ship so the hull name is whole and clear of
   the caption */
.login-photo img { position: absolute; inset: 0; display: block; width: 100%; height: 100%; object-fit: cover; object-position: 82% 70%; }
.login-photo::after { content: ""; position: absolute; inset: 0; background: var(--scrim, var(--intro-fill)); opacity: .72; }
.login-caption { position: relative; z-index: 1; min-width: 0; padding-left: 16px; border-left: 4px solid var(--highlight); color: #fff; overflow-wrap: anywhere; }
.login-caption-name { margin: 0; color: #fff; font-size: 22px; font-weight: 600; line-height: 1.2; letter-spacing: -0.01em; }
.login-caption-line { margin: 4px 0 0; color: #fff; font-size: 14px; line-height: 1.45; }
@media (min-width: 960px) {
  .login-wrap.login-split { grid-template-columns: minmax(0, 46fr) minmax(0, 54fr); grid-template-rows: none; }
  .login-pane { align-items: stretch; padding: 48px clamp(40px, 6vw, 112px); }
  /* the mark at the top and the form in the middle of the room below it; the reset and operator's lines stay under the button
     they belong to (two auto margins share the free height) */
  .login-split .login-card { min-height: calc(100vh - 96px); }
  .login-split .login-card .logo { margin-bottom: auto; padding-bottom: 40px; }
  /* the mark at the prototype's size in the wide white pane (at 80 px the FURETANK wordmark in it was 10 px tall); the file is
     2x of 220x147, so it stays sharp. The short-screen step below brings it back to 80. */
  .login-split .login-card .logo img { height: 120px; }
  .login-split .login-card h1 { font-size: 34px; }
  .login-split .login-card > :last-child { margin-bottom: auto; }
  .login-photo { position: sticky; top: 0; align-self: start; height: 100vh; min-height: 0; padding: 48px 48px 64px 56px; }
  /* 82% 100%: the hull name ("FURE VASA", which the alt names) is WHOLE, never cut, and clear of the caption at every common screen
     from 960 to 2560 (at 26% 50% it read "FURE V" at 1280 and 1440; at one position for all widths it ran into the caption at 960
     and 1024) - measured by scratch/corr-photo/probe.js against the drawn caption */
  .login-photo img { object-position: 82% 100%; }
  .login-caption { padding-left: 24px; }
  .login-caption-name { font-size: 40px; line-height: 1.15; letter-spacing: -0.015em; }
  .login-caption-line { margin-top: 10px; font-size: 15px; }
  /* two lines - "Client portal" / "Crew management by LT Marine" - so the caption is narrow enough to leave the hull name its room
     in a 518 px column; the band below 960 keeps one line (and its height) */
  .login-caption-sep { display: none; }
  .login-caption-by { display: block; }
}
@media (min-width: 1680px) {
  .login-caption-name { font-size: 44px; }
}
/* A SHORT LAPTOP SCREEN (1366x657, 1280x600, 1024x600 - a browser with its bars on a 768 px panel): the pane's padding, the gap
   under the mark and the mark itself give way, so the page fits where the quiet card did (it measured 714 px at 1366x657, the
   card 660) and the Sign in button stays on the first screen at 600 px. The narrower panes (960-1279) wrap more, so they step
   down from a taller screen. */
@media (min-width: 960px) and (max-height: 760px), (min-width: 960px) and (max-width: 1279px) and (max-height: 820px) {
  .login-pane { padding-top: 24px; padding-bottom: 24px; }
  .login-split .login-card { min-height: calc(100vh - 48px); }
  .login-split .login-card .logo { padding-bottom: 20px; }
  .login-split .login-card .logo img { height: 80px; }
}

/* ================================================================ 15. widths
   Desktop-first. Base = 1280-1679. Named widths checked: 320, 360, 390, 640 | 641, 768, 900 | 901, 1024, 1100, 1199, 1200 |
   1280, 1440, 1920. Nothing may scroll sideways between them. */

/* ---- 1680 and up: a wider rail, a wider canvas ---- */
@media (min-width: 1680px) {
  :root { --side-w: 264px; --gutter: 40px; --canvas-max: 1600px; --queue-w: 360px; }
  .grid-aside { grid-template-columns: minmax(0, 1fr) 400px; }
}

/* ---- 1279 and down: the laptop ---- */
@media (max-width: 1279px) {
  :root { --side-w: 232px; --gutter: 24px; --gap: 16px; --pad: 16px 18px; --queue-w: 280px; --mark-h: 80px; --fs-h1: 26px; }
  .tbl th { padding: 10px; }
  .tbl td { padding: 10px; }
  .grid-halves-wide { grid-template-columns: minmax(0, 1fr); }
  /* where you are: the last two crumbs only */
  .where li:not(:nth-last-child(-n+2)) { display: none; }
  .where li:nth-last-child(2)::before { content: none; }
  .where li:nth-last-child(2):first-child a, .where li:nth-last-child(2) a { color: var(--heading); font-weight: 600; }
  /* THE ACCOUNT'S NAME GIVES WAY, ITS ROLE STAYS (a viewer must see they cannot decide - 9 Sep). Correction round 1: from
     here down (it was from 1023) - the name is in the initials disc and, for a screen reader, the link's own words - so the
     crumbs keep room at 1024-1279, where the viewer's bar was 149 px and cut "Fleet & crew / Expiry register". */
  .user-text strong { position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0; overflow: hidden; clip-path: inset(50%); white-space: nowrap; border: 0; }
  .intro-mark img { height: 64px; }
}

/* ---- 901 to 1279, where the bar holds crumbs: the role sentence takes two balanced lines instead of one long one (every word
   kept), so "Viewer · can view; an administrator decides" no longer takes the crumbs' room ---- */
@media (min-width: 901px) and (max-width: 1279px) {
  .may { max-width: 8.5rem; white-space: normal; text-wrap: balance; line-height: 1.3; }   /* a wrapped block keeps its max-width, so it is set to the two lines' own length */
}

/* ---- 1199 and down: the intro's photograph gives way (2 Oct 2026). Beside the copy and the mark it would squeeze the sub-line onto
   two lines and the band would grow - the owner's "avoid large banners" - so the band is the filled panel it is without one. The
   img is loading="lazy" (portal_intro()), so a screen where it is not drawn does not download it. ---- */
@media (max-width: 1199px) {
  .intro.has-photo { grid-template-columns: minmax(0, 1fr); }
  .intro.has-mark.has-photo { grid-template-columns: minmax(0, 1fr) auto; }
  .intro-photo { display: none; }
}

/* ---- 1023 and down ---- */
@media (max-width: 1023px) {
  :root { --gutter: 20px; }
  .grid-halves, .grid-aside { grid-template-columns: minmax(0, 1fr); }
  /* where you are, 901-1023: the page or record alone - the sidebar beside it already marks the section, and two crumbs here
     cut both (correction round 1: the viewer's bar was 34 px at 901). Hidden at 900 and below, as before. */
  .where li:not(:last-child) { display: none; }
  .where li:last-child::before { content: none; }
}

/* ---- 900 and down: the sidebar becomes a band above the page ---- */
@media (max-width: 900px) {
  :root { --gutter: 18px; --mark-h: 36px; --fs-h1: 22px; }
  /* minmax(0, 1fr), not 1fr: a 1fr track's automatic minimum is its content's min-content width, and that once made every
     page 880 px wide on a phone (owner, 8 Sep 2026). auto 1fr, not two automatic rows: on a SHORT page (a refusal, a 404)
     the band was stretched to share the viewport and the menu swallowed the slack (final QA QA-008). */
  .app { grid-template-columns: minmax(0, 1fr); grid-template-rows: auto 1fr; }
  .app::before { display: none; }
  .side { grid-column: 1; grid-row: 1; position: static; height: auto; flex-direction: row; flex-wrap: wrap; align-items: center; column-gap: 12px;
          background: var(--surface); border-bottom: 1px solid var(--line); min-width: 0; max-width: 100%; }
  .body { grid-column: 1; grid-row: 2; }
  .side, .main, .topbar, .side nav { min-width: 0; max-width: 100%; }
  /* the brand row is ONE line: the mark beside "Client portal / Furetank" (D19: the stacked block cost 290-313 px) */
  .side .logo { flex: none; padding: 6px 0 6px var(--gutter); }
  .side .logo img { height: var(--mark-h); max-width: 40vw; }
  .logo-text { font-size: 16px; }
  .side-who { flex: 1 1 auto; min-width: 0; padding: 0; margin: 0; border-bottom: 0; font-size: 12px; line-height: 1.25; }
  .side-who b { font-size: 14px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
  /* WRAP, not a scrolling row: at 760 px a one-row strip put Proposals and Documents past the right edge with nothing to
     say they were there (R6 review, item 6). The row still scrolls if a tenant ever has more than fits. */
  .side nav { flex: 1 0 100%; display: flex; flex-wrap: wrap; gap: 4px; overflow-x: auto; overflow-y: visible; padding: 4px calc(var(--gutter) - 8px) 6px; border-top: 1px solid var(--line-2); }
  .side-sec { display: none; }
  .side-item { flex: 0 0 auto; white-space: nowrap; min-height: 40px; padding: 8px 12px; gap: 8px; }
  .side-item .ic-nav { display: none; }
  .side-item.on::before { left: 12px; right: 12px; top: auto; bottom: 3px; width: auto; height: 3px; }
  .side-foot { display: none; }
  .where { display: none; }
  .topbar { height: auto; min-height: 52px; padding: 6px var(--gutter); justify-content: space-between; }
  .main { padding: 18px var(--gutter) 40px; }
  /* the intro: filled, no photo; the mark smaller */
  .intro.has-photo { grid-template-columns: minmax(0, 1fr); }
  .intro.has-mark, .intro.has-mark.has-photo { grid-template-columns: minmax(0, 1fr) auto; }
  .intro-photo { display: none; }
  .intro-mark { padding: 12px 20px 12px 12px; }
  .intro-mark img { height: 44px; }
  .intro { min-height: 0; }
  .intro-copy { padding: 20px 20px 20px 40px; }
  .intro-copy::before { left: 20px; }
}

/* ---- 641 to 900: the tablet band ---- */
@media (min-width: 641px) and (max-width: 900px) {
  .side .logo { padding-top: 6px; padding-bottom: 6px; }
  /* two rows on every page, whatever the badges (QA-008): the "With LT Marine" label becomes an invisible line break */
  .side nav { row-gap: 2px; }
  .side nav .side-sec-row { display: block; flex: 0 0 100%; height: 0; margin: 0; padding: 0; overflow: hidden; }
  .main { padding-top: 16px; }
  /* the intro stays compact: the band right above it already says "Client portal / Furetank" */
  .intro-kicker { display: none; }
  .intro-copy { padding: 14px 18px 14px 36px; }
  .intro-copy::before { left: 18px; height: 40px; margin-top: -20px; }
}

/* ================================================================ phones and small tablets (640 and down)
   The client portal is read on a phone in a car park as often as at a desk. Below 640 px a table stops being a grid and
   becomes a list of labelled facts: the header row is kept for screen readers, every cell prints its own column name
   (labelCells() in the shell fills data-label from the thead), and nothing scrolls sideways. The menu folds behind one
   44 px control (owner, 13 Sep 2026). No new colour, no chips, no decoration - the same quiet page, laid out for the screen
   it is on. */
@media (max-width: 640px) {
  :root { --gutter: 12px; --gap: 12px; --pad: 13px; --fs-h2: 16px; --fs-stat: 22px; --mark-h: 34px; }
  .main { padding: 10px var(--gutter) 40px; }
  .card { border-radius: 6px; }
  .page-head { margin-bottom: 14px; gap: 10px; }
  .page-head p { font-size: 13.5px; }
  .card h2 { margin-bottom: 10px; }
  /* THE FILTER ON A PHONE: the field takes the whole row (beside the count its placeholder truncated), the count and Clear
     sit together on the line below (R11 usability note, 11 Sep 2026). */
  .filterbar > input[type="search"], .search { flex: 1 1 100%; max-width: none; min-width: 0; }
  .filterbar input[type="search"], .search input { height: 44px; }
  .filterbar .count { order: 2; }
  .filterbar .btn { order: 3; }
  .filterbar .fb-end { margin-left: 0; }
  .ftabs { gap: 0 16px; }
  .ftabs a, .ftabs button { min-height: 44px; min-width: 44px; justify-content: center; }
  /* TAP TARGETS (BRIEF rule 6, 40-44 px; correction round 1): a card head's link and the coverage sentence (a link to its
     vessel) get a 44 px box without making their line taller - the negative margin gives back what the box adds, so the
     larger hit area reaches only into the words above and below it, none of which is a control. */
  .card-link { min-height: 44px; margin-block: -13px; }
  a.cov { min-height: 44px; margin-block: -12px; }

  /* THE CHROME ABOVE THE FIRST HEADING IS A BUDGET: at most 140 px on a 390x844 phone with the menu shut
     (portal-responsive.spec, probe_portal_chrome.js; it measured 106 before the redesign and nothing here adds to it). */
  .side .logo { padding: 7px 12px 6px; }
  .side .logo img { width: auto; height: var(--mark-h); max-width: 60%; object-fit: contain; }
  /* THE ACCOUNT CONTROLS ARE TAP TARGETS TOO (R17): 44 px, and the band is not taller for it. */
  .topbar { height: auto; min-height: 48px; padding: 2px 12px; gap: 10px; flex-wrap: nowrap; }
  .topbar-right { flex: none; gap: 10px; }
  .topbar-right::before { display: none; }
  .user { min-height: 44px; min-width: 44px; justify-content: center; }
  /* the initials disc names the account; its words stay for a screen reader */
  .user-text { position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0; overflow: hidden; clip-path: inset(50%); white-space: nowrap; border: 0; }
  .btn-out { min-height: 44px; padding: 0 12px; gap: 6px; }
  /* one line: the clocks take what is left rather than pushing the account onto a line of its own. THE TIME IS NEVER CUT
     (correction round 1: at 320 and 360 the ellipsis ate the time itself - "Manila …  Gothenburg…"): the time keeps its
     width and the city's name is what shortens, with an ellipsis. The full name stays in the markup for a screen reader. */
  .clocks { flex: 1 1 auto; min-width: 0; gap: 12px; font-size: 12px; overflow: hidden; white-space: nowrap; }
  .clock { display: flex; flex-direction: row; align-items: baseline; flex: 0 1 auto; min-width: 0; white-space: nowrap; line-height: 1.4; }
  .clock b { flex: 0 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 12px; font-weight: 600; color: var(--ink-2); margin-right: 6px; }
  .clock:first-child, .clock:first-child b { flex-shrink: 0; }   /* "Manila", the office calendar's clock, stays whole; the second city shortens */
  .clock time { flex: none; font-size: 12px; font-weight: 500; }

  /* the no-script menu: a wrapped row, items sized to their words */
  .side nav { padding: 5px 8px 6px; gap: 4px; }
  .side-item { padding: 10px 11px; gap: 6px; min-height: 40px; }

  /* ---- THE MENU FOLDS AWAY (owner, 13 September 2026) ------------------------------------------
     Everything above is what a phone gets with NO SCRIPTING: the wrapped row, every link on screen. With scripting
     (html.js-nav, set in the head so the open menu is never painted and then folded) the links become one 44 px control
     and the mark, the principal's name and that control share a single band. Not traded away to get it (R7, R8): the
     links keep 44 px and gain the full width; nothing becomes unreachable; the section names come back in the panel. */
  .js-nav .side { flex-direction: row; flex-wrap: wrap; align-items: center; column-gap: 10px; padding-right: 12px; }
  .js-nav .side .logo { flex: none; padding: 6px 0 6px 12px; border-bottom: 0; }
  /* a percentage max-width is circular once the mark is a flex item sized to its content (probe_portal_mark.js once saw a
     31x34 squeeze); capped by the viewport instead */
  .js-nav .side .logo img { max-width: 40vw; }
  /* a tenant with no mark gets its name as the mark, capped and truncated - the principal is named in full beside it */
  .js-nav .logo-text { display: block; max-width: 40vw; font-size: 14px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
  .js-nav .side-who { flex: 1 1 auto; min-width: 0; margin: 0; padding: 0; border-bottom: 0; font-size: 12px; line-height: 1.25; }
  .js-nav .side-who b { font-size: 14px; margin-top: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
  .js-nav .nav-toggle { display: inline-flex; align-items: center; gap: 7px; flex: none; min-height: 44px; padding: 0 12px;
                        font: inherit; font-size: 13.5px; font-weight: 600; color: var(--ink-2);
                        background: var(--surface); border: 1px solid var(--btn-line); border-radius: var(--r-sm); cursor: pointer; }
  .js-nav .nav-toggle .ic { width: 16px; height: 16px; }
  .js-nav .nav-toggle .ic-close { display: none; }
  .js-nav.nav-open .nav-toggle { background: var(--selected); color: var(--ink); }
  .js-nav.nav-open .nav-toggle .ic-menu { display: none; }
  .js-nav.nav-open .nav-toggle .ic-close { display: inline-block; }
  .js-nav .side nav { display: none; }
  .js-nav.nav-open .side nav { display: flex; flex: 1 0 100%; flex-direction: column; flex-wrap: nowrap; gap: 2px; padding: 6px 10px 10px;
                               border-top: 1px solid var(--line-2); max-height: calc(100vh - 150px); overflow-y: auto; }
  .js-nav.nav-open .side-sec { display: block; padding: 10px 12px 2px; font-size: 13px; }
  .js-nav.nav-open .side-item { width: 100%; flex: none; min-height: 44px; padding: 10px 12px; gap: 12px; justify-content: flex-start; white-space: normal; }
  .js-nav.nav-open .side-item .ic-nav { display: inline-block; }
  .js-nav.nav-open .side-item.on::before { left: 0; right: auto; top: 10px; bottom: 10px; width: 3px; height: auto; }

  /* the intro becomes the plain page head with its thin mark: no fill, no photo, no kicker (the band names the portal) */
  .intro { background: none; color: var(--ink); border-radius: 0; margin-bottom: 14px; overflow: visible; }
  .intro-copy { padding: 2px 0 2px 14px; gap: 2px; }
  .intro-copy::before { left: 0; top: 2px; bottom: 2px; height: auto; margin-top: 0; }
  .intro-kicker { display: none; }
  .intro h1 { color: var(--heading); }
  .intro-sub { color: var(--ink-3); font-size: 13.5px; }
  /* no mark on a phone: it is drawn for the dark band, which a phone does not have (the band above names the principal) */
  .intro.has-mark, .intro.has-mark.has-photo { grid-template-columns: minmax(0, 1fr); }
  .intro-mark { display: none; }

  /* the stat strip: two by two */
  .fleet-strip { margin: 14px 0; }
  .fleet-strip > a { padding: 12px 13px; }

  /* TABLES BECOME LABELLED LISTS */
  .tbl-wrap { overflow-x: visible; }
  table.tbl, .tbl tbody, .tbl tfoot, .tbl tr, .tbl td { display: block; width: 100%; }
  .tbl thead { position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0; overflow: hidden; clip-path: inset(50%); border: 0; }
  .tbl tr { padding: 10px 0; border-bottom: 1px solid var(--line); }
  .tbl tr:last-child { border-bottom: 0; }
  .tbl td { border: 0; padding: 2px 0; display: grid; grid-template-columns: 8.5em minmax(0, 1fr); gap: 10px; align-items: baseline; }
  .tbl td::before { content: attr(data-label); color: var(--ink-3); font-size: 12.5px; }
  .tbl td[data-label=""]::before, .tbl td:not([data-label])::before { content: none; }
  .tbl td[data-label=""], .tbl td:not([data-label]) { display: block; }
  .tbl td[colspan]::before { content: none; }
  .tbl td[colspan] { display: block; }
  .tbl td.num, .tbl .num { text-align: left; }
  .tbl td:empty { display: none; }
  .tbl tbody tr:not(.group-head):has(a[href]):hover > td { background-color: transparent; }
  .tbl tr.group-head td { display: block; background: var(--surface-2); font-weight: 600; padding: 7px 10px; margin: 4px 0; border-radius: 4px; }
  .tbl tr.group-head td::before { content: none; }
  /* a long value WRAPS inside its fact: "Product/Chemical tanker" and "Expires within a contract" spilled 4-15 px past the
     card at 320 (D5) */
  .tbl .nowrap, .tbl .chip, .tbl .pill, .tbl .cov-txt { white-space: normal; }
  .sub .nowrap { white-space: normal; }   /* the Overview's 'proposed for <vessel>' keeps the name whole on desktop only; a long name wraps on a phone */
  .person { align-items: flex-start; }
  .btn, .btn-sm { min-height: 44px; }
  /* 16 px: iOS Safari zooms the page into a field under 16 px when it takes focus - on the sign-in that is the first thing that
     happens (#email has autofocus). #code keeps its 22 px (an id rule). */
  .field input, .field textarea, .field select { min-height: 44px; font-size: 16px; }

  /* facts and boards stack rather than squeeze */
  .facts { grid-template-columns: 1fr; gap: 8px 0; }
  .board { grid-template-columns: 1fr; }
  .av-lg { width: 52px; height: 52px; } .av-lg.av-init { font-size: 17px; }
  .av-pane { width: 48px; height: 48px; }
  .profile-head { gap: 12px; margin-bottom: 14px; }

  /* sign-in */
  .login-wrap { padding: 16px 12px; }
  .login-card { padding: 24px 20px 20px; }
  .login-card .logo img { height: 64px; }
  /* the split sign-in on a phone: the form first at full width, the photograph a short band after it */
  .login-pane { align-items: flex-start; padding: 20px 16px 24px; }
  .login-split .login-card .logo { padding-bottom: 20px; }
  .login-split .login-card .logo img { height: 64px; }
  .login-split .login-card h1 { font-size: 28px; }
  .login-lede { margin-bottom: 18px; font-size: 14.5px; }
  .login-photo { min-height: 128px; padding: 24px 16px 22px; }
  .login-caption { padding-left: 12px; }
  .login-caption-name { font-size: 20px; }
  .login-caption-line { font-size: 13.5px; }
}
/* a short screen showing a refusal (320x568, an iPhone SE with Safari's bars, a 1024x600 laptop): the lede gives way to the
   notice, so the Sign in button stays on the first screen as it did on the quiet card (with the lede it ended at 617 px of 568
   and at 635 of 600) */
@media (max-height: 640px) {
  .login-split .login-card:has(.notice-bad) .login-lede { display: none; }
}

/* ---- 359 and down: the narrowest phones ---- */
@media (max-width: 359px) {
  .tbl td { grid-template-columns: 7em minmax(0, 1fr); }
  .btn-out .ic { display: none; }
  .js-nav .nav-toggle { padding: 0 10px; }
}

/* ---- 389 and down: the tenant's second clock is not drawn - from 360 to 389 px it read "Goth… 11:31" (the final client
   verification, 2 Oct; 360 and 375 are common phones) - and is still read out; Manila, the calendar every date is on, stays.
   A longer city name that is cut at a wider width carries its full name as a title (layout.php). ---- */
@media (max-width: 389px) {
  .clock + .clock { position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0; overflow: hidden; clip-path: inset(50%); white-space: nowrap; border: 0; }
}

/* ---- 720 and down: the briefing rows and the coverage sentence ---- */
@media (max-width: 720px) {
  /* the coverage sentence must WRAP once the table becomes labelled facts: a nowrap phrase in a minmax(0,1fr) column is how a
     page starts scrolling sideways at 390px */
  .cov { flex-wrap: wrap; gap: 6px 10px; }
  .cov-txt { white-space: normal; }
  .cov-bar { flex-basis: 64px; }
  .cov-none::before { display: none; }
  .brief-list li, .brief-list.is-narrow li { grid-template-columns: minmax(0, 1fr); grid-template-areas: none; gap: 6px; }
  .brief-list.is-narrow .bl-who, .brief-list.is-narrow .bl-why, .brief-list.is-narrow .bl-do { grid-area: auto; }
  .brief-list.is-narrow .bl-why { padding-left: 0; }
  .bl-do { justify-self: start; }
  .mv-list li { grid-template-columns: minmax(0, 1fr); gap: 2px; }
}

/* A CONTROL IS A CONTROL, AND A FINGER IS THE SAME SIZE ON A TABLET AS ON A PHONE (UI audit, 17 Sep 2026). The question is
   the pointer, not the width: a mouse is unaffected by this block. */
@media (pointer: coarse) {
  .side-item { min-height: 44px; }
  .btn, .btn-out, .btn-sm { min-height: 44px; }
  .user { min-height: 44px; }
  /* links inside table cells and sentences: padding rather than a height, so the rows keep their shape and only the hit area
     grows (inline boxes only - a block link would grow the row) */
  td a:not(.btn):not(.cov), th a, .card p a:not(.btn), .bl-who a { padding-top: 11px; padding-bottom: 11px; }
  /* the sign-in's "Privacy notice" (12 px words: 15 px more each way makes the 44 px; inline, so the line keeps its height) */
  .login-foot a { padding-top: 15px; padding-bottom: 15px; }
  /* a card head's link, as on a phone (the 640 block): a 44 px box, the line given back by the negative margin */
  .card-link { min-height: 44px; margin-block: -13px; }
}

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

/* ---------------------------------------------------------------- print */
/* PSC inspections and owner reviews still run on paper: chrome off, content full-width. */
@media print {
  .side, .app::before, .topbar, .foot, .btn, .btn-row, .btn-out, .filterbar, .ftabs, .skip, .intro-photo, .intro-mark { display: none !important; }
  .app { display: block; }
  body { background: #fff; font-size: 12px; }
  .main { padding: 0; max-width: none; }
  .card { border: 0; padding: 0 0 14px; }
  .intro { background: none; color: inherit; min-height: 0; }
  /* the kicker and the sub-line are set for the dark band (#fff and its softer ink): on paper the band is gone, so they take
     the page's ink like the h1 - white words on white paper print as nothing */
  .intro h1, .intro-kicker, .intro-sub { color: inherit; }
  .intro .intro-kicker { display: block; }   /* the 641-900 px band hides it, and A4 portrait falls in that band */
  .intro-copy { padding: 0; }
  .intro-copy::before { display: none; }
  .tbl th, .tbl td { padding: 5px 8px; }
  tr { break-inside: avoid; }
  a { color: inherit; text-decoration: none; }
  .empty { display: none; }
}
