/* ============================================================================
   portal.css - the client portal shell
   ----------------------------------------------------------------------------
   tokens/base/ui are the same files the public site uses, copied in by
   `console assets:sync`. This file only adds what is specific to the portal.

   Every value that would otherwise be an inline style attribute lives here as a
   class. The portal CSP has no 'unsafe-inline' for styles, and the per-request
   nonce does not cover style attributes, so an inline style is not merely
   frowned upon, it silently does nothing.
   ========================================================================== */

/* --- Shell ---------------------------------------------------------------- */

/* The column that fills the viewport is .app-body in app.css now, shared with
   the admin and the panel. .portal stays as the hook the rules below scope to. */

.pt-body--centered .app-main {
    display: grid;
    place-items: center;
    padding-block: var(--space-7);
}

/*
   Vertical rhythm for the content column.

   An alert, a .table-wrap and a .pt-card all carry no margin of their own, so
   any screen with more than one block after the page head stacked them flush:
   on a quote the accepted banner, the line items and the terms card touched,
   and an agreement's four cards read as one long surface divided by hairlines.
   Only the screens with a single block looked right, and then only because
   .pt-head brings its own bottom margin.

   One rule rather than a margin on each block, so a block type added later is
   spaced by default instead of by remembering. Sibling margins collapse, so
   .pt-head's larger gap under the title survives and this sets the tighter
   distance between the blocks below it.

   Scoped to .portal because it is the portal's answer to this. The admin
   spaces its screens with .adm-page's grid gap instead, and applying both to
   the shared .app-main would double it.
*/
.portal .app-main > * + * { margin-block-start: var(--space-5); }

/* --- Sign in -------------------------------------------------------------- */

.pt-signin {
    width: 100%;
    max-width: 26rem;
    margin-inline: auto;
    border: 1px solid var(--line);
    border-radius: var(--radius-lg);
    background: var(--surface-raised);
    box-shadow: var(--shadow-2), var(--edge-highlight);
    padding: var(--space-7) var(--space-6) var(--space-6);
    text-align: center;
}

.pt-signin__mark {
    display: grid;
    place-items: center;
    width: 3rem;
    height: 3rem;
    margin: 0 auto var(--space-4);
    border-radius: var(--radius-md);
    background: var(--surface-overlay);
    color: var(--brand);
}

.pt-signin__mark svg { width: 1.75rem; height: 1.75rem; }

.pt-signin h1 {
    margin: 0 0 var(--space-3);
    font-size: var(--text-xl);
}

.pt-signin__lead,
.pt-signin__note {
    margin: 0 0 var(--space-5);
    color: var(--text-secondary);
    font-size: var(--text-sm);
    line-height: 1.55;
}

.pt-signin__note { margin-bottom: 0; }

.pt-signin .field { margin-bottom: var(--space-5); text-align: left; }

.pt-signin .alert { margin-bottom: var(--space-4); text-align: left; }

/* --- Page heading --------------------------------------------------------- */

.pt-head { margin-bottom: var(--space-7); }

.pt-head h1 {
    margin: var(--space-2) 0 0;
    font-size: var(--text-2xl);
}

.pt-head__row {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--space-4);
}

.pt-head__row h1 { margin: 0; }

.pt-head__lead {
    margin: var(--space-3) 0 0;
    max-width: 62ch;
    color: var(--text-secondary);
    font-size: var(--text-md);
    line-height: 1.6;
}

.pt-back {
    display: inline-block;
    margin-bottom: var(--space-4);
    color: var(--text-muted);
    font-family: var(--font-mono);
    font-size: var(--text-3xs);
    letter-spacing: 0.06em;
    text-decoration: none;
    text-transform: uppercase;
}

.pt-back::before { content: "\2190"; margin-right: 0.4em; }

.pt-back:where(:hover) { color: var(--text-primary); }

.pt-muted {
    color: var(--text-secondary);
    font-size: var(--text-sm);
    line-height: 1.6;
}

.pt-num { font-variant-numeric: tabular-nums; }

.pt-optional {
    color: var(--text-muted);
    font-weight: 400;
    text-transform: none;
}

/* --- Two column layout ---------------------------------------------------- */

.pt-columns {
    display: grid;
    grid-template-columns: minmax(0, 1fr) 20rem;
    align-items: start;
    gap: var(--space-7);
    margin-top: var(--space-7);
}

@media (max-width: 62rem) {
    .pt-columns { grid-template-columns: minmax(0, 1fr); }
}

.pt-columns__main { display: grid; gap: var(--space-7); }

.pt-columns__side { display: grid; gap: var(--space-5); }

.pt-section > h2 {
    margin: 0 0 var(--space-5);
    font-size: var(--text-lg);
}

.pt-card {
    border: 1px solid var(--line);
    border-radius: var(--radius-lg);
    background: var(--surface-raised);
    padding: var(--space-5);
}

