/* VGX Job Board — taken from VGX_Job_Board_Concept_20260822.html, the page
   VGX approved. Palette, type and card anatomy are carried across unchanged;
   what is added here is only what a live board needs and a picture did not:
   the drawer, form controls, and touch sizing. */

/* Self-hosted, not Google's CDN. If VGX's office connection blocks it, the
   type falls back and the character disappears on the machines that matter.
   One variable file covers 600/700/800. */
@font-face {
  font-family: "Montserrat";
  src: url("/static/fonts/montserrat-var.woff2") format("woff2");
  font-weight: 600 800;
  font-style: normal;
  font-display: swap;
}

:root{
  --bg:#0B0C0E; --panel:#15181C; --card:#1E2227; --card2:#191D21;
  --line:#2C3137; --line2:#3A4046;
  --steel-hi:#989997; --steel:#5F6262; --steel-dk:#333537;
  --white:#FFFFFF; --dim:#AEB4BA; --mute:#7C848C; --faint:#69717A;

  /* ONLY on lane headers and the card's left bar. */
  --lane1:#85B7EB; --lane2:#97C459; --lane3:#E8843C; --lane4:#EFC23F; --lane5:#E2574C;

  /* Neutral near-white. Never red, never a lane colour. */
  --accent:#F2F4F5; --accent-ink:#101214;

  --head:"Montserrat","Century Gothic",Futura,-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,sans-serif;
  --body:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Helvetica,Arial,sans-serif;

  /* Gloves in daylight. */
  --touch:48px;
}
*{box-sizing:border-box}
body{margin:0; background:var(--bg); color:var(--white); font-family:var(--body);
  line-height:1.6; font-size:16px; -webkit-text-size-adjust:100%;
  /* Belt and braces: nothing on this page may scroll sideways. */
  overflow-x:hidden}
.mono{font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace}
a{color:var(--accent)}

/* ---- header band, from the concept page ---- */
/* ---- demo banner ----------------------------------------------------
   Solid near-white on the dark board: the highest-contrast thing on the page,
   and impossible to crop out of a screenshot of the top of the board.
   NOT red and not a lane colour — red means the Waiting for Order lane and
   nothing else (D-38), and a warning that borrowed it would make the board's
   own colour language ambiguous. The hazard stripe carries the "warning"
   reading instead. */
/* Loud, not shouting. The wording is meant to reassure — "a practice board,
   try anything you like" — and setting it in capitals with wide tracking made
   it read as a warning notice no matter what it said. The prominence is kept
   by size, weight, colour and the hazard edge, which is where it should have
   come from in the first place: a strip in the accent colour with a striped
   border is unmissable in a screenshot without raising its voice. */
.demobanner{
  position:sticky; top:0; z-index:60;
  background:var(--accent); color:var(--accent-ink);
  font-family:var(--head); font-weight:800; font-size:14.5px;
  letter-spacing:.01em; text-align:center;
  padding:calc(7px + env(safe-area-inset-top)) 12px 7px;
  border-bottom:3px solid;
  border-image:repeating-linear-gradient(135deg,
      var(--accent-ink) 0 8px, var(--accent) 8px 16px) 3;
  line-height:1.35;
}
/* When the banner is present it owns the safe-area inset, so the band must not
   add it a second time. */
.demobanner ~ .band{padding-top:0}

