/* ═══════════════════════════════════════════════════════════════════════════
   app-ext.css — layered on top of the approved app.css, never edits it.
   Everything here is the Apple HIG pass: focus, hit targets, motion, states,
   responsiveness, forms, dialogs, toasts, and empty states.
   ═══════════════════════════════════════════════════════════════════════════ */

:root{
  /* --focus and --focus-ring live in theme-apple.css, which owns every colour. */
  --ease:cubic-bezier(.2,.7,.2,1);
  --t-fast:140ms; --t:220ms;
}

/* ── accessibility base ────────────────────────────────────────────────── */
.skip{
  position:absolute; left:8px; top:-40px; z-index:100; background:var(--ink); color:var(--on-accent);
  padding:8px 12px; border-radius:var(--r-c); font-size:12.5px; text-decoration:none;
  transition:top var(--t-fast) var(--ease);
}
.skip:focus{top:8px; outline:none; box-shadow:var(--focus-ring)}
:focus{outline:none}
:focus-visible{box-shadow:var(--focus-ring); border-radius:var(--r-c)}
.rail-user .signout{margin-left:auto}
.rail-user .signout .btn{color:var(--rail-ink-2); padding-inline:4px; min-height:32px}
main:focus{box-shadow:none}
@media (prefers-reduced-motion:reduce){
  *,*::before,*::after{animation-duration:.01ms!important; animation-iteration-count:1!important; transition-duration:.01ms!important; scroll-behavior:auto!important}
}

/* ── hit targets: 32px floor at desktop, 44px at compact width (MOBILE-2) ── */
.btn{min-height:34px; transition:background var(--t-fast) var(--ease), border-color var(--t-fast) var(--ease), transform var(--t-fast) var(--ease)}
.btn.sm{min-height:32px}
.btn:active{transform:translateY(.5px)}
.btn[disabled],.btn[aria-disabled="true"]{cursor:not-allowed; transform:none}
.btn.wide{width:100%; justify-content:center; min-height:38px}
.btn.danger{background:var(--bad); border-color:var(--bad)}
.btn.ghost.danger{background:var(--surface); color:var(--bad); border-color:var(--bad-tint)}
.btn.ok{background:var(--ok); border-color:var(--ok)}
.btn .spin{display:none}
.btn[aria-busy="true"] .spin{display:inline-block}
.btn[aria-busy="true"]{opacity:.7; pointer-events:none}
.spin{width:12px; height:12px; border:2px solid var(--on-accent-dim); border-top-color:var(--on-accent); border-radius:50%; animation:spin .7s linear infinite}
.btn.ghost .spin{border-color:var(--hair); border-top-color:var(--ink)}
@keyframes spin{to{transform:rotate(360deg)}}

.rail a{min-height:36px; transition:background var(--t-fast) var(--ease)}
.tab-bar a{min-height:38px; display:inline-flex; align-items:center}
@media (max-width:920px){
  /* A compact-width tab bar is tapped, not clicked: 44pt is the floor. */
  .tab-bar a{min-height:44px}
}
.icon-btn{text-decoration:none; transition:background var(--t-fast) var(--ease)}

/* the chip is interactive now: a link or a button */
a.chip,button.chip{cursor:pointer; text-decoration:none; font-family:var(--ui); min-height:32px; transition:border-color var(--t-fast) var(--ease), background var(--t-fast) var(--ease)}
.seg a,.seg button{
  font:400 12px/1 var(--ui); padding:6px 11px; border-radius:4px; color:var(--ink-2); white-space:nowrap;
  text-decoration:none; background:transparent; border:0; cursor:pointer; min-height:32px; display:inline-flex; align-items:center;
}
.seg a[aria-current],.seg button[aria-current]{background:var(--surface); color:var(--ink); font-weight:500; box-shadow:var(--shadow-1)}
.seg .n{color:var(--ink-3); font-size:11px; padding-left:5px}

/* ── search is a real input ───────────────────────────────────────────── */
.search{padding:0 9px}
.search input{
  flex:1; min-width:0; border:0; background:transparent; font:400 12px/1 var(--ui); color:var(--ink);
  padding:6px 0; outline:none;
}
.search input::placeholder{color:var(--ink-3)}
.search:focus-within{border-color:var(--ring, var(--navy)); box-shadow:0 0 0 3px var(--ring-tint, var(--navy-tint))}
.search input:focus-visible{box-shadow:none}