.pt-card > h2 {
    margin: 0 0 var(--space-4);
    font-size: var(--text-base);
}

.pt-card__note {
    margin: var(--space-4) 0 0;
    color: var(--text-muted);
    font-size: var(--text-xs);
    line-height: 1.5;
}

.pt-card .field { margin-bottom: var(--space-4); }

.pt-stack { display: grid; gap: var(--space-4); margin: 0; }

.pt-stack dt {
    color: var(--text-muted);
    font-family: var(--font-mono);
    font-size: var(--text-3xs);
    letter-spacing: 0.06em;
    text-transform: uppercase;
}

.pt-stack dd {
    margin: var(--space-1) 0 0;
    color: var(--text-primary);
    font-size: var(--text-sm);
}

/* --- Milestones ----------------------------------------------------------- */

.pt-steps {
    display: grid;
    gap: 0;
    margin: 0;
    padding: 0;
    list-style: none;
}

.pt-step {
    position: relative;
    display: grid;
    grid-template-columns: 1.5rem minmax(0, 1fr);
    gap: var(--space-4);
    padding-bottom: var(--space-5);
}

/* The connecting line, drawn behind the dots. The last step drops it so the
   sequence ends rather than trailing off. */
.pt-step::before {
    content: "";
    position: absolute;
    top: 1.1rem;
    bottom: 0;
    left: 0.6875rem;
    width: 1px;
    background: var(--line);
}

.pt-step:last-child { padding-bottom: 0; }
.pt-step:last-child::before { display: none; }

.pt-step__dot {
    position: relative;
    z-index: 1;
    width: 0.875rem;
    height: 0.875rem;
    margin-top: 0.375rem;
    border: 2px solid var(--line-strong);
    border-radius: var(--radius-full);
    background: var(--surface-page);
}

.pt-step--complete .pt-step__dot {
    border-color: var(--success);
    background: var(--success);
}

.pt-step--in_progress .pt-step__dot {
    border-color: var(--brand);
    background: var(--surface-page);
    box-shadow: 0 0 0 3px var(--warning-surface);
}

.pt-step__body h3 {
    margin: 0;
    font-size: var(--text-base);
    font-weight: 600;
}

.pt-step--complete .pt-step__body h3 { color: var(--text-secondary); }

.pt-step__body p {
    margin: var(--space-2) 0 0;
    color: var(--text-secondary);
    font-size: var(--text-sm);
    line-height: 1.55;
}

.pt-step__meta {
    color: var(--text-muted);
    font-family: var(--font-mono);
    font-size: var(--text-3xs);
    letter-spacing: 0.04em;
    text-transform: uppercase;
}

/* --- Tasks ---------------------------------------------------------------- */

.pt-tasks {
    display: grid;
    gap: 0;
    margin: 0;
    padding: 0;
    list-style: none;
}

.pt-task {
    display: grid;
    grid-template-columns: auto minmax(0, 1fr) auto;
    align-items: start;
    gap: var(--space-4);
    border-bottom: 1px solid var(--line-faint);
    padding: var(--space-4) 0;
}

.pt-task:last-child { border-bottom: 0; }

.pt-task__title {
    margin: 0;
    font-size: var(--text-sm);
    font-weight: 500;
}

.pt-task__desc {
    margin: var(--space-2) 0 0;
    color: var(--text-secondary);
    font-size: var(--text-sm);
    line-height: 1.55;
}

.pt-task__tag {
    margin-left: var(--space-2);
    color: var(--brand-text);
    font-family: var(--font-mono);
    font-size: var(--text-3xs);
    letter-spacing: 0.04em;
    text-transform: uppercase;
}

.pt-task__due {
    color: var(--text-muted);
    font-family: var(--font-mono);
    font-size: var(--text-3xs);
    white-space: nowrap;
}

@media (max-width: 34rem) {
    .pt-task { grid-template-columns: minmax(0, 1fr); }
}

/* --- Message thread ------------------------------------------------------- */

.pt-thread {
    display: grid;
    gap: var(--space-4);
    margin: 0 0 var(--space-6);
    padding: 0;
    list-style: none;
}

.pt-msg {
    max-width: 42rem;
    border: 1px solid var(--line);
    border-radius: var(--radius-md);
    background: var(--surface-raised);
    padding: var(--space-4) var(--space-5);
}

/* The client's own messages are pushed right and tinted, so a thread reads as
   a conversation rather than a log. */
.pt-msg--you {
    margin-left: auto;
    border-color: transparent;
    background: var(--surface-overlay);
}

.pt-msg__head {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: var(--space-4);
    margin-bottom: var(--space-3);
}

.pt-msg__who {
    color: var(--text-primary);
    font-size: var(--text-sm);
    font-weight: 600;
}

.pt-msg__head time {
    color: var(--text-muted);
    font-family: var(--font-mono);
    font-size: var(--text-3xs);
    white-space: nowrap;
}

