/* tools-hub — application styles.
 *
 * Ported from the prototype in `Reference/Hub V1 UI Reference/design_files/Hub Demo.dc.html`,
 * which carries its styling inline. Values (sizes, timings, colours) are the design's;
 * everything resolves through the token custom properties in `css/tokens/` so a token
 * change propagates rather than being hard-coded twice.
 *
 * Sections: base · primitives · app shell · sidebar · page · login.
 */

/* ============================ base ============================ */

:root {
  /* Every interactive control gets a hit box at least this tall and wide. The hub is
     driven by tap on a tablet, where the design's 22–34px icon buttons are well under
     a fingertip. Not a token: `css/tokens/` is the design system copied verbatim, and
     this is a decision about our input device, not about the brand.

     Applied three ways, in order of preference — grow the box (`min-height` on anything
     padded); grow it and cancel the growth with negative margins where a bigger box
     would move its neighbours (.advance-ico); widen the surrounding column where two
     enlarged boxes would otherwise overlap and steal each other's taps. */
  --tap-min: 44px;

  /* Press feedback. Every :hover in this file is now inside `@media (hover: hover)`,
     because a tablet has no pointer: hover never fires, and iOS *sticks* the last
     hovered state onto whatever you tapped, so a row stays lit after you have moved on.
     That leaves :active as the only feedback a tap gets, so it has to exist everywhere
     hover did. The design's rule is hover = lighten, press = darken (Reference README,
     "Interactions & Motion").

     Derived with color-mix off the surface rather than fixed hexes so they follow the
     dark scope: --text-strong is near-black on light and white on dark, so the same
     declaration darkens on one and lightens on the other. */
  --surface-pressed: color-mix(in srgb, var(--surface-sunken) 88%, var(--text-strong));
  --accent-tint-pressed: color-mix(in srgb, var(--accent-tint) 70%, var(--accent));
  --danger-tint-pressed: color-mix(in srgb, var(--cwl-danger-tint) 80%, var(--cwl-danger));
  --success-tint-pressed: color-mix(in srgb, var(--cwl-success-tint) 80%, var(--cwl-success));
}

/* The semantic colours in css/tokens/ are light-surface values with no dark variant:
   #fae3e1 is a pale pink that becomes a glaring patch on onyx, and #d6453d is too dark
   to read on it — the login screen already worked around that with a hand-picked
   salmon. Restated here as lifted ink plus a translucent wash of the same hue, rather
   than edited in tokens/, which is the design system copied verbatim.

   Scoped to .cwl-dark as well as the theme attribute, matching how colors.css scopes
   its own dark aliases, so the always-dark login screen gets them too. */
/* The dark aliases in css/tokens/ put the containers *lighter* than the field they sit
   on — the inverse of the light theme, where a white column is lifted off a grey page.
   Restated so the hierarchy reads the same way in both themes: the chrome that holds
   things (header, sidebar, columns, tables, modals) is the darkest surface, the field
   sits above it, and the things you read and type into are lightest. */
html[data-theme="dark"] {
  --surface-card: #131517;
  --surface-sunken: #202325;
  --surface-page: #2b2f31;
  --surface-raised: #363a3c;
}

.cwl-dark,
html[data-theme="dark"] {
  --cwl-danger: #ff6b63;
  --cwl-danger-tint: rgba(214, 69, 61, 0.22);
  --cwl-success: #46c08d;
  --cwl-success-tint: rgba(31, 157, 107, 0.22);
  --cwl-warning: #eeb03a;
  --cwl-warning-tint: rgba(217, 146, 6, 0.22);
}

/* Both of these mean "lift one step off whatever holds me", which in the light theme
   is white. Inverted, --surface-card is the darkest colour, so the selected segment
   would sink and the hover would do nothing — lift is lighter here, not whiter. */
html[data-theme="dark"] .seg-btn.is-on,
html[data-theme="dark"] .addtask:hover {
  background: var(--surface-page);
}

/* The scrim has to separate the dialog from a page that is already dark. */
html[data-theme="dark"] .modal-overlay {
  background: rgba(0, 0, 0, 0.62);
}

/* A tap on iOS paints its own translucent grey box, which fights the :active styles
   below and lands on the element's bounding box rather than its rounded corners.
   touch-action removes the double-tap-zoom wait, so a press reads as immediate. */
button,
a,
select,
[role="button"] {
  -webkit-tap-highlight-color: transparent;
  touch-action: manipulation;
}

html,
body {
  margin: 0;
}

body {
  background: var(--surface-sunken);
  color: var(--text-body);
  font-family: var(--font-sans);
  font-size: 14px;
  line-height: var(--lh-normal);
  -webkit-font-smoothing: antialiased;
}

a {
  color: var(--accent);
}
@media (hover: hover) {
  a:hover {
    color: var(--accent-hover);
  }
}

/* Alpine sets this until it has initialised; without it, both halves of an x-show
   pair paint for a frame. */
[x-cloak] {
  display: none !important;
}

.mono {
  font-family: var(--font-mono);
  font-feature-settings: "tnum" 1;
}

/* Visually hidden, still read by screen readers. */
.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
  border: 0;
}

/* Rendered in both themes, shown in one. The alternative is swapping src/text in JS,
   which either flashes the wrong one before the script runs or duplicates in script
   what the stylesheet already knows. `light` is the default scope, so it hides only
   when the dark attribute is present. */
html[data-theme="dark"] .theme-only-light,
html:not([data-theme="dark"]) .theme-only-dark {
  display: none;
}

/* ========================== primitives ========================= */

.icon-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: var(--tap-min);
  height: var(--tap-min);
  appearance: none;
  background: transparent;
  border: 1px solid transparent;
  border-radius: var(--radius-md);
  color: var(--text-muted);
  cursor: pointer;
  transition:
    background 0.16s,
    color 0.16s,
    transform var(--dur-base) var(--ease-out);
}
@media (hover: hover) {
  .icon-btn:hover {
    background: var(--surface-sunken);
    color: var(--text-strong);
  }
}
.icon-btn:active {
  background: var(--surface-pressed);
  color: var(--text-strong);
}

