/* Controls rendered by makeControl(). The wrapper is .ctrl-wrapper; the
   editable element is .ctrl-field. Bare element selectors are scoped to the
   wrapper on purpose - the estate learned that bare `label` rules leak. */
.ctrl-wrapper { display: flex; flex-direction: column; gap: 0.2rem; min-width: 0; margin-bottom: var(--net-spacing-md); }
.ctrl-wrapper > label { font-size: var(--net-font-size-sm); color: var(--net-text-muted); font-weight: 600; }
.ctrl-wrapper .req { color: var(--net-color-required); }
.ctrl-wrapper .ctrl-field {
    font: inherit; color: var(--net-text-primary); background: var(--net-bg-primary);
    border: 1px solid var(--net-border-accent); border-radius: var(--net-border-radius-base);
    padding: 0.5rem 0.6rem; min-height: 40px; width: 100%;
    transition: border-color var(--net-transition-fast), box-shadow var(--net-transition-fast);
}
.ctrl-wrapper .ctrl-field:focus { outline: none; border-color: var(--net-color-primary); box-shadow: 0 0 0 3px rgba(31, 111, 235, 0.18); }
.ctrl-wrapper .ctrl-field[readonly], .ctrl-wrapper .ctrl-field:disabled { background: var(--net-bg-accent); color: var(--net-text-muted); }
.ctrl-wrapper textarea.ctrl-field { min-height: 72px; resize: vertical; }
.ctrl-wrapper select.ctrl-field { appearance: auto; }
.ctrl-wrapper .ctrl-help { font-size: var(--net-font-size-xs); color: var(--net-text-muted); }
/* A BARE field (`'class' => 'is-bare'` on makeControl) is dressed by its container - the words typed into a badge, a
   colour box that is a swatch: no box, border, padding or focus ring of its own, in EVERY workspace model (the models'
   field rules skip .is-bare). The container gives it its size and inset. */
.ctrl-wrapper.is-bare { margin: 0; gap: 0; }
.ctrl-wrapper.is-bare .ctrl-field, .ctrl-wrapper.is-bare .ctrl-field[readonly], .ctrl-wrapper.is-bare .ctrl-field:focus {
    border: 0; border-radius: inherit; background: transparent; box-shadow: none; outline: none;
    padding: 0; min-height: 0; height: 100%; color: inherit;
}
.ctrl-checkbox .ctrl-check { display: inline-flex; align-items: center; gap: 0.5rem; cursor: pointer; min-height: 40px; }
/* A checkbox sharing a form-grid row with labelled fields: an empty label-high
   line on top (a flex item, so it gets the wrapper's gap like a real label) puts
   the box level with the inputs beside it. A row of its own (span-12) skips it. */
.form-grid > :not(.span-12) > .ctrl-checkbox::before { content: '\00a0'; font-size: var(--net-font-size-sm); font-weight: 600; }
.ctrl-checkbox .ctrl-field { width: 1.15rem; height: 1.15rem; min-height: 0; padding: 0; accent-color: var(--net-color-primary); }
.ctrl-input.ctrl-type-number .ctrl-field { text-align: right; font-variant-numeric: tabular-nums; }
/* Date and time boxes NEVER outgrow their box, app-wide (owner, 2026-10-07: on an iPhone the Timeline's From box ran
   over To). iOS Safari sizes a native date input from its value ("Oct 7, 2026") and ignores the width it was given, and
   centres the text; appearance:none hands the size back to CSS (the picker still opens), the value sits left like any
   field. Any date / time input, makeControl or hand-written. */
input[type="date"], input[type="datetime-local"], input[type="month"], input[type="time"], input[type="week"] { min-width: 0; max-width: 100%; box-sizing: border-box; }
input[type="date"]::-webkit-date-and-time-value, input[type="datetime-local"]::-webkit-date-and-time-value,
input[type="month"]::-webkit-date-and-time-value, input[type="time"]::-webkit-date-and-time-value { text-align: left; margin: 0; }
@supports (-webkit-touch-callout: none) {
    input[type="date"], input[type="datetime-local"], input[type="month"], input[type="time"], input[type="week"] { -webkit-appearance: none; appearance: none; min-height: 1.6em; }
}
.ctrl-type-date, .ctrl-type-time, .ctrl-type-datetime-local { min-width: 0; }