.band{width:100%; border-bottom:1px solid var(--line2);
  background:
    repeating-linear-gradient(90deg, rgba(255,255,255,.030) 0 1px, rgba(0,0,0,.020) 1px 3px),
    linear-gradient(180deg,#31363B 0%,#22262A 40%,#15181B 100%);
  box-shadow:inset 0 1px 0 rgba(255,255,255,.10), 0 2px 10px rgba(0,0,0,.5);
  position:sticky; top:0; z-index:40}
/* nowrap, not wrap: wrapping is what made the phone header two rows tall.
   Everything in here is sized to survive on one line at 320px. */
/* env() safe areas: iPhones put a notch or a camera cutout over the top of
   the viewport and a home indicator over the bottom. viewport-fit=cover lets
   the page use the full screen, which means WE become responsible for keeping
   content out from under both. max() so a device without insets is unchanged. */
.bandinner{max-width:1600px; margin:0 auto;
  padding:10px max(18px, env(safe-area-inset-right)) 10px max(18px, env(safe-area-inset-left));
  display:flex; align-items:center; gap:14px; flex-wrap:nowrap}
.band{padding-top:env(safe-area-inset-top)}
/* Sized to the header with room around it. It was sitting hard against the
   band's edges, which made the badge look cramped rather than placed. */
.logo{height:44px; width:auto; display:block; flex:0 0 auto;
  padding:3px 0; margin-right:4px}
.kicker{font-family:var(--head); font-weight:800; font-size:16px;
  letter-spacing:.10em; color:var(--accent); white-space:nowrap;
  overflow:hidden; text-overflow:ellipsis}
.bandmeta{margin-left:auto; display:flex; align-items:center; gap:8px;
  font-size:12.5px; color:var(--steel-hi); flex:0 0 auto}

/* --- account menu -------------------------------------------------------
   <details> rather than JavaScript: keyboard-operable, closes on Escape,
   and usable before any script has loaded. */
.usermenu{position:relative; flex:0 0 auto}
.usermenu summary{
  list-style:none; cursor:pointer; display:flex; align-items:center;
  justify-content:center; min-width:38px; min-height:38px; padding:0 11px;
  border:1px solid var(--line2); border-radius:9px; background:transparent}
.usermenu summary::-webkit-details-marker{display:none}
.usermenu summary:hover{border-color:var(--steel-hi)}
/* The NAME is the default and the initial is the fallback, not the other way
   round. Four people share this board; "L" is a puzzle where "Leon" is an
   answer, and there is room for it on anything wider than a phone.
   max-width and ellipsis so a longer name can never break the band's
   one-line rule, which is what the nowrap above is protecting. */
.usermenu-name-inline{font-family:var(--head); font-weight:700; font-size:12.5px;
  letter-spacing:.04em; color:var(--dim);
  max-width:14ch; overflow:hidden; text-overflow:ellipsis; white-space:nowrap}
.usermenu-initials{font-family:var(--head); font-weight:700; font-size:12.5px;
  letter-spacing:.04em; color:var(--dim); display:none}
.usermenu-panel{
  position:absolute; right:0; top:calc(100% + 6px); z-index:60;
  background:var(--panel); border:1px solid var(--line2); border-radius:10px;
  padding:12px; min-width:170px; box-shadow:0 8px 24px rgba(0,0,0,.55)}
.usermenu-name{font-size:13.5px; color:var(--white); margin-bottom:10px;
  white-space:nowrap}
.usermenu-panel .btn{width:100%}

.wrap{max-width:1600px; margin:0 auto;
  padding:16px max(16px, env(safe-area-inset-right))
          max(16px, env(safe-area-inset-bottom)) max(16px, env(safe-area-inset-left))}

/* ---- lanes ---- */
/* --- board summary strip, from the concept page --- */
.boardhead{display:flex; align-items:baseline; gap:10px; flex-wrap:wrap;
  padding:0 2px 10px; border-bottom:1px solid var(--line); margin-bottom:12px}
.boardcount{font-size:12.5px; color:var(--dim)}
.boarddate{font-size:12.5px; color:var(--faint); margin-left:auto}
/* A button, so it is tappable on a phone and focusable from the keyboard,
   but it must look like the quiet label it is. */
.boardver{font-size:10.5px; color:var(--steel); letter-spacing:.02em;
  background:none; border:0; padding:0; margin:0; cursor:pointer;
  font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace}
.boardver:hover, .boardver:focus{color:var(--faint); outline:none}

.lanes{display:grid; grid-template-columns:repeat(5,minmax(0,1fr)); gap:10px; align-items:start}
.lane{background:var(--panel); border:1px solid var(--line); border-radius:12px; padding:10px; min-height:120px}
.lanehead{display:flex; align-items:center; gap:7px; padding-bottom:8px; border-bottom:1px solid var(--line)}
.dot{width:8px; height:8px; border-radius:50%; flex:0 0 auto}
.lanename{font-family:var(--head); font-weight:700; font-size:10.5px; letter-spacing:.07em}
.lanecount{margin-left:auto; font-size:11px; color:var(--faint)}
/* The line that makes a lane self-explaining. Indented under the dot, as the
   concept page has it. */
.lanesub{font-size:11px; color:var(--faint); line-height:1.45;
  margin:6px 0 0 15px}
.lanebody{padding-top:10px; min-height:60px}
.lane.dragover{outline:2px dashed var(--accent); outline-offset:2px}

/* ---- card ---- */
.job{display:flex; background:var(--card); border-radius:10px; overflow:hidden;
  margin-bottom:9px; border:1px solid var(--line); cursor:pointer}
.job:hover{border-color:var(--line2)}
.job.seen{opacity:.82}
.bar{width:3px; flex:0 0 auto}
.jbody{padding:11px 12px; flex:1; min-width:0}
.jtop{display:flex; justify-content:space-between; align-items:center; gap:7px}
.jno{font-size:11.5px; color:var(--mute); flex:0 0 auto}
.refs{display:flex; gap:4px; min-width:0; flex-wrap:wrap}
.ref{font-size:11px; padding:2px 7px; border-radius:5px; white-space:nowrap;
  overflow:hidden; text-overflow:ellipsis; max-width:100%; border:1px solid transparent}
.ref-ok{background:#E7EAEC; color:#14181B; font-weight:600}
.ref-soft{background:#2C3137; color:var(--dim)}
.ref-none{background:transparent; border:1px dashed var(--steel); color:var(--steel-hi)}
.jname{font-family:var(--head); font-weight:700; font-size:15px; margin-top:5px;
  line-height:1.3; word-break:break-word; letter-spacing:-.005em}
.jreg{font-size:11.5px; color:var(--mute); margin-top:2px; letter-spacing:.04em}
.desc{font-size:12px; color:var(--dim); line-height:1.45; margin-top:5px}
.jmid{display:flex; align-items:center; gap:7px; margin-top:9px; flex-wrap:wrap}
.talloc{font-size:11.5px; padding:3px 8px; border-radius:6px; background:#232830;
  color:var(--dim); border:1px solid var(--line2); white-space:nowrap}
.badge{font-size:11.5px; padding:3px 9px; border-radius:6px; background:#2C3137;
  color:var(--dim); border:1px solid var(--line2)}
.badge.live{background:#E7EAEC; color:#14181B; border-color:#E7EAEC; font-weight:600}
.age{font-size:11.5px; padding:3px 9px; border-radius:6px; background:#22262B; color:var(--mute)}
.age.old{background:transparent; border:1px solid var(--accent); color:var(--accent); font-weight:600}
.jfoot{display:flex; align-items:center; justify-content:space-between;
  margin-top:9px; gap:6px 8px; flex-wrap:wrap}
.flags{display:flex; gap:5px; flex-wrap:wrap; min-width:0}
.flag{font-family:var(--head); font-weight:600; font-size:9.5px; letter-spacing:.08em;
  padding:2px 6px; border-radius:5px; border:1px solid var(--line2); color:#5C646C}
.flag.on{border-color:var(--steel-hi); background:rgba(152,153,151,.16); color:var(--accent)}
/* Must not clip at any breakpoint. */
.who{font-size:11.5px; color:var(--faint); white-space:nowrap; flex:0 0 auto; margin-left:auto}

/* ---- buttons and controls ---- */
.btn{background:var(--accent); color:var(--accent-ink); border:none; border-radius:9px;
  padding:0 18px; min-height:var(--touch); font-family:var(--head); font-size:14px;
  font-weight:700; letter-spacing:.03em; cursor:pointer; display:inline-flex;
  align-items:center; justify-content:center}
.btn.alt{background:transparent; color:var(--dim); border:1px solid var(--line2)}
.btn.small{min-height:36px; font-size:12.5px; padding:0 12px}
label{display:block; font-size:12.5px; color:var(--faint); margin-bottom:6px}
/* 16px is a hard floor, not a preference. Below 16px iOS Safari zooms the
   whole page when a field takes focus and does NOT zoom back out — so on the
   card drawer Nicky would tap a field, the board would jump to 150%, and she
   would be left pinching her way back. It costs one pixel of type. */
input[type=text], input[type=password], input[type=datetime-local], select, textarea{
  width:100%; background:var(--card2); color:var(--white); border:1px solid var(--line2);
  border-radius:9px; padding:11px 12px; font-family:inherit; font-size:16px;
  line-height:1.5; min-height:var(--touch)}
textarea{min-height:84px; resize:vertical}
input:focus, select:focus, textarea:focus{outline:none; border-color:var(--accent);
  box-shadow:0 0 0 2px rgba(242,244,245,.16)}

/* Placeholders must never read as entered text.
   On 23 August a job was created with three fields empty because the
   placeholders were realistic sample values — "Chrome Carriers",
   "Scania G460", "Complete truck, 3 x main spec" — and were read as
   pre-filled data. They are phrased as instructions now, and styled so a
   filled field and an empty one cannot be confused at a glance: dimmer than
   the real value colour, and italic, which no entered value ever is. */
input::placeholder, textarea::placeholder{
  color:#4E5661; font-style:italic; opacity:1}
input::-webkit-input-placeholder, textarea::-webkit-input-placeholder{
  color:#4E5661; font-style:italic}
.field{margin-bottom:14px}
.row2{display:grid; grid-template-columns:1fr 1fr; gap:12px}

/* ---- drawer: over the board, never replacing it ---- */
.scrim{position:fixed; inset:0; background:rgba(0,0,0,.55); z-index:50; display:none}
.scrim.open{display:block}
/* dvh, not vh: Safari's address bar hides on scroll and 100vh keeps the
   TALLER measurement, so a vh-sized drawer would have its bottom pushed off
   screen — and the Save controls with it. dvh follows the visible viewport. */
.drawer{position:fixed; top:0; right:0; height:100dvh; width:min(560px,100vw);
  background:var(--panel); border-left:1px solid var(--line2); z-index:51;
  transform:translateX(100%); transition:transform .18s ease-out;
  display:flex; flex-direction:column; overflow:hidden}
.drawer.open{transform:translateX(0)}
.drawerhead{padding:14px 16px; border-bottom:1px solid var(--line); display:flex;
  align-items:center; gap:10px}
.drawerbody{padding:16px 16px calc(16px + env(safe-area-inset-bottom));
  overflow-y:auto; flex:1; -webkit-overflow-scrolling:touch}
.closebtn{margin-left:auto; background:transparent; border:1px solid var(--line2);
  color:var(--dim); border-radius:9px; min-width:var(--touch); min-height:var(--touch);
  font-size:20px; cursor:pointer}
.sec{margin-bottom:22px}
.sec h3{font-family:var(--head); font-weight:700; font-size:12px; letter-spacing:.1em;
  color:var(--faint); margin:0 0 10px; text-transform:uppercase}

/* ---- checklist ---- */
.check{display:flex; align-items:center; gap:10px; padding:6px 0; min-height:var(--touch)}
.check input{width:22px; height:22px; accent-color:var(--accent); flex:0 0 auto}
.check span{font-size:14px; color:var(--dim)}
.check input:checked + span{color:var(--white)}

/* ---- notes ---- */
.note{background:var(--card2); border:1px solid var(--line); border-radius:9px;
  padding:10px 12px; margin-bottom:8px}
.notemeta{font-size:11px; color:var(--faint); margin-bottom:4px}
.notebody{font-size:14px; color:var(--dim); white-space:pre-wrap; word-break:break-word}

/* ---- honest failure ---- */
.msg{padding:10px 12px; border-radius:9px; font-size:13.5px; margin-bottom:12px; display:none}
.msg.show{display:block}
/* Told apart by FORM, not hue. Lane colours never appear here: red means the
   Waiting for Order lane and nothing else (D-38), so an error cannot borrow
   it. A solid accent left-bar reads as "stop" without competing with a lane. */
.msg.err{background:#22262B; border:1px solid var(--line2);
  border-left:4px solid var(--accent); color:var(--white); font-weight:600}
.msg.ok{background:#1B1F24; border:1px solid var(--line2);
  border-left:4px solid var(--steel-hi); color:var(--dim)}
.flashes{margin:0 0 14px; padding:0; list-style:none}
.flashes li{background:#22262B; border:1px solid var(--line2);
  border-left:4px solid var(--accent); color:var(--white); font-weight:600;
  padding:10px 12px; border-radius:9px; margin-bottom:8px; font-size:13.5px}

/* ---- phone: all five lanes, swipeable ---- */
/* No swipe hint anywhere: the phone stacks vertically, so there is nothing
   to swipe and nothing to explain. */
.swipehint{display:none}
/* Column count by width. The target is roughly 300px per lane — what a card
   was designed against on a 1600px desktop showing five.

     > 1240    five    desktop
     821-1240  three   small desktop, tablet landscape
     641-820   two     LANDSCAPE PHONE, tablet portrait
     <= 640    one     portrait phone, stacked vertically

   The 641 floor is the point: a landscape phone is 667-932 CSS px wide, so a
   single-column cutoff at 820 handed an iPhone SE in landscape (667px) one
   lane across the entire screen. Only a portrait device should see one
   column — and 640 keeps a 600px-wide portrait view stacked, which is the
   width this gets checked at. */
@media (max-width:1240px){ .lanes{grid-template-columns:repeat(3,minmax(0,1fr))} }
@media (max-width:820px){ .lanes{grid-template-columns:repeat(2,minmax(0,1fr))} }

/* Compact chrome for every phone, portrait AND landscape. A landscape phone is
   only ~390px tall, so the header matters more there, not less. */
@media (max-width:900px){
  .demobanner{font-size:12.5px; letter-spacing:.01em; padding:5px 8px}
  .wrap{padding:10px}

  /* An empty lane should not take a whole screen of nothing on the way past. */
  .lanebody{min-height:0}
  .swipehint{display:none}
  .boardhead{padding:0 2px 7px; margin-bottom:8px; gap:8px}
  .boardcount, .boarddate{font-size:11.5px}
  .boardver{font-size:10px; width:100%; margin-top:-2px}
  .lanesub{font-size:11px; margin:6px 0 2px 15px}
  .drawer{width:100vw; border-left:none}
  .row2{grid-template-columns:1fr}

  /* One row, and small enough that cards start near the top of the screen.
     Nicky and Jaunn live on this view; every pixel of chrome is a pixel of
     job they cannot see. */
  .bandinner{padding:5px max(10px, env(safe-area-inset-right))
                    5px max(10px, env(safe-area-inset-left)); gap:8px}
  .logo{height:26px}
  .kicker{font-size:12.5px; letter-spacing:.06em}
  .bandmeta{gap:6px}
  .btn.small{min-height:32px; font-size:11.5px; padding:0 9px}
  .usermenu summary{min-width:32px; min-height:32px}
  .usermenu-name-inline{font-size:11.5px; max-width:10ch}
  .usermenu-initials{font-size:11.5px}
  .wrap{padding:8px max(10px, env(safe-area-inset-right))
               max(8px, env(safe-area-inset-bottom)) max(10px, env(safe-area-inset-left))}
}

/* PORTRAIT PHONE ONLY: one vertical column. The whole board stacks — lane
   heading, its cards, next lane heading, its cards — scrolled with a thumb.
   No horizontal scrolling, no swipe panes. */
@media (max-width:640px){
  /* Portrait phone: the band is sized to survive at 320px and a name is about
     40px this view has not got. THIS is "genuinely too narrow" — the initial
     comes back only here, and the full name is still in the open menu. */
  .usermenu-name-inline{display:none}
  .usermenu-initials{display:inline}
  .usermenu summary{padding:0}

  .lanes{display:flex; flex-direction:column; gap:10px;
    overflow-x:visible; padding-bottom:8px;
    /* MUST override the desktop `align-items:start`. In a column flex
       container the cross axis is horizontal, so `start` shrinks every lane to
       its own content width and the board comes out as a ragged staircase. */
    align-items:stretch}
  .lane{flex:0 0 auto; width:auto; min-height:0}
}
