/* Rotamate — modern, light-first UI with a switchable dark theme. */
:root{
  --paper:#f4f4f2;
  --card:#ffffff;
  --ink:#1d1e21;
  --ink-soft:#696b70;
  --line:#e5e5e2;
  --line-strong:#929391;   /* 3.09:1 on card — WCAG 1.4.11 input border */
  /* Yellow is a fill, never a line. That is not a stylistic choice: at
     1.79:1 on a white card this yellow is invisible as a border, and
     --accent used to draw every focus ring in the product. So the brand
     colour fills things and --accent-line draws them, which is the only way
     a yellow brand and WCAG 1.4.11 can both be true. */
  --accent:#f5b800;
  --accent-soft:#fdf4d8;
  --accent-ink:#241a00;   /* ink ON the yellow: 9.60:1 (the orange managed 4.94) */
  --accent-text:#855c00;  /* the brand where it is TEXT: 5.40:1 on paper (was 4.73) */
  --accent-line:#a97400;  /* borders and focus rings: 4.05:1 on card, 3.68:1 on paper */
  --info:#38699e; --info-soft:#e9f0f8;
  --yes:#1d7d45; --yes-soft:#e5f6ec;
  --no:#bc4053;  --no-soft:#fcebee;
  /* Moved from amber (#976514) to orange when the brand went yellow. An
     amber "maybe" beside a yellow brand is two colours doing one job, and
     the one place it matters most is a reply list where the brand accent and
     a Maybe tag sit on the same row. Orange also leaves the trio reading
     green / orange / red, which needs no explaining. */
  --maybe:#b35309; --maybe-soft:#fdf0e4;
  --warn-bg:#fdf6ec;
  --shadow:0 1px 2px rgba(23,25,30,.06),0 4px 14px rgba(23,25,30,.05);
  --shadow-lift:0 2px 6px rgba(23,25,30,.08),0 10px 24px rgba(23,25,30,.08);
  /* Three radii, not eight: controls, cards, panels. Fully round is kept for
     the two circular controls and the crew avatars, which have a reason. */
  --r-ctl:8px; --r-card:10px; --r-panel:12px; --r-badge:6px;
  --r:12px; --r-sm:10px;          /* kept: 30+ rules still name them */
  --tabbar-h:58px;
  /* The floor for the smallest type in the product — the caption over a date
     tile, the label over a figure, a filter's "Sort". It had drifted to 9.5px
     and 10px across five separate rules, which is below what a phone held at
     arm's length resolves. One token, one floor, and nothing smaller. */
  --fs-micro:11px;
  --mono:ui-monospace,"SF Mono",Menlo,Consolas,monospace;
  --sans:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Helvetica,Arial,sans-serif;
  --tt-sun:none; --tt-moon:inline;        /* light mode shows the moon */
  color-scheme:light;
}
@media (prefers-color-scheme: dark){
  :root:not([data-theme="light"]){
    --paper:#191613; --card:#241f1a; --ink:#f1ede6; --ink-soft:#a79f92;
    --line:#38332b; --line-strong:#706b61;
    --accent:#ffc93c; --accent-soft:#3a2e12; --accent-ink:#1a1406;
    --accent-text:#ffc93c;   /* 11.73:1 on paper — no darker variant needed here */
    /* On dark the brand yellow IS a usable line: 10.63:1 on card. The token
       still exists so every border and focus ring names one thing. */
    --accent-line:#ffc93c;
    --info:#86aede; --info-soft:#232a36;
    --yes:#4ec47f; --yes-soft:#20301f; --no:#f07a8c; --no-soft:#372026;
    --maybe:#f59356; --maybe-soft:#3a2317; --warn-bg:#2b2316;
    --shadow:0 1px 2px rgba(0,0,0,.4),0 4px 14px rgba(0,0,0,.3);
    --shadow-lift:0 2px 6px rgba(0,0,0,.5),0 10px 24px rgba(0,0,0,.4);
    --tt-sun:inline; --tt-moon:none;
    color-scheme:dark;
  }
}
:root[data-theme="dark"]{
  --paper:#191613; --card:#241f1a; --ink:#f1ede6; --ink-soft:#a79f92;
  --line:#38332b; --line-strong:#706b61;
  --accent:#ffc93c; --accent-soft:#3a2e12; --accent-ink:#1a1406;
  --accent-text:#ffc93c;
  --accent-line:#ffc93c;
  --info:#86aede; --info-soft:#232a36;
  --yes:#4ec47f; --yes-soft:#20301f; --no:#f07a8c; --no-soft:#372026;
  --maybe:#f59356; --maybe-soft:#3a2317; --warn-bg:#2b2316;
  --shadow:0 1px 2px rgba(0,0,0,.4),0 4px 14px rgba(0,0,0,.3);
  --shadow-lift:0 2px 6px rgba(0,0,0,.5),0 10px 24px rgba(0,0,0,.4);
  --tt-sun:inline; --tt-moon:none;
  color-scheme:dark;
}

*{box-sizing:border-box;margin:0;padding:0}
/* Nothing embedded may be wider than the column it sits in. Text is handled
   on `body`; this is the same promise for the things text is not. */
img,svg,video,canvas,iframe,table{max-width:100%}
/* Every icon in the product comes from one set, at one stroke weight. */
.i{flex:none; display:block; stroke-width:1.75; stroke-linecap:round; stroke-linejoin:round}
/* A focused field must never land under the fixed tab bar or action bar when the
   browser scrolls it into view - the margins tell the native scroll about them. */
input, select, textarea{scroll-margin:80px 0 150px}
/* NOTHING that clips overflow goes on <html>. The root element is the document
   scroller, and WebKit (every browser on iOS) mishandles a clipped root: the page
   scrolls down and then refuses to scroll back up - which is exactly what a real
   iPhone did here, while Chromium on a desktop reported the styles as fine. The
   sideways-pan backstop belongs on <body>, where it has always been and where it
   does the same job without touching the scroller. */
html{-webkit-text-size-adjust:100%}
/* no double-tap zoom on taps — buttons, links and fields respond instantly */
a,button,input,select,textarea,summary,label{touch-action:manipulation}
body{
  font-family:var(--sans); background:var(--paper); color:var(--ink);
  font-size:15.5px; line-height:1.5;
  padding-bottom:calc(66px + env(safe-area-inset-bottom));
  /* Nothing may widen the page. Two separate guards, because long text and
     wide boxes break a layout in different ways.
     
     The text one only acts on a word that genuinely cannot fit, so prose is
     untouched — but a client name with no spaces, a long email address or a
     pasted URL can no longer stretch the page. The legacy alias is kept
     because it is what the oldest engines read, and those are exactly the
     phones this goes wrong on: Safari only honours `overflow-wrap:anywhere`
     from 15.4, so on an older iPhone a 70-character link with nothing to
     break on drags every section sideways with its left edge off-screen.
     
     `overflow-x:clip` rather than `hidden` catches the non-text case — a
     wide table, an image — without breaking position:sticky on the top bar,
     which `hidden` would. Same choice, and same reason, as CrewClean. */
  overflow-wrap:break-word;
  word-wrap:break-word;
  overflow-x:clip;
}
a{color:inherit}
h1,h2,h3{line-height:1.2}
::placeholder{color:var(--ink-soft);opacity:.7}

/* top bar */
.topbar{
  display:flex; align-items:center; gap:14px;
  padding:10px 16px; border-bottom:1px solid var(--line);
  background:var(--paper);
  position:sticky; top:0; z-index:20;
}
.wordmark{font-size:17px; font-weight:650; letter-spacing:-.008em; text-decoration:none;
  white-space:nowrap}
.wordmark span{color:var(--accent-text)}
.topnav{display:none; gap:2px}
.topnav a{
  text-decoration:none; padding:7px 11px; font-size:13.5px; font-weight:600;
  color:var(--ink-soft); border-radius:var(--r-ctl);
}
@media(hover:hover){.topnav a:hover{color:var(--ink); background:var(--card)}}
.topnav a.on, .topnav a.active{color:var(--accent-text); background:var(--accent-soft)}
/* On a phone Sign out lives in the menu, where the rest of the app's
   sections are. Four controls plus the wordmark do not fit a 320px bar. */
.logout-form{display:none; margin-left:auto}
@media(min-width:760px){.logout-form{display:block}}
.theme-toggle{
  margin-left:auto; border:1px solid var(--line); background:var(--card); color:var(--ink);
  width:38px; height:38px; border-radius:50%; cursor:pointer;
  display:inline-flex; align-items:center; justify-content:center; flex:none;
}
.logout-form + .theme-toggle,.logout-form ~ .theme-toggle{margin-left:0}
/* …except on a phone, where the form is hidden and something still has to
   push the controls to the right-hand end of the bar. */
@media(max-width:759px){.logout-form ~ .theme-toggle{margin-left:auto}}
.tt-light{display:var(--tt-sun)} .tt-dark{display:var(--tt-moon)}

/* "More" — everything the five-slot tab bar has no room for. Phones only:
   on a desktop the same links are already across the top. */
.menu-btn{
  border:1px solid var(--line); background:var(--card); color:var(--ink);
  width:38px; height:38px; border-radius:50%; cursor:pointer; flex:none;
  display:inline-flex; align-items:center; justify-content:center;
}
.menu-btn[aria-expanded="true"]{background:var(--accent-soft); border-color:var(--accent-line); color:var(--accent-text)}
.menu-panel{
  position:fixed; top:57px; left:0; right:0; bottom:0; z-index:19;
  overflow-y:auto; padding:16px 16px calc(24px + env(safe-area-inset-bottom));
  background:var(--paper);
  animation:menu-in .16s ease-out;
}
@keyframes menu-in{from{opacity:0; transform:translateY(-6px)}}
.menu-head{font:600 11px var(--sans); letter-spacing:0;
  color:var(--ink-soft); margin:2px 0 10px}