.form-grid { display: grid; grid-template-columns: repeat(12, minmax(0, 1fr)); gap: 0 var(--net-spacing-md); }
.form-grid > .ctrl-wrapper, .form-grid > .span-12 { grid-column: span 12; }
.form-grid > .span-6 { grid-column: span 6; }
.form-grid > .span-4 { grid-column: span 4; }
.form-grid > .span-5 { grid-column: span 5; }
.form-grid > .span-7 { grid-column: span 7; }
.form-grid > .span-10 { grid-column: span 10; }
.form-grid > .span-11 { grid-column: span 11; }
.form-grid > .span-3 { grid-column: span 3; }
.form-grid > .span-8 { grid-column: span 8; }
.form-grid > .span-2 { grid-column: span 2; }
.form-grid > .span-9 { grid-column: span 9; }
.form-actions { display: flex; flex-wrap: wrap; gap: var(--net-spacing-sm); align-items: center; margin: var(--net-spacing-md) 0; }
.form-status { font-size: var(--net-font-size-sm); color: var(--net-text-muted); }
.form-status.is-error { color: var(--net-alert-error-text); }
.form-status.is-ok { color: var(--net-alert-success-text); }
/* Field rows line up on their labels (top), so a help line under one field
   never lifts that field above its neighbours. A member WITHOUT a label (a
   button, a checkbox, the filter buttons) in a row that has labels gets a blank
   label-high line on top - a flex item, so it also gets the wrapper's gap - and
   sits level with the fields. 1.45 = the body line-height (theme.css). */
.field-row { display: flex; flex-wrap: wrap; gap: var(--net-spacing-sm); align-items: flex-start; }
.field-row:has(> .ctrl-wrapper > label:not(.ctrl-check)) > .ctrl-wrapper:not(:has(> label:not(.ctrl-check)))::before { content: '\00a0'; font-size: var(--net-font-size-sm); font-weight: 600; }
.field-row:has(> .ctrl-wrapper > label:not(.ctrl-check)) > .filter-actions { padding-top: calc(var(--net-font-size-sm) * 1.45 + 0.2rem); }
.field-row .ctrl-wrapper { flex: 1 1 140px; margin-bottom: var(--net-spacing-sm); }
.field-row .ctrl-wrapper.narrow { flex: 0 1 110px; }
/* .wide: the field that deserves the leftover room of its row (a long dropdown, e.g. a claim). */
.field-row .ctrl-wrapper.wide { flex: 3 1 180px; }
/* .label-fit: the wrapper is as wide as its one-line label and the field stays
   a short 110px box under it (a number next to a long label). Do not pass
   makeControl 'width' with it - that sizes the wrapper, and the label spills. */
.field-row .ctrl-wrapper.label-fit { flex: 0 0 auto; margin-right: var(--net-spacing-lg); }
.field-row .ctrl-wrapper.label-fit > label { white-space: nowrap; }
.field-row .ctrl-wrapper.label-fit .ctrl-field { width: 110px; }

.form-section { font-size: var(--net-font-size-xs); text-transform: uppercase; letter-spacing: 0.06em; color: var(--net-text-muted); margin: var(--net-spacing-sm) 0 var(--net-spacing-sm); padding-bottom: 0.25rem; border-bottom: 1px solid var(--net-border); }
.field-row > .ctrl-wrapper > .btn { min-height: 40px; }


