/* ===============================================================
   Dois Agentes, her side of it.

   The tokens are the landing page's, so the product she buys and the
   product she opens are recognisably the same thing. Two of them carry
   meaning rather than decoration, and the whole screen depends on the
   distinction: --human is what Sofia did, --machine is what the
   Assistant did. The landing page argues that two agents work the same
   day; this screen is the one place that argument has to be true.

   The page is hers. What the Assistant wrote carries a Telha edge down
   its left side and nothing else: no panel, no fill, no frame. The edge is
   the whole distinction, so do not dilute it by marking anything else with
   that colour except the focus ring on the draft itself.
   =============================================================== */

:root {
  /* The brand's three: Cal (lime wash) is the ground, Tinta (ink) is her and
     the structure, Telha (roof tile) is the Assistant. brand/README.md has
     the ratio, 60/30/10, and it holds here because Telha only marks what the
     Assistant did. */
  --paper:      #F5F0E6;
  --raised:     #FCFAF5;
  --ink:        #1C2B45;
  --ink-soft:   #3F4B62;
  --muted:      #5B6375;
  --rule:       #E0D8C8;
  /* --muted carries the small text on both surfaces: timestamps, the list
     preview, the parish under a price. Its worst pairing, on --paper, is
     5.30:1. Lightening it breaks WCAG AA. */

  --human:      #24406E;
  --human-lo:   #E3E7EF;
  /* Telha itself, #B5502F, is 4.45:1 on Cal, just under AA for body text, so
     the text token is a step darker. The logo and fills keep the real one. */
  --machine:    #A3452A;
  --machine-lo: #F3E1D7;
  --wait:       #83600F;
  --wait-lo:    #F6EBCF;
  /* Amber used to carry both "check this" and "this did not happen". A client
     who has not written back and a model call that failed are not the same
     severity, and they were the same colour. Crimson rather than red, so a
     failure does not read as one more thing the Assistant did in Telha. */
  --fail:       #A0193A;

  /* One family, as the brand guide sets it: Light for display, Regular and
     Medium for text. */
  --display: "Space Grotesk", "Helvetica Neue", Arial, sans-serif;
  --body: "Space Grotesk", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;

  /* One width, every route. The header sits inside this, so a shell that
     changed size per page moved the wordmark 226px between two clicks. */
  --shell: 76rem;
  /* Every block in the thread shares this. Each one used to be sized on its
     own, so the client's message wrapped at one width, the draft ended at a
     second, the listing link floated at a third and the composer ran past all
     of them. Four right edges in one column. */
  --measure: 40rem;
  --gap: clamp(18px, 3.5vw, 40px);

  /* Two small sizes, not four. 0.9rem, 0.86rem and 0.82rem were doing the same
     job in different places. */
  --t-sm: 0.875rem;
  --t-xs: 0.78rem;

  color-scheme: light dark;
}

@media (prefers-color-scheme: dark) {
  :root {
    /* One family of navies, three steps apart. The page used to be near-black
       while the header and the panels were navy, so the header read as a strip
       floating on a different design. Still pulled far enough apart that every
       edge holds, which is the thing dark mode usually loses. */
    --paper:      #121C2E;
    --raised:     #1C2B45;
    --ink:        #F5F0E6;
    --ink-soft:   #CFC8BA;
    --muted:      #A7A497;
    /* Lighter than it was. In dark the card fill and the page ground were
       within a few points of each other, so every edge on the screen nearly
       disappeared and the layout had less structure than in light. */
    --rule:       #3A4D6E;

    --human:      #9FB8E3;
    --human-lo:   #22365A;
    --machine:    #EE9271;
    --machine-lo: #3B2419;
    --wait:       #E2B755;
    --wait-lo:    #33290F;
    --fail:       #FF8FA6;
  }
}

*, *::before, *::after { box-sizing: border-box; }

body {
  margin: 0;
  background: var(--paper);
  color: var(--ink);
  font-family: var(--body);
  font-size: 16px;
  line-height: 1.55;
  -webkit-font-smoothing: antialiased;
}

h1, h2, h3 {
  font-family: var(--display);
  font-weight: 500;
  line-height: 1.1;
  margin: 0 0 0.4em;
  text-wrap: balance;
}

h1 { font-size: clamp(1.25rem, 2vw, 1.5rem); letter-spacing: -0.01em; }
h2 { font-size: 1.2rem; letter-spacing: -0.01em; }

/* A contact's name is context, not the point of the screen. It reads as a
   heading and then gets out of the way. */
.who { display: flex; align-items: baseline; gap: 12px; flex-wrap: wrap; margin: 0 0 10px; }
/* On one baseline beside a 32px name, a 14px qualifier reads as dropped rather
   than placed. Under it, it reads as what it is. */
.who.person, .who.stacked { display: block; }
.who.person .who-meta, .who.stacked .who-meta { margin-top: 2px; }
/* An inline margin:0 in the view used to win over this, so the primary action
   sat welded to the right of the heading instead of at the far edge. */
.who .actions { margin-left: auto; margin-top: 0; }
.who :is(h1, h2) { margin: 0; }
/* A star belongs on its title's line. Allowed to wrap, a two-line title at
   390px pushed it onto a row of its own under the title. */
.who:not(.person):has(.case-title) { flex-wrap: nowrap; align-items: flex-start; }
.who:not(.person) .case-title { min-width: 0; }
/* Centres the 44px target on the title's first line: 1.5rem at line-height
   1.1 is 1.65rem. */
.who:not(.person):has(.case-title) .star { margin-top: calc((1.65rem - 44px) / 2); }
/* A client outranks the word "Conversas". They were the same size, so a page
   title and a person spoke with the same voice. */
.who.person h1 { font-size: clamp(1.5rem, 2.6vw, 1.95rem); }
.who-meta { margin: 0; display: flex; gap: 8px; align-items: center; }
/* "quer comprar" is a fact about Rita. "à espera" is a thing waiting for
   Sofia. They were the same bordered rectangle, so the shape taught nothing. */
.who-meta .kind { font-size: var(--t-sm); color: var(--muted); }
.warn { color: var(--wait); font-weight: 600; }

/* Ink with a rule under it, not a hue. The one link colour on the screen was
   the only violet in the palette, and in dark it sat on navy at a contrast
   nobody could read. */
a { color: var(--ink); text-decoration-color: var(--muted); text-underline-offset: 3px; }
a:hover { text-decoration-color: var(--machine); }

/* --- chrome ------------------------------------------------- */

/* The chrome spans the window; its contents line up with the column, so the
   wordmark sits over the first character of the page and the account sits over
   its right edge. Full-bleed contents left a thousand pixels of nothing in the
   middle of the bar on every screen. */
.top {
  border-bottom: 1px solid var(--rule);
  background: var(--raised);
}
.top-inner {
  max-width: var(--shell);
  margin: 0 auto;
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 16px;
  flex-wrap: wrap;
  padding: 16px var(--gap);
}

/* At 390px the account block ended seven pixels from the screen edge and the
   wordmark had nothing between it and the next thing. */
@media (max-width: 30rem) {
  .top-inner { gap: 10px 14px; }
  .account { gap: 12px; width: 100%; justify-content: space-between; }
}

/* Lockups from brand/d-casa, drawn in currentColor so dark mode needs no
   second file. The guide sets 24px as the mark's floor, below which its gaps
   close. In the primary lockup the mark is 74% of the height, so the lockup
   is 33px tall. The sidebar is too narrow for that and takes the stacked
   lockup, where the mark sits above the name at 28px. */
.wordmark { display: inline-flex; color: var(--ink); text-decoration: none; }
.wordmark svg { display: block; height: 33px; width: auto; }
.sidebar-head .wordmark svg { height: 49px; }
/* A newsletter sender's name where the lockup would be, set as a title. */
.public-brand { font-family: var(--display); font-size: 1.1rem; font-weight: 500; }

/* A star is a button, so it needs the borders and padding taken off it rather
   than added. 44px because it is the smallest thing on the screen and it is
   the one a thumb has to find. */
.star {
  background: none;
  border: 0;
  padding: 0;
  min-width: 44px;
  min-height: 44px;
  font-size: 1.25rem;
  line-height: 1;
  color: var(--muted);
  cursor: pointer;
}
.star:hover { color: var(--ink); }
.star.on { color: var(--ink); }

/* The owner sits under the client's name and is one line, because it is a fact
   about the row and not a section of the page. */
.owner { display: flex; align-items: center; gap: 8px; margin-top: 6px; font-size: var(--t-sm); }
.owner label { color: var(--muted); }
/* Inputs are full width by default, which is right in a form and wrong for one
   select sitting on a line under a name. */
.owner select { width: auto; min-width: 12rem; }



/* --- sidebar ------------------------------------------------ */

/* The top bar held eight links and every new plan adds one. A column holds
   them in groups and still has room. */
/* The window scrolls, not the page column, so the keyboard still does: with
   .page as the scroller, PageDown did nothing until something inside it had
   focus. The sidebar is sticky and only as tall as its links, so a banner
   above it cannot push its foot off screen. */
.frame {
  --side: 14.5rem;
  display: grid;
  grid-template-columns: var(--side) minmax(0, 1fr);
  align-items: start;
  min-height: 100vh;
  /* The column and its rule are painted here, because the sticky sidebar is
     only as tall as its links and would end halfway down a long page. */
  background: linear-gradient(90deg, var(--raised) var(--side), var(--rule) var(--side), var(--rule) calc(var(--side) + 1px), transparent calc(var(--side) + 1px));
}
.page { min-width: 0; }

.sidebar {
  position: sticky;
  top: var(--banner-h, 0px);
  max-height: calc(100vh - var(--banner-h, 0px));
  overflow-y: auto;
  display: flex;
  flex-direction: column;
  padding: 20px 14px;
}
.sidebar-head { display: flex; align-items: center; justify-content: space-between; min-height: 44px; padding: 0 10px; }
.sidebar-body { display: flex; flex-direction: column; }

/* Sentence case. Tracked capitals are the table headers' voice, and group
   names in it read as columns. */
.side-group {
  margin: 20px 10px 4px;
  font-family: var(--body);
  font-size: 0.75rem;
  font-weight: 600;
  color: var(--muted);
}
.side-nav ul { list-style: none; margin: 0; padding: 0; }
.side-nav li + li { margin-top: 2px; }
.side-nav a,
.sidebar .nav.favourites a {
  display: flex;
  align-items: center;
  min-height: 32px;
  padding: 0 10px;
  border-radius: 6px;
  font-size: var(--t-sm);
  color: var(--ink-soft);
  text-decoration: none;
}
.side-nav a:focus-visible, .sidebar .nav.favourites a:focus-visible { outline-offset: 2px; }
.side-nav a:hover,
.sidebar .nav.favourites a:hover { background: color-mix(in srgb, var(--ink) 5%, var(--raised)); color: var(--ink); }
/* Ink, not Telha: Telha means the Assistant acted, and "you are here" is
   not the Assistant. Mixed from ink so it is lighter than the sidebar in dark
   and darker in light; --paper was darker in both. The fill alone marks it. */
.side-nav a[aria-current="page"],
.sidebar .nav.favourites a[aria-current="page"] {
  background: color-mix(in srgb, var(--ink) 8%, var(--raised));
  color: var(--ink);
  font-weight: 600;
}


.sidebar .nav.favourites {
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: 0;
  margin-top: 20px;
  padding: 0;
  border-left: 0;
}
.sidebar .nav-head { margin: 0 10px 4px; color: var(--muted); font-size: 0.75rem; font-weight: 600; }

.side-foot {
  margin-top: 24px;
  padding: 16px 10px 0;
  border-top: 1px solid var(--rule);
  font-size: var(--t-sm);
  color: var(--muted);
}
.side-foot form { display: block; }
.side-foot .linkish { padding: 0; color: var(--muted); }
.side-account { margin: 0; color: var(--ink-soft); }
.side-account span { display: block; font-size: var(--t-xs); color: var(--muted); }

.menu-toggle {
  display: none;
  appearance: none;
  min-height: 44px;
  padding: 0 14px;
  border: 1px solid var(--rule);
  border-radius: 6px;
  background: var(--raised);
  color: var(--ink);
  font: inherit;
  font-size: var(--t-sm);
  cursor: pointer;
}