.pt-msg__body {
    color: var(--text-secondary);
    font-size: var(--text-sm);
    line-height: 1.6;
}

.pt-reply { max-width: 42rem; }

.pt-reply .field { margin-bottom: var(--space-4); }

.pt-reply textarea { resize: vertical; min-height: 6rem; }

/* --- File list ------------------------------------------------------------ */

.pt-filelist {
    display: grid;
    gap: var(--space-3);
    margin: 0 0 var(--space-5);
    padding: 0;
    list-style: none;
}

.pt-filelist li {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: var(--space-3);
    font-size: var(--text-sm);
}

.pt-filelist span {
    flex: none;
    color: var(--text-muted);
    font-family: var(--font-mono);
    font-size: var(--text-3xs);
}

/*
   A second line under a title in a table cell: the file's description, an
   agreement's kind, a scope line's detail.

   .pt-sub had no rule at all, so on the agreements list the kind ran straight
   on from the document link in the same size and color - "Master services
   agreement Master services agreement" - and read as one stuttering sentence
   rather than a title with a caption under it.
*/
.pt-file__desc,
.pt-sub {
    display: block;
    margin-top: var(--space-1);
    color: var(--text-muted);
    font-size: var(--text-xs);
    font-weight: 400;
}

/* The signed date sits beside its badge rather than under it, so it keeps the
   color and the size but not the line of its own. */
.pt-sub--inline {
    display: inline;
    margin-top: 0;
    margin-left: var(--space-2);
}

/* --- Outstanding balance -------------------------------------------------- */

.pt-owed {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-5);
    margin-bottom: var(--space-7);
    border: 1px solid var(--line);
    border-left: 3px solid var(--brand);
    border-radius: var(--radius-md);
    background: var(--surface-raised);
    padding: var(--space-5) var(--space-6);
}

.pt-owed__label {
    margin: 0;
    color: var(--text-muted);
    font-family: var(--font-mono);
    font-size: var(--text-3xs);
    letter-spacing: 0.06em;
    text-transform: uppercase;
}

.pt-owed__figure {
    margin: var(--space-1) 0 0;
    font-family: var(--font-display);
    font-size: var(--text-2xl);
    font-weight: 700;
    font-variant-numeric: tabular-nums;
    line-height: 1.1;
}

.pt-owed__sub {
    margin: var(--space-1) 0 0;
    color: var(--text-secondary);
    font-size: var(--text-sm);
}

/* --- Projects ------------------------------------------------------------- */

.pt-projects {
    display: grid;
    gap: var(--space-5);
}

.pt-project {
    border: 1px solid var(--line);
    border-radius: var(--radius-lg);
    background: var(--surface-raised);
    box-shadow: var(--shadow-1);
    padding: var(--space-6);
}

.pt-project__head {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: var(--space-4);
}

.pt-project__head h2 {
    margin: 0;
    font-size: var(--text-lg);
}

.pt-project__head h2 a {
    color: var(--text-primary);
    text-decoration: none;
}

.pt-project__head h2 a:where(:hover) {
    color: var(--brand-text);
}

.pt-project__summary {
    margin: var(--space-2) 0 0;
    max-width: 58ch;
    color: var(--text-secondary);
    font-size: var(--text-sm);
    line-height: 1.55;
}

.pt-project__note {
    margin: var(--space-5) 0 0;
    border-top: 1px solid var(--line-faint);
    padding-top: var(--space-4);
    color: var(--text-secondary);
    font-size: var(--text-sm);
    line-height: 1.55;
}

.pt-project__note span {
    color: var(--text-muted);
    font-family: var(--font-mono);
    font-size: var(--text-3xs);
    letter-spacing: 0.06em;
    text-transform: uppercase;
}

/* --- Progress ------------------------------------------------------------- */

.pt-progress {
    display: flex;
    align-items: center;
    gap: var(--space-4);
    margin-top: var(--space-5);
}

.pt-progress--wide { margin-top: 0; }

.pt-progress__track {
    flex: 1 1 auto;
    height: 6px;
    overflow: hidden;
    border-radius: var(--radius-full);
    background: var(--surface-overlay);
}

.pt-progress__fill {
    height: 100%;
    border-radius: inherit;
    background: linear-gradient(90deg, var(--brand-strong), var(--brand));
    transition: width 400ms ease;
}

.pt-progress__pct {
    flex: none;
    color: var(--text-secondary);
    font-family: var(--font-mono);
    font-size: var(--text-3xs);
    font-variant-numeric: tabular-nums;
    letter-spacing: 0.04em;
    text-transform: uppercase;
}

/* Widths as classes rather than an inline style attribute, in 5% steps. The
   template floors the stored percentage to the nearest step to pick one. */