/* Switch (checkbox with 'switch' => true): label above, a track with a knob, Yes / No beside it. */
.ctrl-switch::before { content: none !important; }
.ctrl-switch .ctrl-switch-label { display: block; font-size: var(--net-font-size-sm); font-weight: 600; margin-bottom: 0.25rem; white-space: nowrap; }
/* Three classes deep on purpose: page rules like ".tl-line .ctrl-wrapper > label" restyle field labels, and this label is the switch itself. */
.ctrl-wrapper.ctrl-switch > label.ctrl-check { position: relative; display: inline-flex; align-items: center; gap: 0.5rem; min-height: 34px; margin: 0; font-size: var(--net-font-size-sm); color: inherit; white-space: nowrap; cursor: pointer; }
.ctrl-switch .switch-track { display: inline-block; }
.ctrl-switch .ctrl-field { position: absolute; opacity: 0; width: 44px; height: 24px; margin: 0; cursor: pointer; }
.ctrl-switch .switch-track { flex: 0 0 44px; width: 44px; height: 24px; border-radius: 12px; background: var(--net-bg-accent); border: 1px solid var(--net-border); position: relative; transition: background var(--net-transition-fast), border-color var(--net-transition-fast); }
.ctrl-switch .switch-track::after { content: ''; position: absolute; top: 2px; left: 2px; width: 18px; height: 18px; border-radius: 50%; background: var(--net-text-muted); transition: transform var(--net-transition-fast), background var(--net-transition-fast); }
.ctrl-switch .ctrl-field:checked + .switch-track { background: var(--net-color-primary); border-color: var(--net-color-primary); }
.ctrl-switch .ctrl-field:checked + .switch-track::after { transform: translateX(20px); background: #fff; }
.ctrl-switch .ctrl-field:focus-visible + .switch-track { outline: 2px solid var(--net-color-primary); outline-offset: 2px; }
.ctrl-switch .ctrl-field:disabled + .switch-track { opacity: 0.5; }
.ctrl-switch .switch-text { font-size: var(--net-font-size-sm); min-width: 1.8rem; }
.ctrl-switch .switch-text::before { content: attr(data-off); color: var(--net-text-muted); }
.ctrl-switch .ctrl-field:checked ~ .switch-text::before { content: attr(data-on); color: var(--net-text-primary); }

.ctrl-switch-label:empty { display: none; }   /* a label-less switch (a table cell) has no empty line above it */

/* Tags control (partials/controls/tags.php, ui.tags.js): names as badges inside a box that looks like
   a field; the entry at the end takes the leftover room and wraps under the badges when it is full. */
.ctrl-tags .tags-box { display: flex; flex-wrap: wrap; align-items: center; gap: 0.3rem; min-height: 40px; padding: 0.25rem 0.4rem; background: var(--net-bg-primary); border: 1px solid var(--net-border-accent); border-radius: var(--net-border-radius-base); cursor: text; }
.ctrl-tags .tags-box:focus-within { border-color: var(--net-color-primary); box-shadow: 0 0 0 3px rgba(31, 111, 235, 0.18); }
.ctrl-tags .tags-box.is-full { border-color: var(--dp-money-bad); }
.ctrl-tags .tags-list { display: contents; }
.ctrl-tags .tag-chip { display: inline-flex; align-items: center; gap: 0.25rem; max-width: 100%; padding: 0.15rem 0.3rem 0.15rem 0.6rem; cursor: default; }
.ctrl-tags[data-readonly] .tag-chip { padding-right: 0.6rem; }
.ctrl-tags .tag-text { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ctrl-tags .tag-x { flex: 0 0 auto; width: 1.15rem; height: 1.15rem; padding: 0; border: 0; border-radius: 50%; background: transparent; color: inherit; font-size: 0.95rem; line-height: 1; cursor: pointer; opacity: 0.65; }
.ctrl-tags .tag-x:hover, .ctrl-tags .tag-x:focus-visible { opacity: 1; background: var(--dp-money-bad-soft); }
.ctrl-tags .tags-entry { flex: 1 1 7rem; min-width: 7rem; border: 0; outline: 0; background: transparent; color: inherit; font: inherit; padding: 0.15rem 0.2rem; }
.ctrl-tags .tags-suggest { display: flex; flex-wrap: wrap; gap: 0.3rem; margin-top: 0.3rem; }
.ctrl-tags .tags-suggest:empty, .ctrl-tags .tags-suggest.is-empty { display: none; }
/* A search field ('list_out', data-search): the × clears the words; the picked badges are a list of their own, one per line, cut with "…". */
.ctrl-tags[data-search] .tags-box { position: relative; padding-right: 2.1rem; }
.ctrl-tags .tags-list.is-out { display: flex; flex-direction: column; align-items: flex-start; gap: 0.3rem; min-width: 0; }
.ctrl-tags .tags-list.is-out .tag-chip { max-width: 100%; flex: 0 0 auto; height: 1.5rem; box-sizing: border-box; }
/* At most 6 lines; more scroll inside the list (owner, 2026-10-08). */
.ctrl-tags .tags-list.is-out { max-height: calc(6 * 1.5rem + 5 * 0.3rem); overflow-y: auto; overflow-x: hidden; scrollbar-width: thin; scrollbar-gutter: stable; }
.ctrl-tags .tag-suggest { max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; border: 1px dashed var(--net-border); border-radius: var(--net-border-radius-xl); background: transparent; color: var(--net-text-muted); font-size: var(--net-font-size-xs); padding: 0.05rem 0.5rem; cursor: pointer; }
.ctrl-tags .tag-suggest:hover, .ctrl-tags .tag-suggest:focus-visible { color: var(--net-text-primary); border-style: solid; border-color: var(--net-color-primary); }
.ctrl-tags .tag-suggest.is-contact { color: var(--net-text-primary); border-style: solid; }   /* a person on the claim */
.ctrl-tags .tag-suggest.is-role { font-style: italic; }
.ctrl-tags .tag-more { border: 0; background: transparent; color: var(--net-color-primary); font-size: var(--net-font-size-xs); padding: 0.05rem 0.3rem; cursor: pointer; }
.ctrl-tags .tag-more:hover { text-decoration: underline; }
.ctrl-tags .tag-chip[title] { cursor: help; }
/* A page-coloured field (DpTags.recolor: the people fields): quick-adds carry a ring of the colour their badge will have. */
.ctrl-tags .tag-suggest.tg-s { border-color: color-mix(in srgb, var(--tg) 65%, transparent); }
/* The clear "×" inside every search / filter text box (ui.search-clear.js). */
.search-clear-box { position: relative; display: block; }
.search-clear-box > .ctrl-field { padding-right: 2.1rem; }
.search-clear { position: absolute; top: 50%; right: 0.4rem; transform: translateY(-50%); width: 1.5rem; height: 1.5rem; padding: 0; border: 0; border-radius: 50%; background: transparent; color: var(--net-text-muted); font-size: 1.15rem; line-height: 1; cursor: pointer; }
.search-clear:hover, .search-clear:focus-visible { background: var(--net-bg-accent); color: var(--net-text-primary); }
.search-clear[hidden] { display: none; }
/* Money fields (ui.money.js): the formatted amount laid over the field while it is not being edited; the
   field itself keeps the plain number (its text is only hidden, never changed). */
.money-box { position: relative; display: block; }
.money-view { position: absolute; inset: 0; display: none; align-items: center; pointer-events: none; white-space: nowrap; overflow: hidden; font-variant-numeric: tabular-nums; color: var(--net-text-primary); }
.money-box.has-money:not(:focus-within) > .money-view { display: flex; }
.money-box.has-money:not(:focus-within) > .money-view[style*="right"] { justify-content: flex-end; }
.money-box.has-money:not(:focus-within) > input { color: transparent; }
.money-box.has-money:not(:focus-within) > input::placeholder { color: transparent; }
.money-box > input[readonly] ~ .money-view, .money-box > input:disabled ~ .money-view { color: var(--net-text-muted); }
/* a number field's spinner would sit under the amount */
.money-box.has-money:not(:focus-within) > input[type="number"] { -moz-appearance: textfield; }
.money-box.has-money:not(:focus-within) > input[type="number"]::-webkit-inner-spin-button { visibility: hidden; }

/* Roles control (php/partials/controls/roles.php, ui.roles.js): ONE box that looks like every other field and shows
   the main role (+N); it opens a floating list of every role, grouped, with a checkmark beside each one held - tick
   several; ★ on a ticked row makes it the main role. The list is position: fixed so a table never clips it. */
.ctrl-roles { position: relative; }
.ctrl-roles .roles-main {
    display: inline-flex; align-items: center; gap: 0.4rem; font: inherit; color: var(--net-text-primary); background: var(--net-bg-primary);
    border: 1px solid var(--net-border-accent); border-radius: var(--net-border-radius-base); padding: 0.5rem 0.6rem; min-height: 40px; width: 100%;
    text-align: left; cursor: pointer; transition: border-color var(--net-transition-fast), box-shadow var(--net-transition-fast);
}
.ctrl-roles .roles-main:focus-visible { outline: none; border-color: var(--net-color-primary); box-shadow: 0 0 0 3px rgba(31, 111, 235, 0.18); }
.ctrl-roles .roles-main[aria-expanded="true"] { border-color: var(--net-color-primary); }
.ctrl-roles .roles-main:disabled { background: var(--net-bg-accent); color: var(--net-text-muted); cursor: default; }
.ctrl-roles .roles-main.is-empty .roles-text { color: var(--net-text-muted); }
.ctrl-roles .roles-text { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ctrl-roles .roles-plus { flex: 0 0 auto; font-size: var(--net-font-size-xs); font-weight: 700; padding: 0 0.4rem; border-radius: var(--net-border-radius-xl); background: var(--net-bg-accent); color: var(--net-text-muted); }
/* The caret is drawn (an SVG chevron the size and colour of a native <select>'s), never a text glyph - the ▾ was tiny. */
.ctrl-roles .roles-caret { flex: 0 0 auto; display: inline-flex; align-items: center; color: var(--net-text-primary); margin-right: -0.1rem; }
.ctrl-roles .roles-caret svg { width: 12px; height: 12px; display: block; }
.ctrl-roles .roles-main:disabled .roles-caret { color: var(--net-text-muted); }
.ctrl-roles.fit .roles-main { width: auto; min-width: 12rem; }
table.data .table-pick .roles-main { width: 100%; max-width: 24rem; }
.roles-pop { position: fixed; z-index: 1200; min-width: 16rem; max-width: 22rem; overflow: auto; padding: 0.35rem 0; background: var(--net-bg-card); border: 1px solid var(--net-border-accent); border-radius: var(--net-border-radius-md); box-shadow: 0 10px 30px rgba(0, 0, 0, 0.45); }
.roles-pop[hidden] { display: none; }
.roles-group-title { padding: 0.35rem 0.75rem 0.15rem; font-size: var(--net-font-size-xs); font-weight: 700; text-transform: uppercase; letter-spacing: 0.05em; color: var(--net-text-muted); }
.roles-opt { display: flex; align-items: center; gap: 0.5rem; padding: 0.3rem 0.5rem 0.3rem 0.75rem; cursor: pointer; font-size: var(--net-font-size-sm); color: var(--net-text-primary); }
.roles-opt:hover { background: var(--dp-hover-row); }
.roles-opt input[type="checkbox"] { position: absolute; opacity: 0; width: 1px; height: 1px; pointer-events: none; }
.roles-opt .roles-check { flex: 0 0 auto; width: 1.25rem; height: 1.25rem; display: inline-flex; align-items: center; justify-content: center; border-radius: 4px; border: 1px solid var(--net-border-accent); font-size: 0.85rem; font-weight: 700; color: transparent; background: var(--net-bg-primary); }
.roles-opt.is-on .roles-check { color: #fff; background: color-mix(in srgb, var(--tg, #7d8796) 85%, #000); border-color: var(--tg, #7d8796); }
.roles-opt input:focus-visible + .roles-check { box-shadow: 0 0 0 3px rgba(31, 111, 235, 0.25); }
.roles-opt .roles-label { flex: 1 1 auto; }
.roles-opt.is-on .roles-label { font-weight: 600; }
.roles-opt .roles-star { flex: 0 0 auto; border: 0; background: transparent; color: var(--net-text-muted); font-size: 0.9rem; padding: 0 0.3rem; cursor: pointer; opacity: 0; }
.roles-opt:hover .roles-star, .roles-opt.is-main .roles-star { opacity: 1; }
.roles-opt.is-main .roles-star { color: var(--net-color-warning, #e0a04a); }
.roles-opt.is-main::after { content: main; font-size: var(--net-font-size-xs); color: var(--net-text-muted); }
/* Several roles as read-only badges (a contact card's face, a My Team row you cannot change, your profile). */
.role-badges { display: inline-flex; flex-wrap: wrap; gap: 0.25rem; }