/* ── tables ─────────────────────────────────────────────────────────────── */
table.t tbody tr{transition:background var(--t-fast) var(--ease)}
table.t a{color:inherit; text-decoration:none}
table.t tr.link{cursor:pointer}
.t-wrap{overflow-x:auto; -webkit-overflow-scrolling:touch}
/* The 640px floor buys a readable scan line for a real data table. Three
   tables opt out of it:
     * .narrow, which a screen sets by hand;
     * a table with fewer than three columns (the search result cards), which
       has nothing to scan and would only scroll sideways on a phone;
     * a .cards table below 600px, where every row is already a card and a
       floor is what forced the phone to scroll (TABLE-3). components.css
       used to have to beat this rule with !important; it no longer does. */
.t-wrap table{min-width:640px}
.t-wrap table.t.narrow,.t-wrap table.t:not(:has(>thead>tr>th:nth-child(3))){min-width:0}
@media (max-width:600px){
  .t-wrap table.t.cards{min-width:0}
}
table.t td.act{width:1%; white-space:nowrap}
table.t input[type="checkbox"]{width:16px; height:16px; accent-color:var(--navy); margin:0; vertical-align:middle}
.deal:focus-visible,a.deal:focus-visible{box-shadow:var(--focus-ring)}
a.deal{display:block; color:inherit; text-decoration:none; transition:transform var(--t-fast) var(--ease), box-shadow var(--t-fast) var(--ease)}
.deal.funded{border-color:var(--sys-green-tint); background:var(--tint-funded)}
/* .deal.paused: unreachable today — the only .deal call site appends
   ' flag' / ' funded'. Paused state is a "Paused" chip in the schedules
   table and on the deal header, which is text, not colour. Kept as a hook
   with a word alongside it if a paused card is ever rendered. */
.deal.paused{border-left:1px solid var(--hair)}

/* ── forms ─────────────────────────────────────────────────────────────── */
.field{display:flex; flex-direction:column; gap:5px; font-size:12px; color:var(--ink-2)}
.field>span{font-weight:500; color:var(--ink)}
.field .hint{font-size:11px; color:var(--ink-3)}
.field input,.field select,.field textarea{
  font:400 13px/1.3 var(--ui); color:var(--ink); background:var(--surface); border:1px solid var(--input, var(--line));
  border-radius:var(--r-c); padding:8px 10px; min-height:36px; width:100%; transition:border-color var(--t-fast) var(--ease), box-shadow var(--t-fast) var(--ease);
}
.field textarea{min-height:72px; resize:vertical}
.field input:focus-visible,.field select:focus-visible,.field textarea:focus-visible{border-color:var(--ring, var(--navy)); box-shadow:0 0 0 3px var(--ring-tint, var(--navy-tint))}
.field input[aria-invalid="true"]{border-color:var(--bad); box-shadow:0 0 0 3px var(--bad-tint)}
.field .err{font-size:11.5px; color:var(--bad)}
.form-grid{display:grid; grid-template-columns:1fr 1fr; gap:12px 14px}
.form-grid .span{grid-column:1/-1}
.form-acts{display:flex; gap:8px; justify-content:flex-end; padding-top:4px}
.money-in{position:relative}
.money-in::before{content:"$"; position:absolute; left:10px; top:50%; transform:translateY(-50%); color:var(--ink-3); font-size:13px}
.money-in input{padding-left:22px}
.inline-form{display:inline}

/* ── dialogs (native <dialog>) ───────────────────────────────────────────── */
dialog.sheet{
  border:1px solid var(--hair); border-radius:var(--r); padding:0; width:min(560px, calc(100vw - 32px));
  background:var(--surface); color:var(--ink); box-shadow:var(--shadow-3, var(--shadow-2));
}
/* No blur: the design contract forbids glass, and the scrim already reads. */
dialog.sheet::backdrop{background:rgba(20,18,15,.28)}
dialog.sheet[open]{animation:sheet-in var(--t) var(--ease)}
@keyframes sheet-in{from{opacity:0; transform:translateY(6px) scale(.985)} to{opacity:1; transform:none}}
dialog.sheet .card-body{display:flex; flex-direction:column; gap:12px}
dialog.sheet .x{margin-left:auto; width:32px; height:32px; border-radius:var(--r-c); border:1px solid transparent; background:transparent; display:grid; place-items:center; color:var(--ink-3); cursor:pointer}
dialog.sheet .x svg{width:13px; height:13px}

