/* SignSwifly app design system — shared by dashboard and editor.
   Mirrors the marketing palette so product and site feel like one thing. */
:root {
  --bg: #F6F8F9;
  --panel: #FFFFFF;
  --ink: #182430;
  --muted: #5A6B7C;
  --hairline: #DDE4EA;
  /* The edge of a text box, list or menu: 3:1 against white and the page
     grey (WCAG 1.4.11), where the hairline is only a divider's 1.3:1. */
  --field-border: #7F8E9D;
  --accent: #0E7D6E;
  --accent-dark: #0A5F54;
  --accent-soft: #E2F0ED;
  /* Dark enough for 4.5:1 on its own soft tint (the Sent pill). */
  --amber: #AD5008;
  --amber-soft: #FDF0E3;
  --red: #B42318;
  --red-soft: #FDEBE9;
  --blue: #1D4ED8;
  --blue-soft: #E8EEFD;
  --shadow: 0 1px 3px rgba(24, 36, 48, .07);
  --radius: 12px;
}
* { box-sizing: border-box; }
html, body { margin: 0; }
body {
  background: var(--bg); color: var(--ink);
  font-family: "IBM Plex Sans", Arial, sans-serif; font-size: 15px;
}
h1, h2, h3 { font-family: "Bricolage Grotesque", "IBM Plex Sans", sans-serif; }
a { color: var(--accent); }

/* ---- Top bar ---- */
.topbar {
  position: sticky; top: 0; z-index: 40;
  background: var(--panel); border-bottom: 1px solid var(--hairline);
  display: flex; align-items: center; gap: 24px; padding: 0 22px; height: 56px;
}
.topbar .brand {
  font-family: "Bricolage Grotesque", sans-serif; font-weight: 700; font-size: 1.25rem;
  color: var(--ink); text-decoration: none; white-space: nowrap;
}
.topbar .brand span { color: var(--accent); }
.topbar nav { display: flex; gap: 4px; flex: 1; }
.topbar nav a {
  color: var(--muted); text-decoration: none; font-weight: 500; font-size: .93rem;
  padding: 7px 12px; border-radius: 8px; white-space: nowrap;
}
.topbar nav a:hover { background: var(--bg); color: var(--ink); }
.topbar nav a[aria-current="page"] { background: var(--accent-soft); color: var(--accent-dark); }
/* A destination the plan doesn't include: still there, still opens (to the
   page's own note saying why). The padlock in its name says so; faded, its
   words fell to 2.5:1 against the bar, too faint to read. */
.userchip { display: flex; align-items: center; gap: 10px; font-size: .88rem; color: var(--muted); white-space: nowrap; }
.userchip .avatar {
  width: 32px; height: 32px; border-radius: 50%; background: var(--accent); color: #fff;
  display: inline-flex; align-items: center; justify-content: center; font-weight: 600; font-size: .8rem;
}
.userchip .plan, .app-menu .plan {
  font-size: .68rem; text-transform: uppercase; letter-spacing: .07em; font-weight: 600;
  background: var(--accent-soft); color: var(--accent-dark); border-radius: 999px; padding: 3px 9px;
}

/* Phone menu (built by app-shell.js from the bar's own <nav>). Hidden until
   the bar is too narrow for the links; see the 960px block below. */
.menu-btn {
  display: none; align-items: center; gap: 9px; margin-left: auto;
  min-height: 44px; padding: 0 14px; border-radius: 10px; cursor: pointer;
  font: inherit; font-weight: 600; font-size: .93rem; color: var(--ink);
  background: transparent; border: 1px solid var(--hairline);
}
.menu-btn[aria-expanded="true"] { background: var(--accent-soft); border-color: var(--accent); color: var(--accent-dark); }
.menu-btn:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
/* Three bars drawn with borders, so the icon needs no font or image. */
.menu-btn .bars { width: 16px; height: 12px; border-top: 2px solid; border-bottom: 2px solid; position: relative; }
.menu-btn .bars::after { content: ""; position: absolute; left: 0; right: 0; top: 3px; border-top: 2px solid; }
/* Above every page's own sticky and fixed pieces (the editors' toolbars,
   props sheets and toasts reach 60); below only the full-page gate (1000).
   app-shell.js sets top to wherever the bar ends. */