.ti {
  box-sizing: border-box;
  min-height: var(--tap-min);
  font-family: var(--font-sans);
  font-size: 14px;
  color: var(--text-strong);
  background: var(--surface-page);
  border: 1px solid var(--border-default);
  border-radius: var(--radius-md);
  padding: 9px 12px;
  outline: none;
  transition:
    border-color 0.16s,
    box-shadow 0.16s;
}
.ti:focus {
  border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--accent-tint);
}

/* Primary CTA — aqua fill, dark ink. Text on aqua is never white (tokens README). */
.btn-primary {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  appearance: none;
  border: 0;
  background: var(--accent);
  color: var(--text-onbrand);
  font-family: var(--font-sans);
  font-weight: var(--fw-bold);
  font-size: 15px;
  letter-spacing: -0.005em;
  border-radius: var(--radius-md);
  padding: 15px 28px;
  cursor: pointer;
  transition:
    background 0.16s var(--ease-out),
    transform 0.16s var(--ease-out);
}
@media (hover: hover) {
  .btn-primary:hover {
    background: var(--accent-hover);
  }
}
.btn-primary:active {
  background: var(--accent-press);
}

/* ========================== app shell ========================== */

.shell {
  /* Exactly the viewport, not at least it: the sidebar has a footer pinned to its
     bottom, and a shell that grows with the board pushes that footer off-screen and
     makes the theme toggle something you have to scroll the page to reach. Scrolling
     happens inside .main (and inside .board-scroll) instead of on the document.
     dvh so iPadOS' collapsing browser chrome does not leave a dead strip. */
  height: 100vh;
  height: 100dvh;
  display: flex;
  flex-direction: column;
}

.app-header {
  background: var(--surface-card);
  border-bottom: 1px solid var(--border-hairline);
  position: relative;
  z-index: 2;
}
.app-header-inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 20px;
  flex-wrap: wrap;
  padding: 12px 24px;
}
.header-left {
  display: flex;
  align-items: center;
  gap: 12px;
}
.header-right {
  display: flex;
  align-items: center;
  gap: 16px;
  min-width: 0;
}

/* The mark is a link to /, so it is a control, not decoration: the 30px artwork is
   centred in a full-size box rather than being a 30px target. */
.header-left a {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: var(--tap-min);
  min-height: var(--tap-min);
}

.brand-mark {
  display: block;
  height: 30px;
  width: auto;
}
/* Fallback when no logo SVG has been dropped in yet (static/logo/README.md). */
.brand-wordmark {
  font-family: var(--font-sans);
  font-weight: var(--fw-black);
  font-size: 13px;
  letter-spacing: var(--ls-caps);
  text-transform: uppercase;
  color: var(--text-strong);
  white-space: nowrap;
}

.workspace-name {
  font-family: var(--font-sans);
  font-weight: var(--fw-bold);
  font-size: 14px;
  color: var(--text-strong);
  white-space: nowrap;
}

.header-divider {
  width: 1px;
  height: 26px;
  background: var(--border-hairline);
}

.logout {
  display: inline-flex;
  align-items: center;
  min-height: var(--tap-min);
  box-sizing: border-box;
  gap: 7px;
  appearance: none;
  background: transparent;
  border: 1px solid transparent;
  border-radius: var(--radius-md);
  padding: 7px 12px;
  font-family: var(--font-sans);
  font-weight: var(--fw-semibold);
  font-size: 13px;
  color: var(--text-muted);
  cursor: pointer;
  transition:
    background 0.16s,
    color 0.16s;
}
@media (hover: hover) {
  .logout:hover {
    background: var(--surface-sunken);
    color: var(--text-strong);
  }
}
.logout:active {
  background: var(--surface-pressed);
  color: var(--text-strong);
}

.shell-body {
  flex: 1;
  display: flex;
  min-height: 0;
}

/* =========================== sidebar =========================== */

.sidebar {
  flex: none;
  width: 240px;
  background: var(--surface-card);
  border-right: 1px solid var(--border-hairline);
  overflow: hidden;
  transition: width 0.22s var(--ease-out);
}
/* State lives on the root element so it can be restored before first paint
   (static/js/shell.js) — an Alpine-owned sidebar would flash open on every page.

   Collapsed is an icon rail, not zero width: on a tablet the nav has to stay tappable
   with the labels gone, and a sidebar that vanishes entirely makes every tab change a
   two-tap trip through the hamburger. The reference collapses to 0 — this deliberately
   departs from it. 56px = a 44px target plus the rail's single 12px left inset; there is
   no right inset, so the active tint runs flush to the sidebar's edge. */
html[data-sidebar="closed"] .sidebar {
  width: 56px;
}

.sidebar-nav {
  width: 240px;
  overflow-y: auto;
  /* Full height so the version line can be pushed to the bottom with margin-top:auto. */
  height: 100%;
  box-sizing: border-box;
  padding: 20px 14px;
  display: flex;
  flex-direction: column;
  gap: 4px;
}

/* Row = container; the anchor inside it carries the padding so the whole row is
   clickable while the active row can also hold a gear button beside the link. */
.nav-item {
  position: relative;
  display: flex;
  align-items: center;
  width: 100%;
  box-sizing: border-box;
  border-radius: var(--radius-md);
  transition: background 0.16s;
}
@media (hover: hover) {
  .nav-item:hover {
    background: var(--surface-sunken);
  }
}
/* :active applies to the activated element *and its ancestors*, so pressing the link
   lights the whole row — which is the thing that looks pressed. */
.nav-item:active {
  background: var(--surface-pressed);
}
/* A deferred tab is inert: it must not answer a tap as though it did something. */
.nav-item.is-soon:active {
  background: transparent;
}

.nav-link {
  flex: 1;
  min-width: 0;
  display: inline-flex;
  align-items: center;
  gap: 12px;
  padding: 11px 16px;
  font-family: var(--font-sans);
  font-weight: var(--fw-semibold);
  font-size: 14px;
  color: var(--text-muted);
  text-decoration: none;
  white-space: nowrap;
  transition: color 0.16s;
}
.nav-link svg {
  flex: none;
}