.menu-grid{display:grid; grid-template-columns:repeat(2,1fr); gap:10px}
.menu-grid a{
  display:flex; align-items:center; gap:11px; text-decoration:none; color:var(--ink);
  background:var(--card); border:1px solid var(--line); border-radius:var(--r-card);
  padding:13px 14px; font-size:14.5px; font-weight:600;
}
.menu-grid a b{
  color:var(--accent-text); flex:none; display:inline-flex;
}
.menu-grid a.active{border-color:var(--accent-line); background:var(--accent-soft); color:var(--accent-text)}
.menu-out{margin-top:16px}
.menu-out .btn{width:100%; justify-content:center}
@media(prefers-reduced-motion:reduce){.menu-panel{animation:none}}

@media(min-width:760px){
  .topnav{display:flex}
  body{padding-bottom:32px}
  .tabbar{display:none!important}
  .menu-btn{display:none}
  .menu-panel{display:none!important}
}

/* bottom tab bar (mobile) */
.tabbar{
  position:fixed; bottom:0; left:0; right:0; z-index:20;
  display:flex; border-top:1px solid var(--line);
  background:var(--card);
  padding-bottom:env(safe-area-inset-bottom);
}
.tabbar a{
  flex:1; min-width:0; display:flex; flex-direction:column; align-items:center; gap:3px;
  text-decoration:none; padding:9px 2px 8px;
  font-size:11px; font-weight:500; color:var(--ink-soft);
}
.tabbar a span{overflow:hidden; text-overflow:ellipsis; white-space:nowrap; max-width:100%}
.tabbar a.active{color:var(--accent-text); font-weight:600}

/* layout */
/* The gutter scales with the screen instead of costing a 320px phone the same
   32px it costs a 430px one - that is 10% of the narrowest screen handed back
   to the content, which is the difference between a label fitting and wrapping. */
.page{max-width:880px; margin:0 auto; padding:18px clamp(12px, 4.2vw, 18px) 36px}
.backlink{display:inline-block; text-decoration:none; color:var(--ink-soft);
  font:600 13px var(--sans); margin:0 0 10px; padding:4px 0}
@media(hover:hover){.backlink:hover{color:var(--ink)}}
.pagetitle{font-size:28px; font-weight:600; letter-spacing:-.021em; margin:0 0 4px}
.title-row{display:flex; align-items:center; justify-content:space-between; gap:12px; flex-wrap:wrap}
.title-row .pagetitle{margin-bottom:8px}
.muted{color:var(--ink-soft)}
.small-text{font-size:13px}

.sheet{
  /* A titled section of the page: the heading sits on the paper with a
     hairline under it and the rows sit beneath. `.sheet.card` opts into the
     raised surface, for a form or for the one thing that needs lifting. A
     card around every section is what made this heavy to look at. */
  margin:24px 0;
}
.sheet > .sheet-title,
.sheet > .title-row,
.sheet > .fold > summary,
.sheet > .avail-fold > summary{border-bottom:1px solid var(--line); margin-bottom:4px}
.sheet.card{
  background:var(--card); border:1px solid var(--line);
  border-radius:var(--r-panel); padding:16px;
}
.sheet.card > .sheet-title{border-bottom:1px solid var(--line)}
.sheet.warn .sheet-title::before{content:""; display:inline-block; width:7px; height:7px;
  border-radius:50%; background:var(--maybe); margin-right:8px; vertical-align:1px}
.sheet-title{
  font-size:18px; font-weight:600; letter-spacing:-.012em; color:var(--ink);
  margin:0; padding-bottom:8px;
}
.block-link{display:block; text-decoration:none}

/* buttons */
.btn{
  display:inline-flex; align-items:center; justify-content:center; gap:6px;
  border:1px solid var(--line-strong); background:var(--card);
  color:var(--ink); font:600 14px/1.3 var(--sans);
  padding:9px 15px; text-decoration:none; cursor:pointer; text-align:center;
  min-height:44px; border-radius:var(--r-ctl); transition:background .12s,border-color .12s,color .12s;
}
@media(hover:hover){.btn:hover{background:var(--paper); border-color:var(--line-strong)}}
.btn:active{transform:translateY(1px)}
.btn.primary{background:var(--accent); border-color:var(--accent); color:var(--accent-ink)}
@media(hover:hover){.btn.primary:hover{filter:brightness(1.06); background:var(--accent)}}
.btn.danger{color:var(--no); border-color:color-mix(in srgb, var(--no) 45%, var(--line)); background:transparent}
@media(hover:hover){.btn.danger:hover{background:var(--no-soft)}}
.btn.ghost{border-color:var(--line); color:var(--ink-soft)}
/* A button squeezed narrower than its own label breaks the label in half: a
   320px phone showed "Auto-" above "fill". Buttons hold their content width,
   so a tight row wraps or the text beside them reflows instead. .big is
   exempt - it is deliberately full-width. */
.btn:not(.big){flex-shrink:0}
/* ...and the form around it, where there is one. A <form> wrapping a single
   submit button is the flex item in these rows, so the button inherits its
   squeeze: protecting the button alone left "↑ Add" stacked as "↑" over "Add"
   at every width, which is how this was found. */
:where(.wrapup-line, .event-actionbar, .title-row, .line, .assign-line) > form{flex-shrink:0}
.btn.small{padding:7px 12px; font-size:13.5px; min-height:36px}
.btn.tiny{padding:5px 10px; font-size:12.5px; min-height:36px}
.btn.big{width:100%; padding:13px}
@media(min-width:560px){.btn.big{width:auto; min-width:200px}}
.quick-row{display:flex; gap:8px; flex-wrap:wrap; margin:10px 0}
.quick-row form{display:contents}

