/* ============================================================
   Care Pack, v1.1. The shared skin for the whole product.

   This is the design that was drawn and chosen, ported rather
   than reinterpreted: the rail, the ruled panel with ticked
   corners, caps labels in mono, tabular numbers, one accent.
   Nine dashboard pages live inside the rail; seven screens
   replace it, because a roaster meeting them has no dashboard
   to be inside yet.

   THE LOOK IS A ROASTER SETTING, not a constant. Settings has a
   "How this looks" section, so panels, headings, the menu marker,
   the typeface, tips and light-or-dark are all things a roaster
   chooses. Every one of them is an attribute on <html>, not a
   class on <body>, for one reason: a blocking script in <head>
   can set an attribute on the document element before the first
   paint, and cannot touch <body> because it does not exist yet.
   Doing it the other way is a flash of the wrong look on every
   page load, which is exactly the bug this replaces.

     data-theme   light | dark
     data-frames  ticked | card
     data-labels  caps | spoken
     data-mark    arrow | brackets
     data-type    plex | grotesk | archivo
     data-tips    on | off

   Defaults are the ones chosen off the lab: ticked, caps, arrow,
   plex, tips on, light.
   ============================================================ */

@import url('https://fonts.googleapis.com/css2?family=IBM+Plex+Sans:wght@400;500;600;700&family=IBM+Plex+Mono:wght@400;500;600&family=Space+Grotesk:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500;600&family=Archivo:wght@400;500;600;700&display=swap');
/* ============================================================
   THE WHOLE PRODUCT, RESKINNED

   Two halves.

   The dashboard, nine pages, the ones in the live nav: Home,
   Orders, Customers, Products, Coffee, Look, Analytics, Tools,
   Settings. Each one is clickable rather than a still: the
   Products tabs switch, an order row opens its own detail, a
   customer row opens its drawer, picking a coffee changes the
   right pane, the Settings sections open.

   And the screens that happen outside it, which a roaster still
   meets: the set-up wizard, the recipes editor, sign in, sign up,
   password reset, the cafe sign-up page, and paying an invoice.

   Headings, sub-lines, chips, button names and status wording are
   lifted from the real pages. Nothing here is invented copy where
   real copy exists.

   The look is the one chosen off Look Lab 2: light grey ground,
   sans type, an arrow on the selected nav item, hairline frames
   with ticked corners, Care Pack green, small labels in caps, no
   texture. Numbers are mono and tabular everywhere.
   ============================================================ */
:root{
  --ground:#EDEDEF; --panel:#FFFFFF;
  --hair:#DEDEE2; --hair-2:#C2C2C9;
  --ink:#0B0B0C; --ink-2:#4A4A52; --ink-3:#68686F;
  /* --ink-3 is the muted ink, and most of what wears it is 9.5-12px: counts,
     unit suffixes, field labels. It was #75757D, which is 4.57:1 on a white
     panel but only 3.91:1 on the page ground, and a good half of that text
     sits on the ground rather than in a panel. #68686F clears 4.5 on both
     (4.73 ground, 5.53 panel) and is still plainly lighter than --ink-2.
     Dark's #82828D already measures 4.95 on its ground and is left alone. */
  --grid:rgba(11,11,12,.075);

  --accent:#00682F; --accent-ink:#FFFFFF;
  /* Deepened from #00793A. The accent is used as text as well as a fill, and
     --tint is 13% of the accent itself, so accent-coloured text most often
     lands on accent-tinted ground: the selected rail item, a chosen option,
     a selected list row. #00793A measured 3.97 there against the 4.5 that
     12.5px text needs. Lightening the tint cannot rescue it (5% tint still
     only reaches 4.43), so the colour itself had to move. #00682F reads 4.91
     on the tint, 5.96 on the ground, and takes white-on-accent buttons from
     6.28 to 6.96. Dark's #3DBB74 already measures 5.98 on its tint. */
  --a2:#199A54; --a3:#5FBC8B; --a4:#9BD5B5;
  --a2:color-mix(in srgb, var(--accent) 76%, var(--ground));
  --a3:color-mix(in srgb, var(--accent) 50%, var(--ground));
  --a4:color-mix(in srgb, var(--accent) 27%, var(--ground));
  --tint:color-mix(in srgb, var(--accent) 13%, transparent);
  /* The band behind a pinned column heading. Opaque on purpose (mixed into
     the ground, not transparent) because rows scroll under it; a faint green
     so it reads as a title, not as a row that forgot to move (Lukas, 7 Sep). */
  --head-band:color-mix(in srgb, var(--accent) 7%, var(--ground));

  /* the two colours allowed to mean something is wrong */
  --bad:#B3261E; --bad-soft:color-mix(in srgb, #B3261E 9%, transparent);
  --warn:#8A5A2B;

  /* Chart series. Four different hues, not four shades of one: a line graph
     of five cafes has to be readable without hovering every line, and the
     accent ramp above is a single hue by design. Series 1 is the accent, so
     the first line on any chart is the roaster's own colour. */
  --series-1:var(--accent); --series-2:#1D4ED8; --series-3:#B45309; --series-4:#7E22CE;

  /* The warehouse. One more colour, for one more room: the Warehouse tab, its
     panels, the card on Home and the floor screen wear it so the office
     knows when it is looking at the floor rather than at the books. Amber
     red, almost orange, beside the green (Lukas, 6 Sep 2026). #C4470C
     measures 4.9 on the ground, so it can be 12.5px text as well as a
     fill. Dark's #F58A3C reads 8.1. */
  --wh:#C4470C; --wh-ink:#FFFFFF;
  --wh-tint:color-mix(in srgb, var(--wh) 11%, transparent);

  --sans:-apple-system,BlinkMacSystemFont,"Segoe UI","Helvetica Neue",Helvetica,Arial,sans-serif;
  --mono:ui-monospace,"SF Mono",SFMono-Regular,Menlo,Consolas,"Liberation Mono",monospace;

  --s1:4px; --s2:8px; --s3:12px; --s4:18px; --s5:26px; --s6:38px;
  --rail:198px; --cap:uppercase; --caps-ls:.16em;
}
*{box-sizing:border-box}
body{margin:0;background:var(--ground);color:var(--ink);font:14px/1.55 var(--sans);-webkit-font-smoothing:antialiased}
button,input,select,textarea{font:inherit;color:inherit}
button{background:none;border:0;cursor:pointer}
:focus-visible{outline:2px solid var(--accent);outline-offset:2px}
.sprite rect{shape-rendering:crispEdges}

.app{display:flex;min-height:100vh;align-items:stretch}
/* The rail is made of links so a roaster can middle-click a tab open, and so
   a screen reader announces a place rather than a control. Nothing in the
   rail should look like a web link, hence the reset. */
.rail a{color:inherit;text-decoration:none;background:none;border-radius:0}

/* ---------------- the rail ---------------- */
.rail{width:var(--rail);flex:0 0 var(--rail);border-right:1px solid var(--hair);
  padding:var(--s4) 0;display:flex;flex-direction:column;position:sticky;top:0;height:100vh;overflow:auto}
.brand{display:flex;align-items:center;gap:9px;padding:0 var(--s4) var(--s5)}
/* The mark is a drawn wordmark, so it gets the rail's width rather than a
   fixed square: it already contains the words, and a 24px tile beside a
   repeated "Care Pack" was two marks doing one job. */
.brand .mk{display:block;width:auto;height:auto;max-width:100%;max-height:38px;object-fit:contain}
.brand b{font-size:14.5px}

.nav{display:flex;flex-direction:column}
.nav a{display:flex;align-items:center;gap:9px;width:100%;text-align:left;position:relative;
  padding:10px var(--s4) 10px 30px;border-bottom:1px solid var(--hair);font-size:12.5px;color:var(--ink-2);
  text-transform:var(--cap);letter-spacing:var(--caps-ls)}
.nav a:first-child{border-top:1px solid var(--hair)}
.nav a .sp{flex:1}
.nav a .ct{font:11.5px/1 var(--mono);color:var(--ink-3);font-variant-numeric:tabular-nums;letter-spacing:0}
/* The tint under a selection sits between the muted ink and the ground and
   costs about 0.8 of a contrast ratio, in both themes. Muted ink clears 4.5
   on the plain ground and not on the tint, so a selected row promotes its
   quiet text one step rather than every page inventing its own colour. */
.nav a.on .ct,.lookopt.on span{color:var(--ink-2)}
.nav a:hover{color:var(--ink)}
.nav a.on{color:var(--accent);font-weight:600;background:var(--tint)}
.nav a.on::before{content:"\25B6";position:absolute;left:12px;font-size:9px;color:var(--accent)}
.nav a.beta .ct{color:var(--accent)}
/* The Warehouse tab. A gap and a rule above it, because it is not the tenth
   page of the books, it is the door to the other room; and its own colour
   for the same reason. Its count is how many things are low. */
.nav a.wh{margin-top:var(--s3);border-top:1px solid var(--hair);color:var(--wh)}
.nav a.wh:hover{color:var(--wh)}
.nav a.wh.on{color:var(--wh);background:var(--wh-tint)}
.nav a.wh.on::before{color:var(--wh)}
.nav a.wh .ct{color:var(--wh)}
.nav a.wh .ct:empty{display:none}
.nav a.wh+.subnav{background:var(--wh-tint)}
.nav a.wh+.subnav a:hover,.nav a.wh+.subnav a.on{color:var(--wh)}
/* A panel about the warehouse, wherever it sits. The frame and its ticked
   corners take the warehouse colour, which is all it needs to read as the
   other room: the card on Home wears it, and so does the switched-off screen. */