.pt-progress__fill--0   { width: 0; }
.pt-progress__fill--5   { width: 5%; }
.pt-progress__fill--10  { width: 10%; }
.pt-progress__fill--15  { width: 15%; }
.pt-progress__fill--20  { width: 20%; }
.pt-progress__fill--25  { width: 25%; }
.pt-progress__fill--30  { width: 30%; }
.pt-progress__fill--35  { width: 35%; }
.pt-progress__fill--40  { width: 40%; }
.pt-progress__fill--45  { width: 45%; }
.pt-progress__fill--50  { width: 50%; }
.pt-progress__fill--55  { width: 55%; }
.pt-progress__fill--60  { width: 60%; }
.pt-progress__fill--65  { width: 65%; }
.pt-progress__fill--70  { width: 70%; }
.pt-progress__fill--75  { width: 75%; }
.pt-progress__fill--80  { width: 80%; }
.pt-progress__fill--85  { width: 85%; }
.pt-progress__fill--90  { width: 90%; }
.pt-progress__fill--95  { width: 95%; }
.pt-progress__fill--100 { width: 100%; }

/* --- Project meta --------------------------------------------------------- */

.pt-meta {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-6);
    margin: var(--space-5) 0 0;
}

.pt-meta dt {
    color: var(--text-muted);
    font-family: var(--font-mono);
    font-size: var(--text-3xs);
    letter-spacing: 0.06em;
    text-transform: uppercase;
}

.pt-meta dd {
    margin: var(--space-1) 0 0;
    color: var(--text-primary);
    font-size: var(--text-sm);
}

/* --- Status pill ---------------------------------------------------------- */

.pill {
    display: inline-flex;
    align-items: center;
    flex: none;
    border: 1px solid var(--line);
    border-radius: var(--radius-full);
    background: var(--surface-overlay);
    padding: 0.2rem 0.7rem;
    color: var(--text-secondary);
    font-family: var(--font-mono);
    font-size: var(--text-3xs);
    letter-spacing: 0.04em;
    text-transform: uppercase;
    white-space: nowrap;
}

/* Project, task, and invoice statuses all render as pills. They are grouped by
   what they mean rather than which table they came from, so "in progress" reads
   the same wherever it appears. */
.pill--in_progress,
.pill--sent,
.pill--viewed {
    border-color: transparent;
    background: var(--info-surface);
    color: var(--info);
}

.pill--review,
.pill--partial {
    border-color: transparent;
    background: var(--warning-surface);
    color: var(--warning);
}

.pill--complete,
.pill--done,
.pill--paid {
    border-color: transparent;
    background: var(--success-surface);
    color: var(--success);
}

.pill--on_hold,
.pill--cancelled,
.pill--blocked,
.pill--overdue {
    border-color: transparent;
    background: var(--danger-surface);
    color: var(--danger);
}

/* --- Empty state ---------------------------------------------------------- */

.pt-empty {
    border: 1px dashed var(--line-strong);
    border-radius: var(--radius-lg);
    padding: var(--space-9) var(--space-6);
    text-align: center;
}

.pt-empty h2 {
    margin: 0 0 var(--space-3);
    font-size: var(--text-lg);
}

.pt-empty p {
    margin: 0 auto;
    max-width: 42ch;
    color: var(--text-secondary);
    font-size: var(--text-sm);
    line-height: 1.6;
}

/* --- Footer --------------------------------------------------------------- */

.pt-foot {
    border-top: 1px solid var(--line);
    padding-block: var(--space-5);
    color: var(--text-muted);
    font-size: var(--text-xs);
}

.pt-foot .container {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-3);
}

/* -----------------------------------------------------------------------------
   Time log

   Shown only when the project has its time log switched on. Reads as a
   statement of work done, not an invoice: the totals lead, the individual
   rows are quiet underneath.
   -------------------------------------------------------------------------- */

.pt-timesum {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-8);
    margin-bottom: var(--space-5);
    border: 1px solid var(--line);
    border-radius: var(--radius-md);
    background: var(--surface-raised);
    padding: var(--space-5);
}

.pt-timesum__label {
    margin: 0;
    color: var(--text-muted);
    font-family: var(--font-mono);
    font-size: var(--text-3xs);
    letter-spacing: 0.06em;
    text-transform: uppercase;
}

.pt-timesum__figure {
    margin: var(--space-1) 0 0;
    font-family: var(--font-display);
    font-size: var(--text-xl);
    font-weight: 700;
    font-variant-numeric: tabular-nums;
    line-height: 1.1;
}

.pt-timelog {
    margin: 0;
    padding: 0;
    list-style: none;
}

.pt-timelog li {
    display: grid;
    grid-template-columns: 4rem 5rem minmax(0, 1fr) auto;
    align-items: baseline;
    gap: var(--space-4);
    border-bottom: 1px solid var(--line-faint);
    padding: var(--space-3) 0;
}

.pt-timelog li:last-child { border-bottom: 0; }