.nav-item.is-active {
  background: var(--accent-tint);
}
@media (hover: hover) {
  .nav-item.is-active:hover {
    background: var(--accent-tint);
  }
}
.nav-item.is-active .nav-link {
  color: var(--text-strong);
}
.nav-item.is-active::before {
  content: "";
  position: absolute;
  left: 0;
  top: 8px;
  bottom: 8px;
  width: 3px;
  border-radius: 2px;
  background: var(--accent);
}

/* Deferred tabs (Time, Automations, Templates): visible, inert, no hover affordance. */
@media (hover: hover) {
  .nav-item.is-soon:hover {
    background: transparent;
  }
}
.nav-item.is-soon .nav-link {
  color: var(--text-subtle);
  cursor: default;
}

.nav-pill {
  flex: none;
  margin-right: 12px;
  font-family: var(--font-mono);
  font-size: 9px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--text-subtle);
  border: 1px solid var(--border-hairline);
  border-radius: var(--radius-pill);
  padding: 2px 7px;
}

.nav-gear {
  flex: none;
  margin-right: 4px;
}

/* Pinned to the bottom of the nav column. The auto margin lives here rather than on
   the version line so the toggle travels with it. */
.sidebar-foot {
  margin-top: auto;
}

/* A nav row that happens to be a button, so the rail collapses it like the tabs. */
/* The icon and label are each wrapped so CSS can swap the pair, and a bare inline span
   would drop its svg onto the text baseline. */
.theme-toggle > span {
  display: inline-flex;
  align-items: center;
}

.theme-toggle {
  width: 100%;
  box-sizing: border-box;
  appearance: none;
  background: transparent;
  border: 0;
  text-align: left;
  cursor: pointer;
}

/* Build identifier, pinned below the nav. Quiet by design — it is diagnostic, not
   navigation, so it uses the same mono micro-label treatment as the SOON pill. */
.sidebar-version {
  padding: 16px 16px 4px;
  font-family: var(--font-mono);
  font-size: 10px;
  letter-spacing: 0.08em;
  color: var(--text-subtle);
  white-space: nowrap;
}

/* ---- collapsed: the icon rail ---- */

html[data-sidebar="closed"] .sidebar-nav {
  width: 56px;
  padding: 20px 0 20px 12px;
}

/* Flush right, so the active tint meets the sidebar's border rather than floating a
   rounded corner short of it. Only the left corners stay round. */
html[data-sidebar="closed"] .nav-item {
  border-radius: var(--radius-md) 0 0 var(--radius-md);
}

/* Clipped rather than display:none, so the link keeps its accessible name — an
   icon-only rail must still announce "Projects", not an empty link. Same technique as
   .sr-only; it is repeated rather than shared because the label is only hidden in this
   one state and a class swap would need JS. */
html[data-sidebar="closed"] .nav-label {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
  border: 0;
}

html[data-sidebar="closed"] .nav-link {
  justify-content: center;
  gap: 0;
  padding: 0;
  min-height: 44px;
}

/* The gear and the build string are deliberately open-sidebar only: Manage projects is
   rare enough not to earn rail space, and the version is diagnostic, not navigation. */
html[data-sidebar="closed"] .nav-pill,
html[data-sidebar="closed"] .nav-gear,
html[data-sidebar="closed"] .sidebar-version {
  display: none;
}

/* The SOON pill is what separates a deferred tab from a live one, and the rail hides
   it — leaving Clients and Templates as two identical grey icons. Carry the distinction
   on the icon instead, or the rail invites taps on tabs that do nothing. */
html[data-sidebar="closed"] .nav-item.is-soon .nav-link {
  opacity: 0.4;
}

/* ============================= page ============================ */

.main {
  flex: 1;
  min-width: 0;
  /* min-height so a too-tall page scrolls here rather than stretching the shell. */
  min-height: 0;
  overflow-y: auto;
  display: flex;
  flex-direction: column;
}

.page {
  flex: 1;
  padding: 22px 40px 40px;
  display: flex;
  flex-direction: column;
}

.page-title {
  padding: 2px 0 20px;
  margin: 0;
  font-family: var(--font-sans);
  font-weight: var(--fw-black);
  font-size: 20px;
  letter-spacing: -0.015em;
  color: var(--text-strong);
}

/* Heading with a back arrow to its left (Manage projects). */
.page-title-back {
  display: flex;
  align-items: center;
  gap: 10px;
}

.page-placeholder {
  font-size: 13px;
  color: var(--text-subtle);
}

/* ============================ tables =========================== */

.table-card {
  max-width: 1120px;
  background: var(--surface-card);
  border: 1px solid var(--border-hairline);
  border-radius: var(--radius-lg);
  overflow-x: auto;
}

/* Below this the columns stop being readable, so the card scrolls rather than the
   page — the sidebar and header must never move sideways. */
.table-scroll {
  min-width: 700px;
}

/* Actions column is 100px, not the design's 76px: two 44px targets plus their gap need
   92px, and at 76px the second would sit on top of the first and steal its taps. */
.row {
  display: grid;
  grid-template-columns: 1.5fr 1.2fr 1.6fr 1fr 100px;
  gap: 14px;
  align-items: center;
  padding: 14px 20px;
  border-bottom: 1px solid var(--border-hairline);
}
.row:last-child {
  border-bottom: 0;
}

.row-head {
  background: var(--surface-sunken);
}

.body-row {
  transition: background 0.13s;
}
@media (hover: hover) {
  .body-row:hover {
    background: var(--surface-sunken);
  }
}

.th {
  font-family: var(--font-mono);
  font-size: 9.5px;
  letter-spacing: 0.13em;
  text-transform: uppercase;
  color: var(--text-subtle);
}
.th-right {
  text-align: right;
}