.p.wh{--hair-2:var(--wh);border-color:var(--wh)}
.p.wh .ph h2{color:var(--wh)}

/* The sections of the tab you are already on, listed underneath it. Only the
   open tab gets one, so the rail never grows a second level of menu you have
   to navigate before you can navigate. Quieter and tighter than the tabs
   above it, and the caret sits where the tab's own caret sits so the two
   read as one column rather than two indents. */
.nav .subnav{display:flex;flex-direction:column;background:var(--tint);
  border-bottom:1px solid var(--hair)}
.nav .subnav a{border:0;padding:7px var(--s4) 7px 30px;font-size:12px;
  text-transform:none;letter-spacing:0;color:var(--ink-2)}
.nav .subnav a:first-child{border-top:0;padding-top:9px}
.nav .subnav a:last-child{padding-bottom:10px}
.nav .subnav a:hover{color:var(--accent)}
.nav .subnav a.on{background:none;color:var(--accent);font-weight:600}
.nav .subnav a.on::before{content:"\2022";left:14px;font-size:13px}

/* the screens that are not in the menu, listed where they can be found */
.other{margin-top:var(--s5);padding:0 var(--s4)}
.other .lab{margin-bottom:9px}
.other a{display:block;width:100%;text-align:left;padding:7px 0;font-size:12px;color:var(--ink-3);
  border-bottom:1px solid var(--hair)}
.other a:last-child{border-bottom:0}
.other a:hover{color:var(--accent)}
.other a.on{color:var(--accent);font-weight:600}

.setup{margin-top:var(--s5);padding:0 var(--s4)}
.setup a{display:flex;align-items:center;gap:9px;width:100%;text-align:left;color:var(--ink-2);
  border:1px solid var(--hair-2);padding:9px 11px;font-size:11.5px;
  text-transform:var(--cap);letter-spacing:var(--caps-ls)}
.setup a:hover{color:var(--ink);border-color:var(--ink-3)}
.setup .sp{flex:1}
.setup .ct{font:11px/1 var(--mono);color:var(--ink-3);letter-spacing:0}
.setup .prog{height:2px;background:var(--hair);margin-top:9px}
.setup .prog i{display:block;height:2px;background:var(--accent);width:0}

.who{margin-top:auto;padding:var(--s4) var(--s4) 0;display:flex;align-items:center;gap:9px;
  border-top:1px solid var(--hair)}
.who .av{width:26px;height:26px;flex:0 0 26px;border:1px solid var(--hair-2);display:grid;place-items:center;
  font:600 9.5px/1 var(--mono);color:var(--ink-3)}
.who .nm{font-size:12.5px;font-weight:600;line-height:1.25}
.who .rl{font-size:11px;color:var(--ink-3);line-height:1.25}

/* The page column beside the rail. Every dashboard screen mounts into a
   <main>, which is both the landmark a screen reader jumps to and the flex
   child that has to take the rest of the row. There is exactly one per page. */
main{flex:1;min-width:0;padding:var(--s5);max-width:1500px}

/* The measure. A page that is one thing to read, a settings section or one
   cafe's kilos, does not get wider because the monitor did. The column holds
   at 728px of content and the room left over sits either side of it, the way
   a docs page does. Everything on the page lives inside the column: the
   title, the rows, the Save bar. The 5 Sep version capped only the panel
   while the title and the Save bar ran the full width, and Lukas read the
   third of the screen it left blank as a panel that had failed to load.
   Nothing inside the column may be narrower than the column; that is the
   whole rule. Lukas, 6 Sep, pointing at a docs site: "why do sections that
   dont need to be huge take up whole screen".

   A list is wider, because a row of name, number, date and actions needs the
   room, but it still stops: past 1060px the actions sit so far from the name
   that the eye cannot pair them. */
main.measure{max-width:calc(728px + var(--s5) * 2);margin:0 auto}
main.measure.wide{max-width:calc(1060px + var(--s5) * 2)}

.pagehead{display:flex;align-items:flex-end;gap:var(--s4);flex-wrap:wrap;margin-bottom:var(--s5)}
.pagehead h1{margin:0;font-size:24px;font-weight:600;letter-spacing:-.02em}
.pagehead .sub{margin:7px 0 0;color:var(--ink-3);font-size:13px}
.pagehead .sub em{font-style:normal;color:var(--accent);cursor:pointer;text-decoration:underline}
.pagehead .sub b.warnlink{color:var(--bad);font-weight:600;cursor:pointer;text-decoration:underline}
.pagehead .sp{flex:1}

/* ---------------- the panel ---------------- */
.p{position:relative;padding:var(--s5);border:1px solid var(--hair);margin-bottom:var(--s4)}
.p.tight{padding:var(--s4)}
.p::after{
  content:"";position:absolute;inset:-1px;pointer-events:none;
  background:
    linear-gradient(var(--hair-2),var(--hair-2)) 0 0/8px 1px no-repeat,
    linear-gradient(var(--hair-2),var(--hair-2)) 0 0/1px 8px no-repeat,
    linear-gradient(var(--hair-2),var(--hair-2)) 100% 0/8px 1px no-repeat,
    linear-gradient(var(--hair-2),var(--hair-2)) 100% 0/1px 8px no-repeat,
    linear-gradient(var(--hair-2),var(--hair-2)) 0 100%/8px 1px no-repeat,
    linear-gradient(var(--hair-2),var(--hair-2)) 0 100%/1px 8px no-repeat,
    linear-gradient(var(--hair-2),var(--hair-2)) 100% 100%/8px 1px no-repeat,
    linear-gradient(var(--hair-2),var(--hair-2)) 100% 100%/1px 8px no-repeat;
}
.p.flat::after{display:none}
.ph{display:flex;align-items:baseline;gap:var(--s3);margin-bottom:var(--s3);flex-wrap:wrap}
.ph h2{margin:0;font-size:12.5px;font-weight:600;text-transform:var(--cap);letter-spacing:var(--caps-ls)}
.ph .sp{flex:1}
.ph .lnk{font-size:11.5px;color:var(--ink-3);text-transform:var(--cap);letter-spacing:var(--caps-ls);cursor:pointer}
.ph .lnk:hover{color:var(--accent)}

.cap{font-size:12px;color:var(--ink-2);margin-top:6px;line-height:1.6}
.cap.q{color:var(--ink-3)}
.big{font:600 38px/1 var(--sans);letter-spacing:-.02em;font-variant-numeric:tabular-nums}
.unit{font-size:16px;font-weight:600;color:var(--ink-3);margin-left:3px}

/* ---------------- controls ---------------- */
.toolbar{display:flex;gap:var(--s3);align-items:center;flex-wrap:wrap;margin-bottom:var(--s4)}
.toolbar .sp{flex:1}
.srch{display:flex;align-items:center;gap:8px;border:1px solid var(--hair-2);padding:0 11px;height:34px;min-width:300px}
.srch span{color:var(--ink-3)}
.srch input{border:0;background:none;outline:none;width:100%;font-size:13px}
.srch input::placeholder{color:var(--ink-3)}

.chips{display:flex;border:1px solid var(--hair-2)}
.chips button{padding:0 12px;height:34px;font:10px/1 var(--mono);letter-spacing:.12em;text-transform:uppercase;
  color:var(--ink-3);border-left:1px solid var(--hair)}
.chips button:first-child{border-left:0}
.chips button:hover{color:var(--ink)}
.chips button.on{background:var(--accent);color:var(--accent-ink);font-weight:700}
.chips button.red.on{background:var(--bad);color:var(--red-ink)}

.cta{display:inline-flex;align-items:center;justify-content:center;gap:8px;border:1px solid var(--hair-2);
  padding:0 14px;height:34px;font-size:11.5px;color:var(--ink-2);
  text-transform:var(--cap);letter-spacing:var(--caps-ls);white-space:nowrap}
.cta:hover{border-color:var(--ink-3);color:var(--ink)}
.cta.k{background:var(--accent);border-color:var(--accent);color:var(--accent-ink);font-weight:600}
.cta.k:hover{opacity:.9;color:var(--accent-ink)}
.cta.lg{height:40px;padding:0 18px}
.cta.wide{width:100%}
.cta.off,.cta[disabled]{opacity:.45;pointer-events:none}
/* Both, because a page disables a button two ways: .off when it is not the
   thing to press yet, the disabled attribute while its request is in flight.
   Four screens wrote the second one page-local before it lived here. */

.sel{border:1px solid var(--hair-2);height:34px;padding:0 10px;background:none;font-size:12.5px;color:var(--ink-2)}
.lab{font:9.5px/1 var(--mono);letter-spacing:.18em;text-transform:uppercase;color:var(--ink-3);display:block;margin-bottom:6px}
.fld{display:flex;flex-direction:column;min-width:0}
.fld input,.fld select,.fld textarea{border:1px solid var(--hair-2);height:34px;padding:0 10px;background:none;
  font-size:13px;outline:none;width:100%}
.fld textarea{height:auto;padding:9px 10px;resize:vertical;font:13px/1.5 var(--sans)}
.fld input:focus,.fld select:focus,.fld textarea:focus{border-color:var(--accent)}
.fld input::placeholder,.fld textarea::placeholder{color:var(--ink-3)}
.fld .hint{font-size:11px;color:var(--ink-3);margin-top:5px;line-height:1.5}
/* iOS zooms the whole page whenever it focuses a field smaller than 16px,
   and every page but the cafe portal keeps pinch zoom, so it never zooms
   back out on its own. Coarse pointers get a real 16px field and a thumb-
   sized box; a mouse keeps the compact one. */