/* ── toasts ─────────────────────────────────────────────────────────────── */
.toasts{position:fixed; right:16px; bottom:16px; display:flex; flex-direction:column; gap:8px; z-index:90; pointer-events:none}
.toast{
  pointer-events:auto; background:var(--ink); color:var(--on-accent); border-radius:var(--r); padding:10px 14px; font-size:12.5px;
  box-shadow:var(--shadow-2); display:flex; align-items:center; gap:10px; max-width:380px;
  animation:toast-in var(--t) var(--ease);
}
.toast.bad{background:var(--bad)} .toast.ok{background:var(--ok)}
.toast .x{margin-left:auto; color:var(--on-accent); opacity:.72; background:transparent; border:0; cursor:pointer; padding:8px; margin-right:-6px; display:grid; place-items:center}
.toast .x svg{width:12px; height:12px}
.toast.out{animation:toast-out var(--t) var(--ease) forwards}
@keyframes toast-in{from{opacity:0; transform:translateY(8px)} to{opacity:1; transform:none}}
@keyframes toast-out{to{opacity:0; transform:translateY(8px)}}

/* ── empty states ────────────────────────────────────────────────────────── */
.empty{padding:28px 20px; text-align:center; color:var(--ink-2); font-size:12.5px; line-height:1.55}
.empty p{margin:0; max-width:46ch; margin-inline:auto}
.empty .btn{margin-top:12px}
.empty.tall{padding:64px 20px}
.empty-code{font-size:11px; color:var(--ink-3); letter-spacing:.1em}
.empty-row td{padding:22px 12px!important; text-align:center; color:var(--ink-3); height:auto!important}

/* ── auth ──────────────────────────────────────────────────────────────── */
body.bare{min-height:100vh}
.auth{min-height:100vh; display:grid; place-items:center; padding:24px}
.auth-card{width:min(380px, 100%); padding:26px 26px 24px; display:flex; flex-direction:column; gap:14px}
.auth-card .mut{margin:-8px 0 2px; font-size:12.5px}
.auth-brand{display:flex; align-items:center; gap:8px; color:var(--ink)}
.auth-brand svg{height:18px; width:auto}
.auth .banner{margin:0}

/* ── details / disclosure ────────────────────────────────────────────────── */
details.card>summary{list-style:none; cursor:pointer}
details.card>summary::-webkit-details-marker{display:none}
details.card>summary .card-head{border-bottom:0}
details.card[open]>summary .card-head{border-bottom:1px solid var(--hair-2)}
.card-head .st{flex:none}
.card-head .sub{min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap}
/* Opt out where the sub carries a fact that must not be cut (the return-rates
   card's "as of" date). The head is a wrap container, so it takes the line. */
.card-head .sub.wrap{overflow:visible; text-overflow:clip; white-space:normal}

/* ── kanban polish ──────────────────────────────────────────────────────── */
.board{overflow-x:auto; padding-bottom:4px; grid-template-columns:repeat(6,minmax(150px,1fr))}
.board>div{min-width:0}
.col-h .t{white-space:nowrap}
.deal .m .iso{max-width:96px; overflow:hidden; text-overflow:ellipsis; white-space:nowrap}
.stage-acts{display:flex; gap:6px; flex-wrap:wrap; margin-top:8px}
.deal .n{overflow-wrap:anywhere}

/* a <select> dressed as the mock's filter chip */
.chip-select{
  appearance:none; -webkit-appearance:none; font:400 11.5px/1 var(--ui); color:var(--ink-2);
  border:1px solid var(--hair); background:var(--surface) var(--chevron) no-repeat right 9px center/11px 11px;
  border-radius:20px; padding:6px 26px 6px 10px; min-height:32px; cursor:pointer;
}
.chip-select.on{font-weight:500}
.chip-select:focus-visible{border-color:var(--ring, var(--navy)); box-shadow:0 0 0 3px var(--ring-tint, var(--navy-tint))}

/* ── misc ───────────────────────────────────────────────────────────────── */
.page-head h1{overflow-wrap:anywhere}
.crumb{min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap}
.clock{white-space:nowrap}
.kpi-v{overflow-wrap:anywhere}
/* .kpi-v allows any break; the delta pill inside it must not inherit that,
   or "+42.3%" splits across two lines and reads as two numbers. */