.app-menu {
  position: fixed; top: 56px; left: 0; right: 0; bottom: 0; z-index: 900;
  background: rgba(24, 36, 48, .45); overscroll-behavior: contain;
}
.app-menu .sheet {
  background: var(--panel); border-bottom: 1px solid var(--hairline); box-shadow: 0 12px 28px rgba(24, 36, 48, .18);
  max-height: 100%; overflow-y: auto; padding: 8px 12px 14px;
  display: flex; flex-direction: column;
  animation: menu-in .16s ease-out;
}
.app-menu .sheet > a {
  display: flex; align-items: center; min-height: 48px; padding: 0 12px; border-radius: 10px;
  color: var(--ink); text-decoration: none; font-weight: 500; font-size: 1rem;
}
.app-menu .sheet > a:hover { background: var(--bg); }
.app-menu .sheet > a[aria-current="page"] { background: var(--accent-soft); color: var(--accent-dark); font-weight: 600; }
.app-menu .sheet > a:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
.app-menu .account {
  display: flex; align-items: center; flex-wrap: wrap; gap: 10px 12px;
  margin-top: 8px; padding: 14px 12px 4px; border-top: 1px solid var(--hairline);
}
.app-menu .account:empty { display: none; }
.app-menu .account .who { flex: 1 1 160px; min-width: 0; overflow-wrap: anywhere; font-size: .92rem; }
.app-menu .account .who span { display: block; color: var(--muted); font-size: .85rem; }
.app-menu .account .btn { flex: 1 1 100%; justify-content: center; }
@keyframes menu-in { from { transform: translateY(-8px); opacity: 0; } }
html.menu-open { overflow: hidden; }

/* Stands in for the whole page (app-shell.js): a device just handed back from
   an in-person signer, or a first load that could not reach the server. */
.shell-gate { display: flex; align-items: flex-start; justify-content: center; padding: 12vh 16px 24px; }
.shell-gate .card { max-width: 440px; width: 100%; padding: 28px 24px; text-align: center; }
.shell-gate h1 { margin: 0 0 10px; font-size: 1.4rem; }
.shell-gate p { color: var(--muted); margin: 0 0 20px; line-height: 1.5; }
.shell-gate .btn { justify-content: center; min-width: 200px; min-height: 44px; }

/* Skip to main content (app-shell.js): off screen until a keyboard reaches
   it, then over the top-left of the bar. */
.skip-link {
  position: fixed; left: 12px; top: -80px; z-index: 1001;
  padding: 10px 16px; border-radius: 8px; border: 2px solid var(--accent);
  background: var(--panel); color: var(--accent-dark); font-weight: 600; text-decoration: none;
}
.skip-link:focus { top: 8px; }
/* Where the skip link lands: a place to start reading, not a control. */
main:focus, #main:focus { outline: none; }

/* The idle sign-out warning (app-shell.js). */
.idle-warning {
  max-width: 420px; width: calc(100% - 32px); padding: 22px; border: 1px solid var(--hairline);
  border-radius: var(--radius); color: var(--ink); background: var(--panel);
}
.idle-warning::backdrop { background: rgba(24, 36, 48, .45); }
.idle-warning h2 { margin: 0 0 8px; font-size: 1.2rem; }
.idle-warning p { margin: 0 0 18px; color: var(--muted); line-height: 1.5; }
.idle-warning .idle-actions { display: flex; flex-wrap: wrap; gap: 10px; justify-content: flex-end; }