/* Thirteen links sit before the page on every screen. */
.skip {
  position: absolute;
  left: 8px;
  top: -100px;
  z-index: 10;
  padding: 10px 14px;
  background: var(--ink);
  color: var(--paper);
  border-radius: 6px;
  text-decoration: none;
}
.skip:focus-visible { outline-offset: 2px; }
.skip:focus { top: 8px; }
#conteudo:focus { outline: none; }

/* One strip per operator mode, sticky, one line on a desktop. Observing is
   the safe state and gets the quiet amber; working means his clicks write, so
   it gets the solid one. */
body:has(.mode-banner) { --banner-h: 3rem; }
.mode-banner {
  position: sticky;
  top: 0;
  z-index: 5;
  display: flex;
  gap: 0 16px;
  align-items: center;
  justify-content: space-between;
  min-height: var(--banner-h);
  padding: 0 var(--gap);
  font-size: var(--t-sm);
}
.mode-banner .linkish { text-decoration: underline; white-space: nowrap; }
.observation-banner { background: var(--wait-lo); color: var(--ink); border-bottom: 1px solid var(--wait); }
.observation-banner .linkish { color: var(--ink); }
.working-banner { background: var(--wait); color: var(--paper); }
.working-banner .linkish { color: var(--paper); }
.working-banner .linkish:hover { color: var(--paper); text-decoration-color: var(--paper); }