.delta{overflow-wrap:normal; white-space:nowrap}
.kbd{font-family:var(--ui)}
.visually-hidden{position:absolute!important; width:1px; height:1px; overflow:hidden; clip:rect(0 0 0 0); white-space:nowrap}
/* A .code chip holds a .visually-hidden explanation. While the chip was
   `display:inline`, that absolutely-positioned child's static position opened a
   second line box, and the chip painted a 6x17px sliver of its own background
   below itself on every row. An inline-block chip has one box. */
.code{display:inline-block; vertical-align:middle}
.row-acts{display:flex; gap:6px; align-items:center; justify-content:flex-end}
.tag{display:inline-block; font-size:11px; color:var(--ink-3); border:1px solid var(--hair); background:var(--surface-2); border-radius:3px; padding:1px 5px}
.cert{font-family:var(--mono); font-size:11.5px; white-space:pre-wrap; background:var(--surface-2); border:1px solid var(--hair); border-radius:var(--r-c); padding:12px 14px; margin:0; overflow:auto}
.stack-list{display:flex; flex-direction:column; gap:16px; min-width:0}

/* ── responsive ─────────────────────────────────────────────────────────── */
@media (max-width:1180px){
  .g-main,.g-focus{grid-template-columns:minmax(0,1fr)}
  .kpis{grid-template-columns:repeat(auto-fit,minmax(150px,1fr))}
  .kpi{border-bottom:1px solid var(--hair-2)}
}
/* ── the rail is anchored on desktop ──────────────────────────────────────
   .app is a two-column grid, so the rail is as tall as the page and scrolls
   away with it: on a long ledger the navigation and the sign-out control ended
   up thousands of pixels above the content. Stick it to the viewport and give
   it its own scrollbar for the case where the sections are taller than the
   screen. Below 920px it becomes the horizontal strip defined further down, so
   this is desktop only. */
@media (min-width:921px){
  .rail{
    position:sticky; top:0; align-self:start;
    height:100vh; height:100dvh;
    overflow-y:auto; overscroll-behavior:contain;
    scrollbar-width:thin;
  }
}

@media (max-width:920px){
  .app{grid-template-columns:minmax(0,1fr)}
  .rail{min-width:0; max-width:100vw; position:sticky; top:0; z-index:20; flex-direction:row; align-items:center; overflow-x:auto; padding:0 6px; gap:4px; border-right:0; border-bottom:1px solid var(--rail-2)}
  .rail-brand{padding:10px 8px}
  .org{margin:0; max-width:190px}
  .rail nav{flex-direction:row; padding:0; gap:2px}
  /* Scoped to the rail, like .rail .rail-user below: dialog.more reuses this
     markup, and there the five section headings are the only structure the
     phone's whole navigation has. */
  .rail .rail-sec{display:none}
  .rail a{padding:6px 8px; white-space:nowrap}
  .rail-foot{margin:0 0 0 auto; padding:0 6px; border:0}
  /* Scoped to the rail: the same markup is reused inside dialog.more, where
     the signed-in name is the whole point of the block. */
  .rail .rail-user b,.rail .rail-user>span:last-child>span{display:none}
  .top{height:auto; flex-wrap:wrap; padding:10px 14px; gap:10px}
  .search{width:100%; order:5; margin-left:0}
  .page{padding:14px}
  .g-2,.g-3{grid-template-columns:minmax(0,1fr)}
  .g-2>*,.g-3>*{min-width:0}
  .page-head{flex-direction:column}
  .page-head .acts{margin-left:0; flex-wrap:wrap}
  .form-grid{grid-template-columns:1fr}
  .steps{flex-wrap:wrap; gap:6px}
  .rail-x{display:none}
}
@media (max-width:600px){
  .seg{flex-wrap:wrap; max-width:100%}
  .seg a,.seg button{flex:1 1 auto; justify-content:center}
  .filters>span[style]{display:none}
  .kpis{grid-template-columns:1fr 1fr}
  .kpi-v{font-size:22px}
  /* Two tiles per row: give the delta its own line rather than let it wrap
     inside the value, and let an odd last tile span the row (MOBILE-4). */
  .kpi-v .delta{display:block; margin:4px 0 0; width:max-content; top:0}
  .kpis .kpi:last-child:nth-child(odd){grid-column:1/-1}
  .toasts{left:12px; right:12px}
  .toast{max-width:none}
  /* app.css gives .banner .acts `flex:none`, so the button block never shrank
     and the message was squeezed into a 120px column — thirteen wrapped lines
     beside "Submit now" on /app/console at 390. Stack them, the way .form-acts
     already stacks at this width. */
  .banner{flex-wrap:wrap}
  .banner>span,.banner>p{min-width:0}
  .banner .acts{margin-left:0; width:100%; flex:1 1 auto; margin-top:8px}
  .banner .acts .btn{flex:1 1 auto; justify-content:center; min-height:44px}
  /* The 920px block above only unsets the margin, so the three detail-page
     actions kept `justify-content:flex-end` inside a 232px box and landed on
     three lines, none aligned with each other or with the page.
     `.page` is on the selector because origination.css loads after this file
     and caps the block at `max-width:64%` with no media query — this needs to
     out-specify it, not out-order it. */
  .page .page-head .acts{width:100%; max-width:none; justify-content:flex-start}
  .page .page-head .acts .btn{min-height:44px}
}
/* ── hover is a capability, not an assumption ────────────────────────────
   A phone has no pointer that rests: an ungated :hover leaves the tapped card
   lifted and the tapped row shaded until something else is touched. Every
   hover-only affordance lives here. */