/* availability vote buttons */
.respond{display:flex; gap:8px; margin:8px 0}
.respond .vote{flex:1; min-height:44px}
.vote.yes.on{background:var(--yes); border-color:var(--yes); color:#fff}
.vote.maybe.on{background:var(--maybe); border-color:var(--maybe); color:#fff}
.vote.no.on{background:var(--no); border-color:var(--no); color:#fff}
@media(hover:hover){
  .vote.yes:hover{background:var(--yes); border-color:var(--yes); color:#fff}
  .vote.maybe:hover{background:var(--maybe); border-color:var(--maybe); color:#fff}
  .vote.no:hover{background:var(--no); border-color:var(--no); color:#fff}
}

/* partial-shift times */
.partial-times{margin:4px 0 2px}
.partial-times summary{cursor:pointer; -webkit-user-select:none; user-select:none}
.partial-times.indent{margin-left:34px}
.partial-row{display:flex; gap:10px; align-items:end; flex-wrap:wrap; margin-top:8px}
.field-row{display:flex; gap:12px}
.field-row label:first-child input{max-width:110px}
.num-line{display:flex; align-items:center; gap:8px; text-transform:none; letter-spacing:0;
  font:400 14.5px var(--sans); color:var(--ink)}
.num-line input{max-width:90px}
.inline-time{display:flex; flex-direction:column; gap:3px; font:600 11px var(--sans); letter-spacing:0; color:var(--ink-soft)}
.inline-time input{max-width:130px; min-height:38px; padding:6px 10px}
/* Wider than the time inputs they replaced: an overnight shift labels its
   small hours "01:30 next day", and a picker that truncates the one label
   somebody could misread is worse than no label at all. */
.inline-time select{max-width:170px; min-height:38px; padding:6px 28px 6px 10px;
  background-position:right 8px center}
.assigned-block{border-bottom:1px dashed var(--line); padding-bottom:6px; margin-bottom:6px}
.assigned-block:last-of-type{border-bottom:0; margin-bottom:0}

/* quick add (staff & clients) */
.quick-add{display:flex; gap:8px; flex-wrap:wrap; align-items:center}
.quick-add input[type=text],.quick-add input[type=tel]{flex:1 1 160px; min-width:140px}
.quick-add .check-line{flex:none}

/* tags */
.tag{
  display:inline-flex; align-items:center; font:600 11.5px var(--sans);
  letter-spacing:0; padding:0 9px; height:26px; border-radius:var(--r-badge); white-space:nowrap;
  border:1px solid transparent; background:var(--paper); color:var(--ink-soft);
}
.tag.st-draft{color:var(--ink-soft)}
.tag.st-open{color:var(--info); background:var(--info-soft)}
.tag.st-confirmed,.tag.st-worked{color:var(--yes); background:var(--yes-soft)}
.tag.st-done{color:var(--ink-soft)}
.tag.st-cancelled,.tag.st-flagged{color:var(--no); background:var(--no-soft)}
.st-planned{color:var(--maybe); background:var(--maybe-soft)}
.ok-tag{color:var(--yes); background:var(--yes-soft)}
.warn-tag{color:var(--maybe); background:var(--maybe-soft)}
.bad-tag{color:var(--no); background:var(--no-soft)}
/* On-site grade and cover level. A senior reads as information (the neutral
   info blue), a manager and a critical event read as weight (the brand), so
   the two things a rota manager scans a crew list for are the two that carry
   colour, and ordinary staff carry none at all. */
.grade-senior{color:var(--info); background:var(--info-soft)}
.grade-manager{color:var(--accent-text); background:var(--accent-soft);
  border-color:color-mix(in srgb, var(--accent-line) 45%, transparent)}
.cover-important{color:var(--info); background:var(--info-soft)}
.cover-critical{color:var(--accent-text); background:var(--accent-soft);
  border-color:color-mix(in srgb, var(--accent-line) 45%, transparent)}
a.tag{text-decoration:none}
.av-yes{color:var(--yes)} .av-maybe{color:var(--maybe)} .av-no{color:var(--no)}

/* client upcoming chips */
.chip-row{display:flex; gap:6px; flex-wrap:wrap; margin-top:10px}
.chip{
  display:inline-flex; align-items:center; gap:8px; text-decoration:none;
  font:600 12.5px var(--sans); color:var(--ink);
  border:1px solid var(--line); border-radius:var(--r-ctl); padding:4px 10px; background:var(--paper);
}
@media(hover:hover){.chip:hover{border-color:var(--accent-line); background:var(--card)}}
.chip-fill{font:600 11px var(--sans); color:var(--ink-soft)}
.chip-fill.ok{color:var(--yes)}

/* event cards */
.card-grid{display:grid; gap:10px}
@media(min-width:620px){.card-grid{grid-template-columns:1fr 1fr}}
.event-row{margin:12px 0}
/* grid/flex items refuse to shrink below content width by default — allow it,
   so the ellipsis rules inside cards can work instead of overflowing the page */
.card-grid > *, .grid-2 > *, .grid-3 > *, .avail-cols > *, .event-row > *{min-width:0}

.event-card{
  display:flex; gap:12px; align-items:flex-start; text-decoration:none; background:var(--card);
  min-width:0; max-width:100%;
  border:1px solid var(--line); border-radius:var(--r-card);
  padding:12px 14px; transition:border-color .12s;
}
/* Date tile + bar + count = how staffed this event is.
   ---------------------------------------------------------------
   This was the wrong way round: fully staffed painted RED and barely staffed
   painted GREEN. An admin scanning the dashboard for "what still needs people"
   was being drawn to the events that need nothing, and the one event with
   nobody on it read as the healthy one. Green now means staffed, amber means
   part-staffed, red means short. */
.fill-full .ec-tile{background:var(--yes-soft); border-color:transparent}
.fill-full .ec-tile b{color:var(--yes)}
.fill-half .ec-tile{background:var(--maybe-soft); border-color:transparent}
.fill-half .ec-tile b{color:var(--maybe)}
.fill-low .ec-tile{background:var(--no-soft); border-color:transparent}
.fill-low .ec-tile b{color:var(--no)}
.st-cancelled .ec-tile{background:var(--paper); border-color:var(--line)}
.st-cancelled .ec-tile b{color:var(--ink-soft)}
.fill-full .ec-progress i{background:var(--yes)}
.fill-half .ec-progress i{background:var(--maybe)}
.fill-low .ec-progress i{background:var(--no)}
.fill-half .ec-fill{color:var(--maybe)}
.fill-low .ec-fill{color:var(--no)}
@media(hover:hover){.event-card:hover{border-color:var(--accent-line)}}
.event-card:active{background:var(--paper)}
.ec-tile{flex:none; width:48px; text-align:center; border-radius:var(--r-card); padding:5px 0 6px;
  background:var(--paper); border:1px solid var(--line)}
.ec-tile em{display:block; font:600 var(--fs-micro) var(--sans); font-style:normal; letter-spacing:0; color:var(--ink-soft)}
.ec-tile b{display:block; font:600 18px var(--sans); line-height:1.15}
.ec-tile span{display:block; font:600 var(--fs-micro) var(--sans);
  letter-spacing:0; color:var(--ink-soft)}
.ec-body{flex:1; min-width:0}
/* The tags are nowrap and the name is not, so without wrapping the row the
   tags take their width first and the name gets whatever is left: a client
   called "Mills" rendered as "Mill" over "s" beside two status tags. The tags
   drop to their own line instead. */
.ec-top{display:flex; align-items:center; gap:8px; justify-content:space-between; flex-wrap:wrap}
/* The tags travel together: split across a wrap they read as two unrelated
   labels, one of them stranded under the name. */
.ec-tags{display:flex; gap:8px; flex:0 0 auto; margin-left:auto}
/* A client name can be one long unbroken string; `anywhere` is the right
   answer where it is honoured, `break-word` is the floor where it is not.
   `anywhere` also makes the min-content ONE CHARACTER, which is what let a
   short name be squeezed into a column and broken: hence the 9ch floor. */
.ec-client{font-weight:600; font-size:15.5px; flex:1 1 auto; min-width:min(9ch,100%);
  overflow-wrap:anywhere; word-wrap:break-word}
.ec-meta{color:var(--ink-soft); font-size:12.5px; margin-top:1px; overflow:hidden;
  text-overflow:ellipsis; white-space:nowrap}
.ec-addr{white-space:normal; margin-top:0}
.ec-progress{height:4px; border-radius:2px; background:var(--line); margin-top:9px; overflow:hidden}
.ec-progress i{display:block; height:100%; max-width:100%; border-radius:2px; background:var(--info)}
.ec-fill{font:600 12px var(--sans); margin-top:5px; color:var(--ink-soft)}

/* call sheet header */
.callsheet{
  background:var(--card); border:1px solid var(--line); border-radius:var(--r-panel);
  padding:16px; margin:8px 0 16px;
}
.cs-top{display:flex; justify-content:space-between; align-items:center}
.cs-num{font:600 18px var(--mono)}
.cs-client{font-size:26px; font-weight:600; margin:6px 0 12px; letter-spacing:-.021em}
.cs-table{width:100%; border-collapse:collapse; font-size:14.5px}
.cs-table th{
  text-align:left; font:600 11px var(--sans); letter-spacing:0;
  color:var(--ink-soft); padding:7px 12px 7px 0; width:86px; vertical-align:top;
  border-top:1px solid var(--line);
}
.cs-table td{padding:7px 0; border-top:1px solid var(--line)}

/* lines & lists */
.line{display:block; padding:9px 0; border-top:1px solid var(--line); text-decoration:none}
.line:first-of-type{border-top:0}
.line.strong{font-weight:600}
@media(hover:hover){a.line:hover{color:var(--accent-text)}}
a.line:active{color:var(--accent-text)}
.check-line{display:flex; gap:10px; align-items:center; min-height:44px; padding:11px 0;
  border-top:1px solid var(--line); cursor:pointer}
.check-line input{width:22px; height:22px; accent-color:var(--accent-line); flex:none}

/* One-of-three choice: cover level on an event, grade on a person. Rendered
   as radios on a form and as submit buttons on the staff list, so both are
   styled here. Wraps to two rows on a 320px screen rather than shrinking the
   targets below 44px. */
.seg-choice{display:flex; flex-wrap:wrap; gap:8px; align-items:center}
.seg-choice ul,.seg-choice div{display:flex; flex-wrap:wrap; gap:8px; list-style:none; margin:0}
/* Both selectors on purpose: these controls sit inside `.stack` forms, whose
   own `.stack label` rule is the same specificity and comes later in this
   file — it would otherwise stand the radio above its own word. */
.seg-choice label,.stack .seg-choice label{
  display:inline-flex; flex-direction:row; align-items:center; gap:8px; min-height:44px;
  padding:0 14px 0 12px; border:1px solid var(--line-strong); border-radius:var(--r-ctl);
  background:var(--card); font:500 14.5px var(--sans); color:var(--ink);
  letter-spacing:0; text-transform:none; cursor:pointer; flex:none;
}
.seg-choice input[type=radio]{width:20px; height:20px; accent-color:var(--accent-line); flex:none; margin:0}
.seg-choice label:has(input:checked){border-color:var(--accent-line); background:var(--accent-soft);
  color:var(--accent-text)}
.seg-choice input:focus-visible{outline:2px solid var(--accent-line); outline-offset:2px}
/* Two of these stack on a staff row — on-site grade, then app access. Same
   control for two genuinely different questions, so they look identical and
   are told apart by their label rather than by their styling. */
.grade-pick,.access-pick{margin-top:10px; padding-top:10px; border-top:1px solid var(--line)}
.grade-pick .btn.on,.access-pick .btn.on{background:var(--accent-soft); color:var(--accent-text);
  border-color:var(--accent-line)}
/* Rota-manager access reaches pay and settings, so the live state reads as
   something to notice rather than as another neutral choice. */
.access-pick .btn.on[value="1"]{background:var(--maybe-soft); color:var(--ink);
  border-color:color-mix(in srgb, var(--maybe) 55%, var(--line))}

/* Job roles on a staff row. Toggles rather than a one-of-three: somebody can
   do security and the bar, which is the whole reason the list is a list. */
.role-pick{margin-top:10px; padding-top:10px; border-top:1px solid var(--line)}
.role-pick form{display:contents}
.role-pick .btn.on{background:var(--info-soft); color:var(--ink);
  border-color:color-mix(in srgb, var(--info) 55%, var(--line))}

/* The catalogue itself: one row per role, each its own rename form. */
.role-chips{display:grid; gap:8px; margin:10px 0}
.role-chip{
  display:flex; align-items:center; gap:10px; flex-wrap:wrap;
  border:1px solid var(--line); border-radius:var(--r-card);
  background:var(--card); padding:8px 10px;
}
.role-chip.retired{opacity:.6}
.role-chip form{display:flex; gap:6px; align-items:center; margin:0}
.role-rename{flex:1 1 180px}
.role-rename input{
  flex:1; min-width:0; min-height:36px; padding:6px 10px;
  border:1px solid var(--line-strong); border-radius:var(--r-ctl);
  background:var(--paper); color:var(--ink); font:600 14px var(--sans);
}
.role-chip > .muted{margin-left:auto}

/* A group of job-role tickboxes, on the join form and both profile forms.
   Django's CheckboxSelectMultiple renders a div per choice, and app.js
   rebuilds the same shape — one shape to style, both ways in. */
.rolebox{border:1px solid var(--line); border-radius:var(--r-panel);
  background:var(--card); padding:12px 14px}
.rolebox legend{font:600 14px var(--sans); padding:0 4px}
.rolebox > div{display:flex; flex-wrap:wrap; gap:8px; margin-top:8px}
/* Both selectors, for the same reason `.seg-choice` needs both: these sit
   inside `.stack` forms, whose own `.stack label` rule has equal specificity
   and comes later in this file — it would stand each tickbox above its own
   word and turn a row of chips into a column. */
.rolebox label,.stack .rolebox label{
  display:inline-flex; flex-direction:row; align-items:center; gap:8px; min-height:44px;
  padding:0 14px 0 12px; border:1px solid var(--line-strong);
  border-radius:var(--r-ctl); background:var(--paper); letter-spacing:0;
  text-transform:none; font:500 14.5px var(--sans); color:var(--ink); cursor:pointer;
}
.rolebox input[type=checkbox]{width:20px; height:20px; accent-color:var(--accent-line); margin:0}
.rolebox label:has(input:checked){border-color:var(--accent-line); background:var(--accent-soft);
  color:var(--accent-text)}
.rolebox input:focus-visible{outline:2px solid var(--accent-line); outline-offset:2px}
.late-note{color:var(--no)}
.stand-down summary{color:var(--no)}

/* staffing blocks */
.role-block{border-top:1px solid var(--line); padding:10px 0}
.role-head{font-weight:600; display:flex; gap:8px; align-items:center; flex-wrap:wrap}
.assigned-line{display:flex; justify-content:space-between; align-items:center; gap:8px; flex-wrap:wrap; padding:6px 0 4px 12px}
.assign-line{display:flex; gap:8px; margin-top:8px}
.assign-line select{flex:1}
/* generic collapsible section (dashboard etc.) */
.fold > summary{cursor:pointer; -webkit-user-select:none; user-select:none; list-style:none;
  display:flex; align-items:center; justify-content:space-between; gap:10px}
.fold > summary::-webkit-details-marker{display:none}
.fold > summary .sheet-title{margin:0}
.fold > summary::after{content:""; flex:none; align-self:center; width:7px; height:7px;
  border:1.75px solid var(--ink-soft); border-top:0; border-left:0;
  transform:rotate(-45deg) translateY(-1px); border-radius:1px; transition:transform .15s}
.fold[open] > summary::after{transform:rotate(45deg) translateY(-2px)}
.fold[open] > summary{margin-bottom:10px}

/* filter & sort bar */
/* The filter trigger is a BUTTON, because pressing it is the only thing it
   does. As a bare disclosure line it read as a heading somebody had left
   expanded, and expanded is how it shipped - a search box and six chips
   between the page's alerts and its first event, on every visit. Closed by
   default, standing in the quick-row with the page's other actions, it costs
   one tap to reach and nothing to ignore. */
.filterbar{margin:4px 0 10px}
.filterbar > summary{cursor:pointer; -webkit-user-select:none; user-select:none; list-style:none;
  display:inline-flex; align-items:center; justify-content:center; gap:7px;
  width:max-content; max-width:100%;
  border:1px solid var(--line-strong); background:var(--card); color:var(--ink);
  font:600 14px/1.3 var(--sans); padding:9px 15px; min-height:44px;
  border-radius:var(--r-ctl); transition:background .12s,border-color .12s,color .12s}
.filterbar[open] > summary{background:var(--accent-soft); color:var(--accent-text);
  border-color:var(--accent-line)}
.filterbar > summary::-webkit-details-marker{display:none}
.filterbar > summary::after{content:""; flex:none; width:6px; height:6px;
  border:1.75px solid currentColor; border-top:0; border-left:0;
  transform:rotate(45deg) translateY(-2px); border-radius:1px; transition:transform .15s}
.filterbar[open] > summary::after{transform:rotate(-135deg) translateY(2px)}
/* In the action row it is one more chip until it opens, and then it needs the
   whole row: a search box and six chips inside a flex item sized to the word
   "Filters" is a column one control wide. */
.quick-row > .filterbar{margin:0}
.quick-row > .filterbar[open]{flex:1 0 100%}
.fb-row{display:flex; gap:7px; flex-wrap:wrap; align-items:center; padding:4px 0 6px}
.fb-search{flex:1 1 130px; min-width:120px; width:auto; min-height:34px; padding:5px 11px}
.fb-chip.on{background:var(--accent-soft); color:var(--accent-text); border-color:var(--accent-line)}
.fb-sortchip.on{background:var(--info-soft); color:var(--info); border-color:var(--info)}
.fb-label{font:600 var(--fs-micro) var(--sans); letter-spacing:0;
  color:var(--ink-soft); margin-left:3px}
.fb-count{font:600 12px var(--sans); color:var(--ink-soft)}

.avail-fold summary{cursor:pointer; -webkit-user-select:none; user-select:none; list-style:none;
  display:flex; align-items:center; justify-content:space-between; gap:10px; flex-wrap:wrap}
.avail-fold summary::-webkit-details-marker{display:none}
.avail-fold summary .sheet-title{margin:0}
.avail-fold summary::after{content:""; flex:none; align-self:center; width:7px; height:7px;
  border:1.75px solid var(--ink-soft); border-top:0; border-left:0;
  transform:rotate(-45deg) translateY(-1px); border-radius:1px; transition:transform .15s}
.avail-fold[open] summary::after{transform:rotate(45deg) translateY(-2px)}
.avail-fold[open] summary{margin-bottom:10px}
/* Availability counts. Each is its own inline-flex pill so the icon and the
   number can never separate: .i is display:block, and in a plain run of text
   that puts every tick on a line of its own with the number beneath it. */
.av-counts{display:flex; align-items:center; gap:6px; flex-wrap:wrap; margin-left:auto}
.av-c{display:inline-flex; align-items:center; gap:5px; white-space:nowrap;
  padding:3px 9px; border-radius:999px; border:1px solid var(--line);
  background:var(--card); font:500 12px var(--sans); color:var(--ink-soft)}
.av-c b{font-weight:700; font-variant-numeric:tabular-nums}
.av-c.yes{color:var(--yes)} .av-c.maybe{color:var(--maybe)} .av-c.no{color:var(--no)}
/* Wrapped onto their own line, they read as a row - not as a ragged right edge
   under the heading, which is what margin-left:auto leaves behind. */
@media(max-width:600px){
  /* Two rows, not three: heading and its arrow together, counts beneath. As a
     wrapping flex row the arrow was pushed below the counts and sat alone on a
     line of its own, pointing at nothing. */
  .avail-fold summary{display:grid; grid-template-columns:1fr auto; align-items:center}
  .avail-fold summary .sheet-title{grid-column:1; grid-row:1}
  .avail-fold summary::after{grid-column:2; grid-row:1}
  .av-counts{grid-column:1 / -1; margin-left:0; width:auto; margin-top:4px}
}
.more-list summary{cursor:pointer; -webkit-user-select:none; user-select:none; list-style:none;
  color:var(--ink-soft); font-size:13px; font-weight:600; padding:8px 0 2px}
.more-list summary::-webkit-details-marker{display:none}
.more-list summary::before{content:""; display:inline-block; width:6px; height:6px;
  margin-right:7px; border:1.75px solid currentColor; border-top:0; border-left:0;
  transform:rotate(-45deg); border-radius:1px}
.more-list[open] summary::before{transform:rotate(45deg) translateY(-2px)}
.avail-cols{display:grid; gap:14px}
@media(min-width:620px){.avail-cols{grid-template-columns:1fr 1fr 1fr 1fr}}
.av-h{font:600 12px var(--sans); letter-spacing:0; margin-bottom:4px}
.av-h.yes{color:var(--yes)} .av-h.maybe{color:var(--maybe)} .av-h.no{color:var(--no)}
.flagnote{color:var(--no); font-size:13px}
.respond-block{padding:4px 0 12px}
.respond-block .event-card{margin-bottom:8px}

/* whatsapp text */
.wa-text{
  font:12.5px/1.55 var(--mono); white-space:pre-wrap; background:var(--paper);
  border:1px dashed var(--line-strong); border-radius:var(--r-card);
  padding:12px; max-height:340px; overflow:auto;
}

/* forms */
.stack{display:flex; flex-direction:column; gap:14px}
.stack label{display:flex; flex-direction:column; gap:5px; font:600 11.5px var(--sans); letter-spacing:0; color:var(--ink)}
.stack label .small-text,.stack label .muted{text-transform:none; letter-spacing:0; font-weight:400; font-size:12.5px}
.stack label.check-line{flex-direction:row; align-items:center; text-transform:none;
  letter-spacing:0; font:500 14.5px var(--sans); color:var(--ink)}
input[type=text],input[type=password],input[type=date],input[type=time],input[type=number],
input[type=tel],input[type=email],input[type=search],
textarea,select{
  font:400 16px var(--sans); color:var(--ink); background:var(--card);
  border:1px solid var(--line-strong); border-radius:var(--r-ctl); padding:10px 12px;
  width:100%; min-height:44px; -webkit-appearance:none; appearance:none;
}
/* Slightly smaller inputs only on mouse/trackpad devices — any touch screen
   keeps 16px, below which iOS zooms the page when a field is focused. */
@media(min-width:760px) and (pointer:fine){
  input[type=text],input[type=password],input[type=date],input[type=time],input[type=number],
  input[type=tel],input[type=email],input[type=search],textarea,select{font-size:15px}
}
textarea{min-height:64px; line-height:1.45}
/* `appearance:none` strips the native dropdown arrow along with the native
   styling, which leaves a select looking exactly like a text box — people
   tapped them and waited for a keyboard. The arrow goes back as a background
   image rather than a pseudo-element, because a select cannot have one. The
   grey is fixed rather than themed: it clears 3:1 against both the light and
   the dark card, and a `currentColor` mask would cost a second rule in every
   theme to say the same thing. */
select{
  padding-right:32px;
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6' viewBox='0 0 10 6'%3E%3Cpath d='M1 1l4 4 4-4' fill='none' stroke='%238a8a8a' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
  background-repeat:no-repeat; background-position:right 11px center;
}
input:focus-visible,textarea:focus-visible,select:focus-visible{outline:2px solid var(--accent-line);
  outline-offset:1px}
/* Keyboard only, so a tap never paints a ring and a tab key always does. */
:focus-visible{outline:2px solid var(--accent-line); outline-offset:2px; border-radius:2px}
.grid-2{display:grid; gap:12px}
.grid-3{display:grid; gap:12px}
@media(min-width:560px){.grid-2{grid-template-columns:1fr 1fr}.grid-3{grid-template-columns:1fr 1fr 1fr}}
.errorlist{color:var(--no); font-size:13px; list-style:none}

/* flash messages */
.flash-stack{max-width:880px; margin:0 auto; padding:8px 16px 0}
.flash{
  border:1px solid var(--line); border-left:4px solid var(--yes); border-radius:var(--r-card);
  background:var(--card);
  padding:10px 12px; margin-bottom:6px; font-size:14px;
}
.flash.warning{border-left-color:var(--maybe)}
.flash.error{border-left-color:var(--no)}
.flash.info{border-left-color:var(--ink-soft)}

/* licence — the banner sits above the page, in the same place and the same
   shape as a flash message, because it is read the same way: one line, at the
   top, about something that needs attention. It is not dismissible. A banner
   you can close is one somebody closes in March and remembers in June. */
.licbar{max-width:880px; margin:0 auto; padding:8px 16px 0}
.licbar > a{
  display:flex; gap:10px; align-items:baseline; flex-wrap:wrap;
  border:1px solid var(--line); border-left:3px solid var(--maybe);
  border-radius:var(--r-card); background:var(--card);
  padding:10px 13px; font-size:14px; color:var(--ink); text-decoration:none;
}
.licbar.out > a{border-left-color:var(--no); background:var(--no-soft)}
.licbar strong{font-weight:600}
.licbar .licbar-do{font-weight:600; color:var(--accent-text); white-space:nowrap}
.sheet.bad .sheet-title::before{content:""; display:inline-block; width:7px; height:7px;
  border-radius:50%; background:var(--no); margin-right:8px; vertical-align:1px}
.licence-state{font:600 26px/1.2 var(--sans); letter-spacing:-.02em; margin:10px 0 2px}
/* The "or give us the exact number" line under the band radios: indented to
   sit with them rather than reading as the next question. */
.exact-line{display:block; margin-top:12px; padding-top:12px; border-top:1px solid var(--line)}

/* ---- landing: the playable event card ----
   There is almost nothing here, and that is the point. The demo is built from
   `.event-card`, `.respond` and `.partial-times` — the app's own components —
   so it inherits every rule that styles the real thing and cannot quietly
   drift into being a lookalike. The lookalike it replaced had done exactly
   that within a week. What is left is the width it sits in and the two bits
   of furniture the app has no use for. */
.try-section{padding-top:30px}
.try-lede{color:var(--ink-soft); font-size:15px; margin:-4px 0 16px}
.try{max-width:460px}
.try-flash{margin:12px 0 0}
.try-said{
  margin:10px 0 0; padding:11px 13px; font-size:14px; line-height:1.5;
  background:var(--accent-soft); border-radius:var(--r-card); color:var(--ink);
}
.try-foot{margin-top:14px; max-width:460px}

/* ---- landing: one worry at a time ----
   Radios drive this, so the tabs work with no script and arrow keys move
   between them for free. The inputs are off-screen but focusable, which is
   what keeps the focus ring landing on the chip you are actually on. */
.worry-lede{color:var(--ink-soft); font-size:15px; margin:-4px 0 14px}
.worry-tabs{display:flex; gap:8px; flex-wrap:wrap; margin-bottom:14px}
.worry-tabs label{
  cursor:pointer; border:1px solid var(--line-strong); background:var(--card);
  border-radius:var(--r-ctl); padding:9px 13px; min-height:40px;
  display:inline-flex; align-items:center;
  font:600 13.5px var(--sans); color:var(--ink-soft);
}
@media(hover:hover){.worry-tabs label:hover{border-color:var(--accent-line); color:var(--ink)}}
.worry-panel{display:none}
.worry-panel p{margin:0}
.worry-was{
  font-size:14px; color:var(--ink-soft); padding-left:13px;
  border-left:2px solid var(--line-strong); margin-bottom:11px !important;
}
.worry-now{font-size:15.5px; line-height:1.55}
/* Each checked radio lights its own chip and reveals its own panel. Written
   out rather than generated because four is four. */
#worry-1:checked ~ .worry-tabs label[for="worry-1"],
#worry-2:checked ~ .worry-tabs label[for="worry-2"],
#worry-3:checked ~ .worry-tabs label[for="worry-3"],
#worry-4:checked ~ .worry-tabs label[for="worry-4"]{
  background:var(--accent); border-color:var(--accent); color:var(--accent-ink);
}
#worry-1:checked ~ .worry-panels .worry-panel:nth-child(1),
#worry-2:checked ~ .worry-panels .worry-panel:nth-child(2),
#worry-3:checked ~ .worry-panels .worry-panel:nth-child(3),
#worry-4:checked ~ .worry-panels .worry-panel:nth-child(4){display:block}
/* The focus ring belongs on the chip, not on an input nobody can see. */
#worry-1:focus-visible ~ .worry-tabs label[for="worry-1"],
#worry-2:focus-visible ~ .worry-tabs label[for="worry-2"],
#worry-3:focus-visible ~ .worry-tabs label[for="worry-3"],
#worry-4:focus-visible ~ .worry-tabs label[for="worry-4"]{
  outline:2px solid var(--accent-line); outline-offset:2px;
}
/* No script, no :checked support, nothing hidden: show them all rather than
   show nothing. */
@supports not selector(:checked ~ *){.worry-panel{display:block; margin-bottom:16px}}

/* ---- landing: who sees what ----
   A permissions question answered as a table, because that is the shape the
   question has. Yes/No carry a word as well as a colour — the status must
   survive being printed, and being colour-blind. */
.seewhat{width:100%; border-collapse:collapse; margin:4px 0 16px}
.seewhat th,.seewhat td{
  padding:11px 8px; border-bottom:1px solid var(--line);
  font-size:14.5px; text-align:left; vertical-align:middle;
}
.seewhat thead th{
  font:600 11.5px var(--sans); letter-spacing:.02em; color:var(--ink-soft);
  text-transform:uppercase; padding-bottom:8px;
}
.seewhat tbody th{font-weight:400}
.seewhat thead th:not(:first-child),.seewhat td{text-align:center; width:84px}
.seewhat .yes{color:var(--yes); font-weight:600}
.seewhat .no{color:var(--ink-soft)}
@media(max-width:420px){
  .seewhat th,.seewhat td{font-size:13.5px; padding:10px 4px}
  .seewhat thead th:not(:first-child),.seewhat td{width:62px}
}

/* ---- landing: the before/after pairs ----
   One row per swap. The arrow is drawn in CSS and hidden from assistive
   tech: a screen reader gets "Typing the same message out again" followed by
   "Create the event once", which is the comparison already. */
.swaps{display:grid; gap:2px; margin:4px 0 14px}
.swap-row{
  display:grid; gap:4px 14px; align-items:baseline;
  padding:13px 0; border-bottom:1px solid var(--line);
}
.swap-row dt{font-size:14.5px; color:var(--ink-soft); text-decoration:line-through;
  text-decoration-color:var(--line-strong); text-decoration-thickness:1px}
.swap-row dd{margin:0; font-size:15.5px; font-weight:600; letter-spacing:-.008em}
.swap-row dd::before{
  content:"→"; color:var(--accent-text); font-weight:600;
  margin-right:8px; speak:never;
}
@media(min-width:620px){
  .swap-row{grid-template-columns:1fr 1fr; align-items:center}
  .swap-row dd::before{margin-right:10px}
}

/* The same tab mechanism on the How page, where it sits inside a .sheet and
   drives three audiences rather than four problems. */
#who-1:checked ~ .worry-tabs label[for="who-1"],
#who-2:checked ~ .worry-tabs label[for="who-2"],
#who-3:checked ~ .worry-tabs label[for="who-3"]{
  background:var(--accent); border-color:var(--accent); color:var(--accent-ink);
}
#who-1:checked ~ .worry-panels .worry-panel:nth-child(1),
#who-2:checked ~ .worry-panels .worry-panel:nth-child(2),
#who-3:checked ~ .worry-panels .worry-panel:nth-child(3){display:block}
#who-1:focus-visible ~ .worry-tabs label[for="who-1"],
#who-2:focus-visible ~ .worry-tabs label[for="who-2"],
#who-3:focus-visible ~ .worry-tabs label[for="who-3"]{
  outline:2px solid var(--accent-line); outline-offset:2px;
}
.who-tabs-block{margin-top:12px}

/* Legal documents. Measured for reading rather than skimming: a narrower
   column than the app, more room between sections, and headings that are
   findable without shouting. */
.legal{max-width:720px; margin:0 auto; padding:24px 16px 48px}
.legal h1{font-size:28px; letter-spacing:-.021em; margin:8px 0 4px}
.legal h2{font-size:17px; margin:30px 0 8px; letter-spacing:-.012em}
.legal p,.legal li{font-size:15px; line-height:1.62}
.legal p{margin:10px 0}
.legal ul{margin:10px 0 10px 20px; display:grid; gap:7px}
.legal .cs-table{margin:14px 0}
.legal .cs-table th,.legal .cs-table td{padding:8px 10px 8px 0; vertical-align:top;
  border-bottom:1px solid var(--line); font-size:14px; text-align:left; font-weight:400}
.legal .cs-table tr:first-child th{font-weight:600}
.legal-rule{border:0; border-top:1px solid var(--line); margin:32px 0 16px}
.legal-list{list-style:none; margin:20px 0; display:grid; gap:2px}
.legal-list a{display:block; padding:13px 0; border-bottom:1px solid var(--line);
  text-decoration:none; font-weight:600}
@media(hover:hover){.legal-list a:hover{color:var(--accent-text)}}
/* ---- the FAQ on How it works ----
   <details> rather than a stack of open paragraphs: ten questions all open
   is a page nobody scrolls, and the one somebody came for is the one they
   cannot find. Every answer is still in the HTML whether or not it is
   expanded, which is what the markup in the head promises. */
.faqs{display:grid; gap:2px; margin-top:4px}
.faq{border-bottom:1px solid var(--line)}
.faq:last-child{border-bottom:0}
.faq > summary{cursor:pointer; list-style:none; -webkit-user-select:none; user-select:none;
  padding:13px 30px 13px 0; position:relative}
.faq > summary::-webkit-details-marker{display:none}
.faq > summary h3{display:inline; font:600 15.5px var(--sans); letter-spacing:-.008em; margin:0}
.faq > summary::after{content:""; position:absolute; right:6px; top:19px;
  width:9px; height:9px; border-right:1.6px solid var(--ink-soft);
  border-bottom:1.6px solid var(--ink-soft); transform:rotate(45deg);
  transition:transform .15s ease}
.faq[open] > summary::after{transform:rotate(-135deg); top:22px}
.faq > p{margin:0 0 15px; font-size:14.5px; line-height:1.6; color:var(--ink-soft); max-width:62ch}
@media(hover:hover){.faq > summary:hover h3{color:var(--accent-text)}}
@media (prefers-reduced-motion: reduce){.faq > summary::after{transition:none}}

/* misc */
.tabs{display:flex; gap:2px; background:var(--card); border:1px solid var(--line); border-radius:var(--r-ctl); margin:10px 0; width:max-content; padding:3px}
.tabs a{padding:7px 16px; text-decoration:none; font:600 13px var(--sans); border-radius:var(--r-ctl); color:var(--ink-soft)}
.tabs a.on{background:var(--ink); color:var(--paper)}
.pager{display:flex; gap:6px}
.stat-row{display:flex; gap:10px; flex-wrap:wrap; margin:10px 0}
.stat{border:1px solid var(--line); border-radius:var(--r-card); background:var(--card); padding:7px 13px}
.stat-label{display:block; font:600 var(--fs-micro) var(--sans); letter-spacing:0; color:var(--ink-soft)}
.stat-val{font:600 16.5px var(--sans); letter-spacing:-.01em}
.login-box{max-width:420px; margin:6vh auto 0}
.sheet.inactive{opacity:.55}
.confirm-bar{margin-top:14px}
.flag-form{display:inline}

/* pay rates */
.rate-head,.rate-row{display:grid; grid-template-columns:1fr 1fr 80px; gap:10px; align-items:center}
.rate-head{font:600 11px var(--sans); letter-spacing:0; color:var(--ink-soft)}
.check-line.slim{border:0; padding:0; justify-content:center}
.est-line{border-top:2px solid var(--line-strong); margin-top:8px; padding-top:8px}
.rate-src{font-size:12px}

/* mileage */
.miles-form{display:flex; gap:8px; align-items:center; margin-top:6px}
.miles-form input{max-width:140px; min-height:36px; padding:6px 10px}
.wrapup-line{display:flex; justify-content:space-between; align-items:center; gap:10px}

/* avatars — rounded crew photos, initials tile as fallback */
.avatar{
  width:36px; height:36px; object-fit:cover; flex:none;
  border:1px solid var(--line); border-radius:50%; background:var(--card);
}
.avatar.lg{width:56px; height:56px}
.avatar.sm{width:24px; height:24px}
.avatar.initials{
  display:inline-flex; align-items:center; justify-content:center;
  font:600 13px var(--mono); color:#fff; letter-spacing:0;
}
.avatar.sm.initials{font-size:var(--fs-micro)}
.avatar.lg.initials{font-size:19px}
.hue-0{background:#6b7a44} .hue-1{background:#50687a} .hue-2{background:#9a6342}
.hue-3{background:#44785c} .hue-4{background:#7a5068} .hue-5{background:#8a782e}
.avatar-row{display:flex; align-items:center; gap:10px}
.avatar-row .sheet-title{margin-bottom:0}
.avatar-row.hero{margin:8px 0 14px}
.avatar-row.hero .pagetitle{margin:0 0 6px}
.grow{flex:1}
/* file inputs have an intrinsic width that can push layouts off-screen */
input[type=file]{width:100%; max-width:100%; min-width:0; font-size:13.5px; color:var(--ink-soft)}
.avatar-row > *{min-width:0}
.avatar-row .avatar{flex:none}
.title-row > *{min-width:0}
.photo-btn{position:relative; overflow:hidden; display:inline-flex}
.photo-btn input[type=file]{position:absolute; inset:0; opacity:0; cursor:pointer}
input[type=file]{font:400 14px var(--sans); color:var(--ink)}

/* public request form */
.hp{position:absolute; left:-9999px; top:-9999px}

/* first-run walkthrough */
.intro .sheet-title::before{content:""; display:inline-block; width:7px; height:7px;
  border-radius:50%; background:var(--accent-line); margin-right:8px; vertical-align:1px}
.intro-steps{margin:0 0 0 18px; display:grid; gap:8px; font-size:14px}
.intro-steps li::marker{font-family:var(--mono); font-weight:600; color:var(--accent-text)}

/* small phones: keep type compact */
@media(max-width:420px){
  body{font-size:14.5px}
  .pagetitle{font-size:20px}
  .cs-client{font-size:20px}
  .cs-num{font-size:16px}
  .ec-client{font-size:15.5px}
  .sheet{padding:13px}
  .btn{font-size:13.5px}
}

/* compact standby rows + the sticky action bar on the event page */
.standby-row{padding:5px 0}
/* quick staffing rows on the event form */
.role-quick{display:flex; flex-direction:column; gap:7px}
.rq-row{display:flex; align-items:center; justify-content:space-between; gap:10px;
  background:var(--paper); border:1px solid var(--line); border-radius:var(--r-card);
  padding:7px 8px 7px 12px}
.rq-row span{font:500 14px var(--sans); color:var(--ink); text-transform:none; letter-spacing:0}
.rq-row input.qty{max-width:76px; min-height:36px; padding:5px 10px; text-align:center}
/* The stepper app.js wraps around every input.qty: taps, not typing. */
.qty-step{display:inline-flex; align-items:stretch; border:1px solid var(--line);
  border-radius:10px; background:var(--card); overflow:hidden}
.qty-step button{width:42px; min-height:42px; border:0; background:transparent;
  font:600 20px/1 var(--sans); color:var(--ink); cursor:pointer}
.qty-step button:active{background:var(--accent-soft); color:var(--accent-text)}
.qty-step input.qty, .rq-row .qty-step input.qty{max-width:46px; min-height:42px; border:0;
  border-left:1px solid var(--line); border-right:1px solid var(--line);
  border-radius:0; text-align:center; font-weight:600; -moz-appearance:textfield}
.qty-step input::-webkit-outer-spin-button, .qty-step input::-webkit-inner-spin-button{
  -webkit-appearance:none; margin:0}
.repeat-form{padding-top:6px}
.repeat-form input[type=date]{width:auto; flex:1 1 140px; min-width:130px}
.standby-row .avatar-row{gap:8px; flex-wrap:wrap; min-width:0}
.assign-line select{min-height:38px; padding:6px 28px 6px 10px; background-position:right 8px center}
.event-actionbar{display:none}
@media(max-width:759px){
  .event-actionbar{
    position:fixed; left:0; right:0; z-index:19;
    bottom:calc(53px + env(safe-area-inset-bottom));
    display:flex; align-items:center; gap:8px; padding:8px 14px;
    background:var(--card);
    border-top:1px solid var(--line);
  }
  .event-actionbar form:last-child{margin-left:auto}
  /* The staffing sentence is the only part here that can afford to reflow,
     so it is the only part allowed to: min-width:0 lets it wrap below its
     natural width rather than pushing the buttons under theirs. */
  .ab-count{font:600 13px var(--sans); color:var(--ink-soft); flex:1 1 auto; min-width:0}
  .ab-count.ok{color:var(--yes)}
  body:has(.event-actionbar){padding-bottom:calc(112px + env(safe-area-inset-bottom))}
  .cs-table{font-size:13.5px}
  .cs-table td{padding:6px 0}
  .callsheet{padding:14px}
}

/* expandable shift rows on the staff Month page */
.shift-item{border-bottom:1px solid var(--line)}
.shift-item:last-of-type{border-bottom:0}
.shift-item summary{
  display:flex; align-items:center; gap:10px; padding:11px 2px;
  cursor:pointer; list-style:none; -webkit-user-select:none; user-select:none;
}
.shift-item summary::-webkit-details-marker{display:none}
.shift-item summary::after{content:""; flex:none; width:7px; height:7px; margin-left:auto;
  border:1.75px solid var(--ink-soft); border-top:0; border-left:0;
  transform:rotate(-45deg); border-radius:1px; transition:transform .15s}
.shift-item[open] summary::after{transform:rotate(45deg) translateY(-2px)}
.shift-item .tag{margin-left:0}
.shift-date{font:600 12px var(--mono); color:var(--ink-soft); min-width:52px;}
.shift-main{display:flex; flex-direction:column; gap:1px; min-width:0}
.shift-body{padding:2px 2px 12px 62px}
@media(max-width:560px){.shift-body{padding-left:2px}}

/* payments: checkout + SumUp-style day groups */
.co-row{display:flex; gap:10px; align-items:stretch}
.co-amount{display:flex; align-items:center; gap:6px; flex:1 1 120px; min-width:110px;
  border:1px solid var(--line-strong); border-radius:var(--r-card); padding:0 12px; background:var(--paper)}
.co-amount span{font:600 16px var(--sans); color:var(--ink-soft)}
.co-amount input{border:0; background:transparent; padding:9px 0; min-height:42px; width:100%;
  font:600 19px var(--sans); outline:none}
.co-amount:focus-within{outline:2px solid var(--accent-line); outline-offset:-1px}
.day-head{display:flex; justify-content:space-between; align-items:baseline;
  margin:16px 2px 6px; font:600 13px var(--sans); color:var(--ink-soft)}
.day-head b{font-size:13.5px; color:var(--ink)}
.pay-sheet{padding:4px 14px; margin-top:6px}
.pay-row{display:flex; align-items:center; gap:12px; padding:11px 0;
  border-bottom:1px solid var(--line); text-decoration:none}
.pay-sheet .pay-row:last-child{border-bottom:0}
.pay-ico{flex:none; width:34px; height:34px; border-radius:var(--r-ctl); display:inline-flex;
  align-items:center; justify-content:center; font:600 14px var(--sans);
  background:var(--paper); border:1px solid var(--line)}
.pay-ico.cash{color:var(--yes); background:var(--yes-soft); border-color:transparent}
.pay-ico.sumup{background:var(--info-soft); border-color:transparent}
.pay-main{display:flex; flex-direction:column; gap:1px; min-width:0; flex:1}
.pay-main strong{font-size:14.5px; overflow:hidden; text-overflow:ellipsis; white-space:nowrap}
.pay-main small{font-size:12px; overflow:hidden; text-overflow:ellipsis; white-space:nowrap}
.pay-amt{font:600 15.5px var(--sans); white-space:nowrap}
.plain-link{color:inherit; text-decoration:none}
/* A row title that happens to be a link is the row's target, not a 17px line
   of text. Scoped to the title position: .plain-link is also worn by .pay-row,
   whose own display:flex this would otherwise overwrite. */
strong > .plain-link{display:inline-block; padding:4px 0}
.month-row .day-head{margin:8px 2px 2px}
.month-row .pay-main small{white-space:normal}
.month-row .pay-row:last-of-type{border-bottom:0}

.maplink{color:inherit; text-decoration:none; border-bottom:1px dotted var(--line-strong)}
.maplink .map-pin{color:var(--info); font-weight:600}
.join-code{font-family:var(--mono); letter-spacing:0; background:var(--accent-soft);
  color:var(--accent-text); padding:2px 8px; border-radius:var(--r-badge)}
.demo-box .sheet-title{margin-bottom:10px}
.demo-box .quick-row{margin-bottom:10px}
code{font-family:var(--mono); font-size:13px}

/* drop-out & coverage alerts */
.dropout-line{
  display:flex; justify-content:space-between; align-items:center; gap:10px; flex-wrap:wrap;
  border:1px solid color-mix(in srgb, var(--no) 40%, var(--line));
  border-left:4px solid var(--no); border-radius:var(--r-card); background:var(--no-soft);
  padding:10px 12px; margin-bottom:10px; font-size:14px;
}
.dropout-line.over{
  border-color:color-mix(in srgb, var(--maybe) 40%, var(--line));
  border-left-color:var(--maybe); background:var(--maybe-soft);
}

/* how it works */
.how-loop{display:grid; gap:10px; margin:16px 0}
/* auto-fit, not a fixed four: this runs with four steps on the how-it-works
   page and three on the landing page, and a fixed four left the landing one
   with an empty column and three squeezed steps beside it. */
@media(min-width:620px){.how-loop{grid-template-columns:repeat(auto-fit,minmax(150px,1fr))}}
.how-step{
  border:1px solid var(--line); border-radius:var(--r-card);
  background:var(--card); padding:12px;
}
.how-step b{
  display:inline-flex; align-items:center; justify-content:center;
  width:30px; height:30px; border-radius:var(--r-ctl);
  background:var(--accent-soft); color:var(--accent-text);
  font:600 15px var(--sans); margin-bottom:6px;
}
.how-step strong{display:block; margin:2px 0}
.how-step span{color:var(--ink-soft); font-size:14px}
.intro-steps em{font-style:normal; font-family:var(--mono); font-size:13px}

/* CSV import preview */
.csv-preview{overflow-x:auto}
.csv-preview table{border-collapse:collapse; font-size:12.5px; white-space:nowrap}
.csv-preview th{text-align:left; color:var(--ink-soft); font-size:11px;
  letter-spacing:0; padding:4px 12px 4px 0}
.csv-preview td{padding:4px 12px 4px 0; border-top:1px solid var(--line); color:var(--ink)}

/* price list on the how-it-works page */
.price-table th{font-weight:600; color:var(--ink); text-transform:none; letter-spacing:0}
.price-table td{text-align:right; font-weight:600}

/* ===========================================================================
   Signed-out pages: landing, start-your-company.

   Same tokens as the app, so the page somebody signs up on and the product
   they land in are recognisably the same thing. The rules the rest of this
   stylesheet follows apply here too — no gradient, no glass, no pill, nothing
   above 600, sentence case — because a landing page is where the temptation
   to reach for all four is strongest, and where doing so makes a real product
   look like a template.
   =========================================================================== */
body.public{padding-bottom:0}

.pub-bar{
  display:flex; align-items:center; justify-content:space-between; gap:14px;
  padding:14px 16px; border-bottom:1px solid var(--line); background:var(--paper);
  position:sticky; top:0; z-index:20;
}
.pub-nav a{
  font:600 14px var(--sans); color:var(--ink-soft); text-decoration:none;
  padding:9px 12px; border-radius:var(--r-ctl); min-height:44px;
  display:inline-flex; align-items:center;
}
@media(hover:hover){.pub-nav a:hover{color:var(--ink); background:var(--card)}}

.pub-main{max-width:880px; margin:0 auto; padding:0 16px}

/* hero */
.hero{padding:40px 0 28px; border-bottom:1px solid var(--line)}
.eyebrow{font:600 12.5px var(--sans); color:var(--accent-text); margin-bottom:12px}
.hero h1{
  font-size:34px; font-weight:600; letter-spacing:-.022em; line-height:1.12;
  margin-bottom:14px; max-width:16ch;
}
.lede{font-size:17px; line-height:1.5; color:var(--ink-soft); max-width:56ch; margin-bottom:22px}
.hero-do{display:flex; gap:10px; flex-wrap:wrap}
.hero-note{margin-top:16px; font-size:13.5px; color:var(--ink-soft); line-height:1.5}

/* A phone is the whole use case, so the type scales UP from here, not down. */
@media(min-width:680px){
  .hero{padding:68px 0 48px}
  .hero h1{font-size:50px; max-width:18ch}
  .lede{font-size:19px}
}

.pub-section{padding:36px 0; border-bottom:1px solid var(--line)}
.pub-section h2{font-size:22px; font-weight:600; letter-spacing:-.015em; margin-bottom:18px}

/* what it replaces */
.swap{display:grid; gap:14px}
@media(min-width:680px){.swap{grid-template-columns:1fr 1fr; gap:18px}}
.swap-was,.swap-now{
  border:1px solid var(--line); border-radius:var(--r-panel); padding:16px 18px;
  background:var(--card);
}
.swap-was{background:var(--paper)}
.swap h3{font:600 13px var(--sans); color:var(--ink-soft); margin-bottom:10px}
.swap-now h3{color:var(--accent-text)}
.swap ul{list-style:none; display:flex; flex-direction:column; gap:9px}
.swap li{font-size:14.5px; line-height:1.45; padding-left:18px; position:relative}
.swap-was li::before{
  content:""; position:absolute; left:0; top:.55em; width:9px; height:1.5px;
  background:var(--ink-soft);
}
.swap-now li::before{
  content:""; position:absolute; left:1px; top:.3em; width:5px; height:9px;
  border:1.75px solid var(--accent-text); border-top:0; border-left:0;
  transform:rotate(40deg);
}

/* "Which one are you?" — three audiences, three routes, said out loud.
   The owner's door comes first and carries the primary button; the other two
   are the same weight as each other, because a new starter with a code and
   somebody coming back are equally likely and neither is a conversion. */
.doors-section{padding-top:30px}
.doors{display:grid; gap:14px}
@media(min-width:760px){.doors{grid-template-columns:repeat(3,1fr); gap:16px; align-items:start}}
.door{
  border:1px solid var(--line); border-radius:var(--r-panel); padding:18px;
  background:var(--card); display:flex; flex-direction:column; gap:10px;
}
.door h3{font-size:16.5px; font-weight:600; letter-spacing:-.01em}
.door p{font-size:14.5px; line-height:1.5; color:var(--ink-soft)}
.door .btn{align-self:flex-start}
/* On a phone the buttons are the point of the card, so they go full width —
   a 120px button under a paragraph is the thing people miss. */
@media(max-width:559px){.door .btn{align-self:stretch; width:100%}}
.door > :last-child{margin-top:auto}

/* The code box: type it here and the join page opens with it filled in,
   which is the same thing a manager's shared link does. */
.door-code{display:flex; gap:8px; flex-wrap:wrap}
.door-code input{
  flex:1 1 9ch; min-width:9ch; min-height:44px; padding:10px 12px;
  border:1px solid var(--line-strong); border-radius:var(--r-ctl);
  background:var(--paper); color:var(--ink);
  font:600 16px var(--mono); letter-spacing:.06em; text-transform:uppercase;
}
/* Back to the body face for the hint: the monospace tracking that makes a
   typed code easy to read character by character makes "e.g. CREW24" look
   like a gap in the box. */
.door-code input::placeholder{font:400 15px var(--sans); letter-spacing:0;
  text-transform:none; color:var(--ink-soft)}
.door-code .btn{flex:0 0 auto; align-self:auto; width:auto}

/* who does what */
.roles{display:grid; gap:12px; list-style:none; margin:0}
@media(min-width:760px){.roles{grid-template-columns:repeat(3,1fr); gap:16px}}
.role{
  border:1px solid var(--line); border-left:3px solid var(--line-strong);
  border-radius:var(--r-card); background:var(--card); padding:14px 16px;
}
.role-n{
  display:inline-flex; align-items:center; justify-content:center;
  width:22px; height:22px; border-radius:50%; margin-bottom:8px;
  background:var(--accent-soft); color:var(--accent-text);
  font:600 12px var(--sans);
}
.role h3{font-size:15.5px; font-weight:600; margin-bottom:6px; letter-spacing:-.008em}
.role p{font-size:14px; line-height:1.5; color:var(--ink-soft)}

/* A plain term-and-answer list. Deliberately not four cards in a grid: these
   are four answers, and a card around each one adds furniture, not meaning. */
.plain-list{display:grid; gap:0}
.plain-list dt{
  font-size:15.5px; font-weight:600; letter-spacing:-.008em;
  border-top:1px solid var(--line); padding-top:14px; margin-top:14px;
}
.plain-list dt:first-child{border-top:0; padding-top:0; margin-top:0}
.plain-list dd{font-size:14.5px; line-height:1.55; color:var(--ink-soft); margin-top:5px}

/* A short list of plain points, ticked. Same mark as the "Rotamate" column
   of the comparison, so "this is what you get" reads the same way twice. */
.plain-points{list-style:none; display:grid; gap:9px; margin:0}
.plain-points li{font-size:14.5px; line-height:1.45; color:var(--ink-soft);
  padding-left:19px; position:relative}
.plain-points li::before{
  content:""; position:absolute; left:1px; top:.3em; width:5px; height:9px;
  border:1.75px solid var(--accent-text); border-top:0; border-left:0;
  transform:rotate(40deg);
}

.how-strip .how-loop{margin:0; list-style:none}

.pub-cta{padding:40px 0 56px; text-align:center}
.pub-cta h2{font-size:24px; font-weight:600; letter-spacing:-.015em; margin-bottom:8px}
.pub-cta p{color:var(--ink-soft); margin-bottom:20px}
.pub-cta .hero-do{justify-content:center}

.pub-foot{
  border-top:1px solid var(--line); padding:22px 16px calc(22px + env(safe-area-inset-bottom));
  display:flex; gap:18px; flex-wrap:wrap; justify-content:center;
  font-size:13.5px; color:var(--ink-soft);
}
/* Navigation, not a credit line: "Sign in" and "How it works" are reached
   from here, so each gets a 40px target rather than a 20px line of text. */
.pub-foot a{color:var(--ink-soft); display:inline-block; padding:10px 4px}
/* The credit reads as a credit: same muted weight as the rest of the footer,
   not a call to action competing with "Sign in". */
.foot-by{color:var(--ink-soft)}
.foot-by a{text-decoration:underline; text-underline-offset:2px}

/* The in-app credit. Last thing in the scroll, clearing the tab bar so the
   final row of a long rota is never hidden behind it. */
.app-foot{
  max-width:880px; margin:0 auto;
  padding:18px 16px calc(18px + var(--tabbar-h) + env(safe-area-inset-bottom));
  text-align:center; font-size:13px; color:var(--ink-soft);
  display:flex; flex-direction:column; gap:6px; align-items:center;
}
.app-foot a{color:var(--ink-soft); text-decoration:underline; text-underline-offset:2px;
  display:inline-block; padding:5px 2px}
@media(hover:hover){.app-foot a:hover{color:var(--accent-text)}}
/* No tab bar when signed out, so no gap to clear. */
body:not(:has(.tabbar)) .app-foot{padding-bottom:calc(18px + env(safe-area-inset-bottom))}

/* signed-out forms */
.pub-form{max-width:520px; margin:0 auto; padding:28px 0 56px}
.pub-form h1{font-size:28px; font-weight:600; letter-spacing:-.018em; margin:10px 0 10px}
.pub-form .lede{font-size:16px; margin-bottom:20px}
.pub-form .sheet{margin-bottom:16px}

/* The join code, and the company it turns out to belong to.

   The code is the whole of the routing on a signed-out request, so the field
   is set apart from the rest of the form and the answer sits directly under
   it. Reserving the line's height in both states stops the form jumping under
   somebody's thumb the moment the sixth character lands. */
.code-field{
  border:1px solid var(--line-strong); border-radius:var(--r-panel);
  background:var(--card); padding:14px; display:grid; gap:10px;
}
.code-field input{font:600 17px var(--mono); letter-spacing:.08em; text-transform:uppercase}
/* The hint is a sentence, not a code, so it keeps the body face and its own
   case — uppercased monospace reads as something already typed in. */
.code-field input::placeholder{font:400 15px var(--sans); letter-spacing:0;
  text-transform:none; color:var(--ink-soft)}
.code-who{
  display:flex; align-items:center; gap:7px; min-height:38px; margin:0;
  border-radius:var(--r-card); padding:8px 11px; font-size:14px; line-height:1.4;
  background:var(--yes-soft); color:var(--ink);
  border:1px solid color-mix(in srgb, var(--yes) 40%, var(--line));
}
/* The tick belongs to the "yes, this is the company" state only, so it is in
   the markup once and shown by state rather than written by two code paths. */
.code-tick{display:none; align-items:center; color:var(--yes); flex:none}
.code-who:not(.empty):not(.bad) .code-tick{display:inline-flex}
.code-who.empty{background:var(--paper); border-color:var(--line); color:var(--ink-soft)}
.code-who.bad{
  background:var(--no-soft); color:var(--ink);
  border-color:color-mix(in srgb, var(--no) 40%, var(--line));
}
.code-who.busy{opacity:.6}

/* Reachable by a screen reader, invisible on screen — for a label whose
   field is already explained by its placeholder and the text around it. */
.sr-only{
  position:absolute; width:1px; height:1px; padding:0; margin:-1px;
  overflow:hidden; clip-path:inset(50%); white-space:nowrap; border:0;
}
/* The honeypot. Off-screen rather than display:none, which some bots skip. */
.hp{position:absolute; left:-9999px; width:1px; height:1px; overflow:hidden}

/* ---- signup band picker ----------------------------------------------------
   The chips carry the range; the figure appears in ONE revealed line below.
   Pure CSS: `form:has(#id_band_N:checked)` shows the matching .band-fee-N, so
   it works with every script blocker. A browser too old for :has() shows the
   chips and no figure - the price still arrives, worded exactly, on the Stripe
   page that follows - which degrades softer than showing all ten at once. */
.band-grid{display:flex; flex-wrap:wrap; gap:8px; margin:10px 0 2px}
.band-chip,.stack .band-chip{display:inline-flex; align-items:center; min-height:44px;
  padding:8px 14px; border:1px solid var(--line-strong); border-radius:var(--r-ctl);
  background:var(--card); font:500 14px var(--sans); cursor:pointer; margin:0}
.band-chip input{position:absolute; width:1px; height:1px; opacity:0; pointer-events:none}
.band-chip:has(input:checked){background:var(--accent-soft);
  border-color:var(--accent-line); font-weight:600}
.band-chip:has(input:focus-visible){outline:2px solid var(--accent-line); outline-offset:2px}
.band-fee{display:none; margin:8px 0 0; font:400 15px var(--sans)}
.band-fee strong{font-size:17px}
form:has(#id_band_0:checked) .band-fee-0{display:block}
form:has(#id_band_1:checked) .band-fee-1{display:block}
form:has(#id_band_2:checked) .band-fee-2{display:block}
form:has(#id_band_3:checked) .band-fee-3{display:block}
form:has(#id_band_4:checked) .band-fee-4{display:block}
form:has(#id_band_5:checked) .band-fee-5{display:block}
form:has(#id_band_6:checked) .band-fee-6{display:block}
form:has(#id_band_7:checked) .band-fee-7{display:block}
form:has(#id_band_8:checked) .band-fee-8{display:block}
form:has(#id_band_9:checked) .band-fee-9{display:block}
.exact-block{margin-top:12px; padding-top:10px; border-top:1px dashed var(--line)}
.exact-block .exact-line,.stack .exact-block .exact-line{display:flex; flex-wrap:wrap;
  align-items:center; gap:10px}
.exact-block .exact-line > span{flex:1 1 230px}
.exact-block input{width:120px}

/* ── The dashboard's attention stack ──────────────────────────────────────
   New client requests, nobody senior booked, confirm rotas: each is a
   <details class="sheet warn fold">, and as plain sheets they cost 80px of
   phone each while closed - three of them ate a third of the screen to say
   three short sentences. They are one family of alerts, so they read as a
   compact stack of cards: a tappable row each, the count as a badge, and
   the detail unfolding inside the same card.

   Scoped to .sheet.warn.fold so the ordinary sheets elsewhere are untouched. */
.sheet.warn.fold{
  margin:8px 0;
  background:var(--card); border:1px solid var(--line);
  border-radius:var(--r-card); padding:0 12px;
}
.sheet.warn.fold > summary{min-height:46px; padding:6px 0}
.sheet.warn.fold > summary .sheet-title{
  font-size:15.5px; padding-bottom:0; display:flex; align-items:center; gap:8px; min-width:0;
}
/* The count rides as a badge instead of trailing the heading in brackets. */
.sheet.warn.fold > summary .sheet-title .n{
  flex:none; min-width:21px; height:21px; padding:0 6px; border-radius:999px;
  background:var(--accent-soft); color:var(--accent-text);
  font:700 12px/21px var(--sans); text-align:center;
}
.sheet.warn.fold[open] > summary{
  margin-bottom:0; border-bottom:1px solid var(--line);
}
.sheet.warn.fold > summary::after{margin-left:4px}

/* The rows inside. Two lines of text that may wrap, and an action that never
   does: "Staff it →" was breaking across two lines beside a wrapping link. */
.sheet.warn.fold .line{padding:3px 0}
.sheet.warn.fold .wrapup-line{align-items:center; gap:12px}
/* A one-line alert ("No.74 Avanti Coast — Mon 02") was a 19px tap target
   sitting beside a 36px button. The row is the target; give it the height. */
.alert-row{
  min-width:0; flex:1 1 auto; text-decoration:none; color:var(--ink);
  display:flex; flex-direction:column; gap:2px; align-items:flex-start;
  justify-content:center; min-height:44px;
}
.alert-row .t{font-weight:600; line-height:1.3}
.sheet.warn.fold .wrapup-line .btn,
.sheet.warn.fold .wrapup-line form{flex:none; white-space:nowrap}
/* The event's own line reads as a title; what it needs reads as its note. */
.sheet.warn.fold .wrapup-line .muted{font-size:13.5px}
.sheet.warn.fold .wrapup-line .tag{align-self:flex-start}
@media(hover:hover){a.alert-row:hover .t{color:var(--accent-text)}}