/* Every cell truncates: one long email must not widen the column for every row. */
.cell-strong,
.cell,
.cell-mono {
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.cell-strong {
  font-family: var(--font-sans);
  font-weight: var(--fw-semibold);
  font-size: 14px;
  color: var(--text-strong);
}
.cell {
  font-family: var(--font-sans);
  font-size: 13px;
  color: var(--text-muted);
}
.cell-mono {
  font-family: var(--font-mono);
  font-size: 12px;
  color: var(--text-muted);
}

.row-actions {
  display: flex;
  justify-content: flex-end;
  gap: 4px;
}

.rowbtn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: var(--tap-min);
  height: var(--tap-min);
  appearance: none;
  background: transparent;
  border: 0;
  border-radius: var(--radius-sm);
  color: var(--text-subtle);
  cursor: pointer;
  transition:
    background 0.14s,
    color 0.14s;
}
@media (hover: hover) {
  .rowbtn:hover {
    background: var(--accent-tint);
    color: var(--accent-press);
  }
}
.rowbtn:active {
  background: var(--accent-tint-pressed);
  color: var(--accent-press);
}
@media (hover: hover) {
  .rowbtn.del:hover {
    background: var(--cwl-danger-tint);
    color: var(--cwl-danger);
  }
}
.rowbtn.del:active {
  background: var(--danger-tint-pressed);
  color: var(--cwl-danger);
}

.table-empty {
  padding: 40px 20px;
  text-align: center;
  font-family: var(--font-sans);
  font-size: 13px;
  color: var(--text-subtle);
}

/* ============================ board ============================ */

/* The board scrolls sideways rather than squeezing columns: a 308px column is the
   narrowest a task card reads well at.

   The scroller is a child rather than .page itself, so the edge fades can be pinned to
   the page while the columns travel under them — a pseudo-element on the scroller would
   scroll away with its content. .page-board therefore clips (to cut the fades off at
   the page edge) and .board-scroll owns both axes of scrolling. */
.page-board {
  position: relative;
  padding: 0;
  min-height: 0;
  overflow: hidden;
}

.board-scroll {
  flex: 1;
  min-width: 0;
  min-height: 0;
  padding: 22px 40px 40px;
  overflow: auto;
  /* proximity, not mandatory: mandatory fights the small deliberate nudge that brings
     the edge of the next column into view. */
  scroll-snap-type: x proximity;
  scroll-padding-left: 40px;
  /* A swipe past either end stays in the board instead of becoming a back gesture. */
  overscroll-behavior-x: contain;
}

/* There is no scrollbar under a fingertip, so the fade is the only thing that says the
   board continues. Each side is shown only when there is something that way to reach —
   a permanent fade would read as decoration and say nothing. */
.page-board::before,
.page-board::after {
  content: "";
  position: absolute;
  top: 0;
  bottom: 0;
  /* Wider than it looks like it needs to be: white cards on a near-white page is a
     small colour delta, so the dissolve of the hairlines is what actually reads. */
  width: 56px;
  z-index: 1;
  pointer-events: none;
  opacity: 0;
  transition: opacity 0.18s var(--ease-out);
}
.page-board::before {
  left: 0;
  background: linear-gradient(to right, var(--surface-sunken), transparent);
}
.page-board::after {
  right: 0;
  background: linear-gradient(to left, var(--surface-sunken), transparent);
}
.page-board.has-start-fade::before,
.page-board.has-end-fade::after {
  opacity: 1;
}

.board-columns {
  display: flex;
  gap: 18px;
  align-items: flex-start;
  flex: 1;
}

.board-column {
  flex: none;
  width: 308px;
  scroll-snap-align: start;
  background: var(--surface-card);
  border: 1px solid var(--border-hairline);
  border-radius: var(--radius-lg);
  display: flex;
  flex-direction: column;
  max-height: 100%;
}

.board-column-head {
  padding: 16px 16px 14px;
  border-bottom: 1px solid var(--border-hairline);
}
.board-project {
  font-family: var(--font-sans);
  font-weight: var(--fw-black);
  font-size: 15px;
  letter-spacing: -0.01em;
  color: var(--text-strong);
}
.board-client {
  font-family: var(--font-mono);
  font-size: 11px;
  color: var(--text-subtle);
  margin-top: 5px;
}

.board-sections {
  padding: 14px;
  display: flex;
  flex-direction: column;
  gap: 16px;
  overflow-y: auto;
}

.board-label {
  font-family: var(--font-mono);
  font-size: 9.5px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--text-subtle);
  margin-bottom: 9px;
}
.board-label-active {
  color: var(--accent-press);
}
/* Only Done carries a control, so the flex row is a modifier rather than the base:
   the other two labels keep their plain block layout and their margin. */
.board-label-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  /* The ⋯ has a full tap target and is taller than the label text, so the label's own
     bottom margin would stack on top of it and push the stack down. */
  margin-bottom: 0;
}

.board-more {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  /* Square and tappable — the icon is 15px but a thumb is not (v0.3.0's 44px rule). */
  width: var(--tap-min);
  height: var(--tap-min);
  /* Pulled to the column edge so the button's padding does not read as a stray indent;
     the tap target keeps its full size either way. */
  margin: -6px -10px -6px 0;
  appearance: none;
  background: none;
  border: 0;
  color: var(--text-subtle);
  cursor: pointer;
}

/* The highlight is drawn on a pseudo-element sized to the glyph, not on the button.
   The 44px box exists so a thumb can hit it; painting the background on that box put a
   44px slab next to a 9.5px label. A tap target's job is to be easy to hit, not to look
   like the control is bigger than it is. */
.board-more::before {
  content: "";
  position: absolute;
  width: 26px;
  height: 26px;
  border-radius: var(--radius-md);
  transition: background 0.16s;
}
/* Both the pseudo-element and the icon are positioned, so they paint in tree order and
   the icon lands on top of the highlight rather than under it. */
.board-more > svg {
  position: relative;
}
@media (hover: hover) {
  .board-more:hover {
    color: var(--text-strong);
  }
  .board-more:hover::before {
    background: var(--surface-sunken);
  }
}
.board-more:active {
  color: var(--text-strong);
}
.board-more:active::before {
  background: var(--surface-pressed);
}