@media (hover:hover){
  .btn:hover{background:var(--navy-2); border-color:var(--navy-2)}
  .btn.ghost:hover{background:var(--surface-2); border-color:var(--line)}
  .btn.quiet:hover{background:var(--navy-tint)}
  .tab-bar a:hover{color:var(--ink)}
  .icon-btn:hover{background:var(--surface-2)}
  a.chip:hover,button.chip:hover{border-color:var(--line)}
  table.t tbody tr:hover:not(.child):not(.hi):not(.sel){background:var(--surface-2)}
  table.t a:hover{text-decoration:underline; text-underline-offset:2px}
  a.deal:hover{box-shadow:var(--shadow-2); transform:translateY(-1px)}
  dialog.sheet .x:hover{background:var(--surface-2); color:var(--ink)}
}

/* The print story lives in public/css/print.css, linked with media="print".
   These four lines stay as the floor in case a shell has not linked it yet;
   print.css is a strict superset and wins wherever it is loaded. */
@media print{
  .rail,.top,.toasts,.acts,.skip{display:none!important}
  .app{grid-template-columns:1fr}
  .card{box-shadow:none; break-inside:avoid}
  /* Keep the totals with the ledger they total. Left to itself the statement
     put "Collected / Returned / Outstanding" alone on a second sheet with no
     letterhead, no merchant name and no advance id on it. */
  .doc-totals{break-before:avoid; page-break-before:avoid}
}

/* A letterhead is written as lines, and the stored value holds real newlines;
   HTML collapses them, so company, street, city/zip and email ran together on
   the document the merchant receives. */
.doc .from .addr-l{display:block}

/* Two figures and a percentage stacked, so the primary one lands on the same
   baseline in every row. Inline, the break point followed the string length. */
table.t td.money-stack .fig{display:block; white-space:nowrap; font-variant-numeric:tabular-nums}
table.t td.money-stack .mut.fig{font-size:12px}

/* A date-range pair with its own visible micro-labels, so the two identical
   empty boxes say which end of the range they are. */
.range-f{display:inline-flex; flex-direction:column; gap:2px; min-width:0}
.range-f .lbl-sm{margin:0}

/* This note lives inside a tinted banner, not on a white card: --ink-3 on the
   .banner.ok green measured 4.41:1 at 12px (agent-browser at 390). */
.act-note{font-size:12px;color:var(--ink-2);align-self:center;margin-left:8px}

/* ── a sheet's own error line ──────────────────────────────────────────────
   A <dialog> opened with showModal() is in the top layer, so .toast-stack
   (a z-index:90 sibling of .app) can never paint above it: on a phone the
   rejection toast was drawn *underneath* the open sheet and pressing Save
   looked like it had done nothing at all. Errors that carry no `field` land
   here instead. */
dialog.sheet>.sheet-err{
  margin:0; padding:10px 16px; font-size:13px; font-weight:500;
  color:var(--bad); background:var(--bad-tint);
  border-bottom:1px solid var(--hair);
}
dialog.sheet>.sheet-err:focus-visible{outline:none; box-shadow:var(--focus-ring)}