.pt-timelog__date { color: var(--text-muted); font-size: var(--text-3xs); }

.pt-timelog__hours {
    color: var(--text-primary);
    font-size: var(--text-xs);
    font-variant-numeric: tabular-nums;
}

.pt-timelog__what {
    color: var(--text-secondary);
    font-size: var(--text-sm);
    line-height: 1.5;
}

.pt-timelog__cost {
    color: var(--text-secondary);
    font-size: var(--text-xs);
    font-variant-numeric: tabular-nums;
    text-align: right;
    white-space: nowrap;
}

@media (max-width: 40rem) {
    .pt-timelog li {
        grid-template-columns: 4.5rem minmax(0, 1fr);
        row-gap: var(--space-1);
    }

    .pt-timelog__what { grid-column: 1 / -1; }
    .pt-timelog__cost { text-align: left; }
}

/* -----------------------------------------------------------------------------
   Pay page

   Stripe renders the fields themselves inside iframes, so there is very little
   to style here: a summary on one side, Stripe's element on the other. What
   this does control is the frame around it, which is what makes the embedded
   form read as part of the portal rather than as a bolted-on widget.
   -------------------------------------------------------------------------- */

.pt-pay {
    display: grid;
    grid-template-columns: 18rem minmax(0, 1fr);
    align-items: start;
    gap: var(--space-7);
}

@media (max-width: 54rem) {
    .pt-pay { grid-template-columns: minmax(0, 1fr); }
}

.pt-pay__summary {
    border: 1px solid var(--line);
    border-left: 3px solid var(--brand);
    border-radius: var(--radius-md);
    background: var(--surface-raised);
    padding: var(--space-5);
}

.pt-pay__figure {
    margin: var(--space-1) 0 var(--space-5);
    font-family: var(--font-display);
    font-size: var(--text-2xl);
    font-weight: 700;
    font-variant-numeric: tabular-nums;
    line-height: 1.1;
}

.pt-pay__form {
    border: 1px solid var(--line);
    border-radius: var(--radius-lg);
    background: var(--surface-raised);
    padding: var(--space-6);
}

.pt-pay__submit { margin-top: var(--space-5); }

.pt-pay__error { margin-bottom: var(--space-4); }

.pt-pay__note {
    margin: var(--space-4) 0 0;
    color: var(--text-muted);
    font-size: var(--text-xs);
    line-height: 1.5;
}

/* Stripe's iframe sizes itself; this only reserves somewhere for it to land so
   the button does not jump up the page while it loads. */
#payment-element { min-height: 14rem; }

/* -----------------------------------------------------------------------------
   One invoice

   Deliberately reads as a document rather than as a screen: the line items get
   the room, the totals stack to the right of them, and the actions sit off to
   one side instead of on top. This is the page a client forwards to their
   accountant, so it should survive being printed or screenshotted.
   -------------------------------------------------------------------------- */

.pt-invoice {
    display: grid;
    grid-template-columns: minmax(0, 1fr) 18rem;
    align-items: start;
    gap: var(--space-7);
}

@media (max-width: 60rem) {
    .pt-invoice { grid-template-columns: minmax(0, 1fr); }
}

.pt-invoice__doc {
    border: 1px solid var(--line);
    border-radius: var(--radius-lg);
    background: var(--surface-raised);
    overflow: hidden;
}

.pt-invoice__meta {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-8);
    border-bottom: 1px solid var(--line-faint);
    padding: var(--space-5);
}

.pt-invoice__meta p { margin: 0; }
.pt-invoice__meta p + p { margin-top: var(--space-1); font-size: var(--text-sm); }

.pt-invoice__empty { padding: var(--space-6) var(--space-5); }

.pt-invoice__doc .table-wrap { border: 0; border-radius: 0; }

.pt-invoice__totals {
    display: grid;
    gap: var(--space-2);
    margin: 0;
    border-top: 1px solid var(--line-faint);
    padding: var(--space-5);
}

.pt-invoice__totals > div {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: var(--space-5);
    margin-left: auto;
    min-width: 16rem;
    font-size: var(--text-sm);
}

.pt-invoice__totals dt { color: var(--text-secondary); }
.pt-invoice__totals dd { margin: 0; color: var(--text-primary); }

/* The number the client is actually looking for. */
.pt-invoice__due {
    margin-top: var(--space-2);
    border-top: 1px solid var(--line);
    padding-top: var(--space-3);
    font-size: var(--text-md) !important;
}

.pt-invoice__due dt { color: var(--text-primary) !important; font-weight: 600; }

.pt-invoice__due dd {
    font-family: var(--font-display);
    font-size: var(--text-lg);
    font-weight: 700;
}

.pt-invoice__side { display: grid; gap: var(--space-5); }

.pt-invoice__side .pt-card > p:first-child { margin-top: 0; }

.pt-invoice__dl { margin-top: var(--space-3); }

.pt-rowactions {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--space-2);
}