.board-stack {
  display: flex;
  flex-direction: column;
  gap: 9px;
}
/* Reserves the in-progress zone so columns line up whether or not work is underway. */
.board-stack-active {
  min-height: 198px;
}
.board-stack-done {
  gap: 7px;
}

.board-empty {
  flex: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  border: 1px dashed var(--border-hairline);
  border-radius: 9px;
  font-family: var(--font-sans);
  font-size: 12.5px;
  color: var(--text-subtle);
}
.board-done-empty {
  padding: 7px 10px;
  font-family: var(--font-sans);
  font-size: 12.5px;
  color: var(--text-subtle);
}

/* First-run panel: dashed like the board's own empty zones, so an empty hub reads as
   unstarted rather than broken. */
.board-firstrun {
  max-width: 460px;
  padding: 32px 28px;
  border: 1px dashed var(--border-default);
  border-radius: var(--radius-lg);
  background: var(--surface-card);
}
.board-firstrun-title {
  margin: 0 0 8px;
  font-family: var(--font-sans);
  font-weight: var(--fw-bold);
  font-size: 15px;
  color: var(--text-strong);
}
.board-firstrun-body {
  margin: 0 0 18px;
  font-size: 13px;
  line-height: 1.55;
  color: var(--text-muted);
}

/* ---- task cards ---- */

.task-card {
  border: 1px solid var(--border-hairline);
  border-radius: 9px;
  padding: 11px 12px;
  background: var(--surface-page);
  display: flex;
  align-items: flex-start;
  gap: 9px;
}
/* No hover *background* here — a card never had one — but it is the most-tapped thing
   in the app, so the press has to answer. user-select stops a long press raising iOS's
   text-selection callout on something that is a button, not prose.

   The cursor is not inside `@media (hover: hover)` and does not need to be: a device
   with no pointer has no cursor to change. It is also the only thing a card has to
   offer a mouse — it is a div with role="button", so unlike a real <button> the browser
   gives it nothing by default, and on a desktop the card looked inert until clicked. */
.task-card {
  user-select: none;
  -webkit-touch-callout: none;
  cursor: pointer;
  transition: background 0.13s;
}
.task-card:active {
  background: var(--surface-pressed);
}
/* ...but not when the press is on the advance button inside it. :active applies to the
   activated element *and its ancestors*, so advancing a task lit the whole card — which
   reads as "this is opening the task", which is the other thing the card does. */
.task-card:has(.advance-ico:active) {
  background: var(--surface-page);
}

/* The aqua edge is what "in progress" looks like at a glance across the board. */
.task-card.is-active {
  border-left: 3px solid var(--accent);
}

.task-body {
  flex: 1;
  min-width: 0;
}
.task-title {
  font-family: var(--font-sans);
  font-weight: var(--fw-semibold);
  font-size: 13.5px;
  line-height: 1.35;
  color: var(--text-strong);
}
/* One row for the due date and the notes glyph, so the gap under the title belongs to
   the row rather than to whichever marker happens to be present. */
.task-meta {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-top: 6px;
}
.task-due {
  font-family: var(--font-mono);
  font-size: 10.5px;
  color: var(--text-subtle);
  display: inline-flex;
  align-items: center;
  gap: 4px;
}
/* Late and nearly-late, in the semantic colours. Both are restated for dark at the top
   of this file, so these two declarations cover both themes. */
.task-due-overdue {
  color: var(--cwl-danger);
  font-weight: var(--fw-semibold);
}
.task-due-soon {
  color: var(--cwl-warning);
}
.task-note-flag {
  display: inline-flex;
  align-items: center;
  color: var(--text-subtle);
}

/* 44px box around a 15px glyph, with the extra 11px a side taken back as negative
   margin: the target is a fingertip while the card's layout is the design's. The box
   does overlap the card, which opens the task — that is the intent, since the region it
   covers is where the glyph visibly is. */
.advance-ico {
  position: relative;
  flex: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: var(--tap-min);
  height: var(--tap-min);
  appearance: none;
  background: transparent;
  border: 0;
  color: var(--text-subtle);
  cursor: pointer;
  padding: 0;
  margin: -13px -11px -11px -11px;
  transition: color 0.14s;
}

/* The highlight is painted on this inset box, not on the button: the button is 44px so
   a fingertip can hit it, but a 44px highlight is wider than the card's padding and
   bleeds over the card outline. Inset 8px keeps the lit area clear of the border. */
.advance-ico::before {
  content: "";
  position: absolute;
  inset: 8px;
  border-radius: var(--radius-sm);
  transition: background 0.14s;
}
/* Both the pseudo-element and the glyph are children; without this the absolutely
   positioned ::before would paint over the icon. */
.advance-ico svg {
  position: relative;
}

@media (hover: hover) {
  .advance-ico:hover {
    color: var(--accent-press);
  }
}
@media (hover: hover) {
  .advance-ico:hover::before {
    background: var(--accent-tint);
  }
}
.advance-ico:active {
  color: var(--accent-press);
}
.advance-ico:active::before {
  background: var(--accent-tint-pressed);
}
.advance-ico-done {
  margin: -11px;
  color: var(--cwl-success);
}
@media (hover: hover) {
  .advance-ico-done:hover {
    color: var(--cwl-success);
  }
}
@media (hover: hover) {
  .advance-ico-done:hover::before {
    background: var(--cwl-success-tint);
  }
}
.advance-ico-done:active::before {
  background: var(--success-tint-pressed);
}

.task-done {
  display: flex;
  align-items: center;
  min-height: var(--tap-min);
  box-sizing: border-box;
  gap: 9px;
  padding: 7px 10px;
  border-radius: 8px;
  background: var(--surface-sunken);
  user-select: none;
  -webkit-touch-callout: none;
  /* Same as .task-card: a done row opens the task too, and being struck through and
     greyed is exactly what makes it read as inert rather than clickable. */
  cursor: pointer;
  transition: background 0.13s;
}
.task-done:active {
  background: var(--surface-pressed);
}
.task-done:has(.advance-ico:active) {
  background: var(--surface-sunken);
}
.task-done-title {
  flex: 1;
  font-family: var(--font-sans);
  font-size: 12.5px;
  color: var(--text-muted);
  text-decoration: line-through;
}