/* ---- Buttons ---- */
.btn {
  font: inherit; font-weight: 600; font-size: .92rem;
  border-radius: 9px; padding: 9px 16px; cursor: pointer;
  border: 1.5px solid var(--accent); text-decoration: none;
  display: inline-flex; align-items: center; gap: 7px; line-height: 1.2;
}
.btn-primary { background: var(--accent); color: #fff; }
.btn-primary:hover { background: var(--accent-dark); border-color: var(--accent-dark); }
.btn-primary:disabled { background: #9DBFB9; border-color: #9DBFB9; cursor: not-allowed; }
.btn-ghost { background: transparent; color: var(--accent); }
.btn-ghost:hover { background: var(--accent-soft); color: var(--accent-dark); }
.btn-quiet { background: transparent; color: var(--muted); border-color: var(--hairline); }
.btn-quiet:hover { color: var(--ink); border-color: var(--muted); }
.btn-sm { padding: 5px 11px; font-size: .82rem; border-radius: 7px; }
.btn:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
/* A link to something the plan has no room for (app-shell.js locks it). A
   link has no disabled state of its own, so it is dressed as a disabled
   button; it keeps its focus, so a keyboard still reaches the reason. */
.btn[aria-disabled="true"] { cursor: not-allowed; }
.btn-primary[aria-disabled="true"], .btn-primary[aria-disabled="true"]:hover { background: #9DBFB9; border-color: #9DBFB9; }
.btn-ghost[aria-disabled="true"], .btn-ghost[aria-disabled="true"]:hover { background: transparent; opacity: .55; }

/* ---- Page scaffolding ---- */
.page { max-width: 1160px; margin: 0 auto; padding: 28px 22px 60px; }
.page-head { display: flex; align-items: center; justify-content: space-between; gap: 16px; flex-wrap: wrap; margin-bottom: 22px; }
.page-head h1 { margin: 0; font-size: 1.65rem; }
.page-head .sub { color: var(--muted); margin: 4px 0 0; font-size: .95rem; }

/* ---- Stat cards ---- */
.stats { display: grid; grid-template-columns: repeat(4, 1fr); gap: 16px; margin-bottom: 26px; }
.stat {
  background: var(--panel); border: 1px solid var(--hairline); border-radius: var(--radius);
  box-shadow: var(--shadow); padding: 18px 20px; display: flex; align-items: center; gap: 14px;
}
.stat .glyph {
  width: 42px; height: 42px; border-radius: 10px; flex: none;
  display: inline-flex; align-items: center; justify-content: center;
}
.stat .n { font-family: "Bricolage Grotesque", sans-serif; font-size: 1.7rem; font-weight: 700; line-height: 1; }
.stat .label { color: var(--muted); font-size: .85rem; margin-top: 3px; }

/* ---- Cards, lists ---- */
.card {
  background: var(--panel); border: 1px solid var(--hairline); border-radius: var(--radius);
  box-shadow: var(--shadow);
}
.card .card-head { padding: 16px 20px; border-bottom: 1px solid var(--hairline); display: flex; align-items: center; justify-content: space-between; gap: 10px; }
.card .card-head h2 { margin: 0; font-size: 1.05rem; }
.empty { text-align: center; color: var(--muted); padding: 46px 20px; }
.empty .big { font-size: 1.6rem; }

/* ---- Envelope table ---- */
.env-row { border-bottom: 1px solid var(--hairline); }
.env-row:last-child { border-bottom: none; }
.env-summary {
  display: grid; grid-template-columns: minmax(0, 1fr) 190px 130px 34px; gap: 12px; align-items: center;
  padding: 14px 20px; cursor: pointer;
  /* The row draws its own chevron. Older iOS Safari still drew its disclosure
     triangle as a separate box, which a grid turns into an extra cell that
     shoves the subject and the pill one column along. */
  list-style: none;
}
.env-summary::-webkit-details-marker { display: none; }
/* Without this the 1fr track sits at its content's min-content width (the
   no-wrap recipients line) and the row pushes the page sideways — on a
   1024px laptop as much as on a phone. */
.env-summary > * { min-width: 0; }
.env-summary:hover { background: var(--bg); }
/* The subject truncates; the chips beside it do not. A long subject used to push
   them off the end of the row, and one of them is now the warning that this
   envelope never reached somebody — the one thing here that must never be the
   part that gets clipped. min-width:0 is what lets the subject shrink at all. */
.env-summary .subject-line { display: flex; align-items: center; gap: 6px; min-width: 0; }
.env-summary .subject-line > .tagchip, .env-summary .subject-line > .warnchip,
.env-summary .subject-line > .plan-tag { flex: none; }
.env-summary .subject { font-weight: 600; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.env-summary .who { color: var(--muted); font-size: .84rem; margin-top: 2px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.env-summary .when { color: var(--muted); font-size: .85rem; font-variant-numeric: tabular-nums; }
.env-summary .chev { color: var(--muted); text-align: center; transition: transform .15s; }
.env-row[open] .chev { transform: rotate(90deg); }
.env-detail { padding: 4px 20px 18px; background: #FCFDFD; border-top: 1px dashed var(--hairline); }
.env-detail .recipients { margin: 10px 0; }
/* flex-start, not center: a line that grew a second row of explanation would
   otherwise float its status mark halfway down the paragraph. Identical on the
   one-line rows, which is all of them until something bounces. */
.recipient-line { display: flex; align-items: flex-start; gap: 10px; padding: 6px 0; font-size: .92rem; }
.recipient-line .mark { flex: none; width: 20px; text-align: center; }
.recipient-line .rcpt { flex: 1; }
.recipient-line .meta { color: var(--muted); font-size: .82rem; }
/* File names and addresses arrive as one unbroken word
   (Employment_Agreement_JSmith_2026_final_v3.pdf); without these they set the
   row's minimum width and push the page wider than a phone. */
.env-detail, .recipient-line > span { overflow-wrap: anywhere; min-width: 0; }
/* A delivery that came back. Red rather than amber: nothing about it resolves
   on its own, and the sender has to act. */
.warnchip { display: inline-block; background: var(--red-soft); color: var(--red);
            border-radius: 999px; padding: 2px 9px; font-size: .74rem; font-weight: 700; }
.recipient-line .undelivered { display: block; margin-top: 2px; color: var(--red); font-size: .82rem; }
/* The plan a locked control needs. Same look as the badges beside Settings'
   paid sections, so "this is on a higher plan" reads the same everywhere. */
.plan-tag { display: inline-block; font-size: .64rem; font-weight: 700; letter-spacing: .06em;
            color: var(--accent-dark); background: var(--accent-soft); border-radius: 999px;
            padding: 2px 8px; margin-left: 6px; vertical-align: middle; white-space: nowrap; }
/* A section the plan does not allow changing: still shown, because it is what
   upgrading gets you, but plainly not something to click. Disabled alone left
   the reminder switch bright green and every field looking live. */
.opt-section.locked .opt-body > :not([data-session-gate]) { opacity: .5; pointer-events: none; }
/* Documents past a plan's availability window: stated in place of the
   download buttons, in the same quiet green as the other plan notes. */
.doc-locked { margin: 8px 0; padding: 9px 12px; border-radius: 8px; background: var(--accent-soft);
              color: var(--accent-dark); font-size: .85rem; }
/* The teal itself is 4.3:1 on this tint; its darker shade is 6.5:1. */
.doc-locked a { color: var(--accent-dark); }
.env-actions { display: flex; gap: 8px; flex-wrap: wrap; margin-top: 8px; }
.env-actions .btn { max-width: 100%; white-space: normal; overflow-wrap: anywhere; text-align: left; }

/* ---- Status pills ---- */
.pill {
  display: inline-block; font-size: .74rem; font-weight: 600; letter-spacing: .03em;
  border-radius: 999px; padding: 4px 11px; white-space: nowrap;
}
.pill.Draft { background: var(--blue-soft); color: var(--blue); }
.pill.Sent { background: var(--amber-soft); color: var(--amber); }
.pill.Completed { background: var(--accent-soft); color: var(--accent-dark); }
.pill.Declined, .pill.Voided, .pill.Expired { background: var(--red-soft); color: var(--red); }

/* ---- Forms ---- */
input, select, textarea {
  width: 100%; font: inherit; padding: 9px 11px; border: 1px solid var(--field-border);
  border-radius: 8px; margin-bottom: 8px; background: #fff; color: var(--ink);
}
input:focus-visible, select:focus-visible, textarea:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; border-color: var(--accent); }
label { font-size: .82rem; font-weight: 600; color: var(--muted); display: block; margin-bottom: 4px; }
/* The browser's own placeholder grey is about 2.3:1; examples in a box are
   still words to read. */
::placeholder { color: var(--muted); opacity: 1; }
/* Words for a screen reader alone. */
.sr-only { position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0; }
/* iOS Safari zooms the page into any field under 16px when it takes focus and
   leaves it zoomed, sideways-pannable, after the keyboard goes. Every app page
   loads this file, the editors included, so the one rule covers them all.
   Zooming stays allowed: maximum-scale would take pinch-zoom away from people
   who need it. */
@media (max-width: 900px), (pointer: coarse) {
  input, select, textarea { font-size: 16px; }
}
.hint { color: var(--muted); font-size: .85rem; }
/* Something that acts, dressed as the link it used to be (Sign out, SSO's
   Change and Remove): a button to a screen reader, since it does something
   rather than go somewhere (WCAG 4.1.2). */
.linkbtn { background: none; border: 0; padding: 0; margin: 0; font: inherit; color: var(--accent); text-decoration: underline; cursor: pointer; }
.linkbtn.hint { color: var(--muted); font-size: .85rem; }
.linkbtn:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
/* What is wrong with a field, under it and tied to it (aria-describedby),
   rather than in a pop-up that closes before it can be read beside the box. */
.field-error { color: var(--red); font-size: .85rem; font-weight: 600; margin: 6px 0 0; }
.field-error:empty { display: none; }

/* ---- Loading / toast ---- */
.spinner {
  width: 18px; height: 18px; border-radius: 50%; display: inline-block; vertical-align: -4px;
  border: 2.5px solid var(--accent-soft); border-top-color: var(--accent);
  animation: spin .8s linear infinite;
}
@keyframes spin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) {
  .spinner { animation-duration: 2s; }
  .env-summary .chev { transition: none; }
  .app-menu .sheet { animation: none; }
}

/* ---- Responsive ---- */
/* Six links in the bar (two editors among them since 6 Oct 2026) beside the
   plan, the avatar, Help and Sign out: below 1100px the bar's own gaps and
   the links' padding and type come in a little (the signed-in name has
   already gone, app-shell.js), measured on a copy of the bar with both
   editors padlocked, the widest the links get. */
@media (max-width: 1100px) {
  .topbar { gap: 12px; padding: 0 14px; }
  .topbar nav { gap: 2px; }
  .topbar nav a { padding: 7px 8px; font-size: .88rem; }
}

@media (max-width: 900px) {
  .stats { grid-template-columns: repeat(2, 1fr); }
  .env-summary { grid-template-columns: minmax(0, 1fr) 110px 34px; }
  .env-summary .when { display: none; }
}

/* Below this the six links, the plan and Sign out no longer fit the bar on
   one line (at 940 the chip ran off the bar's right edge, with the two editors
   padlocked; five links fit down to 860): they move into the Menu sheet,
   which lists all of them and who is signed in. */
@media (max-width: 960px) {
  .topbar { gap: 10px; padding: 0 12px 0 16px; }
  .topbar > nav, .topbar > .userchip { display: none; }
  .menu-btn { display: inline-flex; }
}

@media (max-width: 640px) {
  .page { padding: 20px 16px 48px; }
  .page-head { flex-direction: column; align-items: flex-start; gap: 12px; }
  .page-head > * { max-width: 100%; }

  /* Envelope rows on a phone. The first column is ~180px here; the chips
     (⚠ Not delivered, 🔒 Documents locked, #tags) can't shrink, so on one line
     they squeezed the subject to nothing and ran over the status pill. The
     subject gets its own line (two, if it needs them) and the chips wrap
     beneath it, clipped at the cell's edge if one is longer than the cell.
     90px fits the widest pill (Completed) and keeps the pills in one column. */
  .env-summary { grid-template-columns: minmax(0, 1fr) 90px 16px; gap: 8px; padding: 14px 14px 14px 16px; }
  .env-summary > div:nth-child(3) { text-align: right; }
  .env-summary > div:first-child { overflow: hidden; }
  .env-summary .subject-line { flex-wrap: wrap; row-gap: 4px; }
  .env-summary .subject {
    flex: 1 1 100%; white-space: normal; overflow-wrap: anywhere;
    display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2;
  }
  .env-summary .subject-line > :not(.subject) {
    margin-left: 0; max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  }
  .env-detail { padding: 4px 16px 18px; }
  /* "Hand over the device" gets a line of its own under the name rather than
     squeezing the address into a column a word wide. */
  .recipient-line { flex-wrap: wrap; }
  .recipient-line .rcpt { flex-basis: calc(100% - 30px); }
  .recipient-line > .btn { margin-left: 30px; }
}

/* Sideways strips (SignSwifly.strip): a fade on whichever edge has more
   behind it. The part-cut tab under the fade is what says "swipe". */
.strip.more-right { -webkit-mask-image: linear-gradient(to right, #000 calc(100% - 44px), transparent); mask-image: linear-gradient(to right, #000 calc(100% - 44px), transparent); }
.strip.more-left { -webkit-mask-image: linear-gradient(to left, #000 calc(100% - 44px), transparent); mask-image: linear-gradient(to left, #000 calc(100% - 44px), transparent); }
.strip.more-left.more-right {
  -webkit-mask-image: linear-gradient(to right, transparent, #000 44px, #000 calc(100% - 44px), transparent);
  mask-image: linear-gradient(to right, transparent, #000 44px, #000 calc(100% - 44px), transparent);
}

/* Touch screens: a thumb needs ~44px. .btn-sm was ~28px tall and packed up to
   seven to a row 8px apart; plain .btn and the fields were ~39px. Mouse
   layouts keep their compact sizes. */
@media (pointer: coarse) {
  .btn, .btn-sm { min-height: 44px; }
  .btn-sm { padding: 8px 12px; }
  .env-actions { gap: 10px; }
  input:not([type=checkbox]):not([type=radio]):not([type=color]):not([type=range]):not([type=file]), select { min-height: 44px; }
  #signout { display: inline-flex; align-items: center; min-height: 44px; padding: 0 6px; }
  .topbar .brand { padding: 10px 0; }
}

/* The hidden attribute must beat display:flex/grid utility classes. */
[hidden] { display: none !important; }

/* ---- Tag chips ---- */
.tagchip {
  display: inline-block; font-size: .7rem; font-weight: 600; vertical-align: 2px;
  color: var(--accent-dark); background: var(--accent-soft);
  border-radius: 999px; padding: 2px 8px; margin-left: 6px;
}