@media (pointer:coarse){
  .fld input,.fld select,.fld textarea,.inl,.sel{font-size:16px}
  .fld input,.fld select,.inl,.sel{height:44px}
}
.f2{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:var(--s4)}
.f3{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:var(--s4)}

.swb{display:inline-flex;align-items:center;gap:9px;font:10px/1 var(--mono);letter-spacing:.12em;
  text-transform:uppercase;color:var(--ink-3);cursor:pointer}
.swb .sw{width:34px;height:18px;border:1px solid var(--hair-2);display:block;position:relative;flex:0 0 34px}
.swb .sw i{position:absolute;top:2px;left:2px;width:12px;height:12px;background:var(--ink-3);transition:left .12s}
.swb.on{color:var(--accent)}
.swb.on .sw{border-color:var(--accent)}
.swb.on .sw i{left:18px;background:var(--accent)}

/* ---------------- tables ---------------- */
table.t{width:100%;border-collapse:collapse;font-size:13px}
/* Headings stay on screen while a long table scrolls under them, so the
   column a cell belongs to is never a guess. Pinned inside a .scroll box they
   pin to the box; on a page they pin to the window. --pin is an offset a page
   sets when it has a bar of its own pinned above (Products' selection bar). */
table.t th{text-align:left;font:9.5px/1 var(--mono);letter-spacing:.18em;text-transform:uppercase;
  color:var(--ink-2);font-weight:600;padding:9px 10px 9px 0;
  position:sticky;top:var(--pin,0px);z-index:2;background:var(--head-band)}
table.t th:first-child{border-radius:8px 0 0 8px}
table.t th:last-child{border-radius:0 8px 8px 0}
table.t th:only-child{border-radius:8px}
/* The band reaches 10px past the text on each side, like a title bar rather
   than a strip painted behind the words (Lukas, 7 Sep 2026: "round the edges
   off"). The whole table shifts out by that much and the end cells pad back
   in, so every column stays exactly where it was. Wide screens only: inside
   the sideways-scrolling wrapper below 1101px an overhang is a scrollbar. */
@media (min-width:1101px){
  table.t{margin:0 -10px;width:calc(100% + 20px)}
  table.t th:first-child,table.t td:first-child{padding-left:10px}
  table.t th:last-child,table.t td:last-child{padding-right:10px}
}
table.t th.r,table.t td.r{text-align:right;padding-right:0}
table.t td{padding:11px 10px 11px 0;border-bottom:1px solid var(--hair);vertical-align:top}
table.t tr.clk{cursor:pointer}
table.t tr.clk:hover td{background:color-mix(in srgb, var(--ink) 3%, transparent)}
table.t tr.sel td{background:var(--tint)}
table.t td.day{text-align:left;font:9.5px/1 var(--mono);letter-spacing:.18em;text-transform:uppercase;
  color:var(--ink-3);padding:var(--s4) 0 8px;border-bottom:1px solid var(--hair-2)}
.nm{font-weight:600}
.m{font-size:11.5px;color:var(--ink-3);margin-top:4px}
.num{font:12.5px/1 var(--mono);font-variant-numeric:tabular-nums}
.code{font:11.5px/1 var(--mono);letter-spacing:.04em;color:var(--ink-2);border:1px solid var(--hair);
  padding:4px 7px;display:inline-block}

.st{font:9.5px/1 var(--mono);letter-spacing:.12em;text-transform:uppercase;padding:5px 8px;
  border:1px solid var(--hair-2);color:var(--ink-3);display:inline-block;white-space:nowrap}
.st.ok{border-color:transparent;padding-left:0;padding-right:0;color:var(--ink-3)}
.st.bad{border-color:var(--bad);color:var(--bad);background:var(--bad-soft)}
.st.wait{border-color:var(--warn);color:var(--warn)}

.rd{display:flex;align-items:baseline;gap:9px;padding:9px 0;border-top:1px solid var(--hair)}
.rd:first-of-type{border-top:0;padding-top:0}
.rd .l{font-size:12.5px;color:var(--ink-2);font-weight:600;text-transform:var(--cap);letter-spacing:var(--caps-ls)}
.rd .fill{flex:1;min-width:12px;border-bottom:1px dotted var(--hair-2);transform:translateY(-4px)}
.rd .v{font:600 14.5px/1 var(--mono);font-variant-numeric:tabular-nums;white-space:nowrap}
.rd .n{display:block;font-size:11px;color:var(--ink-3);margin-top:4px;letter-spacing:0;text-transform:none;font-weight:400}

/* ---------------- home widgets ---------------- */
.grid{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:var(--s4);align-items:start}
.w2{grid-column:span 2}
.grid .p{margin-bottom:0}

/* Home is two columns of stacked panels, not a grid of cells.
   A grid row is only as tall as its tallest panel, so a short panel leaves a
   hole underneath it the exact size of its taller neighbour. On this page
   that was 94px of nothing under Money and 250px under Your week. Stacking
   each column closes the holes without stretching a small panel to fill one,
   which is the other way to line the frames up and looks worse.

   Every panel on the page is in one of these two columns and there is no
   third full-width row underneath them. There used to be, holding the
   feedback box, San Pedro and Your cafes, and it was the cause of the hole
   Lukas kept seeing: a full-width row cannot start until BOTH columns have
   finished, so whichever column ends first shows the difference as blank
   space partway down the page. Under Your week it was 390px of nothing,
   sitting in the middle of the screen where it read as a panel that had
   failed to load. With those three panels dealt into the two columns the
   only unevenness left is at the very bottom, past the last panel, where a
   page ending is the normal thing for a page to do. Lukas, 5 Sep: "this gap
   is doing my head in".

   Which panel went where is by weight, not by height. The wide column keeps
   the day's work (what is shipping, the week around it) and then the two
   things a roaster does occasionally rather than daily. The narrow column
   keeps the money, the suggestions, the cafe roster and the recipes, all of
   which read perfectly well narrow where the week's chart would not. */
.home{display:grid;grid-template-columns:minmax(0,2fr) minmax(0,1fr);gap:var(--s4);align-items:start}
.home .col{display:flex;flex-direction:column;gap:var(--s4);min-width:0}
.home .p{margin-bottom:0}

/* Worth a look now lives in the narrow column, where a suggestion's button
   cannot sit beside its sentence. Wrapping puts it underneath, which is the
   order the two are read in anyway; the indent lines it up with the text
   rather than with the marker. */
.home .col .wd{flex-wrap:wrap}
.home .col .wd .cta{margin-left:19px;margin-top:var(--s2)}
.stack{display:flex;height:12px;margin:var(--s4) 0 var(--s3);gap:2px}
.stack i{display:block;height:12px}
.lr{display:flex;align-items:center;gap:10px;padding:8px 0;border-top:1px solid var(--hair);font-size:13px}
.lr:first-child{border-top:0}
.lr .sw2{display:block;width:9px;height:9px;flex:0 0 9px}
.lr .nmm{flex:1;min-width:0}
.lr .kg{font:12.5px/1 var(--mono);font-variant-numeric:tabular-nums;color:var(--ink-2)}

.weekwrap{position:relative;margin:var(--s4) 0 var(--s2);padding:6px 6px 0;border:1px solid var(--hair);
  background:repeating-linear-gradient(to right,var(--grid) 0 1px,transparent 1px 22px),
             repeating-linear-gradient(to bottom,var(--grid) 0 1px,transparent 1px 22px)}
.week{display:flex;align-items:flex-end;gap:7px;height:104px}
.week .d{flex:1;display:flex;flex-direction:column;justify-content:flex-end;gap:6px;height:100%}
.week .d i{display:block;background:var(--a3)}
.week .d.now i{background:var(--accent)}
.week .d span{font:10px/1 var(--mono);color:var(--ink-3);text-align:center}
.week .d.now span{color:var(--ink);font-weight:600}
/* The kilos, above the bar. A chart of seven unlabelled columns can only say
   which day was busier; the number is the part worth reading, so it is on the
   chart rather than under it. A day with nothing in it says 0 quietly, because
   a gap in the week is itself the thing worth seeing. */
.week .d b{font:10px/1 var(--mono);font-weight:600;color:var(--ink-2);text-align:center}
.week .d.now b{color:var(--accent)}
.week .d.zero b{color:var(--ink-3);font-weight:400}

.rcard{border:1px solid var(--hair);padding:11px 12px;margin-top:var(--s3)}
.rcard .rn{font-size:13px;font-weight:600;display:flex;align-items:center;gap:8px}
.rcard .rn .dot{width:6px;height:6px;background:var(--accent);flex:0 0 6px}
.rfields{display:flex;gap:var(--s5);margin-top:11px;flex-wrap:wrap}
.rfields .f{display:block}
.rfields .f .fv{font:600 15px/1 var(--mono);font-variant-numeric:tabular-nums}
.rfields .f .fl{display:block;font-size:10px;color:var(--ink-3);margin-top:5px;
  text-transform:var(--cap);letter-spacing:var(--caps-ls)}

.fbtop{display:flex;gap:12px;align-items:flex-start}
.tile{position:relative;flex:0 0 46px;height:46px;display:grid;place-items:center;color:var(--accent)}
.tile::after{content:"";position:absolute;inset:0;pointer-events:none;
  background:
    linear-gradient(var(--hair-2),var(--hair-2)) 0 0/9px 1px no-repeat,
    linear-gradient(var(--hair-2),var(--hair-2)) 0 0/1px 9px no-repeat,
    linear-gradient(var(--hair-2),var(--hair-2)) 100% 0/9px 1px no-repeat,
    linear-gradient(var(--hair-2),var(--hair-2)) 100% 0/1px 9px no-repeat,
    linear-gradient(var(--hair-2),var(--hair-2)) 0 100%/9px 1px no-repeat,
    linear-gradient(var(--hair-2),var(--hair-2)) 0 100%/1px 9px no-repeat,
    linear-gradient(var(--hair-2),var(--hair-2)) 100% 100%/9px 1px no-repeat,
    linear-gradient(var(--hair-2),var(--hair-2)) 100% 100%/1px 9px no-repeat}