/* ---- add task ---- */

.addtask {
  display: flex;
  align-items: center;
  min-height: var(--tap-min);
  gap: 8px;
  width: 100%;
  box-sizing: border-box;
  appearance: none;
  background: transparent;
  border: 1px dashed var(--border-default);
  border-radius: var(--radius-md);
  padding: 10px 12px;
  font-family: var(--font-sans);
  font-weight: var(--fw-semibold);
  font-size: 13px;
  color: var(--text-muted);
  cursor: pointer;
  transition:
    background 0.14s,
    border-color 0.14s,
    color 0.14s;
}
@media (hover: hover) {
  .addtask:hover {
    background: var(--surface-card);
    border-color: var(--accent);
    color: var(--accent-press);
  }
}
.addtask:active {
  background: var(--surface-pressed);
  border-color: var(--accent);
  color: var(--accent-press);
}

.addtask-form {
  display: flex;
  gap: 8px;
}
.addtask-title {
  flex: 1;
  min-width: 0;
  font-size: 13px;
  padding: 8px 10px;
}
.addtask-due {
  width: 62px;
  flex: none;
  font-family: var(--font-mono);
  font-size: 13px;
  padding: 8px;
}

/* ====================== grid table (manage) ==================== */

/* Denser than .table-card: this one is an editing surface, not a directory, and its
   rows swap between display and input controls without changing height much. */
.grid-table {
  border: 1px solid var(--border-hairline);
  border-radius: var(--radius-md);
  max-width: 1120px;
}

.grid-row {
  display: grid;
  grid-template-columns: 1.4fr 1fr 1fr 88px 100px 100px;
  gap: 10px;
  align-items: center;
  padding: 11px 12px;
  border-top: 1px solid var(--border-hairline);
  margin: 0;
}
.grid-row:first-child {
  border-top: 0;
}

.grid-head {
  padding: 9px 12px;
  background: var(--surface-sunken);
  border-radius: var(--radius-md) var(--radius-md) 0 0;
  font-family: var(--font-mono);
  font-size: 9.5px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--text-subtle);
}

.grid-edit,
.grid-new {
  padding: 8px 12px;
}
.grid-edit {
  background: var(--surface-sunken);
}

.grid-name,
.grid-cell,
.grid-rate,
.grid-mono {
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.grid-name {
  font-family: var(--font-sans);
  font-weight: var(--fw-bold);
  font-size: 13.5px;
  color: var(--text-strong);
}
.grid-cell {
  font-family: var(--font-sans);
  font-size: 13px;
  color: var(--text-muted);
}
.grid-rate {
  font-family: var(--font-mono);
  font-size: 12px;
  font-weight: var(--fw-semibold);
  color: var(--accent-press);
}
.grid-rate.is-nonbillable {
  font-weight: var(--fw-regular);
  color: var(--text-muted);
}
.grid-mono {
  font-family: var(--font-mono);
  font-size: 12px;
  color: var(--text-muted);
}

/* Archived rows stay legible but visibly out of play. */
.grid-row.is-archived .grid-name,
.grid-row.is-archived .grid-cell,
.grid-row.is-archived .grid-rate,
.grid-row.is-archived .grid-mono {
  color: var(--text-subtle);
  font-weight: var(--fw-regular);
}

.grid-actions {
  display: flex;
  gap: 4px;
  justify-content: flex-end;
}

.grid-help {
  margin: 8px 0 0;
  font-family: var(--font-mono);
  font-size: 10.5px;
  color: var(--text-subtle);
}

/* Inputs sized to sit in a table row rather than a form. */
.ti-cell {
  width: 100%;
  min-width: 0;
  padding: 7px 10px;
  font-size: 13px;
}

/* Native select, chevron drawn in CSS — see _manage.html for why it is not a popover. */
.sel {
  box-sizing: border-box;
  min-height: var(--tap-min);
  width: 100%;
  min-width: 0;
  font-family: var(--font-sans);
  font-size: 12.5px;
  color: var(--text-strong);
  background-color: var(--surface-page);
  border: 1px solid var(--border-default);
  border-radius: var(--radius-md);
  padding: 7px 28px 7px 10px;
  outline: none;
  cursor: pointer;
  appearance: none;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='%236b7072' stroke-width='2.4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 9px center;
}
.sel:focus {
  border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--accent-tint);
}

/* Same hit box as .icon-btn — the class survives because its glyph is smaller, which is
   the only thing that made it "sm". A denser table does not justify a denser target. */
.icon-btn-sm {
  width: var(--tap-min);
  height: var(--tap-min);
}
.icon-btn-accent {
  color: var(--accent-press);
}

/* =========================== banners =========================== */

.banner {
  display: flex;
  align-items: center;
  gap: 9px;
  max-width: 1120px;
  box-sizing: border-box;
  margin-bottom: 14px;
  padding: 11px 14px;
  border-radius: var(--radius-md);
  font-size: 13px;
  line-height: 1.45;
}
.banner-danger {
  background: var(--cwl-danger-tint);
  color: var(--cwl-danger);
}
.banner svg {
  flex: none;
}

/* =========================== controls ========================== */

.page-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  flex-wrap: wrap;
}
/* Auto margin rather than the row's space-between: with the page titles gone the
   actions are often the only child, and space-between would park them on the left. */
.page-actions {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-left: auto;
  padding-bottom: 20px;
}

.search {
  position: relative;
  width: 220px;
}
.search svg {
  position: absolute;
  left: 11px;
  top: 50%;
  transform: translateY(-50%);
  color: var(--text-subtle);
  pointer-events: none;
}
.search-input {
  width: 100%;
  padding-left: 33px;
  font-size: 13px;
}
/* Chrome's clear affordance sits on top of nothing useful and breaks the row. */
.search-input::-webkit-search-cancel-button {
  appearance: none;
}

/* Also used on anchors (the first-run panels), so it has to beat the global link rule
   — an underlined, aqua-on-aqua button is unreadable. */