/* --- Quotes ----------------------------------------------------------------
   `sent` already exists for invoices and reads the same here: waiting on you.
   The rest are the states a quote can reach that an invoice cannot. */

.pill--accepted { background: color-mix(in oklab, var(--success) 18%, transparent); color: var(--success); }
.pill--declined { background: var(--surface-sunken); color: var(--text-muted); }
.pill--expired  { background: color-mix(in oklab, var(--warning) 18%, transparent); color: var(--warning); }

/* Declining sits under accepting and is deliberately quieter: it is a real
   answer and should be easy to give, without competing with the other one. */
.pt-decline {
    margin-block-start: var(--space-6);
    padding-block-start: var(--space-5);
    border-block-start: 1px solid var(--line);
}

/* -----------------------------------------------------------------------------
   Project brief

   A long form, so the job of the styling is to make it feel shorter: sections
   that read as separate questions rather than one wall, and enough room per
   answer that a paragraph does not feel like it is being squeezed out of you.
   -------------------------------------------------------------------------- */

.pt-brief { display: grid; gap: var(--space-4); }

.pt-brief__section { padding: var(--space-5); }

.pt-brief__title {
    margin: 0 0 var(--space-4);
    font-size: var(--text-lg);
    letter-spacing: var(--tracking-tight);
}

/* Two columns for short answers, one for anything with a paragraph in it. */
.pt-brief__fields {
    display: grid;
    gap: var(--space-4);
}

@media (min-width: 42rem) {
    .pt-brief__fields { grid-template-columns: 1fr 1fr; }
    .pt-brief__fields .field--wide { grid-column: 1 / -1; }
}

.pt-brief__links { display: grid; gap: var(--space-3); }

.pt-brief__link {
    display: grid;
    gap: var(--space-2);
}

@media (min-width: 42rem) {
    .pt-brief__link { grid-template-columns: minmax(0, 2fr) minmax(0, 3fr); gap: var(--space-3); }
}

.pt-brief__actions {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-3);
    align-items: center;
}

.pt-brief__note {
    margin: 0;
    font-size: var(--text-xs);
    color: var(--text-muted);
}

/* .field__error is already defined in ui.css and is not redefined here: a
   second copy is a second thing to keep in step. Only the field's own state is
   new, so the input and its message are obviously about each other. */
.field--error .input { border-color: var(--danger); }

.pt-brief [readonly] {
    background: var(--surface-sunken);
    color: var(--text-secondary);
    cursor: default;
}

/* The brief prompt reuses the outstanding-balance band, because it does the
   same job: one thing that needs doing, stated plainly, with the action beside
   it. Only the accent differs, so the two are not mistaken for each other. */
.pt-owed--ask {
    border-color: color-mix(in oklab, var(--brand) 32%, var(--line));
    background: color-mix(in oklab, var(--brand) 7%, var(--surface-raised));
}

.pt-owed__ask {
    margin: var(--space-1) 0 0;
    font-family: var(--font-display);
    font-stretch: var(--width-display);
    font-weight: 800;
    font-size: var(--text-xl);
    letter-spacing: var(--tracking-tight);
    line-height: 1.1;
}

/* -----------------------------------------------------------------------------
   Agreements
   -------------------------------------------------------------------------- */

.pt-agreement__meta {
    display: grid;
    gap: var(--space-3);
    margin: 0;
    padding: var(--space-5);
}

.pt-agreement__meta > div { display: grid; gap: 0.15rem; }

.pt-agreement__meta dt {
    font-family: var(--font-mono);
    font-size: var(--text-xs);
    letter-spacing: var(--tracking-label);
    text-transform: uppercase;
    color: var(--text-muted);
}

.pt-agreement__meta dd { margin: 0; }

.pt-agreement__scope h2,
.pt-terms h2,
.pt-sign h2 { padding: var(--space-4) var(--space-5) 0; font-size: var(--text-base); }

.pt-agreement__qty,
.pt-agreement__amount { text-align: end; white-space: nowrap; }

/* Terms held to a readable measure. A contract run the full width of a monitor
   is a wall, and the one document nobody should be encouraged to skim. */
.pt-terms { padding-block-end: var(--space-4); }

.pt-terms p {
    max-width: 62ch;
    margin: 0 0 var(--space-3);
    padding-inline: var(--space-5);
    color: var(--text-secondary);
}

.pt-terms__heading {
    margin-block-start: var(--space-5);
    padding-inline: var(--space-5);
}

/* ---- signatures --------------------------------------------------------- */

.pt-sign { padding-block-end: var(--space-5); }

/*
   These three each carried their own horizontal padding on top of .pt-card's,
   so inside the signing card the party block, the form fields and the closing
   note all sat 24px further right than the card's own heading, and the record
   line under the button was indented further still. Every other card on the
   page puts its content on one edge. The card supplies the inset; these only
   need the space above them.
*/
.pt-sign__party {
    padding-block-start: var(--space-4);
    margin-block-end: var(--space-4);
}