/* ── the fourth rate tier ──────────────────────────────────────────────────
   DOMAIN-14 split the old three-tier gauge into ok / hold / red / bad. `red`
   is where funding has to stop, short of the ceiling itself, so it needs its
   own fill and its own mark on the track — and the word beside the number is
   what actually carries the meaning (A11Y-3). */
.track i.red{background:var(--sys-orange, var(--hold))}
.track .mark.red{width:1.5px; opacity:.75}
.gauge-v .st-w{
  font:500 11px/1 var(--ui); margin-left:8px; padding:3px 7px; border-radius:999px;
  vertical-align:middle; white-space:nowrap; color:var(--ink-2); background:var(--hair-2, rgba(120,120,128,.12));
}
.gauge-v .st-w.hold,.gauge-v .st-w.red{color:var(--ink); background:var(--tint-hold, rgba(255,149,0,.14))}
.gauge-v .st-w.bad{color:var(--ink); background:var(--tint-bad, rgba(255,59,48,.14))}

/* ── hidden means hidden ───────────────────────────────────────────────────
   `.field{display:flex}` (and every other display rule below) outranks the
   user-agent's `[hidden]{display:none}`, so `el.hidden = true` on a labelled
   control did nothing — measured: the confirm sheet's "Type to confirm" input
   showed on every confirmation, including the ones that do not ask for it.
   One rule, in the layer that introduced the display rules. */
[hidden]{display:none !important}

/* ── the confirmation sheet ────────────────────────────────────────────────
   It replaces window.confirm, which could not lay out an amount and a count. */
dialog.sheet.confirm{max-width:420px}
dialog.sheet.confirm .card-body{display:flex; flex-direction:column; gap:12px}
dialog.sheet.confirm p{margin:0; font-size:13px; line-height:1.45; color:var(--ink)}
dialog.sheet.confirm dl{
  display:grid; grid-template-columns:auto 1fr; gap:6px 16px; margin:0;
  padding:10px 12px; border-radius:var(--r-c, 8px); background:var(--hair-2, rgba(120,120,128,.08));
}
dialog.sheet.confirm dt{font-size:12px; color:var(--ink-2)}
dialog.sheet.confirm dd{margin:0; font-size:12px; font-weight:500; color:var(--ink); text-align:right; font-variant-numeric:tabular-nums}

/* ── a ticked row reads as ticked ─────────────────────────────────────────── */
table.t tbody tr.sel{background:var(--navy-tint, rgba(0,122,255,.08))}

/* ── a secondary link is still tapped with a thumb ─────────────────────────
   The alerts feed's "Open" measured 42×16 on a phone. Below 600px every
   interactive thing gets the 44pt box, even when its text is small. */
@media (max-width:600px){
  .feed .meta .quiet-link,.fd .meta .quiet-link{display:inline-flex; align-items:center; min-height:44px}
}

/* ── a control that looks like a button is not underlined ──────────────────
   `.btn` (public/app.css) never sets text-decoration and nothing after it
   restored the default, so an ANCHOR styled as a button kept the browser's
   underline while the <button> beside it — same classes, same row — had none.
   Measured on /app/console: `a.btn.ghost.sm` "Export CSV" underlined,
   `button.btn.ghost.sm` "Import returns…" immediately to its right not; the
   same split on all 17 list screens, on the statement's Print / Export CSV /
   Back row, and on 50 `a.btn.quiet.sm` on /app/notifications. The rule is
   scoped to anchors that are dressed as a control; prose links (`.banner a`,
   `table.t a`, `.quiet-link`) keep their underline, which is what tells them
   apart from the words around them. */
a.btn,a.chip,a.seg-o,a.icon-btn,a.day-cell,a.tab,a.kpi-link{text-decoration:none}
a.btn:hover,a.chip:hover,a.day-cell:hover{text-decoration:none}
/* `.banner a` underlines the prose link inside a banner, which is right — and
   it is loaded after this file, so it also underlined the banner's own BUTTON
   ("Open the file" on /app/pipeline, "Open corrections" on /app/returns, "Show
   them" on /app/authorizations). One more class of specificity, so the sentence
   keeps its underline and the control does not. */
.banner a.btn,.banner a.chip,.card-foot a.btn,.empty a.btn{text-decoration:none}