.btn {
  display: inline-flex;
  align-items: center;
  min-height: var(--tap-min);
  box-sizing: border-box;
  justify-content: center;
  gap: 8px;
  appearance: none;
  text-decoration: none;
  border: 1px solid transparent;
  border-radius: var(--radius-md);
  padding: 11px 18px;
  font-family: var(--font-sans);
  font-weight: var(--fw-bold);
  font-size: 13.5px;
  cursor: pointer;
  transition:
    background 0.16s,
    border-color 0.16s,
    color 0.16s;
}
.btn-sm {
  padding: 8px 14px;
  font-size: 13px;
}
.btn.btn-primary,
a.btn.btn-primary {
  background: var(--accent);
  color: var(--text-onbrand);
}
@media (hover: hover) {
  .btn.btn-primary:hover,
  a.btn.btn-primary:hover {
    background: var(--accent-hover);
    color: var(--text-onbrand);
  }
}
.btn.btn-primary:active,
a.btn.btn-primary:active {
  background: var(--accent-press);
  color: var(--text-onbrand);
}
.btn-secondary {
  background: var(--surface-card);
  border-color: var(--border-default);
  color: var(--text-strong);
}
@media (hover: hover) {
  .btn-secondary:hover {
    background: var(--surface-sunken);
  }
}
.btn-secondary:active {
  background: var(--surface-pressed);
}
.btn-ghost {
  background: transparent;
  color: var(--text-muted);
  font-weight: var(--fw-semibold);
}
@media (hover: hover) {
  .btn-ghost:hover {
    background: var(--surface-sunken);
    color: var(--text-strong);
  }
}
.btn-ghost:active {
  background: var(--surface-pressed);
  color: var(--text-strong);
}

/* ========================= task modal ========================== */

.modal-task {
  max-width: 720px;
}

.task-title-slot {
  flex: 1;
  min-width: 0;
  margin-right: 12px;
}
.task-modal-title {
  margin: 0;
  font-family: var(--font-sans);
  font-weight: var(--fw-black);
  font-size: 18px;
  color: var(--text-strong);
  cursor: text;
  border-bottom: 1px dashed transparent;
}
@media (hover: hover) {
  .task-modal-title:hover {
    border-bottom-color: var(--border-default);
  }
}
.task-title-input {
  width: 100%;
  font-weight: var(--fw-black);
  font-size: 17px;
  padding: 6px 10px;
}

.modal-banner {
  margin: 14px 24px 0;
}

.task-modal-body {
  display: flex;
  align-items: stretch;
}

.task-notes {
  flex: 1;
  min-width: 0;
  padding: 22px 24px;
  display: flex;
  flex-direction: column;
  gap: 10px;
}
.task-notes-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
}
.task-notes-input {
  width: 100%;
  box-sizing: border-box;
  /* Grows with its content (notes.js), so the height is set by script rather than by
     the user — hence no resize handle. Capped so a long note scrolls inside the field
     instead of pushing the footer off a tablet screen. */
  min-height: 220px;
  max-height: 52vh;
  resize: none;
  overflow-y: auto;
  padding: 12px 14px;
  line-height: 1.6;
  /* The app's own face. Notes are prose, not source — mono here was for the Markdown
     this box no longer holds, and it read as something to be parsed. */
  font-family: var(--font-sans);
  font-size: 13.5px;
}

/* Sunken rail for the metadata, so the notes stay the subject of the dialog. */
.task-rail {
  width: 210px;
  flex: none;
  border-left: 1px solid var(--border-hairline);
  background: var(--surface-page);
  padding: 22px 20px;
  display: flex;
  flex-direction: column;
  gap: 18px;
}
.task-rail-control {
  margin-top: 6px;
  font-size: 13px;
  padding: 9px 28px 9px 11px;
}
.task-due-input {
  /* Sized for "12/31/26" plus the padding — the year made the old 90px clip. */
  width: 104px;
  margin-top: 6px;
  display: block;
  font-family: var(--font-mono);
}

.field-label {
  font-family: var(--font-mono);
  font-size: 10px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--text-subtle);
}

.modal-foot {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  padding: 16px 24px;
  border-top: 1px solid var(--border-hairline);
}
.modal-foot-actions {
  display: flex;
  gap: 10px;
}

.link-danger {
  display: inline-flex;
  align-items: center;
  min-height: var(--tap-min);
  appearance: none;
  background: none;
  border: 0;
  padding: 0;
  font-family: var(--font-sans);
  font-weight: var(--fw-semibold);
  font-size: 13px;
  color: var(--cwl-danger);
  cursor: pointer;
}
@media (hover: hover) {
  .link-danger:hover {
    text-decoration: underline;
  }
}

/* ===================== completed-tasks modal ==================== */

.done-modal-heading {
  min-width: 0;
}
.done-modal-project {
  margin-top: 2px;
  font-family: var(--font-sans);
  font-size: 12.5px;
  color: var(--text-subtle);
}

/* Scrolls inside the modal rather than growing it: this is a full history, so on an old
   project it is the one list in the app with no upper bound. */
.done-list {
  max-height: 52vh;
  overflow-y: auto;
  padding: 8px 0;
}

.done-list-row {
  display: flex;
  align-items: center;
  gap: 10px;
  min-height: var(--tap-min);
  padding: 6px 24px;
  cursor: pointer;
}
@media (hover: hover) {
  .done-list-row:hover {
    background: var(--surface-sunken);
  }
}
.done-list-row:active {
  background: var(--surface-pressed);
}

.done-list-check {
  display: inline-flex;
  flex: none;
  color: var(--text-subtle);
}
.done-list-title {
  flex: 1;
  min-width: 0;
  font-family: var(--font-sans);
  font-size: 13.5px;
  color: var(--text-muted);
  /* One line per task: the list is for finding something, and a wrapped title makes the
     dates stop lining up, which is what the eye actually scans. */
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
}
.done-list-when {
  flex: none;
  font-family: var(--font-mono);
  font-size: 11px;
  color: var(--text-subtle);
}