/*
   Stored ink. An SVG path drawn from the points the pen went through, so it
   stays sharp at any size and needs no script to appear, which is what a
   record of something already signed should require.
*/
.pt-ink {
    display: block;
    inline-size: min(15rem, 100%);
    block-size: 3.6rem;
    margin-block: var(--space-2);
    overflow: visible;
}

/*
   The width is in CSS pixels, not in the 0..1 coordinate space of the path.
   non-scaling-stroke is what makes that true, and it is wanted here: the
   viewBox is a unit square stretched by preserveAspectRatio="none", so a
   width in user units would come out thicker horizontally than vertically.
   It also means a fraction of a unit is a fraction of a pixel, which is how
   this first shipped, and eight thousandths of a pixel draws nothing.
*/
.pt-ink path {
    fill: none;
    stroke: var(--text-primary);
    stroke-width: 1.6;
    stroke-linecap: round;
    stroke-linejoin: round;
    vector-effect: non-scaling-stroke;
}

.pt-sign__caption {
    max-width: 34rem;
    margin: 0;
    padding-block-start: var(--space-2);
    border-block-start: 1px solid var(--line);
    font-size: var(--text-sm);
    color: var(--text-secondary);
}

.pt-sign__record {
    padding-block-start: var(--space-3);
    font-family: var(--font-mono);
    font-size: var(--text-xs);
    color: var(--text-muted);
}

.pt-sign__form { display: grid; gap: var(--space-4); padding-block-start: var(--space-5); }
.pt-sign__form .btn { justify-self: start; }

/* ---- the pad ------------------------------------------------------------ */

/*
   Starts unusable and says so. The script adds is-ready once it has bound its
   handlers, so a browser that never runs it shows the note rather than an
   inert gray box somebody scribbles at and then cannot submit.
*/
/* Dark is the default theme, so the ink drawn on screen is light. The stored
   points carry no color, so the same signature prints as dark ink on the
   white page of the PDF regardless of which theme it was drawn in. */