@media (max-width: 56.24rem) {
  .frame { display: block; min-height: 0; background: none; }
  .sidebar { position: static; max-height: none; background: var(--raised); padding: 10px var(--gap); border-right: 0; border-bottom: 1px solid var(--rule); }
  .sidebar-head { padding: 0; }
  .side-nav a, .sidebar .nav.favourites a { min-height: 44px; }
  html.js-menu .sidebar-body:not(.open) { display: none; }
  html.js-menu .menu-toggle { display: inline-flex; align-items: center; }
  .sidebar-body { padding-bottom: 16px; }
  .mode-detail { display: none; }
  /* The row scrolls sideways here; the fade says there is more. */
  .filter { mask-image: linear-gradient(90deg, #000 calc(100% - 24px), transparent); }
}

/* The message for a field sits under that field, in the colour for "this did
   not happen". */
.field-error { margin: 6px 0 0; font-size: var(--t-sm); color: var(--fail); }
.nowrap { white-space: nowrap; }
.field input[aria-invalid="true"] { border: 2px solid var(--fail); }

.who-count { color: var(--muted); font-size: var(--t-sm); }

/* The count, then the way on. Links, not buttons: a page is a place. */
.pages { display: flex; flex-wrap: wrap; align-items: center; gap: 0 20px; margin-top: 16px; font-size: var(--t-sm); color: var(--muted); }
.pages a { display: inline-flex; align-items: center; min-height: 44px; color: var(--ink); }

/* The bulk bar: four selects that start on "manter" and a button that names
   how many will change. Stuck to the bottom of the window below the table, so
   ticking a row never moves the row. */
.bulk-bar {
  position: sticky;
  bottom: 0;
  z-index: 4;
  display: flex;
  flex-wrap: wrap;
  align-items: end;
  gap: 12px 16px;
  margin: 16px 0 0;
  padding: 14px 16px;
  background: var(--raised);
  border: 0;
  border-top: 1px solid var(--rule);
  box-shadow: 0 -4px 12px color-mix(in srgb, var(--ink) 12%, transparent);
  font-size: var(--t-sm);
}
/* A legend sits on the fieldset's top border unless it floats. */
.bulk-bar legend { float: left; width: 100%; padding: 0; margin: 0; font-weight: 600; }
/* With the script, the count line is the heading and the legend is only the
   fieldset's name for a screen reader. */
.js-bulk .bulk-bar legend,
.js-bulk .bulk-bar .bulk-all { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; }
.bulk-head { flex-basis: 100%; display: flex; flex-wrap: wrap; align-items: baseline; gap: 0 16px; color: var(--ink); }
.bulk-head strong { font-size: 0.95rem; }
.bulk-head .linkish { text-decoration: underline; }
/* Both set display, which would otherwise beat the hidden attribute. */
.bulk-head[hidden],
.bulk-head .linkish[hidden] { display: none; }
.bulk-bar label { display: flex; flex-direction: column; gap: 4px; color: var(--muted); }
.bulk-bar select { width: 11rem; }
.bulk-bar > .btn { margin: 0; min-height: 44px; align-self: flex-end; }
/* The global label margin put the button 6px below the selects. */
.bulk-bar label { margin: 0; }
/* The row itself shows it is chosen, not only its box. */
.contacts-table tr:has(input[name="ids[]"]:checked) td { background: color-mix(in srgb, var(--human) 9%, var(--raised)); }
input[type="checkbox"]:indeterminate { background: var(--ink); border-color: var(--ink); }
input[type="checkbox"]:indeterminate::before { content: ""; width: 10px; height: 2px; background: var(--raised); }
/* "manter" is quiet and a chosen value is bold ink, so the fields about to
   change stand out without borrowing the ticked rows' tint. */
.bulk-bar select:has(option[value=""]:checked) { color: var(--muted); }
.bulk-bar select:not(:has(option[value=""]:checked)) { color: var(--ink); font-weight: 600; }
.bulk-bar > .btn:disabled { opacity: 0.4; cursor: not-allowed; }
form.js-bulk:not(.picking) .bulk-bar { display: none; }
/* While the bar is up, a row reached by Tab scrolls clear of it. */
html:has(form.picking) { scroll-padding-bottom: 12rem; }
@media (max-width: 40rem) {
  /* One field per row, label beside it, so a colleague's full name fits. */
  .bulk-bar { display: grid; grid-template-columns: 1fr; gap: 8px; }
  .bulk-bar label { display: grid; grid-template-columns: 7rem 1fr; align-items: center; }
  /* Out of sight on a phone, but still the fieldset's name for a screen reader. */
  .bulk-bar legend { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; }
  .bulk-bar select { width: 100%; }
}
.bulk-bar .bulk-all { flex-direction: row; align-items: center; gap: 8px; min-height: 44px; color: var(--ink-soft); }
.bulk-bar .bulk-all[hidden] { display: none; }
td.pick, th.pick { width: 3rem; padding-right: 0; }
th.pick { vertical-align: middle; }
/* Drawn from tokens, because the browser's own box takes a grey-brown fill in
   dark that exists nowhere else on the page. */
input[type="checkbox"] {
  appearance: none;
  display: inline-grid;
  place-content: center;
  width: 20px;
  height: 20px;
  margin: 0;
  border: 1px solid var(--muted);
  border-radius: 4px;
  background: var(--raised);
  cursor: pointer;
}
input[type="checkbox"]:checked { background: var(--ink); border-color: var(--ink); }
input[type="checkbox"]:checked::before {
  content: "";
  width: 11px;
  height: 11px;
  background: var(--raised);
  clip-path: polygon(14% 44%, 0 65%, 50% 100%, 100% 16%, 80% 0, 43% 62%);
}
/* A page of contacts keeps its columns where they were on the last page. */
.contacts-table { table-layout: fixed; }
.contacts-table th:nth-child(2) { width: 24%; }
.contacts-table th:nth-child(3) { width: 14%; }
.contacts-table th:nth-child(4) { width: 12%; }
.contacts-table th:nth-child(5) { width: 17%; }
.contacts-table th:nth-child(6) { width: 16%; }
.contact-tags { display: flex; flex-wrap: wrap; gap: 4px; margin-top: 4px; }
.contact-tags .tag { min-height: 24px; display: inline-flex; align-items: center; color: var(--muted); border-color: var(--rule); text-decoration: none; }
.contact-tags .tag:hover { color: var(--ink); border-color: var(--ink); }
/* Grows with her tags, so six of them on a phone are all visible. */
.tag-list { field-sizing: content; }
.contact-tags .tag.on { color: var(--paper); background: var(--ink); border-color: var(--ink); }
/* Search and tag side by side, so the list starts one row higher. */
@media (min-width: 40rem) {
  .narrow-form.contact-search { max-width: 60rem; display: grid; grid-template-columns: 3fr 2fr auto; column-gap: 16px; align-items: end; }
  .contact-search .field { margin-bottom: 0; }
  .contact-search .actions { margin-top: 0; }
}

/* Done is the checkbox that starts the row; changing the date or owner, and
   cancelling, sit one step down in a sub-row that spans the table. */
.task-actions { white-space: nowrap; text-align: right; }
.task-actions .linkish { text-decoration: underline; }
/* .linkish keeps a 44px target; centre its text in it so it sits on the row's line. */
.task-table .linkish { align-items: center; vertical-align: middle; margin-block: -11px; }
.js .task-change-row:not(.open) { display: none; }
/* Nova ação: a quiet button that opens the same fields the change strip uses. */
.task-new { margin: 0 0 16px; }
.task-new-toggle[hidden] { display: none; }
.js form.task-new-form:not(.open) { display: none; }
form.task-new-form { margin-top: 12px; padding: 18px 20px; background: var(--raised); border: 1px solid var(--rule); }
/* One grid for both: the title and the note run the full width, date, owner
   and property share a row, the link and its name the next. */
form.task-change { display: grid; grid-template-columns: minmax(0, 1fr); gap: 12px 16px; align-items: start; }
@media (min-width: 40rem) { form.task-change { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (min-width: 64rem) { form.task-change { grid-template-columns: 11rem 14rem minmax(0, 1fr) minmax(0, 1fr); } }
.task-change :is(.task-field-title, .task-field-body) { grid-column: 1 / -1; }
/* The name only matters once there is a link to name. */
.task-change:has(input[name="property_url"]:placeholder-shown) .task-field-link-name { display: none; }
.task-change input[type="text"], .task-change input[type="url"], .task-change textarea { width: 100%; }
.task-change .btn { justify-self: start; }
.task-property, .task-note { display: block; margin-top: 2px; font-size: var(--t-sm); font-weight: 400; color: var(--ink-soft); }
.task-property a { color: inherit; text-underline-offset: 3px; }
.task-property a > [aria-hidden] { margin-left: 3px; }
.task-note { color: var(--muted); }
.seen-list { list-style: none; margin: 0; padding: 0; }
.seen-list li { display: flex; flex-wrap: wrap; align-items: baseline; gap: 4px 12px; padding: 10px 0; border-bottom: 1px solid var(--rule); }
.seen-list li:last-child { border-bottom: 0; }
.seen-list .task-property { margin: 0; font-size: inherit; color: var(--ink); }
.seen-list .task-property a { display: inline-flex; align-items: center; min-height: 44px; margin-block: -12px; }
@media (max-width: 30rem) { .seen-when { flex-basis: 100%; } }
.seen-when { font-size: var(--t-sm); color: var(--muted); }
.seen-when .is-ahead { color: var(--ink); }
.task-table th.pick, .task-table td.pick { width: 3rem; }
/* The open row and its task read as one block: same tint, no rule between,
   and the form starts under the title. */
.task-change-row td { padding: 4px 12px 14px calc(3rem + 12px); background: color-mix(in srgb, var(--ink) 3%, var(--raised)); }
.task-table tr:has(+ tr.task-change-row.open) td { background: color-mix(in srgb, var(--ink) 3%, var(--raised)); border-bottom-color: transparent; }
.task-cancel { display: flex; align-items: baseline; gap: 12px; margin-top: 12px; padding-top: 8px; border-top: 1px solid var(--rule); }
.task-cancel form { display: inline; }
/* Round, so marking a task done never looks like picking a row. */
.task-table input.done-box { border-radius: 50%; }
.task-table input.done-box:checked::before { width: 10px; height: 10px; }
/* .linkish sets display, which would otherwise beat the hidden attribute. */
.task-toggle[hidden] { display: none; }
/* The value doubles as its own control: plain until hovered or focused, and
   only with the script, since without it the button does nothing. */
.cell-edit { appearance: none; background: none; border: 1px solid transparent; border-radius: 4px; padding: 2px 6px; margin: -3px -7px; font: inherit; color: inherit; cursor: pointer; text-align: left; }
.js .cell-edit:hover { border-color: var(--rule); background: var(--raised); }
html:not(.js) .cell-edit { cursor: default; }
.task-change label { margin: 0; display: flex; flex-direction: column; gap: 4px; }
.task-change input[type="date"], .task-change select { width: 100%; }
.task-change-row form.button_to { display: inline; }
.linkish.danger { color: var(--fail); text-decoration: underline; }
/* Finished business, done or cancelled, is faded below the open work. Not
   struck through, which to-do lists use for done. */
.task-table td { vertical-align: middle; }
.task-table tr.is-finished td:not(.task-actions) { opacity: 0.7; }
/* Overdue is said once, in the date it is about. */
.task-when.is-overdue, .task-when.is-overdue .cell-edit { color: var(--fail); }
.task-when { white-space: nowrap; }
.overdue-word { margin-left: 8px; font-size: var(--t-sm); color: var(--fail); }
.task-none td { color: var(--muted); }
/* A cancelled task keeps a dashed grey circle where the done box would be; its
   group's heading says what it is. */
.cancel-glyph { display: inline-block; width: 20px; height: 20px; border: 1px dashed var(--muted); border-radius: 50%; vertical-align: middle; }
/* Done and cancelled tasks fold under their counts; without the script they stay open. */
.task-finished-head td { border-top: 1px solid var(--rule); padding-top: 12px; padding-bottom: 12px; font-size: var(--t-sm); color: var(--ink-soft); }
.finished-toggle[hidden] { display: none; }
.js .finished-label { display: none; }
.finished-toggle::before { content: "▸"; display: inline-block; width: 1em; text-decoration: none; }
.finished-toggle[aria-expanded="true"]::before { content: "▾"; }
.js .task-finished:not(.open) tr.is-finished { display: none; }
.task-table input.done-box:checked { background: transparent; border-color: var(--muted); }
.task-table input.done-box:checked::before { background: var(--muted); }
/* On a phone each task is a card: done box and title, one line of when, who
   and state, then its actions. */
@media (max-width: 40rem) {
  .scroller table.task-table { min-width: 0; }
  .task-table thead { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); }
  .task-table, .task-table tbody { display: block; width: 100%; }
  .task-table tr:not(.task-change-row) {
    display: grid;
    grid-template-columns: 28px auto auto 1fr;
    align-items: center;
    gap: 4px 10px;
    padding: 14px 0 6px;
    border-bottom: 1px solid var(--rule);
  }
  .task-table td { display: block; border: 0; padding: 0; }
  .task-table td.pick { grid-column: 1; grid-row: 1; }
  .task-table td.task-title { grid-column: 2 / -1; grid-row: 1; font-weight: 600; }
  .task-table td.task-when { grid-column: 2; grid-row: 2; }
  .task-table td.task-owner { grid-column: 3; grid-row: 2; }
  .task-table td.task-when, .task-table td.task-owner { font-size: var(--t-sm); }
  .task-table tr.task-finished-head, .task-table tr.task-none { display: block; padding: 12px 0; border-bottom: 1px solid var(--rule); }
  .task-table td.task-actions { grid-column: 2 / -1; grid-row: 3; text-align: left; }
  .task-change-row { display: block; }
  .task-change-row td { display: block; padding: 10px 0 14px; }
}

/* Only while fechado is the chosen stage; the box does nothing otherwise. */
.close-tasks { white-space: nowrap; }
.field .close-tasks { margin-top: 12px; padding-left: 12px; }
.bulk-bar label.close-tasks { margin-top: 16px; }
.close-tasks,
.bulk-bar label.close-tasks { display: none; flex-direction: row; align-items: center; gap: 8px; margin-top: 8px; color: var(--ink-soft); }
form:has(#contact_stage option[value="closed"]:checked) .close-tasks,
.bulk-bar:has(select[name="changes[stage]"] option[value="closed"]:checked) label.close-tasks { display: flex; flex-basis: 100%; }

/* Whose tasks. A row of links rather than a select, because there are three
   people and a select hides two of them. */
.filter {
  display: flex;
  align-items: center;
  gap: 0 18px;
  margin: -12px 0 20px;
  overflow-x: auto;
  white-space: nowrap;
  font-size: var(--t-sm);
  box-shadow: inset 0 -1px 0 var(--rule);
}
.filter-label { color: var(--muted); }
.filter a { display: inline-flex; align-items: center; min-height: 44px; color: var(--ink-soft); text-decoration: none; }
.filter a:hover { color: var(--ink); text-decoration: underline; text-decoration-color: var(--machine); }
.filter a[aria-current="true"] { color: var(--ink); font-weight: 600; box-shadow: inset 0 -2px 0 var(--ink); }

.site-foot {
  max-width: var(--shell);
  margin: 0 auto;
  padding: 0 var(--gap) 28px;
  font-size: var(--t-sm);
  color: var(--muted);
}

.shell { max-width: var(--shell); margin: 0 auto; padding: var(--gap); }

/* Rail, thread, context. Below this width there is no room for three of
   anything, so the rails fold away and the thread is the page: the rail is
   replaced by the back link at the top, and the context follows the draft. */
.inbox { display: grid; gap: 34px; }

@media (min-width: 82rem) {
  .inbox {
    grid-template-columns: 15rem minmax(0, var(--measure)) 14rem;
    align-items: start;
  }
  /* Nothing was read, so there is no third track. It used to stay open and
     empty, which is how the waiting screen came to look broken. */
  .inbox:not(:has(.context)) { grid-template-columns: 15rem minmax(0, var(--measure)); }
  .inbox .back-top { display: none; }
}

@media (max-width: 81.99rem) {
  .rail { display: none; }
  .context:empty { display: none; }
}

/* A heading, and the one act that is unreachable from inside a thread. */
.rail-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 10px;
  margin: 0 0 10px;
  font-size: var(--t-sm);
  color: var(--muted);
}
.rail-head strong { color: var(--ink); font-weight: 600; }
.rail .rows { border-top: 1px solid var(--rule); }
.rail .row {
  grid-template-columns: 1fr;
  gap: 2px;
  padding: 11px 10px;
  min-height: 0;
}
.rail .row-last { font-size: var(--t-sm); }
.rail .row-when { text-align: left; }

/* Where she is. A rail with no current row is a list, not a position. */
.row-here { background: var(--raised); box-shadow: inset 3px 0 0 var(--ink); }
.row-here:hover { background: var(--raised); }

/* The bar means "you are here" and nothing else. It used to be magenta on a
   waiting row and ink on any other, so the same 3px in the same place meant
   selected on one screen and something else on the next. Waiting is the filled
   pill's job. */

.context { font-size: var(--t-sm); }
.context:empty { display: none; }

/* No inner column. A narrower measure nested inside a wider centred one is
   what produced the lopsided margins: symmetric centring on the outside,
   dead space on the right from the width the inner column never used. The
   shell is the measure now, so every screen agrees with every other. */

.lede { color: var(--ink-soft); max-width: 62ch; margin: 0 0 28px; }

/* The answer a settings card exists to give, at reading size, with the answer
   itself as the first word. Not a KPI: two 24px numerals under letterspaced
   micro-caps was dashboard styling applied to a count of one, and it made the
   property count louder than whether the thing works at all. */
.state { margin: 0 0 4px; max-width: 62ch; }
.state .bad { color: var(--fail); }

/* One card, two halves. The state and the form were two identical bordered
   slabs 20px apart, which is a card doing no work: the fill is 1.21:1 against
   the page in dark, so the box was not separating anything. */
.rule { border: 0; border-top: 1px solid var(--rule); margin: 26px 0 22px; }

/* --- flashes ------------------------------------------------ */

/* A tint and no edge. A left bar is the Assistant's mark, and a notice that
   wore one differed from a draft only in hue. */
.flash {
  margin: 0 0 22px;
  padding: 12px 16px;
  background: var(--human-lo);
  color: var(--ink);
  font-size: 0.92rem;
}
.flash.alert { background: var(--wait-lo); }

/* --- her surfaces, raised ----------------------------------- */

.card {
  background: var(--raised);
  border: 1px solid var(--rule);
  padding: 22px;
  margin: 0 0 20px;
}

/* A connector card with a manual action has a 44px control on its title row and
   one without has only the 21px name, which put the two status lines at
   different heights down a page whose whole job is comparing them. Measured at
   23px against 10px. The space is reserved on both. */
.who.connector { min-height: 44px; align-items: center; }

/* One field in .pairs takes the whole card, so the same "Chave da API" box was
   535px on one card and 1090px on the next. Above the width where the grid
   stops collapsing, a lone field takes the same track a paired one would. */
.pairs.single .field { max-width: var(--measure); }

/* The fieldset exists to give a screen reader the connector's name before the
   field label; it is not a visible grouping, and its default border would draw
   a second box inside the card. */
.card fieldset { border: 0; padding: 0; margin: 0; min-width: 0; }
@media (min-width: 60rem) {
  .pairs.single .field { max-width: calc(50% - 10px); }
}

/* --- what the Assistant wrote ------------------------------- */

/* The Telha edge runs down the side of the proposal itself. It used to be a
   rule across the top of a grey panel, which meant the draft sat in a box,
   inside a box, on a page: three nested rectangles before you reach the words.
   The container is gone and the edge stayed, because the edge is the part that
   carries meaning. */
.proposal {
  /* The whole block is the measure, edge included, so the draft ends on the
     same x as the composer under it and the message above it. Measuring from
     inside the padding put its right edge 23px past both. */
  max-width: var(--measure);
  margin: 0 0 30px;
  padding-left: 20px;
  border-left: 3px solid var(--machine);
}
.proposal :is(.proposal-label, #sent_body, .read, .hint, .reject) { max-width: 100%; }

/* Colour alone said whose block this was, which is nothing to a reader who
   cannot see it and not much to one who can. The words say it now. */
.proposal-label {
  font-family: var(--body);
  font-size: var(--t-sm);
  font-weight: 600;
  line-height: 1.55;
  letter-spacing: 0;
  color: var(--machine);
  margin: 0 0 10px;
}
.proposal-label .when { font-weight: 400; color: var(--muted); }
.proposal-label .when { color: var(--muted); font-weight: 400; }

/* The one state that decides whether this product feels alive. A model call
   takes several seconds, and until this existed the screen said "actualize
   daqui a alguns segundos" and left her to it. */
/* The wait wears the shape of the thing it is waiting for: three lines at the
   lengths a reply runs to, inside the same frame, so nothing moves when the
   text lands. A centred sentence in an empty rail advertised the wait instead
   of absorbing it. */
.skeleton { display: grid; gap: 12px; padding: 15px 17px; border: 1px solid var(--rule); background: var(--raised); }
.skeleton span {
  height: 0.72rem;
  background: linear-gradient(90deg, var(--rule) 0%, var(--paper) 50%, var(--rule) 100%);
  background-size: 300% 100%;
  animation: skeleton-sweep 1.6s ease-in-out infinite;
}
.skeleton span:nth-child(2) { animation-delay: 0.12s; }
.skeleton span:nth-child(3) { animation-delay: 0.24s; }
@keyframes skeleton-sweep {
  from { background-position: 150% 0; }
  to   { background-position: -150% 0; }
}

/* The blanket reduced-motion rule below snaps every animation to its last
   frame, which here leaves three lines mid-sweep. Hold them flat instead. */
@media (prefers-reduced-motion: reduce) {
  .skeleton span { animation: none; background: var(--rule); }
}

/* Roof lines drawing themselves while the Assistant writes. The second starts
   once the first is under way, as the mark's two lines do. Scoped to the two
   waiting states, so the partial dropped anywhere else draws nothing. */
.telhas { display: none; }
:is(.consult-waiting, .proposal.waiting) .telhas { display: block; width: 96px; height: auto; margin: 0 0 8px; overflow: visible; }
.telhas path {
  fill: none; stroke: var(--machine); stroke-width: 2.4; stroke-linejoin: miter;
  stroke-dasharray: 1; stroke-dashoffset: 1;
  animation: telhas-draw 2.4s cubic-bezier(.45, 0, .25, 1) infinite;
}
.telhas path + path { animation-delay: 0.3s; }
@keyframes telhas-draw {
  0%       { stroke-dashoffset: 1; }
  45%, 75% { stroke-dashoffset: 0; }
  100%     { stroke-dashoffset: -1; }
}
/* Its last frame is erased, so the blanket rule would hide it. Hold it drawn. */
@media (prefers-reduced-motion: reduce) {
  .telhas path { animation: none; stroke-dashoffset: 0; }
}


/* --- forms -------------------------------------------------- */

label { display: block; font-size: var(--t-sm); font-weight: 500; color: var(--ink-soft); margin-bottom: 6px; }

input[type="text"], input[type="email"], input[type="url"], input[type="tel"], input[type="password"], input[type="file"], input[type="date"], input[type="number"], select {
  width: 100%;
  min-height: 44px;
  padding: 9px 11px;
  font: inherit;
  font-size: 1rem;
  color: var(--ink);
  background: var(--raised);
  /* Not --rule. A field's fill matches the card behind it, so the border is
     the only thing marking the control, and --rule against --raised is 1.10:1
     where WCAG wants 3:1. --muted is 5.30:1 on --raised and 5.17:1 in dark. */
  border: 1px solid var(--muted);
  border-radius: 0;
}

/* A select has to look like one. The chevron is drawn rather than left to the
   platform, because the platform's own control ignores the border and type
   here, and a field with no affordance reads as a disabled text box. */
/* Drawn as two strokes. As a solid filled triangle it was pixel-for-pixel the
   shape the platform draws, so a control that is in fact fully restyled read
   as one nobody had touched. */
select { appearance: none; padding-right: 40px; }

/* The native picker draws its own calendar glyph and picks its colours from
   color-scheme rather than from ours, so without this it renders a dark icon
   on a dark field and disappears. */
input[type="date"] { color-scheme: light dark; }

/* The browser draws its own "Choose file" button inside the control and picks
   its colours from the platform, so the one styled select and the one native
   grey button sat side by side in the same row. */
input[type="file"]::file-selector-button {
  font: inherit;
  font-weight: 600;
  color: var(--ink);
  background: transparent;
  border: 1px solid var(--muted);
  border-radius: 0;
  padding: 4px 12px;
  margin-right: 12px;
  cursor: pointer;
}
input[type="file"]::file-selector-button:hover { border-color: var(--ink); }

.select { position: relative; display: block; }
.select::after {
  content: "";
  position: absolute;
  right: 17px;
  top: 50%;
  width: 8px;
  height: 8px;
  border-right: 2px solid var(--ink);
  border-bottom: 2px solid var(--ink);
  transform: translateY(-70%) rotate(45deg);
  pointer-events: none;
}

/* In forced-colors, background-image computes to none unless it holds a url(),
   so the drawn chevron vanishes and appearance: none has already taken the
   platform's own away. Hand the control back for the people who need the
   affordance most. */
/* In forced-colors a drawn chevron is a border colour the user has overridden,
   so hand the platform's own control back to the people who need the
   affordance most. */
@media (forced-colors: active) {
  select { appearance: auto; padding-right: 11px; }
  .select::after { display: none; }
}

input:focus-visible, select:focus-visible, textarea:focus-visible,
button:focus-visible, a:focus-visible, [tabindex]:focus-visible {
  outline: 2px solid var(--human);
  outline-offset: 2px;
}

.field { margin-bottom: 16px; }

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: 44px;
  padding: 10px 20px;
  font: inherit;
  font-weight: 600;
  color: var(--paper);
  background: var(--ink);
  border: 1px solid var(--ink);
  border-radius: 0;
  cursor: pointer;
  text-decoration: none;
  transition: background 160ms cubic-bezier(0.22, 1, 0.36, 1), color 160ms cubic-bezier(0.22, 1, 0.36, 1);
}
.btn:hover { background: var(--human); border-color: var(--human); }
.btn.quiet { background: transparent; color: var(--ink); border-color: var(--muted); }
.btn.quiet:hover { border-color: var(--ink); background: transparent; }

/* A submit that is not a button. Recording that she already sent something, or
   signing out, is not a peer of the one action the screen exists for, and three
   controls in descending border weight is the shape every generated interface
   has. One button, then words. */
.linkish {
  appearance: none;
  background: none;
  border: 0;
  padding: 0 2px;
  font: inherit;
  font-size: var(--t-sm);
  color: var(--ink-soft);
  min-height: 44px;
  display: inline-flex;
  align-items: center;
  text-decoration: none;
  text-underline-offset: 3px;
  cursor: pointer;
}
.linkish:hover { color: var(--ink); text-decoration: underline; text-decoration-color: var(--machine); }
.account form { display: inline; }

/* Repeated inline styles, lifted into the stylesheet so a spacing decision
   lives in one place instead of five views. */
.narrow { max-width: 26rem; }
.narrow-form { max-width: 34rem; margin: 26px 0 28px; }

/* Cancel is a way out, not a peer of the thing you came to do. */
.cancel { color: var(--ink-soft); font-size: var(--t-sm); }
.note.tight { margin: 0 0 18px; }

.actions { display: flex; gap: 12px; flex-wrap: wrap; align-items: center; margin-top: 22px; }
.actions.top { margin-top: 0; margin-bottom: 28px; }

/* Six fields stacked full width pushed the preview off the screen, and the
   preview is the thing she is here to read. */
.pairs {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(230px, 1fr));
  gap: 0 20px;
}

/* The remember-me row and the forgot-password link are the two controls that
   would otherwise sit at about 23px. */
.checkline { display: flex; align-items: center; gap: 10px; min-height: 44px;
  text-transform: none; font-size: 0.92rem; color: var(--ink); margin: 0; }
.checkline input { width: auto; min-height: 0; }
/* The rule above ties with the drawn checkbox's 20px and comes later, so the
   box collapsed to its 2px border. */
.checkline input[type="checkbox"] { width: 20px; flex: none; }

.hint { display: block; margin-top: 5px; font-size: var(--t-sm); color: var(--muted); }

.errors { margin: 0 0 18px; padding: 14px 16px 14px 32px; border-left: 3px solid var(--wait);
  background: var(--wait-lo); font-size: 0.9rem; }
.errors ul { margin: 0; padding-left: 0; list-style: disc; }

/* --- the proposal: one action, then everything else quieter ------ */

/* The draft is what she came to read, so it gets the reading measure, the
   size, and the only strong border on the screen. */
/* Measured in characters, because that is what she is reading. It ran to about
   95 a line against a comfortable 45 to 75, on the one text on the screen she
   has to proofread word by word before it goes to a client. */
#sent_body {
  width: 100%;
  max-width: var(--measure);
  min-height: 7.5rem;
  /* Grows with what it holds. At 390px a fixed four rows cut the last line of
     the draft in half, on the one screen the product is for. field-sizing is
     the native version; autogrow() in application.js covers the browsers that
     do not have it yet. */
  field-sizing: content;
  padding: 14px 16px;
  font-size: 1.18rem;
  line-height: 1.65;
  border-color: var(--muted);
  background: var(--raised);
  cursor: text;
}
#sent_body:focus-visible { outline: 2px solid var(--machine); outline-offset: 2px; }

.hint { margin: 10px 0 0; max-width: var(--measure); font-size: var(--t-sm); color: var(--muted); }

/* What it read, so a price in the draft can be checked against the listing it
   came from. This is the only independent signal she has that a number was not
   invented, which is why it sits with the draft and not in a panel of its own. */
/* Stacked in the rail beside the draft, so a price in the reply can be read
   against the listing it came from without either of them moving. */
.read-label { margin: 0 0 10px; color: var(--muted); }
.listings { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 18px; }
.listing { display: grid; gap: 3px; line-height: 1.4; }
.listing img {
  width: 100%;
  height: auto;
  max-height: 210px;
  aspect-ratio: 3 / 2;
  object-fit: cover;
  display: block;
  margin-bottom: 7px;
}

/* Shot for daylight, it glared out of the dark page at full brightness. */
@media (prefers-color-scheme: dark) {
  .listing img, .first-run-art { filter: brightness(0.82) saturate(0.9); }
}
.listing-text { display: grid; gap: 2px; }
.listing-text strong { font-weight: 600; }
.listing-price { color: var(--ink); font-weight: 600; }
.listing-where { color: var(--muted); }
/* Follows the listing it opens. Pushed to the far edge of the block it floated
   in the middle of nothing, which was survivable while the block had a border
   around it and is not now that the border is gone. */
/* 44px tall so it is a real target. As twenty pixels of underlined text at the
   far right of a row it was 590px from the thumbnail it belonged to. */
.listing-link { display: inline-flex; align-items: center; min-height: 44px; }

/* Discarding sits on the action row rather than orphaned below it, and looks
   like something you can press. As plain body text three hundred pixels down it
   read as a caption. */
/* Copy, then the thing that follows copying, then the way out. Discarding
   sits at the far end rather than a thumb's width from the two she means to
   press. */
.decide { display: flex; align-items: center; gap: 14px; flex-wrap: wrap; margin: 16px 0 0; max-width: 100%; }
/* Apart from the two buttons, not banished. Pushed to the far right margin it
   became the hardest thing on the screen to find; wrapped onto its own line it
   was indented off the grid the buttons sit on. */
.decide .aside { margin-left: 10px; }
@media (max-width: 30rem) {
  .decide { gap: 6px 14px; }
  .decide .aside { margin-left: 0; }
}

.aside summary {
  cursor: pointer;
  font-size: var(--t-sm);
  color: var(--ink-soft);
  min-height: 44px;
  display: inline-flex;
  align-items: center;
  text-decoration: underline;
  text-decoration-color: var(--muted);
  text-underline-offset: 3px;
  list-style: none;
}
.aside summary::-webkit-details-marker { display: none; }
.aside summary:hover { color: var(--ink); text-decoration-color: var(--ink); }
.aside summary:focus-visible { outline: 2px solid var(--human); outline-offset: 2px; }
/* Shown by the summary above it, with a sibling selector rather than script.
   details/summary is native, so this collapses with or without JavaScript and
   nobody sees a discard field they did not ask for. */
.reject { display: none; gap: 10px; flex-wrap: wrap; align-items: center;
          max-width: 62ch; margin: 14px 0 0; }
.proposal:has(details.aside[open]) .reject { display: flex; }
.reject input[type="text"] { flex: 1; min-width: 12rem; }

.btn.is-done { background: var(--human); border-color: var(--human); }

.quiet-note { color: var(--muted); margin: 0 0 22px; }

/* Not Telha, which on this screen means the Assistant wrote something worth
   reading, and not amber, which means check this. */
.proposal.failed { border-left-color: var(--fail); }
.proposal.failed .proposal-label { color: var(--fail); }
.failed-why { margin: 0 0 16px; color: var(--ink); font-size: 1.1rem; max-width: 100%; }
/* The conversation page uses .failed-why with no reason under it. */
.failed-why:has(+ .failed-reason) { margin-bottom: 4px; }
.failed-reason { margin: 0 0 16px; color: var(--ink-soft); max-width: var(--measure); }

/* One composer, on one line where there is room, instead of a card. */
/* One control, one outline. It was a bordered field beside a bordered button
   under a bordered draft, three rectangles at three widths, and the outlined
   button pulled almost as hard as the primary one it sits below. Now the
   border belongs to the pair and the send is a word inside it. */
.composer {
  display: flex;
  align-items: stretch;
  max-width: var(--measure);
  margin: 0 0 26px;
  background: var(--raised);
  border: 1px solid var(--muted);
}
.composer:focus-within { outline: 2px solid var(--human); outline-offset: 2px; }
.composer-label { max-width: var(--measure); }
.composer.is-busy { border-color: var(--rule); }
.composer.is-busy .btn {
  background: none;
  border-color: transparent;
  color: var(--muted);
  cursor: default;
}
.composer.is-busy textarea { color: var(--muted); }
/* Two rows, not four. Filled navy the send is a real control again, and at the
   height a four-row field gave it that block outweighed the copy button it
   sits under, which is the one she came for. */
.composer textarea { flex: 1; border: 0; background: none; min-height: 3.2rem; }
.composer textarea:focus-visible { outline: none; }
/* A key on the end of the field. Filled in ink it was a second primary button
   and in dark it outshouted the copy button above it; filled in blue it was
   a third action colour, and the same label wore two of them on two screens.
   Surface-derived, it ranks below the primary in both themes without inventing
   a hue. The negative margin puts it over the wrapper's border so the edge
   does not stop mid-run. */
.composer .btn {
  flex: none;
  margin: -1px;
  border: 1px solid var(--muted);
  background: var(--raised);
  color: var(--ink);
  font-size: var(--t-sm);
  padding: 10px 18px;
}
.composer .btn:hover { background: var(--paper); }
/* At the top, because at the bottom it sat under the composer and fell off the
   end of a phone screen. */
/* 8px rather than 14px because the link below adds 6px of padding under
   itself, and the two together are the gap this had before. */
.back-top { margin: 0 0 8px; font-size: var(--t-sm); }
/* The link is its own paragraph rather than words inside a sentence, so the
   inline exception in WCAG 2.5.8 does not cover it. At this size it was 17px
   tall against a 24px floor, which on a phone is a link she has to aim at.
   The negative top margin gives back the space the padding adds above, so the
   text stays where it was. */
.back-top a {
  display: inline-block;
  padding: 6px 0;
  margin-top: -6px;
}

/* Stacked, the send lost its dividing edge and became bare bold text floating
   inside the field's border. It keeps a top edge and a fill instead. */
@media (max-width: 34rem) {
  .composer { flex-direction: column; align-items: stretch; }
  .composer .btn { justify-content: center; }
}

/* --- the thread -------------------------------------------- */

/* Her turns and his turns are both plain paper, because both are people
   talking. Only the Assistant's proposal gets the Telha edge, and that is the
   point: this screen is where the landing page's argument has to be true. */
/* Tight inside a turn, loose between the thread and the proposal. Every gap on
   the page used to be within a few pixels of every other, so nothing read as
   belonging to anything. */
.thread { margin: 0 0 32px; }

/* No box. Space and a name do the separating; a border around every block made
   the border stop meaning anything. */
.turn { padding: 0; margin: 0 0 14px; }
.turn.outbound { padding-left: 14px; border-left: 2px solid var(--rule); }

.turn-who {
  margin: 0 0 3px;
  font-size: var(--t-sm);
  color: var(--muted);
  display: flex;
  gap: 10px;
}
.turn-body {
  margin: 0;
  white-space: pre-wrap;
  overflow-wrap: anywhere;
  max-width: var(--measure);
  /* Full ink, near the draft's size. It is the question the whole screen exists
     to answer, and as grey body text under a bordered, magenta-bracketed reply
     it was the weakest thing on the page. */
  font-size: 1.1rem;
  color: var(--ink);
}

.signature { margin: 0 0 6px; font-size: 0.84rem; color: var(--muted); }

.run { margin: 18px 0 0; font-size: 0.78rem; color: var(--muted); }

/* --- the conversation list ---------------------------------- */

/* One rule between rows instead of a box around each. Every row being its own
   bordered card made the border stop meaning anything, and the rows were
   different heights depending on how far the preview wrapped. */
.row {
  display: grid;
  /* Fixed, not max-content. The badges are different widths, so every row
     started its preview at a different x. */
  grid-template-columns: 14rem 1fr auto;
  gap: 22px;
  align-items: center;
  padding: 15px 4px;
  border-bottom: 1px solid var(--rule);
  color: var(--ink);
  text-decoration: none;
  min-height: 62px;
}
.row:first-of-type { border-top: 1px solid var(--rule); }
.row:hover { background: var(--raised); }
/* Bottom margin because a list is followed by the next stage's heading on the
   pipeline, and the last row's rule was landing against it. */
.rows { list-style: none; margin: 0 0 28px; padding: 0; }
.row-tag:empty { display: none; }
/* Grid, not flex. Inline after names of four different lengths, the pills
   started at four different x positions down a column that is otherwise a
   table. */
.row-who { display: grid; grid-template-columns: 1fr auto; align-items: baseline; gap: 8px; font-weight: 600; }
.rail .row-who { grid-template-columns: auto; grid-auto-flow: column; justify-content: start; }
.row-last { color: var(--muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.row-when { font-size: var(--t-sm); color: var(--muted); text-align: right; }

.count {
  font-family: var(--body);
  font-size: 0.9rem;
  font-weight: 500;
  color: var(--muted);
  vertical-align: middle;
  margin-left: 6px;
}

@media (max-width: 42rem) {
  .row { grid-template-columns: 1fr auto; gap: 4px 12px; align-items: baseline; }
  .row-last { grid-column: 1 / -1; white-space: normal; }
}

textarea {
  width: 100%;
  min-height: 44px;
  padding: 9px 11px;
  font: inherit;
  font-size: 1rem;
  line-height: 1.5;
  color: var(--ink);
  background: var(--raised);
  border: 1px solid var(--muted);
  border-radius: 0;
  /* none, not vertical. The diagonal handle is the one control on the screen
     nobody styled, and it announces itself as a browser default next to
     borders that were all set by hand. */
  resize: none;
}

/* --- tables ------------------------------------------------- */

/* The attribution under a table it covers. Without the space below it, the
   next heading sat on the line and read as part of the source. */
.source-line { margin: 6px 0 26px; }

/* Which of the three kinds of price this is, under the number it labels. An
   advertised price and a completed sale look alike in a column of figures, so
   the label rides in the same cell, quieter than the number it qualifies. */
.price-kind { display: block; font-size: var(--t-sm); color: var(--muted); }

/* The report is text she pastes into the listing group, so its line breaks are
   the document and not incidental formatting. Set in the reading face rather
   than a monospace one: nothing in it is code. */
.report { font: inherit; white-space: pre-wrap; margin: 0; }

/* Positioned so a .sr label in a header cell is clipped with the table instead
   of widening the page. */
.scroller { overflow-x: auto; position: relative; }
/* Without a floor the table squeezes to the viewport instead of overflowing,
   so at 390px every cell wraps and the last column still gets clipped. Give it
   a width and let the scroller do the job it exists for. */
.scroller table { min-width: 34rem; }

table { width: 100%; border-collapse: collapse; font-size: 0.9rem; }
th, td { text-align: left; padding: 9px 12px; border-bottom: 1px solid var(--rule); vertical-align: top; }
th {
  font-size: 0.72rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--muted);
  font-weight: 600;
  white-space: nowrap;
}
.num { font-variant-numeric: tabular-nums; }
td.num { color: var(--muted); white-space: nowrap; }
tbody tr:last-child td { border-bottom: 0; }

.missing { color: var(--muted); font-style: italic; }

/* No opacity. Three quarters of --muted lands around 3.2:1 on the page, and a
   placeholder is text somebody has to read. */
::placeholder { color: var(--muted); opacity: 1; }

/* button_to renders a form, and a block form in a table cell pushes its button
   off the row's baseline. The 44px target stays; only the label moves up to sit
   on the first line of the row, where every other cell starts. */
td form { margin: 0; }
td .linkish { align-items: flex-start; padding-top: 1px; }

/* --- states ------------------------------------------------- */

.tag {
  display: inline-block;
  border-radius: 999px;
  /* "procura arrendamento" wrapped inside the pill and took the client's name
     onto two lines with it, because both sit in the same grid cell. */
  white-space: nowrap;
  padding: 2px 10px;
  font-size: var(--t-xs);
  letter-spacing: 0.06em;
  text-transform: uppercase;
  font-weight: 600;
  border: 1px solid currentColor;
}
.tag { text-transform: none; letter-spacing: 0; font-weight: 500; }

/* Three pills in three hues read as decoration, because Telha, amber and
   blue are not an order. One filled pill means she is needed. One outline
   means the Assistant is still working. Answered is the resting state and
   wears nothing at all. */
.tag.pending { color: var(--paper); background: var(--machine); border-color: var(--machine); }
.tag.writing { color: var(--muted); border-color: var(--rule); }
.tag.failed { color: var(--fail); border-color: var(--fail); }
/* Answered is where a conversation ends up, so it is the one state that does
   not need marking. As a fourth outline pill it made a four-style set for one
   attribute, and nothing in the shapes said which of the four mattered. */
.tag.done { border-color: transparent; padding-left: 0; padding-right: 0; color: var(--muted); }

.note {
  border-left: 3px solid var(--wait);
  background: var(--wait-lo);
  padding: 14px 16px;
  margin: 0 0 20px;
  font-size: 0.9rem;
}

.tally { display: flex; gap: 28px; flex-wrap: wrap; margin: 0 0 4px; padding: 0; list-style: none; }
.tally div { font-variant-numeric: tabular-nums; }
.tally b { font-family: var(--display); font-size: 1.5rem; font-weight: 500; display: block; }
.tally span { font-size: 0.78rem; color: var(--muted); text-transform: uppercase; letter-spacing: 0.07em; }

.empty { color: var(--muted); }

/* The first screen a new customer sees, and the only one she sees on day one.
   A Pombalino facade shot flat on, the buildings she actually sells, rather
   than a sunlit living room or a set of keys on a table. */
.first-run { max-width: var(--measure); padding: 8px 0 0; }
.first-run-art { display: block; width: 100%; height: auto; margin: 0 0 24px; }
/* The photo's whites are cold beside Cal. Multiplied onto the paper they take
   its warmth, and the stone keeps its own greys. */
@media (prefers-color-scheme: light) { .first-run-art { mix-blend-mode: multiply; } }
.first-run-lead {
  font-family: var(--display);
  font-weight: 300;
  font-size: 1.75rem;
  letter-spacing: -0.015em;
  margin: 0 0 8px;
}
.first-run-body { color: var(--ink-soft); margin: 0 0 20px; }
.first-run-do { display: flex; align-items: center; gap: 18px; flex-wrap: wrap; margin: 0; }

.account { display: flex; align-items: baseline; gap: 18px; font-size: var(--t-sm); }
.account span { color: var(--ink-soft); }

/* The service checklist. A row is a tick, a code, a name and a responsável,
   and the next open step is the only one that needs finding at a glance. */
.steps { list-style: none; margin: 0; padding: 0; }
.steps li {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 7px 0;
  border-top: 1px solid var(--rule);
}
.steps li:first-child { border-top: 0; }
.steps .step, .steps .step-owner { display: flex; margin: 0; }
.steps .step-owner { margin-left: auto; }
.steps .step-owner select { width: auto; min-width: 11rem; font-size: var(--t-sm); }
.steps .step-code { color: var(--muted); font-size: var(--t-sm); font-variant-numeric: tabular-nums; }
/* 44px like .star, and for the same reason: it is the smallest thing in the
   row and it is the one a thumb has to find. The circle is drawn at 28px
   inside it, so the target is bigger than the mark.

   --muted and not --rule for the border. --rule is 1.36:1 on the raised paper
   and 2.0:1 on navy, which is fine for a divider and well under the 3:1 a
   control's boundary needs, and an unticked step is most of this list. */
.tick {
  width: 44px; height: 44px;
  padding: 0;
  border: 0;
  background: none;
  color: var(--ink);
  cursor: pointer;
  line-height: 1;
  display: grid;
  place-items: center;
}
.tick > span {
  width: 28px; height: 28px;
  border: 1px solid var(--muted);
  border-radius: 999px;
  display: grid;
  place-items: center;
}
.tick:hover > span { border-color: var(--machine); }
.steps li.done .step-name { color: var(--muted); text-decoration: line-through; }
.steps li.done .tick > span { border-color: var(--machine); }
/* Hung off aria-current, not a class, so the marker and the announcement
   cannot drift apart. */
.steps li[aria-current="step"] { box-shadow: inset 2px 0 0 var(--machine); padding-left: 8px; }

/* On a phone the responsável select was taking nearly half the row, which
   pushed "Documentos e estudo de mercado antes da visita" onto three lines.
   The name gets the width and the select goes under it. */
@media (max-width: 42rem) {
  .steps li { flex-wrap: wrap; }
  .steps .step-name { flex: 1 1 auto; }
    /* Lined up under the name, past the 44px tick and the 10px gap. */
  .steps .step-owner { margin-left: 54px; flex-basis: 100%; }
  .steps .step-owner select { width: 100%; }
}

/* Visible to screen readers only. Carries the severity of an alert, which
   otherwise reaches a sighted reader through colour alone. */
.sr {
  position: absolute;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

@media (prefers-reduced-motion: reduce) {
  * { transition-duration: 0.01ms !important; animation-duration: 0.01ms !important; }
}

/* The operator surface. Internal, so it borrows the app's own table and
   button styles and adds only the banner, which has to be impossible to
   mistake for the customer's own chrome. */
.operator-banner {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem 1rem;
  align-items: center;
  justify-content: space-between;
  padding: 0.6rem 1rem;
  /* Plum, the one hue the brand never uses. It was brick red, which next to
     Telha would read as part of her screen. */
  background: #4E1F55;
  color: #fff;
  font-size: 0.9rem;
}

.operator-banner .linkish { color: #fff; text-decoration: underline; }

ul.plain { margin: 0; padding: 0; list-style: none; }
.enter { display: flex; flex-direction: column; gap: 0.35rem; min-width: 10rem; }

/* The ficha de visita, filled on a phone in somebody's hallway. Four options
   on one line per criterion, so seven criteria are seven rows and not
   twenty-eight. 44px targets because it is answered with a thumb. */
.scale {
  display: flex;
  flex-wrap: wrap;
  gap: 0 1.1rem;
}

.scale-option {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  min-height: 44px;
  cursor: pointer;
}

.scale-option input { margin: 0; }

.field-label {
  display: block;
  font-weight: 500;
  margin-bottom: 0.2rem;
}

/* Comparing five visits. Four dots read as a level at a glance where four
   words read as a sentence you have to finish. */
.compare th[scope="col"] { font-size: 0.72rem; line-height: 1.3; min-width: 8rem; }
.compare th[scope="col"] .missing { display: block; font-weight: 400; text-transform: none; letter-spacing: 0; }
.compare th[scope="row"] { font-size: 0.78rem; white-space: nowrap; padding-right: 1rem; }
.compare .open-answer td { font-size: 0.85rem; min-width: 10rem; }

.rating { display: inline-flex; gap: 3px; align-items: center; }
.rating i {
  width: 9px;
  height: 9px;
  border-radius: 50%;
  border: 1px solid var(--rule);
  background: transparent;
}
.rating i.on { background: var(--ink); border-color: var(--ink); }

.compare th[scope="col"] .pending {
  display: block;
  font-weight: 400;
  text-transform: none;
  letter-spacing: 0;
  color: var(--machine);
}

/* Meetings. What each one was is a row of plain buttons rather than a select,
   because the correction is one tap and a select is two. The chosen one is
   tinted blue when she chose it and Telha while it is the Assistant's
   guess: the same --human and --machine split the rest of the app makes. */
.meeting { max-width: var(--measure); }
.choices { border: 0; padding: 0; min-width: 0; margin: 0 0 24px; }
.choices legend { padding: 0; display: flex; gap: 10px; align-items: baseline; margin-bottom: 10px; }
.choice-row { display: flex; flex-wrap: wrap; gap: 8px; }
.choice-form { display: contents; }
.choice {
  min-height: 44px;
  padding: 10px 16px;
  font: inherit;
  color: var(--ink);
  background: transparent;
  /* --muted rather than --rule: at --rule the edge was 1.9:1 in the dark and
     the choices stopped reading as buttons. Same edge as .btn.quiet. */
  border: 1px solid var(--muted);
  border-radius: 0;
  cursor: pointer;
}
.choice:hover { border-color: var(--ink); }
.choice[aria-pressed="true"] { background: var(--human-lo); border-color: var(--human); font-weight: 600; }
.choice.is-guess[aria-pressed="true"] { background: var(--machine-lo); border-color: var(--machine); }
.guess { font-size: var(--t-sm); font-weight: 500; color: var(--machine); }
td .hint.guess { margin-top: 2px; }

.briefing { margin-top: 8px; }
.prose :is(h1, h2, h3) { font-size: 1.05rem; margin: 22px 0 6px; }
.prose > :first-child { margin-top: 0; }
.prose :is(ul, ol) { padding-left: 1.2em; }
.prose table { font-size: var(--t-sm); }

.recording { margin: 0 0 14px; border-top: 1px solid var(--rule); padding-top: 12px; }
.recording summary { cursor: pointer; font-weight: 600; padding: 10px 0; }
.recording[open] summary { margin-bottom: 6px; }
.transcript { font-size: var(--t-sm); color: var(--ink-soft); }

/* On a phone the four columns clipped mid-word inside the scroller, so each
   row becomes two lines: when and who, then what it was and the briefing. */
@media (max-width: 40rem) {
  .scroller .meetings-table { min-width: 0; }
  .meetings-table thead { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); }
  .meetings-table tr { display: grid; grid-template-columns: auto 1fr; column-gap: 12px; padding: 10px 0; border-bottom: 1px solid var(--rule); }
  .meetings-table td { display: block; border: 0; padding: 2px 0; }
  /* The date is the way into a meeting, and at text height it was a 17px
     target beside the contact link. The padding widens the hit area and the
     negative margin keeps the row the same height. */
  .meetings-table td a { display: inline-block; padding: 12px 0; margin: -12px 0; }
}

/* Especialistas: a case file, not a chat. The index is the question box
   over a ledger of every thread; a thread is its answer, with what it
   consulted beside it. Blue is what she did, Telha the specialist's
   voice and nothing else. */
.consult-box { max-width: 46rem; margin: 16px 0 32px; background: var(--raised); border: 1px solid var(--muted); }
.consult-box:focus-within { border-color: var(--human); box-shadow: 0 0 0 1px var(--human); }
.consult-to { display: flex; align-items: center; gap: 8px; margin: 0; padding: 8px 12px; border-bottom: 1px solid var(--rule);
  color: var(--human); font-weight: 600; }
.consult-to select { width: auto; min-width: 12rem; border-color: transparent; color: var(--human); font-weight: 600; background-color: transparent; }
.consult-box textarea { display: block; width: 100%; border: 0; background: none; min-height: 5.5rem; }
.consult-box textarea:focus-visible { outline: none; }
/* What the chosen specialist is for, kept in view while she types. */
.consult-to:has(+ .consult-about:not(:empty)) { border-bottom: 0; padding-bottom: 0; }
.consult-about { margin: 0; padding: 2px 12px 8px; border-bottom: 1px solid var(--rule); font-size: var(--t-sm); color: var(--ink-soft); }
.consult-about:empty { display: none; }
.consult-send { display: flex; flex-wrap: wrap; justify-content: space-between; align-items: center; gap: 8px 12px; padding: 0 12px 12px; }
/* The file goes with the question, so it sits by Perguntar, quieter than it. */
.consult-file { display: inline-flex; flex-wrap: wrap; align-items: center; gap: 4px 8px; min-height: 44px; font-size: var(--t-sm); color: var(--ink-soft); }
.consult-file > span { display: inline; margin: 0; }
.consult-file input[type="file"] { width: auto; max-width: 100%; min-height: 0; padding: 0; border: 0; background: none; font-size: var(--t-sm); color: var(--ink-soft); }
.consult-file-hint { margin: 0; padding: 0 12px 12px; }

.ledger-question a { color: var(--ink); }
.thread-busy { color: var(--machine); }
.thread-failed { color: var(--fail); }

.back { margin: 0 0 8px; font-size: var(--t-sm); }
.case-title { font-size: 1.5rem; }
.turns { list-style: none; margin: 24px 0; padding: 0; display: flex; flex-direction: column; gap: 24px; }
.turn-user p { margin: 0; max-width: var(--measure); color: var(--human); font-weight: 600; }
.turn-user .turn-who { font-weight: 400; color: var(--muted); }
/* The sources sit against the answer's text, not the column's edge. */
.turn-assistant { display: grid; grid-template-columns: minmax(0, calc(var(--measure) + 23px)) 15rem; gap: 32px; align-items: start; }
.turn-answer { padding-left: 20px; border-left: 3px solid var(--machine); }
.turn-head { display: flex; align-items: baseline; gap: 16px; margin-bottom: 8px; }
.turn-head .turn-who { font-size: var(--t-sm); font-weight: 600; color: var(--machine); }
.turn-head .linkish[hidden] { display: none; }
.turn-body { font-size: 1.0625rem; line-height: 1.6; }
.turn-body :is(p, ul) { margin: 0 0 12px; max-width: var(--measure); }
/* disc, not the circle a browser gives a list nested in the thread ol */
.turn-body ul { padding-left: 1.2em; list-style: disc; }
.turn-body li + li { margin-top: 4px; }
.turn-body > :last-child { margin-bottom: 0; }
/* Her answer to its question, so blue on hover like everything she picks. */
.turn-options-lead { margin: 16px 0 8px; font-size: var(--t-sm); color: var(--ink-soft); }
.turn-options { max-width: var(--measure); }
.turn-options .choice:hover { border-color: var(--human); color: var(--human); }
.turn-sources { font-size: var(--t-sm); color: var(--ink-soft); padding-top: 4px; }
.turn-sources h2 { margin: 0 0 6px; font-size: var(--t-sm); color: var(--muted); font-weight: 600; }
.turn-sources ul { margin: 0; padding: 0; list-style: none; }
.turn-sources li { padding: 4px 0; border-bottom: 1px solid var(--rule); }
.turn-sources p { margin: 0; color: var(--muted); }
.turn-sources .source-links { margin: 2px 0 4px; padding-left: 0; }
/* A source with nothing to open is a plain note, not a link that failed. */
.turn-sources li.no-links { color: var(--muted); }
.turn-sources .source-links li { padding: 0; border: 0; }
/* A full tap tall on a phone, without spreading the list on a desk. */
.turn-sources .source-links a { display: inline-flex; align-items: center; min-height: 44px; margin-block: -10px; color: var(--ink); }
/* Comparar: one question, two answers read side by side, nothing saying
   which model wrote which. */
.compare { margin-top: 24px; }
.compare-count { margin: 0 0 12px; font-size: var(--t-sm); color: var(--muted); font-variant-numeric: tabular-nums; }
.compare-question { margin: 0 0 24px; max-width: var(--measure); color: var(--human); font-weight: 600; }
.compare-question .turn-who { display: block; font-size: var(--t-sm); font-weight: 400; color: var(--muted); }
.compare-pair { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 32px; align-items: start; }
.compare-side h2.turn-who { margin: 0 0 8px; font-size: var(--t-sm); font-weight: 600; color: var(--machine); }
/* Under both answers, not under A: the row votes on the pair. */
.compare-vote { margin-top: 24px; justify-content: center; }
.compare-none { margin-top: 24px; max-width: var(--measure); }
.compare-tally { margin-top: 40px; max-width: 46rem; }
.compare-tally h3 { margin: 20px 0 0; font-size: 1rem; }
@media (max-width: 56rem) {
  .compare-pair { grid-template-columns: minmax(0, 1fr); gap: 24px; }
}
/* The file she sent, under her question, quieter than it. */
.turn-file { margin: 4px 0 0; font-size: var(--t-sm); }
.turn-file .hint { display: inline; margin: 0; }
/* Gerar outra vez sits with Copiar: both act on the answer beside them. */
.turn-head form.regenerate { display: inline; }
/* Editar is a fold under her question, closed, so the thread reads as asked. */
.edit-question { margin: 6px 0 0; max-width: var(--measure); }
/* Title-case question: the meta line and its Editar share a row, and the
   form opens on a row of its own under them. */
.case-meta { display: flex; flex-wrap: wrap; align-items: baseline; gap: 0 20px; margin: 0 0 28px; }
.case-meta .lede { margin: 0; }
.case-meta .edit-question { margin: 0; }
.case-meta .edit-question[open] { flex-basis: 100%; }
.edit-question summary { display: inline-flex; align-items: center; min-height: 44px; cursor: pointer; font-size: var(--t-sm); color: var(--ink-soft); }
.edit-question summary:focus-visible { outline: 2px solid var(--human); outline-offset: 2px; }
.edit-question-form { display: grid; gap: 8px; margin-top: 4px; }
.edit-question-form textarea { width: 100%; }
.edit-question-form .hint { margin: 0; }
.edit-question-form .btn { justify-self: start; }
/* The answer as it is written: the same rule as a saved one, so the words do
   not jump when the page reloads onto it. */
.streaming:not(:empty) { margin: 0 0 16px; padding-left: 20px; border-left: 3px solid var(--machine); }
.streaming-status { margin: 0; color: var(--ink-soft); }
/* A status line, not news: the rule says whose turn it is, the words stay plain. */
.consult-waiting { margin: 0 0 24px; padding-left: 20px; border-left: 3px solid var(--machine); color: var(--ink-soft); }

@media (max-width: 56rem) {
  .turn-assistant { grid-template-columns: minmax(0, 1fr); gap: 12px; }
  .turn-sources { padding-left: 23px; }
}
/* On a phone each thread is two lines: the question, then who and when,
   with its state on the right. */
@media (max-width: 40rem) {
  .scroller table.ledger { min-width: 0; }
  .ledger thead { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); }
  .ledger, .ledger tbody { display: block; width: 100%; }
  .ledger tr { display: grid; grid-template-columns: auto auto 1fr auto; gap: 2px 8px; padding: 12px 0; border-bottom: 1px solid var(--rule); }
  .ledger td { display: block; border: 0; padding: 0; font-size: var(--t-sm); color: var(--muted); }
  .ledger td.ledger-question { grid-column: 1 / -1; font-size: 1rem; }
  .ledger td:nth-child(4) { grid-column: 4; grid-row: 2; text-align: right; }
  .ledger td:nth-child(5) { grid-column: 3; grid-row: 2; }
}

/* Regras e memória: two short lists of sentences, each with its count
   against the cap, and the switched-off ones folded below. */
.notes { max-width: 46rem; margin-top: 32px; }
/* Side by side where there is room: both lists are read together. */
@media (min-width: 72rem) {
  .notes-pair { display: grid; grid-template-columns: 1fr 1fr; gap: 40px; align-items: start; }
  .notes-pair .notes { max-width: none; }
}
.notes-head { display: flex; align-items: baseline; justify-content: space-between; gap: 16px; }
.notes-head h2 { margin: 0; }
.notes-count { font-size: var(--t-sm); color: var(--muted); font-variant-numeric: tabular-nums; }
.note-list { list-style: none; margin: 12px 0 0; padding: 0; border-top: 1px solid var(--rule); }
.note-list li { display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: 2px 16px; align-items: center; padding: 10px 0; border-bottom: 1px solid var(--rule); }
.note-body { grid-column: 1; }
.note-who { grid-column: 1; font-size: var(--t-sm); color: var(--muted); }
.note-list li > form, .note-actions { grid-column: 2; grid-row: 1 / span 2; display: flex; gap: 12px; }
.note-list form.button_to { display: inline; }
.note-new { display: flex; gap: 8px; margin-top: 12px; }
.note-new input[type="text"] { flex: 1; min-width: 0; }
.notes-off { margin-top: 16px; }
.notes-off summary { display: flex; align-items: center; min-height: 44px; cursor: pointer; font-size: var(--t-sm); color: var(--ink-soft); }
/* display: flex drops the browser's marker, so it is drawn back. */
.notes-off summary::before { content: "▸"; width: 1em; }
.notes-off[open] summary::before { content: "▾"; }
.notes-off summary:focus-visible { outline: 2px solid var(--human); outline-offset: 2px; }
:is(.note-actions, .deal-delete) .linkish.quiet-danger { color: var(--muted); text-decoration: none; }
:is(.note-actions, .deal-delete) .linkish.quiet-danger:hover, :is(.note-actions, .deal-delete) .linkish.quiet-danger:focus-visible { color: var(--fail); text-decoration: underline; }

/* A specialist's proposal: its status, what it would change from and to,
   why, and her decision as two equal buttons. A decided one is a record. */
.proposals { display: flex; flex-direction: column; gap: 12px; margin-top: 16px; }
.proposal-card { padding: 12px 16px; background: var(--raised); border: 1px solid var(--rule); }
.proposal-card p { margin: 0; }
.proposal-card .proposal-status { margin-bottom: 4px; font-size: 0.75rem; font-weight: 600; letter-spacing: 0.06em;
  text-transform: uppercase; color: var(--human); }
.proposal-what { font-weight: 600; }
/* Contact, before, after: columns, so the arrows line up down the card. */
.proposal-changes { width: auto; margin: 6px 0 0; border-collapse: collapse; font-size: var(--t-sm); }
.proposal-changes :is(th, td) { padding: 2px 16px 2px 0; border: 0; text-align: left; vertical-align: top; font-weight: 400; }
/* A name, not a column header: undo the table header style. */
.proposal-changes th { color: var(--ink); font-size: inherit; letter-spacing: normal; text-transform: none; white-space: normal; }
/* Values never break; on a phone the name column wraps instead. */
.proposal-changes td { color: var(--muted); white-space: nowrap; }
@media (max-width: 40rem) { .proposal-changes :is(th, td) { padding-right: 10px; } }
/* What it becomes reads first; what it was is grey. */
.proposal-changes td.proposal-to { color: var(--ink); }
.proposal-card .proposal-already { margin-top: 4px; font-size: var(--t-sm); color: var(--muted); }
.proposal-card .proposal-why { margin-top: 4px; font-size: var(--t-sm); color: var(--ink-soft); }
.proposal-card .proposal-source { margin-top: 8px; font-size: var(--t-sm); color: var(--wait); }
.proposal-card .proposal-stale { margin-top: 8px; font-size: var(--t-sm); color: var(--wait); }
.proposal-decide { display: flex; align-items: center; gap: 12px; margin-top: 12px; }
.proposal-decide form.button_to { display: inline; }
.proposal-decide .btn { min-width: 8rem; justify-content: center; }
.proposal-decide .btn.is-yes { color: var(--human); border-color: var(--human); }
.proposal-card .proposal-done { margin-top: 6px; font-size: var(--t-sm); color: var(--muted); }
/* A record: no fill. Dashed, the edge read as a slot to drop something in. */
.proposal-card.is-applied, .proposal-card.is-rejected { background: transparent; }
.proposal-card.is-applied .proposal-status, .proposal-card.is-rejected .proposal-status { color: var(--muted); }
.proposal-card.is-rejected .proposal-what { color: var(--muted); }

/* Configurar especialistas: the list, and a form whose tools are a column of
   checkboxes. */
.specialist-list { list-style: none; margin: 24px 0 0; padding: 0; max-width: 56rem; border-top: 1px solid var(--rule); }
.specialist-list li { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: 4px 24px; padding: 14px 0; border-bottom: 1px solid var(--rule); }
.specialist-list li.is-off { opacity: 0.7; }
.specialist-list .specialist-name { font-weight: 600; }
.specialist-list .specialist-summary { margin: 2px 0 0; font-size: var(--t-sm); color: var(--ink-soft); }
.specialist-tools p { margin: 0 0 2px; font-size: var(--t-sm); color: var(--ink-soft); }
/* The verb is what to scan for, so it carries the weight, not the list. */
.specialist-tools span { color: var(--ink); font-weight: 600; }
.specialist-form { max-width: 40rem; }
.specialist-tool-boxes { border: 0; margin: 0 0 16px; padding: 0; }
.specialist-tool-boxes legend { font-weight: 600; margin-bottom: 8px; }
label.check { display: flex; flex-direction: row; align-items: center; gap: 10px; min-height: 44px; margin: 0; font-weight: 400; }
/* Where a reading tool's answers come from, under its own box and aligned
   with its label, so it does not read as the next one's. */
.specialist-tool-boxes label.check + .hint { margin: -8px 0 10px 30px; }
@media (max-width: 40rem) { .specialist-list li { grid-template-columns: minmax(0, 1fr); } }

/* Newsletter: blocks on the left, the issue as the inbox will draw it on the
   right. The preview is an iframe, so nothing in this sheet reaches it. */
.nl-new { max-width: 40rem; margin: 0 0 32px; }
.nl-list { list-style: none; margin: 16px 0 32px; padding: 0; max-width: 56rem; border-top: 1px solid var(--rule); }
.nl-list li { display: flex; flex-wrap: wrap; align-items: baseline; gap: 4px 16px; padding: 12px 0; border-bottom: 1px solid var(--rule); }
.nl-list form.button_to { margin-left: auto; }
.nl-name { font-weight: 600; }
.nl-meta { margin: 0; font-size: var(--t-sm); color: var(--ink-soft); }
.nl-heading { font-size: 1.15rem; margin: 28px 0 12px; }
.nl-editor { display: grid; grid-template-columns: minmax(0, 26rem) minmax(0, 1fr); gap: var(--gap); align-items: start; margin-top: 16px; }
.nl-meta-form .field input { width: 100%; }
.nl-block-list { list-style: none; margin: 0 0 20px; padding: 0; border-top: 1px solid var(--rule); }
.nl-block { border-bottom: 1px solid var(--rule); }
.nl-block summary { display: flex; gap: 12px; align-items: baseline; min-height: 44px; padding: 10px 0; cursor: pointer; }
.nl-block-type { font-weight: 600; }
.nl-block-gist { color: var(--muted); font-size: var(--t-sm); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.nl-block-form, .nl-keep { margin: 4px 0 16px; }
.nl-block-form :is(input, select, textarea), .nl-keep input { width: 100%; }
.nl-block-tools { display: flex; gap: 16px; margin: 0 0 12px; }
.nl-block-tools form.button_to { display: inline; }
.nl-listings { border: 0; margin: 0 0 16px; padding: 0; }
.nl-listings legend { font-weight: 600; margin-bottom: 4px; }
.nl-listing { border-top: 1px solid var(--rule); padding-top: 8px; margin-top: 8px; }
.nl-listing summary { font-size: var(--t-sm); min-height: 32px; padding: 4px 0; }
.nl-photo-fold > summary { display: flex; align-items: center; gap: 10px; font-size: var(--t-sm); min-height: 44px; padding: 4px 0; cursor: pointer; }
.nl-photo-now { width: 48px; height: 32px; object-fit: cover; border-radius: 3px; flex: none; }
.nl-photo-count { color: var(--muted); }
.nl-photos { border: 0; margin: 0 0 16px; padding: 0; }
/* The summary above already says it; the legend names the group for a screen reader. */
.nl-photos legend { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; }
/* About three rows, then it scrolls; the padding keeps the rings inside the scroll box. */
.nl-photo-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(96px, 1fr)); gap: 8px; max-height: 15rem; overflow-y: auto; padding: 6px; }
/* On a phone a nested scroll box shows two rows at a time; the page scrolls instead. */
@media (max-width: 48rem) { .nl-photo-grid { max-height: none; overflow: visible; } }
.nl-photo { position: relative; display: grid; place-items: center; aspect-ratio: 3/2; border-radius: 4px; outline: 3px solid transparent; outline-offset: 2px; cursor: pointer; overflow: hidden; background: var(--raised); }
.nl-photo img { width: 100%; height: 100%; object-fit: cover; display: block; }
button.nl-photo { border: 0; padding: 0; font: inherit; }
button.nl-photo:focus-visible { outline-color: var(--ink); outline-style: dashed; }
.nl-photo-upload { margin: 12px 0 16px; }
.nl-photo-upload input[type="file"] { min-width: 0; }
.nl-photo-caption { position: absolute; inset: auto 0 0; padding: 2px 6px; font-size: var(--t-xs); color: #F5F0E6; background: rgb(28 43 69 / 0.8); }
/* The radio stays in the form and the tab order; the photo is what shows it. */
.nl-block-form .nl-photo input[type="radio"] { position: absolute; inset: 0; width: 100%; height: 100%; margin: 0; opacity: 0; cursor: pointer; }
.nl-photo:has(:checked) { outline-color: var(--human); }
.nl-photo:has(:checked)::after { content: "✓"; position: absolute; top: 4px; right: 4px; display: grid; place-items: center; width: 22px; height: 22px; border-radius: 50%; background: var(--human); color: #F5F0E6; font-size: 13px; font-weight: 700; box-shadow: 0 0 0 2px var(--paper); }
.nl-photo:has(:focus-visible) { outline-color: var(--ink); outline-style: dashed; }
.nl-add { display: grid; gap: 8px; margin: 0 0 20px; }
.nl-add :is(input[type="text"], select) { width: 100%; }
/* The control and its button on one line, so the button reads as the
   action on that field rather than as a second empty field. */
.nl-add-row { display: flex; gap: 8px; align-items: stretch; }
.nl-add-row > :first-child { flex: 1; min-width: 0; }
.nl-add-row .btn { flex: none; margin: 0; }
.nl-fold { margin: 0 0 20px; }
.nl-fold > summary { min-height: 44px; display: flex; align-items: center; cursor: pointer; color: var(--ink-soft); font-size: var(--t-sm); }
.nl-fold[open] > summary { margin-bottom: 8px; }
/* A row says it opens, and says it closes once it is open. */
.nl-block summary::after { content: "Editar" / ""; margin-left: auto; font-size: var(--t-sm); color: var(--muted); }
.nl-block details[open] > summary::after { content: "Fechar" / ""; }
.nl-preview { position: sticky; top: 16px; }
.nl-preview-head { display: flex; flex-wrap: wrap; align-items: baseline; justify-content: space-between; gap: 12px; }
.nl-preview-head .nl-heading { hyphens: manual; }
.nl-preview-head .nl-heading { margin-top: 0; }
.nl-width { display: flex; gap: 4px; }
.nl-width a { padding: 8px 12px; min-height: 44px; display: inline-flex; align-items: center; border: 1px solid var(--rule); text-decoration: none; font-size: var(--t-sm); }
.nl-width a.is-on { border-color: var(--human); color: var(--human); font-weight: 600; }
/* The iframe is a white page of its own, whatever mode the app is in. */
.nl-frame { display: block; width: 100%; height: 70vh; border: 1px solid var(--rule); background: #fff; margin: 0 auto; }
.nl-frame.is-telemovel { width: 390px; max-width: 100%; }
.nl-copy { margin-top: 16px; }
.nl-copy-label { display: block; font-size: var(--t-sm); color: var(--ink-soft); margin: 8px 0 4px; }
.nl-html { width: 100%; font-family: ui-monospace, Menlo, monospace; font-size: var(--t-xs); }
.nl-locked { border: 0; margin: 0; padding: 0; min-width: 0; }
.nl-send { margin-top: 48px; padding-top: 24px; border-top: 1px solid var(--rule); max-width: 40rem; }
/* Three steps in order, each with room around it, because the last one
   reaches a few hundred people and cannot be taken back. */
.nl-send-step { margin: 0 0 32px; }
.nl-step-title { display: flex; align-items: center; gap: 10px; font-size: 1.05rem; margin: 0 0 12px; }
.nl-step-number { display: inline-grid; place-items: center; width: 1.6rem; height: 1.6rem; border-radius: 50%;
  border: 1px solid var(--muted); font-size: var(--t-sm); font-variant-numeric: tabular-nums; }
.nl-send-count { margin: 12px 0 4px; font-size: 1.1rem; }
.nl-left-out ul { margin: 8px 0 0; padding-left: 18px; }
.nl-tested { color: var(--human); margin: 0 0 8px; }
.nl-recap { display: grid; grid-template-columns: max-content minmax(0, 1fr); gap: 4px 16px; margin: 0 0 16px; }
.nl-recap dt { color: var(--muted); font-size: var(--t-sm); }
.nl-recap dd { margin: 0; overflow-wrap: anywhere; }
.btn.send { background: var(--human); border-color: var(--human); color: var(--paper); }
.btn.send:disabled { opacity: 0.45; cursor: not-allowed; }
.public-unsubscribe .btn { width: 100%; }
@media (min-width: 40rem) { .public-unsubscribe .btn { width: auto; } }
.nl-send-tag { display: inline-flex; align-items: center; gap: 6px; min-height: 44px; margin-right: 16px; }
.nl-send-counts { margin: 8px 0 0; padding-left: 18px; }
.hint-inline { color: var(--muted); font-size: var(--t-sm); }
.nl-theme-form { max-width: 40rem; }
.nl-layout { display: flex; align-items: baseline; gap: 10px; min-height: 44px; }
.nl-layout label { display: flex; flex-wrap: wrap; gap: 4px 8px; margin: 0; flex: 1; }
.nl-layout-name { font-weight: 600; }
.nl-layout-see { font-size: var(--t-sm); }
.nl-layout-see.is-on { font-weight: 600; text-decoration: none; }
.nl-theme-editor { grid-template-columns: minmax(0, 28rem) minmax(0, 1fr); }
.nl-theme-form .field input:not([type="color"]), .nl-theme-form textarea { width: 100%; }
.nl-fieldset { border: 0; margin: 0 0 20px; padding: 0; }
.nl-fieldset legend { font-weight: 600; margin-bottom: 8px; }
.nl-colour { display: flex; align-items: center; gap: 12px; }
.nl-colour label { min-width: 12rem; margin: 0; }
@media (max-width: 64rem) { .nl-editor { grid-template-columns: minmax(0, 1fr); } .nl-preview { position: static; } }
/* Planos: a step is a card with its own small form, and the move, archive and
   restore buttons beside it. */
.plan-steps { list-style: none; margin: 0 0 20px; padding: 0; max-width: 56rem; border-top: 1px solid var(--rule); }
/* The day is the spine: a cadence is read by when each step lands, and the
   gaps between days show the silences. */
.plan-step { border-bottom: 1px solid var(--rule); }
.plan-step summary { display: grid; grid-template-columns: 4.5rem minmax(0, 1fr) auto; gap: 4px 16px; align-items: baseline;
  min-height: 44px; padding: 12px 8px; cursor: pointer; list-style: none; }
.plan-step summary::-webkit-details-marker { display: none; }
.plan-step summary::after { content: "Editar" / ""; font-size: var(--t-sm); color: var(--muted); }
.plan-step details[open] { background: var(--raised); box-shadow: inset 3px 0 0 var(--human); }
.plan-step details[open] > summary::after { content: "Fechar" / ""; }
.plan-step-day { font-variant-numeric: tabular-nums; font-weight: 600; color: var(--ink); white-space: nowrap; }
.plan-step-day.is-guess { color: var(--wait); text-decoration: underline dashed; text-underline-offset: 4px; }
.plan-step-main { display: grid; gap: 2px; min-width: 0; }
.plan-step-name { font-weight: 600; }
.plan-step-meta { font-size: var(--t-sm); color: var(--muted); }
.plan-guessed { margin: 0 0 16px; max-width: 56rem; font-size: var(--t-sm); color: var(--wait); }
.plan-step-body { padding: 4px 8px 20px calc(4.5rem + 24px); }
.plan-step-form { display: grid; grid-template-columns: minmax(8rem, 1fr) 6rem minmax(13rem, 1.6fr) minmax(15rem, 1.6fr); gap: 12px 16px; align-items: end; }
.plan-step-form label { display: flex; flex-direction: column; gap: 4px; color: var(--muted); margin: 0; }
.plan-step-form .plan-step-title, .plan-step-form .plan-step-note, .plan-step-form .actions { grid-column: 1 / -1; }
.plan-step-form .actions { margin: 0; }
.plan-step-actions { display: flex; flex-wrap: wrap; align-items: center; gap: 4px 20px; margin-top: 12px; }
.plan-step-actions form { margin: 0; }
.plan-step-actions .plan-step-archive { margin-left: auto; }
.plan-step-actions form:has(.plan-step-archive) { margin-left: auto; }
.plan-step-actions button:disabled { opacity: 0.4; cursor: not-allowed; }
.plan-fold { margin: 0 0 24px; max-width: 56rem; }
/* Folded actions look like the actions they are. */
.plan-fold > summary { display: inline-flex; align-items: center; min-height: 44px; padding: 0 16px; cursor: pointer;
  list-style: none; border: 1px solid var(--muted); background: var(--raised); color: var(--ink); font-weight: 600; }
.plan-fold > summary::-webkit-details-marker { display: none; }
.plan-fold[open] > summary { margin-bottom: 12px; }
.plan-fold.is-quiet > summary { min-height: 44px; padding: 0; border: 0; background: none; font-weight: 400; color: var(--ink-soft);
  text-decoration: underline; text-underline-offset: 3px; font-size: var(--t-sm); }
.plan-archived { list-style: none; margin: 0 0 20px; padding: 0; max-width: 56rem; }
.plan-archived li { display: flex; justify-content: space-between; align-items: center; gap: 16px; }
.plan-history .who { margin-bottom: 8px; }
.plan-firings { list-style: none; margin: 0; padding: 0; }
.plan-firings li { display: flex; gap: 16px; padding: 4px 0; }
.plan-enrol { display: flex; gap: 12px; align-items: end; flex-wrap: wrap; }
.plan-enrol label { display: flex; flex-direction: column; gap: 4px; color: var(--muted); margin: 0; }
@media (max-width: 40rem) {
  .plan-step summary { grid-template-columns: 3.5rem minmax(0, 1fr) auto; gap: 4px 10px; }
  .plan-step-body { padding-left: 8px; }
  .plan-step-form { grid-template-columns: minmax(0, 1fr) 6rem; }
  .plan-step-form label:nth-of-type(n+3) { grid-column: 1 / -1; }
}
/* Picked Drive files. A folder's contents sit indented under it. Each row
   leads with a mark for its state, matching the counts in the state line. A
   folder has none: its own line says it is a folder. */
.drive-docs { list-style: none; margin: 0; padding: 0; }
.drive-docs > li { border-bottom: 1px solid var(--rule); }
.drive-docs.inside { padding-left: 18px; border-left: 1px solid var(--rule); margin: 0 0 12px 8px; }
.drive-docs.inside > li { border-bottom: 0; }
.drive-doc { display: grid; grid-template-columns: 1.25rem minmax(0, 1fr) auto; align-items: baseline; gap: 2px 10px; padding: 12px 0; }
.drive-doc::before { grid-row: 1; grid-column: 1; font-weight: 700; text-align: center; }
.drive-doc[data-state="read"]::before { content: "✓" / ""; color: var(--human); }
.drive-doc[data-state="reading"]::before { content: "…" / ""; color: var(--wait); }
.drive-doc[data-state="failed"]::before { content: "!" / ""; color: var(--fail); }
.drive-doc[data-state="unsupported"]::before { content: "–" / ""; color: var(--muted); }
.drive-doc-name { grid-column: 2; font-weight: 600; overflow-wrap: anywhere; }
.drive-doc-meta { grid-column: 2; font-size: var(--t-sm); color: var(--muted); }
.drive-doc[data-state="failed"] .drive-doc-meta { color: var(--fail); }
.drive-doc[data-state="reading"] .drive-doc-meta { color: var(--wait); }
.drive-doc form { grid-column: 3; grid-row: 1; align-self: center; margin-block: -12px; }
/* Last on the deal page and quiet, because it is for a deal opened by mistake. */
.deal-delete { margin-top: 40px; padding-top: 12px; border-top: 1px solid var(--rule); }
.deal-delete .hint { margin-top: 0; }
.legal { max-width: 68ch; }
.legal + .legal { margin-top: 64px; padding-top: 32px; border-top: 1px solid var(--rule); }
.legal h2 { font-size: 1.05rem; margin: 28px 0 8px; }
.legal ul { padding-left: 1.2em; }
.legal li + li { margin-top: 6px; }
.signin-foot { margin-top: 24px; font-size: var(--t-sm); }

/* The folded line field from the brand: the mark's own stroke and gap, turning
   from the roof's 45 degrees into the stem. Sign-in is the one screen with no
   work on it, so it is where the brand gets room. */
.auth { display: grid; grid-template-columns: minmax(0, 26rem) minmax(0, 1fr); gap: var(--gap); align-items: stretch; }
/* The mark's centre is 55% down the drawing, so a crop centred there keeps it.
   The frame is Tinta in both themes (--ink turns Cal in dark), a stripe wide,
   so the field ends on a clean edge rather than on half-cut stripes. */
.auth-field { min-height: 32rem; border: 12px solid #1C2B45; background: #1C2B45 url("/assets/brand-field-6bb4ffcf.svg") 50% 55% / cover no-repeat; }
/* Dark gets its own drawing, tone on tone with the mark still in Cal, so the
   field rests behind the form. Dimming the Cal stripes turned them a grey the
   brand never chose, and lost a blind ranking 3 to 9. */
@media (prefers-color-scheme: dark) { .auth-field { background-image: url("/assets/brand-field-dark-7ad645be.svg"); } }
/* A band under the form that grows with the screen, so the mark fits at any
   width up to the two-column layout. */
@media (max-width: 48rem) {
  .auth { grid-template-columns: minmax(0, 1fr); }
  .auth-field { min-height: 0; aspect-ratio: 2 / 1; }
}