textarea.fb{width:100%;border:1px solid var(--hair-2);background:transparent;color:var(--ink);
  font:13px/1.5 var(--sans);padding:10px 11px;resize:vertical;min-height:70px;margin-top:var(--s3);outline:none}
textarea.fb:focus{border-color:var(--accent)}
textarea.fb::placeholder{color:var(--ink-3)}

.needs{display:flex;align-items:center;gap:var(--s4);flex-wrap:wrap;border:1px solid var(--hair);
  border-left:3px solid var(--bad);padding:13px var(--s4);margin-bottom:var(--s4)}
.needs .tx{flex:1;min-width:220px;font-size:13.5px}
.needs .tx b{font-weight:600}
.needs .tx span{display:block;font-size:11.5px;color:var(--ink-3);margin-top:4px}

/* ---------------- two pane ---------------- */
.two{display:grid;grid-template-columns:minmax(0,300px) minmax(0,1fr);gap:var(--s4);align-items:start}
.list{border:1px solid var(--hair)}
.list button{display:flex;align-items:center;gap:10px;width:100%;text-align:left;padding:11px var(--s4);
  border-bottom:1px solid var(--hair);font-size:13px}
.list button:last-child{border-bottom:0}
.list button .sp{flex:1}
.list button:hover{background:color-mix(in srgb, var(--ink) 3%, transparent)}
.list button.on{background:var(--tint);color:var(--accent);font-weight:600}
.list .meta{font:11px/1 var(--mono);color:var(--ink-3)}
.list button.on .meta{color:var(--accent)}