.done-list-empty {
  margin: 0;
  padding: 10px 24px 14px;
  font-family: var(--font-sans);
  font-size: 12.5px;
  color: var(--text-subtle);
}

/* ---- segmented control ---- */

/* Currently unused: the task modal's Write/Preview tabs were the only caller, and the
   notes editor no longer has modes. Kept because this is a design-system primitive
   ported from the prototype rather than something the modal invented, and the Time
   release's filters are the next thing shaped like it. */
.seg {
  display: inline-flex;
  background: var(--surface-sunken);
  border: 1px solid var(--border-hairline);
  border-radius: var(--radius-md);
  padding: 3px;
}
.seg-btn {
  min-height: var(--tap-min);
  appearance: none;
  border: 0;
  background: transparent;
  font-family: var(--font-sans);
  font-weight: var(--fw-semibold);
  font-size: 13px;
  color: var(--text-muted);
  padding: 7px 16px;
  border-radius: 7px;
  cursor: pointer;
  transition:
    background 0.16s,
    color 0.16s;
}
.seg-btn:active {
  background: var(--surface-pressed);
  color: var(--text-strong);
}
.seg-btn.is-on {
  background: var(--surface-card);
  color: var(--text-strong);
}

/* ============================ modals =========================== */

.modal-overlay {
  position: fixed;
  inset: 0;
  background: rgba(30, 32, 33, 0.42);
  display: flex;
  align-items: flex-start;
  justify-content: center;
  padding: 56px 20px;
  z-index: 50;
  overflow: auto;
}

.modal {
  width: 100%;
  max-width: 720px;
  background: var(--surface-card);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-lg);
  overflow: hidden;
}
.modal-sm {
  max-width: 480px;
}

.modal-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 20px 24px;
  border-bottom: 1px solid var(--border-hairline);
}
.modal-title {
  margin: 0;
  font-family: var(--font-sans);
  font-weight: var(--fw-black);
  font-size: 18px;
  color: var(--text-strong);
}

.modal-body {
  padding: 22px 24px;
  display: flex;
  flex-direction: column;
  gap: 14px;
}

.modal-actions {
  display: flex;
  justify-content: flex-end;
  gap: 10px;
  margin-top: 4px;
}

/* Mono uppercase micro-label above its input — the form equivalent of .th. */
.field {
  display: block;
  font-family: var(--font-mono);
  font-size: 10px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--text-subtle);
}
.field .ti {
  width: 100%;
  margin-top: 6px;
  /* The label is mono-uppercase; the value the operator types is not. */
  font-family: var(--font-sans);
  font-size: 14px;
  letter-spacing: normal;
  text-transform: none;
}
.field-row {
  display: flex;
  gap: 12px;
}
.field-grow {
  flex: 1.4;
}
.field-row .field {
  flex: 1;
  min-width: 0;
}

/* ============================ login ============================ */

.login-page {
  position: relative;
  min-height: 100vh;
  background: var(--surface-page);
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
  padding: 48px 24px;
  box-sizing: border-box;
}

/* Two outsized aqua outline circles bleeding off opposite corners. Decorative. */
.login-orb {
  position: absolute;
  border-radius: 50%;
}
.login-orb-tr {
  top: -190px;
  right: -150px;
  width: 520px;
  height: 520px;
  border: 1.5px solid var(--accent);
  opacity: 0.16;
}
.login-orb-bl {
  bottom: -260px;
  left: -160px;
  width: 460px;
  height: 460px;
  border: 1px solid var(--accent);
  opacity: 0.08;
}

.login-card {
  position: relative;
  width: 100%;
  max-width: 400px;
  box-sizing: border-box;
}

.login-logo {
  margin-bottom: 30px;
  display: flex;
  justify-content: center;
}
.login-logo img {
  /* The lockup is ~5:1, so height is what blows the 400px card: 72px tall would be
     ~360px wide. Sized by width instead, with height following, so the mark can never
     push the column wider than the access-key field beneath it. */
  width: 100%;
  max-width: 320px;
  height: auto;
}
.login-logo .brand-wordmark {
  font-size: 22px;
  letter-spacing: 0.12em;
  color: var(--text-strong);
}

.login-label {
  display: block;
  font-family: var(--font-mono);
  font-size: 10.5px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--text-subtle);
  margin-bottom: 8px;
}

.login-field {
  position: relative;
  margin-bottom: 16px;
}

.login-input {
  width: 100%;
  box-sizing: border-box;
  font-family: var(--font-mono);
  font-size: 15px;
  letter-spacing: 0.02em;
  color: var(--text-strong);
  background: var(--surface-page);
  border: 1px solid var(--border-default);
  border-radius: var(--radius-md);
  padding: 14px 46px 14px 15px;
  outline: none;
  transition:
    border-color 0.16s var(--ease-out),
    box-shadow 0.16s var(--ease-out);
}
.login-input::placeholder {
  color: var(--text-subtle);
  letter-spacing: 0.04em;
}
.login-input:focus {
  border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--accent-tint);
}

.login-eye {
  position: absolute;
  right: 3px;
  top: 50%;
  transform: translateY(-50%);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: var(--tap-min);
  height: var(--tap-min);
  border: 0;
  background: transparent;
  color: var(--text-muted);
  cursor: pointer;
  border-radius: var(--radius-sm);
  transition:
    color 0.16s,
    background 0.16s;
}
@media (hover: hover) {
  .login-eye:hover {
    color: var(--text-strong);
    background: var(--surface-sunken);
  }
}
.login-eye:active {
  color: var(--text-strong);
  background: var(--surface-pressed);
}

.login-error {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-bottom: 16px;
  /* --cwl-danger is the lifted salmon inside .cwl-dark, not the light-surface red. */
  color: var(--cwl-danger);
  font-size: 12.5px;
  line-height: 1.4;
}

.login-submit {
  display: flex;
  justify-content: center;
}

.login-footer {
  margin-top: 28px;
  font-family: var(--font-mono);
  font-size: 10.5px;
  letter-spacing: 0.06em;
  color: var(--text-subtle);
  text-align: center;
}