.pt-pad { --ink-color: #F4F6FB; max-width: 30rem; }

.pt-pad__canvas,
.pt-pad__tools { display: none; }

.pt-pad.is-ready .pt-pad__canvas { display: block; }
.pt-pad.is-ready .pt-pad__tools { display: flex; }
.pt-pad.is-ready .pt-pad__fallback { display: none; }

.pt-pad__canvas {
    inline-size: 100%;
    block-size: 9rem;
    border: 1px dashed var(--line-strong);
    border-radius: var(--radius-sm);
    background: var(--surface-sunken);
    cursor: crosshair;
    touch-action: none;
}

.pt-pad.is-signed .pt-pad__canvas {
    border-style: solid;
    border-color: var(--brand);
}

.pt-pad__fallback {
    margin: 0;
    padding: var(--space-4);
    border: 1px solid var(--line);
    border-radius: var(--radius-sm);
    background: var(--surface-sunken);
    font-size: var(--text-sm);
    color: var(--text-secondary);
}

.pt-pad__tools {
    align-items: center;
    justify-content: space-between;
    gap: var(--space-3);
    margin-block-start: var(--space-2);
}

.pt-pad__hint {
    font-family: var(--font-mono);
    font-size: var(--text-xs);
    color: var(--text-muted);
}

[data-theme="light"] .pt-pad { --ink-color: #111827; }

@media (prefers-color-scheme: light) {
    :root:not([data-theme="dark"]) .pt-pad { --ink-color: #111827; }
}

/* -----------------------------------------------------------------------------
   Preview
   -------------------------------------------------------------------------- */

.pt-preview { padding: var(--space-5); }
.pt-preview h2 { margin-block-end: var(--space-2); }
.pt-preview__note { margin-block-end: var(--space-4); color: var(--text-secondary); }

.pt-preview__creds {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-5);
    margin: 0 0 var(--space-5);
    padding: var(--space-4);
    border: 1px solid var(--line);
    border-radius: var(--radius-sm);
    background: var(--surface-sunken);
}

.pt-preview__creds > div { display: grid; gap: 0.2rem; }

.pt-preview__creds dt {
    font-family: var(--font-mono);
    font-size: var(--text-xs);
    letter-spacing: var(--tracking-label);
    text-transform: uppercase;
    color: var(--text-muted);
}

/* The point of this box is being read off a screen and typed into a prompt,
   so the values are large enough to read and selectable as a unit. */
.pt-preview__creds dd {
    margin: 0;
    font-size: var(--text-md);
    color: var(--text-primary);
    user-select: all;
}

.pt-preview__small {
    margin-block-start: var(--space-4);
    font-size: var(--text-sm);
    color: var(--text-muted);
}

/* ---------------------------------------------------------------- ways to pay
   The routes that are not a card. Cards are the fast path and sit above; this
   is for the client who would rather not use one, or whose accounts department
   only ever posts a check. */

.pt-ways {
    margin-top: var(--space-7);
    padding-top: var(--space-6);
    border-top: 1px solid var(--line);
}

.pt-ways__title { margin: 0 0 var(--space-3); font-size: 1.15rem; }

.pt-ways__lede {
    margin: 0 0 var(--space-5);
    max-width: 46ch;
    color: var(--text-secondary);
}

.pt-ways__list {
    display: grid;
    gap: var(--space-4);
    grid-template-columns: repeat(auto-fit, minmax(17rem, 1fr));
    margin: 0;
    padding: 0;
    list-style: none;
}

.pt-way {
    padding: var(--space-4);
    border: 1px solid var(--line);
    border-radius: var(--radius-sm);
    background: var(--surface);
}

.pt-way__head {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-2);
    align-items: baseline;
    justify-content: space-between;
}

.pt-way__label { margin: 0; font-size: 1rem; }

.pt-way__figure {
    margin: 0;
    font-family: var(--font-mono);
    font-size: 1.05rem;
    font-weight: 600;
}

/* The old figure, kept small and struck through so the saving is visible
   without the discounted price having to argue for itself. */
.pt-way__was {
    display: block;
    font-size: .75rem;
    font-weight: 400;
    text-decoration: line-through;
    color: var(--text-muted);
}

.pt-way__blurb {
    margin: var(--space-3) 0 0;
    font-size: .9rem;
    color: var(--text-secondary);
}

/* pre-line rather than a <pre>, so a posted address keeps the line breaks it
   was typed with without also inheriting a code block's look. */
.pt-way__details {
    margin-top: var(--space-3);
    padding: var(--space-3);
    border-radius: var(--radius-sm);
    background: var(--surface-2, rgba(127, 127, 127, .08));
    font-family: var(--font-mono);
    font-size: .82rem;
    line-height: 1.6;
    white-space: pre-line;
    overflow-wrap: anywhere;
}

.pt-way__ref {
    margin: var(--space-3) 0 0;
    font-size: .82rem;
    color: var(--text-muted);
}

.pt-ways__note {
    margin: var(--space-5) 0 0;
    max-width: 60ch;
    font-size: .85rem;
    color: var(--text-muted);
}

/* ------------------------------------------------------------------- uploads
   The client's own files, shown as a grid because most of what arrives is a
   photograph and a filename off a phone camera says nothing about it. */

.pt-uploads {
    display: grid;
    gap: var(--space-4);
    grid-template-columns: repeat(auto-fill, minmax(15rem, 1fr));
    margin: 0;
    padding: 0;
    list-style: none;
}

.pt-upload {
    display: flex;
    flex-direction: column;
    border: 1px solid var(--line);
    border-radius: var(--radius-sm);
    background: var(--surface);
    overflow: hidden;
}

.pt-upload__frame {
    display: grid;
    place-items: center;
    aspect-ratio: 4 / 3;
    background: var(--surface-2, rgba(127, 127, 127, .08));
    border-bottom: 1px solid var(--line);
    text-decoration: none;
    overflow: hidden;
}

.pt-upload__frame img {
    width: 100%;
    height: 100%;
    object-fit: cover;
    display: block;
}

.pt-upload__ext {
    font-family: var(--font-mono);
    font-size: .95rem;
    letter-spacing: .08em;
    color: var(--text-muted);
}

.pt-upload__body {
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
    padding: var(--space-3);
}

.pt-upload__name {
    margin: 0;
    font-size: .92rem;
    font-weight: 600;
    overflow-wrap: anywhere;
}

.pt-upload__name a { text-decoration: none; }
.pt-upload__name a:hover { text-decoration: underline; }

.pt-upload__meta {
    margin: 0;
    font-family: var(--font-mono);
    font-size: .74rem;
    color: var(--text-muted);
}

/* The description sits on its own row under a narrow tile rather than beside
   the button, which at this width would leave room for about four characters. */
.pt-upload__rename {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-2);
    margin-top: var(--space-1);
}

.pt-upload__rename .input {
    flex: 1 1 8rem;
    min-width: 0;
}

.input--sm {
    padding: .4rem .6rem;
    font-size: .85rem;
}

/* Removing is folded away, so it is never one stray click, and the disclosure
   itself is the confirmation step. No script involved. */
.pt-upload__remove > summary {
    cursor: pointer;
    font-size: .78rem;
    color: var(--text-muted);
    list-style: none;
}

.pt-upload__remove > summary::-webkit-details-marker { display: none; }

.pt-upload__remove > summary:hover,
.pt-upload__remove[open] > summary { color: var(--danger); }

.pt-upload__remove > p {
    margin: var(--space-2) 0;
    font-size: .78rem;
    line-height: 1.5;
    color: var(--text-secondary);
}