.phonewrap{display:flex;justify-content:center}
.phone{width:264px;border:1px solid var(--hair-2);padding:8px;background:var(--panel)}
.phone .scr{background:#F5F3EE;color:#14181C;height:430px;overflow:hidden;font-size:12px}
.phone .ptop{padding:11px 12px;border-bottom:1px solid #DEDACF}
.phone .ptop b{display:block;font:600 9px/1 var(--mono);letter-spacing:.2em;text-transform:uppercase;color:#0A7A3E}
.phone .ptop .cn{font-size:14px;font-weight:600;margin-top:8px}
.phone .psec{padding:11px 12px 5px;font:8.5px/1 var(--mono);letter-spacing:.2em;text-transform:uppercase;color:#6C6558}
.phone .pit{display:flex;gap:9px;align-items:center;padding:8px 12px;border-top:1px solid #E4E0D6}
.phone .pit .pn{flex:1;font-size:12px;font-weight:600}
.phone .pit .pq{font:10px/1 var(--mono);border:1px solid #C6BFB0;padding:6px 9px;color:#6C6558}

.drag{border:1px solid var(--hair)}
.drag .row{display:flex;align-items:center;gap:11px;padding:10px var(--s4);border-bottom:1px solid var(--hair);font-size:13px}
.drag .row:last-child{border-bottom:0}
.drag .row .gr{color:var(--ink-3);cursor:grab;font:12px/1 var(--mono);letter-spacing:-.1em}
.drag .row .sp{flex:1}
.drag .row.off{color:var(--ink-3)}

.savebar{position:sticky;bottom:0;display:flex;align-items:center;gap:var(--s3);flex-wrap:wrap;
  background:var(--ground);border-top:1px solid var(--hair-2);padding:var(--s3) 0;margin-top:var(--s4)}
.savebar .hint{font-size:12px;color:var(--ink-3);flex:1;min-width:240px}

/* ---------------- analytics ---------------- */
.hero{display:flex;gap:var(--s5);flex-wrap:wrap;align-items:flex-start}
.hero .l{flex:1;min-width:280px}
.kicker{font:9.5px/1 var(--mono);letter-spacing:.18em;text-transform:uppercase;color:var(--ink-3)}
.avg{font:600 40px/1 var(--sans);letter-spacing:-.025em;font-variant-numeric:tabular-nums;margin-top:11px}
.ministat{border:1px solid var(--hair);padding:var(--s4);font-size:12px;color:var(--ink-3);min-width:190px}
.ministat b{display:block;font:600 19px/1 var(--mono);color:var(--ink);font-variant-numeric:tabular-nums;margin-bottom:3px}
.ministat b+span{display:block;margin-bottom:11px}
.chart{margin-top:var(--s5);border:1px solid var(--hair);padding:var(--s4) var(--s4) var(--s3);
  background:repeating-linear-gradient(to right,var(--grid) 0 1px,transparent 1px 26px),
             repeating-linear-gradient(to bottom,var(--grid) 0 1px,transparent 1px 26px)}
.chart svg{display:block;width:100%;height:150px;overflow:visible}
.chart .xs{display:flex;justify-content:space-between;margin-top:9px;font:10px/1 var(--mono);color:var(--ink-3)}

/* ---------------- tools ---------------- */
.cards{display:grid;grid-template-columns:repeat(auto-fill,minmax(310px,1fr));gap:var(--s4)}
.tool{border:1px solid var(--hair);padding:var(--s4);display:flex;flex-direction:column}
.tool .tt{display:flex;align-items:center;gap:9px;flex-wrap:wrap}
.tool .tt b{font-size:14px}
.tool .for{font:9.5px/1 var(--mono);letter-spacing:.12em;text-transform:uppercase;color:var(--ink-3)}
.tool .line{font-size:12px;color:var(--ink-3);margin-top:7px}
.tool p{margin:11px 0 var(--s4);font-size:12.5px;line-height:1.6;color:var(--ink-2)}
.tool .acts{display:flex;align-items:center;gap:var(--s2);margin-top:auto;padding-top:var(--s3);
  border-top:1px solid var(--hair);flex-wrap:wrap}
.tool .acts .sp{flex:1}

/* ---------------- settings ---------------- */
.setrow{display:flex;gap:var(--s4);align-items:flex-start;padding:var(--s4) 0;border-top:1px solid var(--hair);flex-wrap:wrap}
.setrow:first-of-type{border-top:0;padding-top:0}
.setrow .tx{flex:1;min-width:250px}
.setrow .tx b{font-size:13.5px;font-weight:600;display:block}
.setrow .tx span{display:block;font-size:12px;color:var(--ink-3);margin-top:5px;line-height:1.6}
.setrow.danger .tx b{color:var(--bad)}

.status{display:flex;flex-wrap:wrap;gap:0 var(--s4);align-items:center;margin-top:var(--s5);
  border:1px solid var(--hair);padding:9px var(--s4);font:10.5px/1.9 var(--mono);
  color:var(--ink-3);text-transform:uppercase;letter-spacing:.14em}
.status b{color:var(--ink-2);font-weight:600}
.status .dot{display:inline-block;width:6px;height:6px;background:var(--accent);margin-right:7px}

/* ============================================================
   The screens outside the dashboard. No rail: a roaster meets
   these before there is an app to be inside, so they get the
   whole viewport and one slim bar back.
   ============================================================ */
.flow{min-height:100vh;display:flex;flex-direction:column}
.flowbar{position:sticky;top:0;z-index:5;display:flex;align-items:center;gap:var(--s4);flex-wrap:wrap;
  background:var(--ground);border-bottom:1px solid var(--hair-2);padding:10px var(--s5)}
.flowbar .nmm{font:10px/1 var(--mono);letter-spacing:.16em;text-transform:uppercase;color:var(--ink-3)}
.flowbar .nmm b{color:var(--ink);font-weight:600}
.flowbar .sp{flex:1}
.flowbody{flex:1;padding:var(--s6) var(--s5) var(--s6)}
/* The bar above is sticky, so anything the browser scrolls to lands UNDER it.
   On the cafe sign-up, pressing the button with the form empty scrolled the
   first missing field to viewport top, entirely behind the bar, and the
   browser's own "Please fill in this field" bubble then pointed at nothing.
   Park a scrolled-to field below the bar with its label still showing: the bar
   is 10px padding either side of a 10px line plus a hairline (31px), and the
   rest is the label and a gap. */
.flowbody input,.flowbody select,.flowbody textarea{scroll-margin-top:64px}
/* A flow screen's body is the whole viewport, not the dashboard's page
   column, so it opts out of main's cap. */
main.flowbody{max-width:none}
.sheet{max-width:430px;margin:0 auto}
.sheet.wide{max-width:760px}
.sheet h1{font-size:27px;font-weight:600;letter-spacing:-.025em;margin:0}
.sheet .lede{color:var(--ink-2);font-size:13.5px;line-height:1.65;margin:11px 0 0}
.sheet .p{margin-top:var(--s5)}
.stackf{display:flex;flex-direction:column;gap:var(--s4)}
.orline{display:flex;align-items:center;gap:var(--s3);margin:var(--s4) 0;
  font:9.5px/1 var(--mono);letter-spacing:.18em;text-transform:uppercase;color:var(--ink-3)}
.orline::before,.orline::after{content:"";flex:1;height:1px;background:var(--hair-2)}
.foot{text-align:center;font-size:12px;color:var(--ink-3);margin-top:var(--s4);line-height:1.7}
.foot a,.foot .lnk{color:var(--accent);cursor:pointer;text-decoration:underline}

/* the wizard */
.wiz{display:grid;grid-template-columns:minmax(0,1fr) minmax(0,420px);gap:var(--s6);align-items:start}
.wiz h2{font-size:30px;font-weight:600;letter-spacing:-.028em;line-height:1.15;margin:0}
/* .wiz h2 is the question. A panel head inside a wizard column is not, and
   it was inheriting the question's 30px because .wiz h2 is declared after
   .ph h2 at the same specificity. */
.wiz .ph h2{font-size:12.5px;font-weight:600;letter-spacing:var(--caps-ls);line-height:1.55}
.wiz .lede{color:var(--ink-2);font-size:14px;line-height:1.65;margin:var(--s4) 0 0;max-width:60ch}
.wizsteps{display:flex;gap:5px;margin-bottom:var(--s5);flex-wrap:wrap}
.wizsteps i{display:block;height:3px;flex:1 1 34px;max-width:34px;background:var(--hair-2)}
.wizsteps i.done{background:var(--a3)}
.wizsteps i.now{background:var(--accent)}
.wizcount{font:9.5px/1 var(--mono);letter-spacing:.18em;text-transform:uppercase;color:var(--ink-3);margin-bottom:11px}

/* the helper. Small, one line, beside the question and never over it. */
.helper{display:flex;gap:11px;align-items:flex-start;margin-top:var(--s5);
  border-left:2px solid var(--accent);padding:2px 0 2px 13px}
.helper .who2{color:var(--accent);flex:0 0 auto;padding-top:1px}
.helper .say{font-size:12.5px;color:var(--ink-2);line-height:1.6;max-width:46ch}
.helper .say b{display:block;font:9.5px/1 var(--mono);letter-spacing:.18em;text-transform:uppercase;
  color:var(--ink-3);margin-bottom:6px;font-weight:400}

.opt{display:flex;gap:12px;align-items:flex-start;border:1px solid var(--hair-2);padding:14px var(--s4);
  cursor:pointer;margin-bottom:var(--s3)}
.opt .dot{width:13px;height:13px;flex:0 0 13px;border:1px solid var(--hair-2);margin-top:2px;position:relative}
.opt.on{border-color:var(--accent);background:var(--tint)}
.opt.on .dot{border-color:var(--accent)}
.opt.on .dot::after{content:"";position:absolute;inset:2px;background:var(--accent)}
.opt .t{display:block;font-size:13.5px;font-weight:600}
.opt .d{display:block;font-size:12px;color:var(--ink-2);margin-top:4px;line-height:1.55}
/* --ink-2, not --ink-3: a chosen option's row carries the 13% accent tint, and
   the tint darkens the background enough that even the corrected --ink-3 lands
   at 3.96 here. This is explanatory copy under the choice, not a whisper. */

.pick{display:flex;align-items:center;gap:11px;padding:9px 0;border-bottom:1px solid var(--hair);
  font-size:13px;cursor:pointer}
.pick:last-child{border-bottom:0}
.pick .box{width:15px;height:15px;flex:0 0 15px;border:1px solid var(--hair-2);display:grid;place-items:center;
  font-size:10px;color:var(--accent)}
.pick.on .box{border-color:var(--accent)}
.pick .nmm{flex:1;min-width:0}
.pick .tag{font:9.5px/1 var(--mono);letter-spacing:.1em;text-transform:uppercase;color:var(--ink-3);
  border:1px solid var(--hair);padding:4px 6px}
.pick.off{color:var(--ink-3)}
.scroll{max-height:330px;overflow:auto}
.chipset{display:flex;flex-wrap:wrap;gap:var(--s2);margin-top:var(--s3)}
.chipone{font:10px/1 var(--mono);letter-spacing:.1em;text-transform:uppercase;color:var(--ink-2);
  border:1px solid var(--hair-2);padding:7px 9px}
.chipone span{color:var(--ink-3);margin-left:6px}
.wizacts{display:flex;gap:var(--s3);align-items:center;margin-top:var(--s5);flex-wrap:wrap}
.hintline{font:11px/1 var(--mono);color:var(--ink-3);letter-spacing:.1em;text-transform:uppercase}

/* paying an invoice, on a phone */
.paywrap{display:flex;justify-content:center}
.payphone{width:330px;border:1px solid var(--hair-2);padding:10px;background:var(--panel)}
.payscr{background:#F5F3EE;color:#14181C;padding:var(--s5) var(--s4) var(--s4);min-height:560px;
  display:flex;flex-direction:column}
.payscr h3{margin:0;font-size:19px;font-weight:600;letter-spacing:-.02em}
.payscr .sub2{font-size:12.5px;color:#5C5648;margin-top:8px;line-height:1.6}
.payrow{display:flex;gap:11px;align-items:flex-start;padding:13px 0;border-top:1px solid #DEDACF;font-size:12.5px}
.payrow .bl{width:8px;height:8px;flex:0 0 8px;background:#0A7A3E;margin-top:5px}
.payrow b{display:block;font-weight:600}
.payrow span{display:block;color:#5C5648;margin-top:3px;line-height:1.55}
.payfield{border:1px solid #C6BFB0;background:#FBFAF7;padding:11px 12px;font:12.5px/1 var(--mono);
  color:#8A8375;margin-top:var(--s3)}
.paybtn{background:#0A7A3E;color:#fff;text-align:center;padding:14px;font-size:13px;font-weight:600;
  letter-spacing:.05em;text-transform:uppercase;margin-top:var(--s4)}
.payfoot{text-align:center;font:9.5px/1 var(--mono);letter-spacing:.16em;text-transform:uppercase;
  color:#8A8375;margin-top:var(--s3)}

/* Side by side: the reskin, and the screen as it stands today. The plain one
   is not a lesser draft. It is what a cafe already trusts, reproduced so the
   two can be judged against each other rather than from memory. */
.paycmp{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:var(--s6);margin-top:var(--s5);align-items:start}
.paycmp .cap{font:9.5px/1 var(--mono);letter-spacing:var(--caps-ls);text-transform:var(--cap);
  color:var(--ink-3);margin-bottom:var(--s3)}
.paycmp .cap b{color:var(--ink);font-weight:600}
.paycmp .note{font-size:12.5px;color:var(--ink-2);line-height:1.6;margin-top:var(--s3)}

/* the live shape: dimmed order behind, white sheet, round corners, pill buttons */
.dimmer{background:rgba(11,11,12,.55);padding:26px 16px 0;display:flex;justify-content:center}
.pcard{width:100%;max-width:330px;background:#fff;border-radius:16px 16px 0 0;
  padding:26px 22px 0;font:14px/1.5 var(--sans);color:#0B0B0C}
.pbrand{font-size:10px;font-weight:700;letter-spacing:.14em;text-transform:uppercase;color:#6B6B73;margin-bottom:12px}
.pcard h3{margin:0 0 7px;font-size:20px;font-weight:700;letter-spacing:-.01em}
.pcard .psub{color:#6B6B73;font-size:13.5px;line-height:1.5;margin:0 0 17px}
.passure{background:#F7F7F8;border-radius:12px;padding:15px 16px;display:grid;gap:13px}
.passure li{display:flex;align-items:flex-start;gap:11px;list-style:none}
.passure svg{width:17px;height:17px;flex:none;margin-top:1px;fill:none;stroke:#16A34A;
  stroke-width:1.6;stroke-linecap:round;stroke-linejoin:round}
.passure b{display:block;font-size:12.5px;font-weight:600;line-height:1.35}
.passure span{display:block;font-size:12px;color:#6B6B73;line-height:1.45;margin-top:1px}
.pform{margin-top:22px;padding-top:20px;border-top:1px solid #E6E6E9}
.pfield{border:1px solid #E0E0E5;border-radius:8px;padding:12px 13px;font-size:13.5px;
  color:#9B9BA4;background:#fff;margin-bottom:10px}
.psecure{display:flex;align-items:center;justify-content:center;gap:6px;font-size:12.5px;
  color:#9B9BA4;margin:18px 0}
/* The shield takes its size from the list rule when it sits in the list. Down
   here there is no list, so without this it renders at whatever width is going
   and swallows the row. */
.psecure svg{width:13px;height:13px;flex:none;fill:none;stroke:currentColor;stroke-width:1.6;
  stroke-linecap:round;stroke-linejoin:round}
.pacts{display:flex;flex-direction:column-reverse;gap:10px;border-top:1px solid #E6E6E9;
  margin:0 -22px;padding:14px 22px 18px}
.pbtn{border-radius:999px;padding:13px 24px;font-size:14.5px;font-weight:600;text-align:center}
.pbtn.sec{background:#F7F7F8;color:#0B0B0C}
.pbtn.pri{background:#0B0B0C;color:#fff}


/* ============================================================
   The second look, switchable at the bottom of the page.
   Same words, same layout, same numbers. What changes is how
   much the surface announces itself: cards instead of ruled
   frames, labels spoken instead of shouted, brackets instead
   of the arrow. Kept as one class on <body> so all sixteen
   screens answer to it at once.
   ============================================================ */

/* Typeface. The caps labels are mono, so the body has to be chosen to sit
   with a mono rather than against one. Each pairing sets both at once: a
   sans picked on its own is how the two ended up fighting. */
html[data-type=plex]{--sans:"IBM Plex Sans",-apple-system,BlinkMacSystemFont,sans-serif;
  --mono:"IBM Plex Mono",ui-monospace,Menlo,monospace}
html[data-type=grotesk]{--sans:"Space Grotesk",-apple-system,BlinkMacSystemFont,sans-serif;
  --mono:"JetBrains Mono",ui-monospace,Menlo,monospace}
html[data-type=archivo]{--sans:"Archivo",-apple-system,BlinkMacSystemFont,sans-serif;
  --mono:"IBM Plex Mono",ui-monospace,Menlo,monospace}

/* frames: card */
html[data-frames=card]{--ground:#EFEFF1;--head-band:color-mix(in srgb, var(--accent) 7%, var(--panel))}
html[data-frames=card] .p{border:0;background:var(--panel);border-radius:12px;
  box-shadow:0 1px 2px rgba(11,11,12,.06),0 2px 14px rgba(11,11,12,.045)}
html[data-frames=card] .p::after{display:none}
html[data-frames=card] .rail{background:var(--panel);border-right:1px solid var(--hair)}
html[data-frames=card] .flowbar{background:var(--panel)}
html[data-frames=card] .sheet .p,html[data-frames=card] .wiz .p{box-shadow:0 1px 2px rgba(11,11,12,.06),0 2px 14px rgba(11,11,12,.045)}
html[data-frames=card] .payphone{border:0;border-radius:12px;
  box-shadow:0 1px 2px rgba(11,11,12,.06),0 2px 14px rgba(11,11,12,.045)}
html[data-frames=card] .helper{border-left-width:2px}
html[data-frames=card] table.t th{border-bottom-color:var(--hair)}
html[data-frames=card] .opt,html[data-frames=card] .pick,html[data-frames=card] .lookopt{border-radius:10px}
html[data-frames=card] .swb{border-radius:999px}
html[data-frames=card] .cta,html[data-frames=card] .btn{border-radius:8px}

/* small labels: spoken */
html[data-labels=spoken]{--cap:none;--caps-ls:0}
html[data-labels=spoken] .lab,html[data-labels=spoken] .kicker,html[data-labels=spoken] .wizcount,
html[data-labels=spoken] .flowbar .nmm,html[data-labels=spoken] .tool .for,html[data-labels=spoken] .helper .say b,
html[data-labels=spoken] .pick .tag,html[data-labels=spoken] .chipone,html[data-labels=spoken] .hintline,
html[data-labels=spoken] .paycmp .cap{font-family:var(--sans);font-size:11.5px;
  text-transform:none;letter-spacing:0}
html[data-labels=spoken] table.t th,html[data-labels=spoken] table.t td.day{font-family:var(--sans);
  font-size:11.5px;text-transform:none;letter-spacing:0}
html[data-labels=spoken] .st{font-family:var(--sans);font-size:11px;text-transform:none;letter-spacing:0}
html[data-labels=spoken] .chips button{font-family:var(--sans);font-size:12px;text-transform:none;letter-spacing:0}
html[data-labels=spoken] .status,html[data-labels=spoken] .status b{font-family:var(--sans);font-size:11.5px;
  text-transform:none;letter-spacing:0}
html[data-labels=spoken] .ph h2{font-size:13.5px}
html[data-labels=spoken] .rd .l{font-size:13px}
html[data-labels=spoken] .paybtn{text-transform:none;letter-spacing:0;font-size:14px}

/* Dark.

   Not the light palette inverted. A near-black ground with white type on it
   glares, so the ground is lifted off true black and the type is pulled off
   true white, and the hairlines have to go UP in contrast rather than down,
   because a 1px line that reads on paper disappears on a dark screen.

   The green also has to move. #00682F is the brand on a light ground and a
   dark smudge on a dark one, so the dark side runs a lifted version of the
   same hue and flips the ink on top of it to near-black. Everything derived
   from the accent is redeclared here rather than inherited: a color-mix()
   written on :root computes there, so overriding --accent lower down leaves
   every mix of it still pointing at the old colour.

   The cafe's phone preview, the pay screen and the payment sheet stay light
   on purpose. They are pictures of somebody else's screen. */
html[data-theme=dark]{
  --wh:#F58A3C; --wh-ink:#2A1204;
  --ground:#131316; --panel:#1C1C22;
  --hair:#2C2C34; --hair-2:#484852;
  --ink:#F1F1F3; --ink-2:#B2B2BC; --ink-3:#84848F;
  /* #82828D cleared 4.5 on the dark ground (4.95) but landed at 4.46 on a
     panel, and the wizard's phone preview puts 9px muted text on exactly
     that. Two points lighter takes the panel case to 4.58. */
  --grid:rgba(255,255,255,.055);

  --accent:#3DBB74; --accent-ink:#08160D;
  --a2:color-mix(in srgb, #3DBB74 78%, #131316);
  --a3:color-mix(in srgb, #3DBB74 54%, #131316);
  --a4:color-mix(in srgb, #3DBB74 31%, #131316);
  --tint:color-mix(in srgb, #3DBB74 15%, transparent);

  --bad:#FF6F61; --bad-soft:color-mix(in srgb, #FF6F61 14%, transparent);
  --warn:#D6A25F;

  --series-2:#7BA6FF; --series-3:#E0A458; --series-4:#C79BF2;

  color-scheme:dark;
}
/* Raised panels need a real shadow in the dark, and the ground has to come
   back down: the light card look lifts it to #EFEFF1. */
html[data-theme=dark][data-frames=card]{--ground:#131316}
html[data-theme=dark][data-frames=card] .p,
html[data-theme=dark][data-frames=card] .sheet .p,
html[data-theme=dark][data-frames=card] .wiz .p,
html[data-theme=dark][data-frames=card] .payphone{box-shadow:0 1px 2px rgba(0,0,0,.5),0 3px 16px rgba(0,0,0,.42)}

/* selected: brackets */
html[data-mark=brackets] .nav a{padding-left:var(--s4)}
html[data-mark=brackets] .nav a.on{background:transparent}
html[data-mark=brackets] .nav a.on::before{content:none}
html[data-mark=brackets] .nav a.on .sp::before{content:"[\00a0"}
html[data-mark=brackets] .nav a.on .sp::after{content:"\00a0]"}

/* The look control, inside Settings. Two picture-less cards to a row: a
   roaster is choosing a feeling, so each option carries a sentence saying
   what it feels like rather than a style name on its own. The page repaints
   as they press, which is the whole point of it living here and not in a
   modal. */
.lookset{display:grid;grid-template-columns:minmax(0,200px) minmax(0,1fr);gap:var(--s4);
  padding:var(--s4) 0;border-top:1px solid var(--hair)}
.lookset:first-child{border-top:0;padding-top:0}
.lookset .tx b{display:block;font-size:13px;font-weight:600}
.lookset .tx span{display:block;font-size:12px;color:var(--ink-3);margin-top:4px;line-height:1.55}
.lookopts{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:var(--s3)}
.lookset.four .lookopts{grid-template-columns:repeat(2,minmax(0,1fr))}
.lookopt{border:1px solid var(--hair-2);padding:12px 13px;text-align:left;cursor:pointer;
  background:transparent;transition:border-color .12s,background .12s}
.lookopt:hover{border-color:var(--ink-3)}
.lookopt b{display:block;font-size:12.5px;font-weight:600;color:var(--ink)}
.lookopt span{display:block;font-size:11.5px;color:var(--ink-3);margin-top:5px;line-height:1.5}
.lookopt.on{border-color:var(--accent);background:var(--tint)}
.lookopt.on b{color:var(--accent)}
.looksave{display:flex;align-items:center;gap:var(--s3);flex-wrap:wrap;
  margin-top:var(--s4);padding-top:var(--s4);border-top:1px solid var(--hair)}
.looksave .msg{flex:1;min-width:220px;font-size:12px;color:var(--ink-2);line-height:1.55}
.looksave .msg.done{color:var(--ink-3)}
.cta.primary{background:var(--accent);color:var(--accent-ink);border-color:var(--accent);font-weight:600}
@media (max-width:820px){ .lookset{grid-template-columns:1fr} .lookopts{grid-template-columns:1fr} }


/* ============================================================
   MOVEMENT

   Nothing here decorates. Each one answers a question a person
   asks without noticing: is this screen new, did that press
   land, and is this number the real number or is it still
   arriving. A dashboard with none of it reads as a picture of
   a product rather than the product.

   All of it is off under prefers-reduced-motion, at the bottom
   of the stylesheet, which is why none of it is load-bearing.
   ============================================================ */
@keyframes rise{from{opacity:0;transform:translateY(7px)}to{opacity:1;transform:none}}
@keyframes growx{from{transform:scaleX(0)}to{transform:none}}
@keyframes growy{from{transform:scaleY(0)}to{transform:none}}
@keyframes slidein{from{opacity:0;transform:translateX(-5px)}to{opacity:1;transform:none}}

/* Panels arrive in reading order, briefly. The stagger is capped so a long
   page never makes anyone wait for the bottom of it. */
.anim .p,.anim .needs{animation:rise .32s cubic-bezier(.2,.7,.3,1) both}
.anim .stack i{animation:growx .55s cubic-bezier(.2,.7,.3,1) both;transform-origin:left}
.anim .week .d i{animation:growy .55s cubic-bezier(.2,.7,.3,1) both;transform-origin:bottom}
.anim .wizsteps i{animation:growx .4s cubic-bezier(.2,.7,.3,1) both;transform-origin:left}
.anim main h1,.anim .sheet h1,.anim .wiz h2{animation:slidein .34s cubic-bezier(.2,.7,.3,1) both}

/* Press. A button that does not move under the finger is the single most
   common reason a screen feels dead. */
button{transition:background .12s,color .12s,border-color .12s,transform .06s}
button:active{transform:translateY(1px)}
.cta:active,.lookopt:active,.opt:active,.pick:active{transform:translateY(1px)}
.nav a,.list button{transition:background .14s,color .14s}
table.t tr.clk td{transition:background .12s}
.tool,.p{transition:border-color .16s}
.tool:hover{border-color:var(--hair-2)}

/* The number is still arriving. Tabular figures mean the column does not
   jitter while it counts. */
.big,.avg,.rd .v,.ministat b{font-variant-numeric:tabular-nums}

/* The clock is the real one. It is the only thing on the page that is
   actually now, and it costs one interval. */
.status .clock{font-variant-numeric:tabular-nums}


/* ============================================================
   WORTH DOING

   Not notifications and not a nag. Every line is something the
   roaster's own numbers already say, phrased as the money or the
   kilos it is worth, with the verb that does it. A roaster who
   reads one of these and does nothing has still learned something
   true about their business, which is the test for whether a line
   earns its place here.
   ============================================================ */
.wd{display:flex;gap:12px;align-items:flex-start;padding:13px 0;border-top:1px solid var(--hair)}
.wd:first-of-type{border-top:0;padding-top:2px}
.wd .mk2{width:7px;height:7px;flex:0 0 7px;background:var(--accent);margin-top:6px}
.wd .mk2.soft{background:var(--a3)}
.wd .bd{flex:1;min-width:0}
.wd .bd b{display:block;font-size:13px;font-weight:600;line-height:1.4}
.wd .bd span{display:block;font-size:12px;color:var(--ink-3);margin-top:4px;line-height:1.55}
.wd .bd .worth{color:var(--accent);font-weight:600}
.wd .cta{flex:0 0 auto}

/* ============================================================
   TIPS

   One line, the first time you open a screen, from the same
   character that helped in the wizard. It says what the screen is
   for and the one thing worth doing on it. Dismissed for good on
   press, and switchable off in Settings, because a product that
   keeps explaining itself to someone who already knows is worse
   than one that never explained itself at all.
   ============================================================ */
.tip{display:flex;gap:11px;align-items:flex-start;border-left:2px solid var(--accent);
  padding:2px 0 2px 13px;margin:0 0 var(--s5)}
.tip .who2{color:var(--accent);flex:0 0 auto;padding-top:1px}
.tip .bd{flex:1;min-width:0;font-size:12.5px;color:var(--ink-2);line-height:1.6;max-width:74ch}
.tip .bd b{display:block;font:9.5px/1 var(--mono);letter-spacing:var(--caps-ls);
  text-transform:var(--cap);color:var(--ink-3);margin-bottom:6px;font-weight:400}
.tip .x{flex:0 0 auto;font-size:11px;color:var(--ink-3);padding:2px 6px;cursor:pointer}
.tip .x:hover{color:var(--ink)}
html[data-labels=spoken] .tip .bd b{font-family:var(--sans);font-size:11.5px}
/* The sixth look setting. A roaster who has turned the little tips off has
   said they know the product; hiding them is the whole of what that switch
   does, so it lives here rather than in each page's render. */
html[data-tips=off] .tip{display:none}

@media (max-width:1180px){ .grid{grid-template-columns:repeat(2,minmax(0,1fr))} .two{grid-template-columns:1fr}
  .wiz{grid-template-columns:minmax(0,1fr)}
  /* Below this the wide column is too narrow to hold two columns at all.
     One column instead: display:contents makes the panels flex children of
     .home so they can take an order of their own. The week stays directly
     under the day's shipping, which is the pairing that has to survive the
     collapse, and the money and the suggestion follow. */
  /* align-items:stretch, because the wide rule sets start and in a column
     flex that is the CROSS axis: every panel shrank to the width of its own
     widest line, so Your week and Money came out narrower than the panels
     above and below them and the stack read as ragged. */
  .home{display:flex;flex-direction:column;gap:var(--s4);align-items:stretch}
  .home .col{display:contents}
  /* Anything a column grows beyond these sorts to the end rather than to the
     front. Without this an unnumbered panel takes order 0 and jumps above the
     shipping list, which is what happened to the San Pedro panel. */
  .home .col>*{order:9}
  .home .col:first-child>:nth-child(1){order:1}
  .home .col:first-child>:nth-child(2){order:2}
  .home .col:last-child >:nth-child(1){order:3}
  .home .col:last-child >:nth-child(2){order:4}
  .home .col:last-child >:nth-child(3){order:5}
  .home .col:last-child >:nth-child(4){order:6}
  /* The two occasional panels last, in one stack: on a phone the day's work
     and the money come first and nobody scrolls past them to send feedback. */
  .home .col:first-child>:nth-child(3){order:7}
  .home .col:first-child>:nth-child(4){order:8} }
@media (max-width:820px){
  .grid{grid-template-columns:minmax(0,1fr)} .w2{grid-column:span 1}
  main{padding:var(--s4)} .rail{--rail:152px} .f2,.f3{grid-template-columns:minmax(0,1fr)}
  .flowbody{padding:var(--s5) var(--s4)}
}

/* On a phone the rail lies down. A 198px column beside a 390px screen leaves
   the actual work half a screen wide, so below this width the menu becomes a
   band across the top with the tabs in a strip you swipe sideways. Everything
   the rail holds is still reachable — the setup card and the screens that are
   not in the menu sit under the strip rather than being hidden, because a
   roaster finishing setup is exactly the person most likely to be on a phone. */
@media (max-width:700px){
  .app{display:block}
  .rail{width:auto;flex:none;height:auto;position:static;overflow:visible;
    border-right:0;border-bottom:1px solid var(--hair);padding:var(--s3) 0 0;
    display:grid;grid-template-columns:minmax(0,1fr) auto;align-items:center}
  .brand{padding:0 var(--s4) var(--s3)}
  .who{grid-row:1;grid-column:2;margin-top:0;padding:0 var(--s4) var(--s3);border-top:0}
  .who .nm,.who .rl{max-width:33vw;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
  .nav{grid-column:1/-1;flex-direction:row;overflow-x:auto;-webkit-overflow-scrolling:touch;
    scrollbar-width:none}
  .nav::-webkit-scrollbar{display:none}
  .nav a{width:auto;flex:0 0 auto;border-bottom:0;white-space:nowrap}
  .nav a:first-child{border-top:0}
  /* In a row the gap above the warehouse tab becomes a gap to its left. */
  .nav a.wh{margin-top:0;border-top:0;margin-left:var(--s3);border-left:1px solid var(--hair)}
  /* The strip is one row you swipe, so a tab's sections join that row behind
     it rather than opening a second row below it. Same tint, so they still
     read as belonging to the tab in front of them. */
  .nav .subnav{flex-direction:row;flex:0 0 auto;border-bottom:0}
  .nav .subnav a{padding:10px var(--s3)}
  .nav .subnav a:first-child{padding-top:10px;padding-left:var(--s4)}
  .nav .subnav a:last-child{padding-bottom:10px;padding-right:var(--s4)}
  .nav .subnav a.on::before{left:4px}
  .setup,.other{grid-column:1/-1;margin-top:0;padding:var(--s4) var(--s4) 0;
    border-top:1px solid var(--hair)}
  .other{padding-bottom:var(--s3)}
  .other a{display:inline-block;width:auto;margin-right:var(--s4);border-bottom:0;padding:5px 0}
}

/* ------------------------------------------------------------
   Compatibility names.

   The pages are being rebuilt onto the vocabulary above one at a
   time, and a page that has not been rebuilt yet still asks for
   the old token names. Aliasing them costs nothing and means the
   product is never half-broken between two commits, which is the
   only way a cutover this size can be done without a flag day.

   These are scaffolding. When no page references a name here, the
   line goes. Nothing new may be written against them.
   ------------------------------------------------------------ */
:root,html[data-theme=dark]{
  --card:var(--panel);      --card-2:var(--panel);
  --ground-2:var(--panel);  --ground-3:var(--hair);
  --line:var(--hair);       --line-2:var(--hair-2);
  --ink-soft:var(--ink-2);  --ink-mute:var(--ink-3);
  --cream:var(--ink);       --stone:var(--ink-3);  --body:var(--ink-2);
  --red:var(--bad);         --red-soft:var(--bad-soft);
  --green:var(--accent);    --amber:var(--warn);
  --accent-2:var(--a2);     --accent-3:var(--a3);  --accent-4:var(--a4);
  /* Square is the look. A page that still hard-codes a radius is a page
     not yet rebuilt, and these zeros stop the ones that use the tokens
     from being half-round in the meantime. */
  --r:0px; --r-c:0px; --r-b:0px; --r-pill:0px;
  --pad:var(--s5); --spring:cubic-bezier(.32,.72,0,1); --caps-ls:.16em;
  --caps:var(--cap);
  /* The boundary of a control the user has to find, as opposed to one they
     read past. Measured, not guessed: 3.25 on the light ground, 3.28 on the
     dark one, against the 3.0 WCAG asks of a control boundary. --hair-2 is
     quieter and is for panel frames, which sit in whitespace. */
  --edge:#82828C;
}
html[data-theme=dark]{--edge:#666674}

/* A filled red control needs its own ink for the same reason the accent
   does: white on the dark theme's #FF6F61 measures 2.73 and fails even the
   3:1 that large text gets. */
:root{--red-ink:#FFFFFF}
html[data-theme=dark]{--red-ink:#2A0A07}


/* ============================================================
   THE PARTS THE DRAWING HAD NO REASON TO SHOW

   A mockup shows a screen at rest. These are the rules that only
   exist while something is happening: a write in flight, a write
   that landed, a message you can act on, a table on a phone. They
   carry behaviour, and several of them are mirrored by constants
   in JavaScript, so they are commented with what they must agree
   with rather than left to be rediscovered.
   ============================================================ */

/* .wrap was the centred page container from before the rail existed. It is
   kept only for a page that has not been rebuilt yet; a dashboard screen's
   column is <main>, and putting both on one element narrowed seven screens
   to 1180 and nudged them 9px off the rail while two ran to 1500. */
.wrap{max-width:1180px;margin:0 auto;padding:var(--s5) var(--s5) 60px}
main.wrap{max-width:1500px;margin:0;padding:var(--s5)}
/* A screen that is one column of panels rather than a dashboard. Left-hugging
   at 1500px it puts every control against the left edge with half the monitor
   empty beside it, which reads as a page that failed to load the rest of
   itself. Centred and narrow it reads as a document, which is what Recipes,
   and anything else shaped like it, actually is. */
main.wrap.narrow{max-width:780px;margin:0 auto}
.boot{padding:60px var(--s5);color:var(--ink-3);font-size:15px}
.empty{padding:26px 10px;text-align:center;color:var(--ink-3);font-size:13px}
.hint{font-size:11.5px;color:var(--ink-3);line-height:1.55}
.tw{overflow-x:auto;-webkit-overflow-scrolling:touch}
/* A scrolling wrapper is a scroll container, and pinned headings pin to the
   nearest one, which here never scrolls vertically, so they would stay put.
   On a wide screen the tables fit, so the wrapper stands down and the
   headings pin to the window. Below that the sideways scroll is worth more. */
@media (min-width:1101px){ .tw{overflow-x:visible} }
.rowacts{display:flex;gap:var(--s2);align-items:center;flex-wrap:wrap;justify-content:flex-end}

/* Toolbars keep filters together on the left and page actions together on
   the right. Never loose children separated by an anonymous flex spacer:
   that is how a button ends up in the middle of nowhere at 900px. */
.toolbar-main{display:flex;align-items:center;gap:var(--s2);flex:1 1 520px;min-width:0;flex-wrap:wrap}
/* 240 was more than the row could afford: with the chips and the sort control
   beside it the search pushed the group past the line and the sort dropped to
   a second row on its own. The box still grows into whatever is left, so on a
   wide screen it is no smaller than it was; the basis only decides who wraps
   first, and the answer should be nobody. */
.toolbar-main .srch{flex:1 1 200px;min-width:0}
.filter-group,.toolbar-actions,.button-group{display:flex;align-items:center;gap:var(--s2);flex-wrap:wrap}
/* Top, not centre. When the left group does wrap, centring floats the buttons
   halfway between its two rows, lined up with neither, which is the thing that
   reads as crooked. */
.toolbar-actions{justify-content:flex-end;margin-left:auto;flex:0 0 auto;max-width:100%;align-self:flex-start}

/* A smaller .cta, for the ones that sit inside a row rather than at the top
   of a page. Same shape, same rules, less of it. */
.mini{display:inline-flex;align-items:center;justify-content:center;gap:6px;
  border:1px solid var(--hair-2);background:transparent;color:var(--ink-2);
  padding:0 11px;height:29px;font-size:11px;font-weight:600;
  text-transform:var(--cap);letter-spacing:var(--caps-ls);white-space:nowrap}
.mini:hover:not([disabled]){border-color:var(--ink-3);color:var(--ink)}
.mini.k{background:var(--accent);border-color:var(--accent);color:var(--accent-ink)}
.mini.k:hover:not([disabled]){opacity:.9;color:var(--accent-ink)}
.mini.red{color:var(--bad);border-color:var(--bad)}
.mini.eq{min-width:82px}
.mini[disabled]{opacity:.45;cursor:default}

/* An inline field: one that sits in a table cell or a row, where a full
   .fld with its label above would break the line. */
.inl{border:1px solid var(--edge);background:transparent;color:var(--ink);
  height:29px;padding:0 9px;font-size:12.5px;min-width:0;outline:none}
.inl:focus{border-color:var(--accent)}
.inl.w{width:100%}

.warnbox{border:1px solid var(--hair);border-left:3px solid var(--bad);
  background:var(--bad-soft);padding:13px var(--s4);margin-top:var(--s3);color:var(--ink)}
.warnbox b{font-weight:600}
.warnbox p{margin:5px 0 0;font-size:12px;color:var(--ink-2);line-height:1.6}

/* --- the switch ---------------------------------------------------------
   The knob's transition duration is hand-copied in settings.html as
   SLIDE_MS = 240, which is this plus a frame. flip() waits that long before
   replacing the card. Change one and the knob teleports, which is the exact
   bug that constant was written to remove. Change both together. */
.swb .sw i{transition:left .22s var(--spring),background .18s}
.swb:focus-visible .sw{outline:2px solid var(--accent);outline-offset:2px}
/* In flight. The switch has already moved; this says the write is still
   going, in the place the eye already is, rather than in a chip in the far
   corner of the screen that a roaster mid-click never sees. */
.swb.busy .sw{border-color:var(--accent)}
.swb.busy .sw i{animation:swpulse .9s ease-in-out infinite}
@keyframes swpulse{0%,100%{opacity:1}50%{opacity:.35}}
/* .inv was the version that sat on a dark card while the page was light.
   One surface means there is no second background to invert against, so it
   inherits the normal switch and only the resting colour differs. */
.swb.inv{color:var(--ink-3)}
.swb.inv.on{color:var(--accent)}

/* --- a pressed button answers where it stands ---------------------------
   Driven by write() in _shell.js. Named is-* because `.done` is already
   taken by four pages, and a global class that quietly repaints somebody
   else's element is how the last two invisible bugs happened.

   In flight the button dims and breathes, keeping its label and its exact
   width. Nothing is swapped for the word "Saving", because a button that
   changes size on click shoves the page around under the finger that
   pressed it. Done is green with a tick, laid over the top so the width
   still cannot move: that pair needs no reading, which matters on the one
   confirmation a roaster sees a hundred times.

   _shell.js holds the tick for DONE_MS = 1100. A CSS animation retimed
   longer than that is cut off mid-way. */
.is-working,.is-done{position:relative}
.is-working{animation:actpulse .9s ease-in-out infinite}
@keyframes actpulse{0%,100%{opacity:1}50%{opacity:.55}}
.is-done::after{content:'✓';position:absolute;inset:0;display:flex;align-items:center;
  justify-content:center;background:var(--green);color:var(--accent-ink);border-radius:inherit;
  font-size:15px;font-weight:700;line-height:1;animation:actdone .16s ease-out}
@keyframes actdone{from{opacity:0}to{opacity:1}}

.toast{position:fixed;left:50%;bottom:26px;transform:translateX(-50%) translateY(12px);
  background:var(--ink);color:var(--ground);padding:12px 18px;font-size:13px;font-weight:600;
  opacity:0;transition:opacity .2s,transform .2s var(--spring);z-index:200;
  pointer-events:none;max-width:min(560px,90vw);text-align:center}
.toast.on{opacity:1;transform:translateX(-50%)}
/* The plain toast is pointer-events:none so it never eats a click meant for
   the page underneath. One with an action has to take clicks, or the button
   in it is decoration. */
.toast.has-act{pointer-events:auto;display:flex;align-items:center;gap:var(--s4)}
/* And a toast that is not on screen takes none, action or not. After this rule,
   because it and .toast.has-act carry the same weight and the last one wins. */
.toast:not(.on){pointer-events:none}
.toast .toast-act{color:var(--accent);font-weight:700;text-transform:var(--cap);
  letter-spacing:var(--caps-ls);font-size:11px;min-height:44px;padding:0 10px}
.toast .toast-act:focus-visible{outline:2px solid var(--accent);outline-offset:2px}
.saving{position:fixed;right:16px;bottom:16px;font:10px/1 var(--mono);letter-spacing:var(--caps-ls);
  text-transform:uppercase;color:var(--ink-3);opacity:0;transition:opacity .2s;z-index:190}
.saving.on{opacity:1}

/* The payment-terms row from _terms.js. It lives here rather than in the
   module because Settings and the cafe drawer must not drift apart: a
   roaster reading "Friday" on one screen and a differently-shaped control
   on the other is how you end up unsure which one is real. The row wraps
   rather than squeezing, because at 375px the mode label alone ("only when
   they pay it themselves") is wider than the phone. */
.terms{display:flex;align-items:center;gap:var(--s2);flex-wrap:wrap}
.terms>span{font-size:12px;color:var(--ink-3)}
.terms select,.terms input{border:1px solid var(--edge);background:transparent;color:var(--ink);
  height:34px;padding:0 9px;font-size:12.5px;min-height:34px;outline:none}
.terms select:focus,.terms input:focus{border-color:var(--accent)}
.terms input[data-terms-days-input]{width:72px}
.terms [hidden]{display:none}

@media (max-width:700px){
  /* A table row becomes a stacked block rather than a wide table with its
     last columns hanging off the side. This is the phone layout, not
     decoration. .tw is the backstop so the PAGE never scrolls sideways even
     if something turns out wider than expected. */
  table.t thead{display:none}
  table.t,table.t tbody,table.t tr,table.t td{display:block;width:auto!important}
  table.t tr{border-top:1px solid var(--hair);padding:10px 0}
  table.t tr:first-child{border-top:0}
  table.t td{border-bottom:0;padding:3px 0;text-align:left!important}
  table.t td:empty{display:none}
  .rowacts{justify-content:flex-start;margin-top:6px}
  .toolbar-main,.toolbar-actions{flex:1 1 100%}
  .toolbar-actions{justify-content:flex-start;margin-left:0}
  .srch{min-width:0}
}

/* Transitions flatten to .01ms rather than 0, deliberately: transitionend
   still fires, so anything waiting on one does not stall. Animations stop
   outright, because nothing waits on animationend — the tick still appears,
   it just does not fade in. */
@media (prefers-reduced-motion:reduce){
  .is-working{animation:none}
  .is-done::after{animation:none}
  .swb.busy .sw{animation:none}
  .anim,[class*=skel]{animation:none!important}
  *,*::before,*::after{transition-duration:.01ms!important}
}
