/* Shared visual styles for Kaston Family Office. Reused across
   module list/table pages so every module's data table looks and
   behaves the same without re-styling per page. */

:root {
    --kfo-purple-900: #2e1065;
    --kfo-purple-700: #4c1d95;
    --kfo-purple-600: #5b21b6;
    --kfo-purple-100: #ede9fe;
    --kfo-purple-200: #ddd6fe;
    --kfo-gray-500: #6b7280;
    --kfo-gray-100: #f3f4f6;
    --kfo-gray-200: #e5e7eb;
    --kfo-green-700: #166534;
    --kfo-green-100: #dcfce7;
    --kfo-red-700: #b91c1c;
    --kfo-red-100: #fee2e2;
    --kfo-amber-700: #92400e;
    --kfo-amber-100: #fef3c7;
}

body {
    margin: 0;
    font-family: system-ui, sans-serif;
    background: #faf9fc;
    color: #1f2937;
    display: flex;
    flex-direction: column;
    min-height: 100vh;
}

main {
    padding: 24px;
    flex: 1 1 auto;
    display: flex;
    flex-direction: column;
    min-height: 0;
}

/* An element with the hidden attribute stays hidden even when one of its
   classes sets display (e.g. a .btn-secondary "Finish" button that a
   script reveals by clearing hidden). */
[hidden] {
    display: none !important;
}

h1 {
    margin: 0 0 16px;
    font-size: 1.5rem;
    color: var(--kfo-purple-900);
}

.page-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    margin-bottom: 20px;
}

.page-header h1 {
    margin: 0;
}

.btn-primary {
    display: inline-block;
    background: var(--kfo-purple-600);
    color: #fff;
    text-decoration: none;
    padding: 8px 16px;
    border-radius: 6px;
    font-size: 0.9rem;
    font-weight: 600;
    border: none;
    cursor: pointer;
}

.btn-primary:hover {
    background: var(--kfo-purple-900);
}

.data-card {
    background: #fff;
    border: 1px solid var(--kfo-gray-200);
    border-radius: 10px;
    overflow: hidden;
    box-shadow: 0 1px 2px rgba(0, 0, 0, 0.04);
}

.data-table {
    width: 100%;
    border-collapse: collapse;
    font-size: 0.9rem;
}

.data-table thead th {
    text-align: left;
    padding: 12px 16px;
    background: var(--kfo-purple-100);
    color: var(--kfo-purple-900);
    font-weight: 600;
    border-bottom: 1px solid var(--kfo-purple-200);
    white-space: nowrap;
}

.data-table tbody td {
    padding: 12px 16px;
    border-bottom: 1px solid var(--kfo-gray-100);
    vertical-align: middle;
}

.data-table tbody tr:last-child td {
    border-bottom: none;
}

.data-table tbody tr:nth-child(even) {
    background: #fbfaff;
}

.data-table tbody tr:hover {
    background: var(--kfo-purple-100);
}

.data-table .actions-cell {
    white-space: nowrap;
}

.status-badge {
    display: inline-block;
    padding: 3px 10px;
    border-radius: 999px;
    font-size: 0.78rem;
    font-weight: 600;
}

.status-badge--active {
    background: var(--kfo-green-100);
    color: var(--kfo-green-700);
}

.status-badge--disabled {
    background: var(--kfo-gray-100);
    color: var(--kfo-gray-500);
}

/* Passengers marked as flying most often (shown after the name), and a
   Landscaping feature's type in its page header. */
.status-badge--frequent,
.status-badge--type {
    background: var(--kfo-purple-100);
    color: var(--kfo-purple-700);
}

.action-link {
    background: none;
    border: none;
    padding: 4px 8px;
    margin: 0;
    font: inherit;
    font-size: 0.85rem;
    color: var(--kfo-purple-700);
    text-decoration: none;
    border-radius: 4px;
    cursor: pointer;
}

.action-link:hover {
    background: var(--kfo-purple-100);
    color: var(--kfo-purple-900);
    text-decoration: none;
}

.action-link--danger {
    color: var(--kfo-red-700);
}

.action-link--danger:hover {
    background: var(--kfo-red-100);
    color: var(--kfo-red-700);
}

/* Two-column master-detail layout, used by the Properties detail
   screen (a page list on the left, the selected page's editor on
   the right). */
.master-detail {
    display: flex;
    align-items: stretch;
    gap: 20px;
    flex-wrap: wrap;
    flex: 1 1 auto;
    min-height: 0;
}

.master-detail-nav {
    flex: 0 0 260px;
    display: flex;
    flex-direction: column;
    align-self: stretch;
    overflow-y: auto;
}

.master-detail-panel {
    flex: 1 1 480px;
    min-width: 0;
    display: flex;
    flex-direction: column;
    align-self: stretch;
    padding: 12px 20px 20px;
}

.master-detail-list {
    list-style: none;
    margin: 0;
    padding: 0;
}

.master-detail-list li {
    border-bottom: 1px solid var(--kfo-gray-100);
}

.master-detail-list li:last-child {
    border-bottom: none;
}

.master-detail-list a {
    display: block;
    padding: 10px 16px;
    color: var(--kfo-purple-700);
    text-decoration: none;
    font-size: 0.9rem;
}

.master-detail-list a:hover {
    background: var(--kfo-purple-100);
}

.master-detail-list a.selected {
    background: var(--kfo-purple-100);
    color: var(--kfo-purple-900);
    font-weight: 600;
}

/* Page editor (inside .master-detail-panel): fills the panel's full
   height so Quill's toolbar sits at natural height and the editor
   body consumes the rest, instead of everything collapsing to
   content height. Quill's own CSS already gives .ql-container and
   .ql-editor "height: 100%", but a percentage height only resolves
   against a parent with a *definite* height — nothing above them
   had one until .page-editor/.page-editor-body establish it via
   flex-grow (not percentages), which is what makes that chain
   finally have something to fill. */
.page-editor {
    flex: 1 1 auto;
    min-height: 0;
    display: flex;
    flex-direction: column;
    height: 100%;
}

/* h2 has no reset elsewhere in this file (only h1 does), so it was
   falling back to its ~20px browser-default top margin on top of
   .master-detail-panel's own padding — by far the biggest
   contributor to the gap above a CORE page's title. */
.page-editor h2 {
    margin: 0 0 12px;
}

.page-editor-body {
    flex: 1 1 auto;
    min-height: 0;
    display: flex;
    flex-direction: column;
}

/* flex-grow (not the element's own height:100%) is what correctly
   leaves room for the toolbar's natural height above it — height:100%
   alone doesn't account for a sibling's size. */
.page-editor-body .ql-toolbar.ql-snow {
    flex: 0 0 auto;
    border-color: var(--kfo-gray-200);
}

.page-editor-body .ql-container.ql-snow {
    flex: 1 1 auto;
    min-height: 0;
    border-color: var(--kfo-gray-200);
    font-size: 1rem;
}

/* Login / auth screens: a single centered card on the light page
   background, with a dark purple strip across the top. */
.auth-page {
    align-items: center;
    justify-content: center;
    box-sizing: border-box;
    border-top: 6px solid var(--kfo-purple-900);
}

/* .auth-card is a <main>, so undo main's flex-grow/flex layout. */
.auth-card {
    display: block;
    flex: 0 0 auto;
    box-sizing: border-box;
    width: 100%;
    max-width: 380px;
    margin: 16px;
    padding: 32px;
    background: #fff;
    border: 1px solid var(--kfo-gray-200);
    border-radius: 12px;
    box-shadow: 0 4px 16px rgba(46, 16, 101, 0.08);
    text-align: center;
}

.auth-logo {
    display: block;
    width: 180px;
    max-width: 100%;
    height: auto;
    margin: 0 auto 16px;
}

.auth-card h1 {
    margin: 0 0 8px;
}

.auth-tagline {
    margin: 0 0 24px;
    font-size: 0.85rem;
    color: var(--kfo-gray-500);
}

.form-field {
    text-align: left;
    margin-bottom: 16px;
}

.form-label {
    display: block;
    margin-bottom: 6px;
    font-size: 0.85rem;
    font-weight: 600;
    color: var(--kfo-purple-900);
}

.form-input {
    display: block;
    width: 100%;
    box-sizing: border-box;
    padding: 10px 12px;
    border: 1px solid var(--kfo-gray-200);
    border-radius: 6px;
    font: inherit;
    font-size: 1rem;
}

.form-input:focus {
    outline: none;
    border-color: var(--kfo-purple-600);
    box-shadow: 0 0 0 3px var(--kfo-purple-100);
}

.btn-block {
    display: block;
    width: 100%;
    padding: 10px;
    font-size: 1rem;
}

/* Soft red error box. .auth-error is login.php's name for it;
   .form-alert is the same look for every other form (see "Forms"). */
.auth-error,
.form-alert {
    text-align: left;
    margin-bottom: 16px;
    padding: 10px 12px;
    border-radius: 6px;
    font-size: 0.9rem;
    background: var(--kfo-red-100);
    color: var(--kfo-red-700);
}

.auth-footer {
    margin: 24px 0 0;
    font-size: 0.75rem;
    color: var(--kfo-gray-500);
}

/* Public home page (index.php, #0113): the login page's card, a little
   wider, with a Contact block and a footer under the card. Unlike the
   app's internal screens, it must also read well at phone width: the
   card shrinks to the screen with 16px margins, never scrolling sideways. */
.home-card {
    width: calc(100% - 32px);
    max-width: 520px;
}

.home-description {
    margin: 0 0 24px;
    line-height: 1.5;
    color: #1f2937;
}

.home-contact {
    margin: 0 0 24px;
    padding-top: 20px;
    border-top: 1px solid var(--kfo-gray-200);
}

.home-contact h2 {
    margin: 0 0 8px;
    font-size: 1.1rem;
    color: var(--kfo-purple-900);
}

.home-contact address {
    font-style: normal;
    line-height: 1.6;
    color: #1f2937;
}

.home-contact a {
    color: var(--kfo-purple-700);
}

/* .btn-block is made for <button>; on a link it also needs these. */
.home-login {
    box-sizing: border-box;
    text-align: center;
}

.home-login:focus-visible,
.home-contact a:focus-visible {
    outline: 3px solid var(--kfo-purple-600);
    outline-offset: 2px;
}

.home-footer {
    margin: 0 16px 24px;
    font-size: 0.8rem;
    color: var(--kfo-gray-500);
    text-align: center;
}

@media (max-width: 480px) {
    .home-card {
        padding: 24px 20px;
    }
}

/* Empty state: shown inside a .data-card when a list has nothing in
   it yet (or a module isn't built), instead of a bare header-only
   table. The page passes in an inline SVG icon (stroke currentColor)
   and, optionally, a .btn-primary action. */
.empty-state {
    display: flex;
    flex-direction: column;
    align-items: center;
    padding: 48px 24px;
    text-align: center;
}

.empty-state__icon {
    width: 40px;
    height: 40px;
    margin-bottom: 12px;
    color: var(--kfo-purple-200);
}

.empty-state__title {
    margin: 0;
    font-size: 1.05rem;
    font-weight: 600;
    color: var(--kfo-purple-900);
}

.empty-state__text {
    max-width: 380px;
    margin: 6px auto 18px;
    font-size: 0.9rem;
    color: var(--kfo-gray-500);
}

/* Forms: one look for every create/edit form (login.php keeps its own
   auth card). A form sits in a .form-card; each field is a .form-field
   with a .form-label and a .form-input (inputs, selects and textareas
   alike); errors go in a .form-alert at the top; buttons sit in a
   .form-actions row. */
.form-card {
    box-sizing: border-box;
    max-width: 560px;
    margin-bottom: 20px;
    padding: 24px;
    background: #fff;
    border: 1px solid var(--kfo-gray-200);
    border-radius: 10px;
    box-shadow: 0 1px 2px rgba(0, 0, 0, 0.04);
}

/* For a form holding a rich-text editor, which needs the full width. */
.form-card--wide {
    max-width: none;
}

.form-card__title {
    margin: 0 0 16px;
    font-size: 1.1rem;
    color: var(--kfo-purple-900);
}

/* Selects get the input look plus a chevron in place of the native
   arrow. (Data-URI colors can't use var(); #6b7280 is --kfo-gray-500.) */
select.form-input {
    appearance: none;
    padding-right: 36px;
    background: #fff url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12'%3E%3Cpath d='M2.5 4.5 6 8l3.5-3.5' fill='none' stroke='%236b7280' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E") no-repeat right 12px center;
}

textarea.form-input {
    min-height: 96px;
    resize: vertical;
}

.form-hint {
    margin-top: 6px;
    font-size: 0.8rem;
    color: var(--kfo-gray-500);
}

/* Checkbox row: box and label inline; a hint lines up under the label
   text, not under the box. */
.form-check {
    display: grid;
    grid-template-columns: auto 1fr;
    column-gap: 8px;
    align-items: center;
}

.form-check input[type="checkbox"] {
    width: 16px;
    height: 16px;
    margin: 0;
    accent-color: var(--kfo-purple-600);
}

.form-check label {
    font-size: 0.9rem;
    color: #1f2937;
    cursor: pointer;
}

.form-check .form-hint {
    grid-column: 2;
}

/* A titled group of fields inside a form (a <fieldset> with a <legend>),
   e.g. User Manager's per-module Admin-only checkboxes: no fieldset
   box, a light divider above, and a title a step up from .form-label.
   The legend is floated so it sits below the divider rather than in it;
   everything after it clears the float (not just the next element,
   which may be hidden, like the booking form's "No passengers yet."
   note). */
.form-section {
    min-width: 0;
    margin: 8px 0 16px;
    padding: 16px 0 0;
    border: 0;
    border-top: 1px solid var(--kfo-gray-200);
}

.form-section__title {
    float: left;
    width: 100%;
    margin-bottom: 12px;
    padding: 0;
    font-size: 0.9rem;
    font-weight: 700;
    color: var(--kfo-purple-900);
}

.form-section__title ~ * {
    clear: both;
}

.form-section > .form-field:last-child {
    margin-bottom: 0;
}

/* .form-alert shares .auth-error's red box (above). Several messages
   go in a plain list. */
.form-alert ul {
    margin: 0;
    padding: 0;
    list-style: none;
}

.form-alert li + li {
    margin-top: 4px;
}

/* An error shown directly under the field it's about. */
.form-alert--inline {
    margin: 6px 0 0;
}

/* Green "saved" confirmation, same box shape; shown above the form
   cards, so it matches their width. */
.form-alert--success {
    box-sizing: border-box;
    max-width: 560px;
    background: var(--kfo-green-100);
    color: var(--kfo-green-700);
}

.form-actions {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 12px;
    margin-top: 8px;
}

/* Same size and shape as .btn-primary (its 1px border is taken out of
   the padding), for Cancel and other secondary actions; works on <a>
   and <button>. */
.btn-secondary {
    display: inline-block;
    padding: 7px 15px;
    border: 1px solid var(--kfo-gray-200);
    border-radius: 6px;
    background: #fff;
    color: #1f2937;
    font-family: inherit;
    font-size: 0.9rem;
    font-weight: 600;
    text-decoration: none;
    cursor: pointer;
}

.btn-secondary:hover {
    background: var(--kfo-purple-100);
    border-color: var(--kfo-purple-200);
    color: var(--kfo-purple-900);
}

/* Red button for a destructive action that stands alone, e.g. "Delete
   feature type" on Landscaping's Feature Types edit screen. Same shape
   as .btn-secondary. */
.btn-danger {
    display: inline-block;
    padding: 7px 15px;
    border: 1px solid var(--kfo-red-700);
    border-radius: 6px;
    background: var(--kfo-red-700);
    color: #fff;
    font-family: inherit;
    font-size: 0.9rem;
    font-weight: 600;
    text-decoration: none;
    cursor: pointer;
}

.btn-danger:hover {
    background: #991b1b;
    border-color: #991b1b;
}

/* Table cell menu in the Properties page editors (built by
   properties_quill_create_js()): a small round "⋮" button pinned to the
   active cell's upper-right corner, muted until hovered or focused, that
   opens a dropdown of row/column/table actions. Both sit inside Quill's
   .ql-snow container, so the menu icons pick up Quill's own .ql-stroke
   styling, the same as the editor toolbar's table button. */
.kfo-cell-menu-trigger {
    position: absolute;
    z-index: 10;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    box-sizing: border-box;
    width: 20px;
    height: 20px;
    padding: 0;
    border: 1px solid var(--kfo-gray-200);
    border-radius: 50%;
    background: #fff;
    color: var(--kfo-gray-500);
    box-shadow: 0 1px 2px rgba(0, 0, 0, 0.08);
    opacity: 0.45;
    cursor: pointer;
    transition: opacity 0.15s;
}

.kfo-cell-menu-trigger[hidden],
.kfo-cell-menu[hidden] {
    display: none;
}

.kfo-cell-menu-trigger svg {
    width: 14px;
    height: 14px;
    fill: currentColor;
}

.kfo-cell-menu-trigger:hover,
.kfo-cell-menu-trigger:focus-visible,
.kfo-cell-menu-trigger[aria-expanded="true"] {
    opacity: 1;
    border-color: var(--kfo-purple-200);
    background: var(--kfo-purple-100);
    color: var(--kfo-purple-700);
    outline: none;
}

.kfo-cell-menu {
    position: absolute;
    z-index: 11;
    min-width: 200px;
    padding: 4px 0;
    background: #fff;
    border: 1px solid var(--kfo-gray-200);
    border-radius: 8px;
    box-shadow: 0 8px 24px rgba(46, 16, 101, 0.12);
}

.kfo-cell-menu__item {
    display: flex;
    align-items: center;
    gap: 8px;
    box-sizing: border-box;
    width: 100%;
    padding: 6px 12px;
    border: 0;
    background: none;
    color: #1f2937;
    font-family: system-ui, sans-serif;
    font-size: 0.85rem;
    line-height: 1.4;
    text-align: left;
    cursor: pointer;
}

.kfo-cell-menu__item svg {
    flex: 0 0 auto;
    width: 16px;
    height: 16px;
}

.kfo-cell-menu__item:hover,
.kfo-cell-menu__item:focus-visible {
    background: var(--kfo-purple-100);
    outline: none;
}

.kfo-cell-menu__item:hover .ql-stroke,
.kfo-cell-menu__item:focus-visible .ql-stroke {
    stroke: var(--kfo-purple-600);
}

.kfo-cell-menu__divider {
    height: 1px;
    margin: 4px 0;
    background: var(--kfo-gray-200);
}

.kfo-cell-menu__item--danger {
    color: var(--kfo-red-700);
}

.kfo-cell-menu .kfo-cell-menu__item--danger .ql-stroke {
    stroke: var(--kfo-red-700);
}

.kfo-cell-menu__item--danger:hover,
.kfo-cell-menu__item--danger:focus-visible {
    background: var(--kfo-red-100);
}

/* Row "⋮" menus in Properties (properties_row_menu_script()): one per
   free-form page in the detail screen's page sidebar (core pages have
   none), and one per property in the index list. Same look as the
   editor's table cell menu (.kfo-cell-menu), but separate: muted dot that
   brightens on its row's hover/focus, white rounded dropdown, red delete
   item. The dropdown is position: fixed (placed by script under its dot)
   so a card's overflow doesn't clip it. */
.master-detail-list li.has-row-menu {
    position: relative;
}

.master-detail-list li.has-row-menu > a {
    padding-right: 44px;
}

/* Core pages in the sidebar: title on the left, CORE badge at the
   right edge, vertically centered, its right edge 10px in, lined up with
   the "⋮" button on free-form rows. A long title wraps instead of running
   under the badge. */
.master-detail-list li.has-core-badge > a {
    display: flex;
    align-items: center;
    gap: 8px;
    padding-right: 10px;
}

.master-detail-list__title {
    flex: 1 1 auto;
    min-width: 0;
    overflow-wrap: anywhere;
}

.status-badge--core {
    flex: none;
    background: var(--kfo-purple-100);
    color: var(--kfo-purple-700);
}

/* In the page sidebar the dot sits over the right end of the row. */
.master-detail-list li.has-row-menu .kfo-row-menu-trigger {
    position: absolute;
    top: 50%;
    right: 10px;
    margin-top: -12px;
}

.kfo-row-menu-trigger {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    box-sizing: border-box;
    width: 24px;
    height: 24px;
    padding: 0;
    border: 1px solid transparent;
    border-radius: 50%;
    background: none;
    color: var(--kfo-gray-500);
    opacity: 0.35;
    cursor: pointer;
    transition: opacity 0.15s;
}

.kfo-row-menu-trigger svg {
    width: 14px;
    height: 14px;
    fill: currentColor;
}

.master-detail-list li.has-row-menu:hover .kfo-row-menu-trigger,
.master-detail-list li.has-row-menu:focus-within .kfo-row-menu-trigger {
    opacity: 1;
}

/* In a table row (index list) the dot sits inline after the row's other
   actions, and brightens when the row is hovered or focused. */
.data-table .kfo-row-menu-trigger {
    margin-left: 4px;
    vertical-align: middle;
}

.data-table tbody tr:hover .kfo-row-menu-trigger,
.data-table tbody tr:focus-within .kfo-row-menu-trigger {
    opacity: 1;
}

.kfo-row-menu-trigger:hover,
.kfo-row-menu-trigger:focus-visible,
.kfo-row-menu-trigger[aria-expanded="true"] {
    opacity: 1;
    border-color: var(--kfo-purple-200);
    background: #fff;
    color: var(--kfo-purple-700);
    outline: none;
}

.kfo-row-menu {
    position: fixed;
    z-index: 1000;
    min-width: 160px;
    padding: 4px 0;
    background: #fff;
    border: 1px solid var(--kfo-gray-200);
    border-radius: 8px;
    box-shadow: 0 8px 24px rgba(46, 16, 101, 0.12);
}

.kfo-row-menu[hidden] {
    display: none;
}

.kfo-row-menu__item {
    display: block;
    box-sizing: border-box;
    width: 100%;
    padding: 7px 14px;
    border: 0;
    background: none;
    color: #1f2937;
    font: inherit;
    font-size: 0.85rem;
    line-height: 1.4;
    text-align: left;
    cursor: pointer;
}

.kfo-row-menu__item:hover,
.kfo-row-menu__item:focus-visible {
    background: var(--kfo-purple-100);
    outline: none;
}

.kfo-row-menu__item--danger {
    color: var(--kfo-red-700);
}

.kfo-row-menu__item--danger:hover,
.kfo-row-menu__item--danger:focus-visible {
    background: var(--kfo-red-100);
}

/* Renaming a free-form page in the sidebar (#0072, "Rename page" in the
   row's "⋮" menu): the row's link is hidden and a small text box takes
   its place, the same height and inset, leaving room for the "⋮" button
   at the right. A row being renamed keeps its "selected" highlight. */
.master-detail-list li.is-renaming > a {
    display: none;
}

.master-detail-list li.is-renaming--selected {
    background: var(--kfo-purple-100);
}

.kfo-rename-input {
    display: block;
    box-sizing: border-box;
    width: calc(100% - 50px);
    margin: 5px 0 5px 10px;
    padding: 4px 6px;
    border: 1px solid var(--kfo-purple-200);
    border-radius: 4px;
    background: #fff;
    color: var(--kfo-purple-900);
    font: inherit;
    font-size: 0.9rem;
}

.kfo-rename-input:focus {
    outline: 2px solid var(--kfo-purple-600);
    outline-offset: 0;
}

/* While the new title is being saved. */
.master-detail-list li.is-saving > a {
    opacity: 0.6;
}

/* The rename result message: the editor notice's look, shown under the
   page list instead of floating over the editor. */
.kfo-rename-notice {
    position: static;
    max-width: none;
    margin: 8px 12px 4px;
    box-shadow: none;
}

/* Horizontal divider (<hr>) in the Properties page editors, inserted by
   the toolbar's divider button (properties_quill_create_js()): a thin
   gray line instead of the browser's default beveled rule. */
.ql-editor hr {
    height: 0;
    margin: 16px 0;
    border: 0;
    border-top: 1px solid var(--kfo-gray-200);
}

/* Toolbar in both Properties page editors (the page editor and the Add
   Page form), right-aligned so it doesn't draw the eye from the page
   content. Snow's groups (.ql-formats) are inline-block, so text-align
   moves them, wrapped rows included; inside each group, text is set back
   to left so the "Text style" picker's label and dropdown items read as
   before. The last group drops Snow's 15px gap after each group, which
   would otherwise leave it short of the right edge. */
.ql-toolbar.ql-snow {
    text-align: right;
}

.ql-toolbar.ql-snow .ql-formats {
    text-align: left;
}

.ql-toolbar.ql-snow .ql-formats:last-child {
    margin-right: 0;
}

/* Toolbar tooltips in the Properties page editors: a small dark label
   drawn just above each control from its data-tooltip attribute (set by
   properties_quill_create_js()), on hover or keyboard focus. Used
   instead of the native title tooltip, which appears under the mouse
   pointer and gets covered by it. ::after because Snow already uses
   ::before on the "Text style" picker label for its current value. */
.ql-toolbar.ql-snow [data-tooltip] {
    position: relative;
}

.ql-toolbar.ql-snow [data-tooltip]:hover::after,
.ql-toolbar.ql-snow [data-tooltip]:focus-visible::after {
    content: attr(data-tooltip);
    position: absolute;
    bottom: calc(100% + 6px);
    left: 50%;
    z-index: 20;
    transform: translateX(-50%);
    padding: 4px 8px;
    border-radius: 4px;
    background: var(--kfo-purple-900);
    color: #f5f3ff;
    font-family: system-ui, sans-serif;
    font-size: 0.75rem;
    font-weight: 500;
    line-height: 1.3;
    white-space: nowrap;
    pointer-events: none;
    box-shadow: 0 2px 6px rgba(46, 16, 101, 0.2);
}

/* The last group's controls sit at the toolbar's right edge (the
   toolbar is right-aligned, above), so a centered label would stick out
   past the editor's card; anchor it to the control's right edge instead. */
.ql-toolbar.ql-snow .ql-formats:last-child [data-tooltip]:hover::after,
.ql-toolbar.ql-snow .ql-formats:last-child [data-tooltip]:focus-visible::after {
    left: auto;
    right: 0;
    transform: none;
}

/* Breadcrumb heading, the page's one h1 on every Property Notes and
   Landscaping screen (render_breadcrumb() in lib/layout.php; #0057,
   #0071), e.g. "<Category> / <Type> / <Property name> / <Page title>".
   Smaller than a plain page h1 (1.5rem). The category and the "/" are
   muted plain text; the type and the property are muted purple links
   (to the module's list and to the property's first page); the open
   page's title, the current location, stays bold. */
.kfo-breadcrumb {
    min-width: 0;
    font-size: 1.125rem;
    margin-right: 16px;
    overflow-wrap: anywhere;
}

.kfo-breadcrumb__link {
    color: var(--kfo-purple-600);
    font-weight: 500;
    text-decoration: none;
    border-radius: 3px;
}

.kfo-breadcrumb__link:hover {
    color: var(--kfo-purple-900);
    text-decoration: underline;
}

.kfo-breadcrumb__link:focus-visible {
    outline: 2px solid var(--kfo-purple-600);
    outline-offset: 2px;
}

.kfo-breadcrumb__sep {
    color: var(--kfo-gray-500);
    font-weight: 400;
}

.kfo-breadcrumb__category {
    color: var(--kfo-gray-500);
    font-weight: 400;
}

/* The current segment: plain text in the h1's own strong style. */
.kfo-breadcrumb__current {
    color: var(--kfo-purple-900);
    font-weight: 700;
}

/* Keep "+ Add Page" on one line beside a long, wrapping name. */
.page-header > .kfo-breadcrumb ~ .btn-primary {
    flex-shrink: 0;
    white-space: nowrap;
}

/* Attachments in the Properties page editors (#0058). Image attachments
   show as small thumbnails (the browser scales the stored image); click
   one to open the full image. The notice is the small status/error
   message the attach button shows in the editor's corner.
   The thumbnail selector starts with .ql-snow so it outranks Quill's own
   ".ql-snow .ql-editor img { max-width: 100% }": both were (0,2,1), and
   quill.snow.css loads after this file, so Quill's rule won the tie and
   images showed at full width. */
.ql-snow .ql-editor img[src^="attachment.php"] {
    max-width: 200px;
    height: auto;
    vertical-align: middle;
    border: 1px solid var(--kfo-gray-200);
    border-radius: 4px;
}

.kfo-editor-notice {
    position: absolute;
    top: 8px;
    right: 8px;
    z-index: 12;
    box-sizing: border-box;
    max-width: calc(100% - 16px);
    padding: 6px 10px;
    border-radius: 6px;
    background: var(--kfo-purple-100);
    color: var(--kfo-purple-900);
    font-family: system-ui, sans-serif;
    font-size: 0.85rem;
    line-height: 1.4;
    box-shadow: 0 2px 8px rgba(46, 16, 101, 0.12);
}

.kfo-editor-notice[hidden] {
    display: none;
}

.kfo-editor-notice--error {
    background: var(--kfo-red-100);
    color: var(--kfo-red-700);
}

/* File links in the editor's link popup (#0060; class set by
   properties_quill_create_js()): no "Visit URL:" label (the link itself
   reads "Open file"), no "Edit", and "Remove from page" (which takes
   the whole item off the page). Web links keep Quill's normal popup,
   including its plain "Remove". */
.ql-snow .ql-tooltip.kfo-file-link::before {
    content: none;
}

.ql-snow .ql-tooltip.kfo-file-link a.ql-action {
    display: none;
}

.ql-snow .ql-tooltip.kfo-file-link a.ql-remove::before {
    content: "Remove from page";
    margin-left: 12px;
}

/* Picture "⋮" menu in the editor (#0061; built by
   properties_quill_create_js()). The button and dropdown reuse the table
   cell menu's classes for the same look (.kfo-cell-menu-trigger,
   .kfo-cell-menu, .kfo-cell-menu__item). A picture's button is invisible
   and lets clicks through to the picture until it is needed: pointer
   over the picture or the button, cursor on the picture, button focused,
   or its menu open (.is-visible). Touch screens have no hover, so there
   every picture's button stays visible. */
.kfo-picture-menu-trigger {
    opacity: 0;
    pointer-events: none;
}

.kfo-picture-menu-trigger.is-visible,
.kfo-picture-menu-trigger:focus-visible {
    opacity: 1;
    pointer-events: auto;
}

@media (hover: none), (pointer: coarse) {
    .kfo-picture-menu-trigger {
        opacity: 1;
        pointer-events: auto;
    }
}

/* Properties search results (properties_route_search()): one row per
   matching page, the whole row a link; matched words in the excerpt are
   highlighted with <mark>. */
.kfo-search-summary {
    margin: 0 0 12px;
    font-size: 0.9rem;
    color: var(--kfo-gray-500);
}

.kfo-search-results {
    list-style: none;
    margin: 0;
    padding: 0;
}

.kfo-search-results li {
    border-bottom: 1px solid var(--kfo-gray-100);
}

.kfo-search-results li:last-child {
    border-bottom: none;
}

.kfo-search-result {
    display: block;
    padding: 12px 16px;
    color: inherit;
    text-decoration: none;
}

.kfo-search-result:hover,
.kfo-search-result:focus-visible {
    background: var(--kfo-purple-100);
    outline: none;
}

.kfo-search-result__path {
    display: block;
    font-size: 0.85rem;
    color: var(--kfo-gray-500);
    overflow-wrap: anywhere;
}

.kfo-search-result__path strong {
    color: var(--kfo-purple-700);
}

.kfo-search-result__excerpt {
    display: block;
    margin-top: 4px;
    font-size: 0.9rem;
    line-height: 1.5;
    color: #1f2937;
    overflow-wrap: anywhere;
}

.kfo-search-result mark {
    padding: 0 2px;
    border-radius: 2px;
    background: var(--kfo-purple-200);
    color: var(--kfo-purple-900);
    font-weight: 600;
}

/* Flight Scheduler booking (flight-scheduler/new.php). Screen 1: each
   aircraft is a large card in a grid that wraps to fewer columns as
   the screen narrows. Screen 2: one form, wider than other form cards,
   with one card per leg; each leg's fields sit two to a row and stack
   into one column on narrow screens. Nothing has a fixed width, so the
   page never scrolls sideways (iPad included). */
.flight-step-title {
    margin: 0 0 16px;
    font-size: 1.1rem;
    color: var(--kfo-purple-900);
}

.flight-aircraft-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
    gap: 16px;
    margin: 0 0 20px;
    padding: 0;
    list-style: none;
}

.flight-aircraft-card {
    display: flex;
    flex-direction: column;
    gap: 4px;
    box-sizing: border-box;
    height: 100%;
    padding: 20px;
    background: #fff;
    border: 1px solid var(--kfo-gray-200);
    border-radius: 10px;
    box-shadow: 0 1px 2px rgba(0, 0, 0, 0.04);
    color: var(--kfo-purple-900);
    text-decoration: none;
    overflow-wrap: anywhere;
}

.flight-aircraft-card:hover {
    border-color: var(--kfo-purple-200);
    background: var(--kfo-purple-100);
}

.flight-aircraft-card:focus-visible {
    outline: 2px solid var(--kfo-purple-600);
    outline-offset: 2px;
}

/* Make and model: the card's main line. */
.flight-aircraft-card__type {
    font-size: 1.25rem;
    font-weight: 700;
}

/* Tail number: the secondary line. */
.flight-aircraft-card__tail {
    font-size: 0.9rem;
    color: var(--kfo-gray-500);
}

.flight-chosen-aircraft {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: 4px 16px;
    margin-bottom: 16px;
}

.flight-chosen-aircraft .flight-step-title {
    margin: 0;
    overflow-wrap: anywhere;
}

.flight-chosen-aircraft__tail {
    margin-left: 6px;
    font-weight: 400;
    color: var(--kfo-gray-500);
}

.flight-form {
    max-width: 860px;
}

.flight-legs {
    display: flex;
    flex-direction: column;
    gap: 24px;
}

/* Each leg is a card with a purple bar down its left edge and a purple
   header band (title with the route, and Remove leg) across its top. The
   band bleeds to the card's edges with negative margins rather than the
   card clipping its overflow, which would cut off the passenger and
   airport dropdowns that open inside it. */
.flight-leg {
    min-width: 0;
    padding: 16px;
    border: 1px solid var(--kfo-gray-200);
    border-left: 4px solid var(--kfo-purple-600);
    border-radius: 8px;
    background: #fff;
}

.flight-leg__header {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: 8px;
    margin: -16px -16px 16px;
    padding: 12px 16px;
    border-bottom: 1px solid var(--kfo-purple-200);
    border-radius: 7px 7px 0 0;
    background: var(--kfo-purple-100);
}

.flight-leg__title {
    margin: 0;
    font-size: 1rem;
    color: var(--kfo-purple-900);
}

.flight-leg__title:focus {
    outline: none;
}

.flight-leg__title:focus-visible {
    outline: 2px solid var(--kfo-purple-600);
    outline-offset: 2px;
}

.flight-leg__remove[hidden] {
    display: none;
}

.flight-leg__grid {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    column-gap: 16px;
}

@media (max-width: 640px) {
    .flight-leg__grid {
        grid-template-columns: minmax(0, 1fr);
    }
}

/* Long airport names: the select is as wide as its column, never wider. */
.flight-leg select.form-input {
    max-width: 100%;
    text-overflow: ellipsis;
}

/* iOS Safari collapses empty date/time inputs; keep them input-height. */
.flight-leg input[type="date"].form-input,
.flight-leg input[type="time"].form-input {
    min-height: 44px;
    text-align: left;
}

/* A time input with its airport's time zone beside it (wraps below
   when there isn't room). */
.flight-time {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 6px 10px;
}

.flight-time .form-input {
    flex: 1 1 130px;
    width: auto;
    min-width: 0;
}

.flight-time__zone {
    flex: 1 1 auto;
    font-size: 0.8rem;
    color: var(--kfo-gray-500);
}

.flight-optional {
    font-weight: 400;
    color: var(--kfo-gray-500);
}

.flight-leg__passengers {
    margin-top: 0;
}

.flight-passenger-list {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
    gap: 8px 16px;
    margin-bottom: 16px;
}

.flight-passenger-list .form-check label {
    overflow-wrap: anywhere;
}

.flight-leg > .form-check {
    margin-bottom: 0;
}

/* Catering details (#0109): under the Catering checkbox, shown only while
   it's ticked (the script sets hidden); the hint on the left, the
   character counter on the right. */
.flight-leg__catering-details {
    max-width: 560px;
    margin: 12px 0 0;
}

.flight-leg__catering-details[hidden] {
    display: none;
}

.flight-leg__catering-meta {
    display: flex;
    justify-content: space-between;
    gap: 12px;
}

.flight-leg__catering-meta .form-hint {
    margin-bottom: 0;
}

.flight-leg__catering-count {
    flex: none;
    font-variant-numeric: tabular-nums;
}

/* Ground transportation at the leg's destination (#0085): a dropdown just
   after Catering, styled like the form's other selects, not full width. */
.flight-leg__ground {
    max-width: 260px;
    margin: 12px 0 0;
}

.flight-leg-actions {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 12px;
    margin: 16px 0 8px;
}

.flight-leg-actions .btn-secondary:disabled {
    opacity: 0.5;
    cursor: not-allowed;
}

/* A leg's estimated arrival (filled in from estimate.php): a quiet,
   read-only line under the departure time. Its height is kept while it
   is empty, so the card doesn't jump when the text arrives. */
.flight-estimate {
    min-height: 1.3em;
    margin: 6px 0 0;
    font-size: 0.85rem;
    line-height: 1.3;
    color: var(--kfo-gray-500);
}

/* Text for screen readers only (e.g. a table caption). */
.visually-hidden {
    position: absolute;
    width: 1px;
    height: 1px;
    margin: -1px;
    padding: 0;
    overflow: hidden;
    clip: rect(0, 0, 0, 0);
    white-space: nowrap;
    border: 0;
}

/* Flight Scheduler trip list (flight-scheduler/index.php). The section
   of trips waiting for the viewer's acknowledgment is highlighted with
   a purple band. Each row's date is a real link, stretched over the
   whole row so any part of it opens the trip. Tables scroll inside
   their card on narrow screens instead of widening the page. */
.flight-section-title {
    margin: 0 0 12px;
    font-size: 1.05rem;
    color: var(--kfo-purple-900);
}

.flight-section-header {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    justify-content: space-between;
    gap: 8px;
}

.flight-waiting {
    margin-bottom: 28px;
    padding: 16px;
    border-left: 4px solid var(--kfo-purple-600);
    border-radius: 10px;
    background: var(--kfo-purple-100);
}

.flight-trips {
    overflow-x: auto;
    margin-bottom: 20px;
}

.flight-trip-row {
    position: relative;
}

.flight-trip-row:hover {
    background: var(--kfo-purple-100);
}

.flight-trip-row:focus-within {
    outline: 2px solid var(--kfo-purple-600);
    outline-offset: -2px;
}

.flight-trip-link {
    color: var(--kfo-purple-700);
    font-weight: 600;
    text-decoration: none;
    white-space: nowrap;
}

.flight-trip-link:focus {
    outline: none;
}

.flight-trip-link::after {
    content: "";
    position: absolute;
    inset: 0;
}

/* Acknowledgment badges: green with a check when done, gray while
   waiting; "None required" is plain muted text. */
.status-badge--acknowledged {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    background: var(--kfo-green-100);
    color: var(--kfo-green-700);
}

.status-badge--waiting {
    background: var(--kfo-gray-100);
    color: var(--kfo-gray-500);
}

.flight-check {
    flex: none;
    width: 12px;
    height: 12px;
}

.flight-ack-none,
.flight-muted {
    color: var(--kfo-gray-500);
}

/* Trip detail (flight-scheduler/trip.php): legs and trip details as
   cards on the left, acknowledgments in a panel on the right; one
   column on narrower screens (iPad portrait). */
.flight-trip-title {
    margin-bottom: 4px;
    overflow-wrap: anywhere;
}

/* The aircraft ("make model / tail") as a subtitle under the route, in
   the heading color; "Requested by" stays small and gray below it. */
.flight-trip-aircraft {
    margin: 2px 0 4px;
    font-size: 1.15rem;
    font-weight: 600;
    color: var(--kfo-purple-900);
    overflow-wrap: anywhere;
}

.flight-trip-meta {
    display: flex;
    flex-direction: column;
    gap: 2px;
    margin: 0 0 20px;
    font-size: 0.9rem;
    color: var(--kfo-gray-500);
}

.flight-notice {
    background: var(--kfo-purple-100);
    color: var(--kfo-purple-900);
}

.flight-ack-request {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 12px;
    margin-bottom: 20px;
    padding: 12px 16px;
    border-left: 4px solid var(--kfo-purple-600);
    border-radius: 10px;
    background: var(--kfo-purple-100);
    color: var(--kfo-purple-900);
}

.flight-ack-request p {
    margin: 0;
    font-weight: 600;
}

.flight-trip-layout {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(240px, 320px);
    gap: 20px;
    align-items: start;
}

@media (max-width: 900px) {
    .flight-trip-layout {
        grid-template-columns: minmax(0, 1fr);
    }
}

.flight-trip-legs {
    display: flex;
    flex-direction: column;
    gap: 16px;
    min-width: 0;
}

.flight-leg-detail,
.flight-acks {
    padding: 16px 20px;
}

.flight-leg-detail__title {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: 4px 10px;
    margin: 0 0 12px;
    font-size: 1rem;
    color: var(--kfo-purple-900);
}

.flight-leg-detail__route {
    font-weight: 400;
    color: var(--kfo-gray-500);
}

.flight-leg-detail__facts {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
    gap: 12px 20px;
    margin: 0;
}

.flight-leg-detail__facts dt {
    margin-bottom: 2px;
    font-size: 0.8rem;
    font-weight: 600;
    color: var(--kfo-gray-500);
}

.flight-leg-detail__facts dd {
    margin: 0;
    font-size: 0.95rem;
    color: #1f2937;
    overflow-wrap: anywhere;
}

.flight-leg-detail__wide {
    grid-column: 1 / -1;
}

.flight-acks__list {
    display: flex;
    flex-direction: column;
    gap: 10px;
    margin: 0;
    padding: 0;
    list-style: none;
}

.flight-acks__list li {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: 4px;
}

.flight-acks__person {
    font-weight: 600;
    color: #1f2937;
    overflow-wrap: anywhere;
}

.flight-acks__list .status-badge {
    white-space: normal;
}

/* "Add passenger" under a leg's passengers (booking form): a search
   field with its matches in a dropdown just below it (a combobox run by
   flight-scheduler.js). The highlighted option is purple; the last
   option, when shown, adds the typed name as a new passenger. The
   dropdown is as wide as the field, so it never widens the page. */
.flight-passenger-search {
    margin-bottom: 16px;
}

.flight-combobox {
    position: relative;
    max-width: 420px;
}

.flight-combobox__list {
    position: absolute;
    top: calc(100% + 4px);
    left: 0;
    right: 0;
    z-index: 20;
    max-height: 260px;
    margin: 0;
    padding: 4px 0;
    overflow-y: auto;
    list-style: none;
    background: #fff;
    border: 1px solid var(--kfo-gray-200);
    border-radius: 8px;
    box-shadow: 0 8px 24px rgba(46, 16, 101, 0.12);
}

.flight-combobox__list[hidden] {
    display: none;
}

.flight-combobox__option {
    padding: 8px 12px;
    font-size: 0.9rem;
    color: #1f2937;
    cursor: pointer;
    overflow-wrap: anywhere;
}

.flight-combobox__option.is-active,
.flight-combobox__option:hover {
    background: var(--kfo-purple-100);
    color: var(--kfo-purple-900);
}

.flight-combobox__option--new {
    border-top: 1px solid var(--kfo-gray-100);
    font-weight: 600;
    color: var(--kfo-purple-700);
}

.flight-passenger-search__status {
    margin: 6px 0 0;
    font-size: 0.85rem;
    color: var(--kfo-gray-500);
}

.flight-passenger-search__status:empty {
    display: none;
}

/* "Look up another airport…" under a From/To dropdown (booking form): a
   header with Cancel, then the search field with its dropdown (the same
   .flight-combobox styles as the passenger search). The status line under
   it says what happened, and stays after the lookup closes. */
.flight-airport-lookup {
    margin-top: 8px;
    padding: 10px 12px;
    border: 1px solid var(--kfo-purple-200);
    border-radius: 8px;
    background: var(--kfo-purple-100);
}

.flight-airport-lookup[hidden] {
    display: none;
}

.flight-airport-lookup__header {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    justify-content: space-between;
    gap: 4px 8px;
}

.flight-airport-lookup__header .action-link {
    padding: 2px 6px;
}

.flight-airport-lookup .flight-combobox {
    max-width: none;
}

.flight-airport-lookup__status {
    margin: 6px 0 0;
    font-size: 0.85rem;
    color: var(--kfo-gray-500);
}

.flight-airport-lookup__status:empty {
    display: none;
}

/* "No airports found." in a combobox dropdown: not an option. */
.flight-combobox__empty {
    padding: 8px 12px;
    font-size: 0.9rem;
    color: var(--kfo-gray-500);
}

/* "(Depart)" / "(Return)" after a leg title (booking form banners and
   trip detail headings): quieter than the title itself. */
.flight-leg-role {
    font-weight: 400;
    color: var(--kfo-gray-500);
}

/* Airports New/Edit forms: "Look up airport" at the top (assets/js/
   airports.js). It reuses the booking lookup's combobox and status-line
   styles (.flight-combobox, .flight-airport-lookup__status); a divider
   sets it apart from the fields it fills in. */
.airport-lookup {
    margin-bottom: 20px;
    padding-bottom: 16px;
    border-bottom: 1px solid var(--kfo-gray-200);
}

/* Both airport lookups (booking form, Airports New/Edit) show up to 20
   results: about 10 rows are visible and the list scrolls on its own,
   without scrolling the page when it reaches the end. Row height is set
   so "10 rows" holds; a long label wraps onto a second line. Arrow keys
   keep the highlighted row in view (scrollIntoView in the scripts). The
   passenger search keeps the shared .flight-combobox__list height. */
.flight-airport-lookup .flight-combobox__option,
.airport-lookup .flight-combobox__option {
    line-height: 1.35;
}

.flight-airport-lookup .flight-combobox__list,
.airport-lookup .flight-combobox__list {
    max-height: calc(10 * (0.9rem * 1.35 + 16px) + 8px);
    overscroll-behavior: contain;
}

/* Passengers list: the user a passenger is linked to (#0086), as a small
   muted note after the name, and "(user)" after a linked passenger's
   invite email (#0089) and cell phone (#0105). */
.passenger-linked-user {
    margin-left: 8px;
    font-size: 0.8rem;
    color: var(--kfo-gray-500);
}

/* Passengers list (#0105): the Email, Cell and Passport columns wrap
   inside their cells (long addresses break anywhere) so the table keeps
   to the page width on an iPad, and the passport's expiry badge:
   red "Expired", amber "Expires soon" (within 6 months). */
.data-table .passenger-wrap {
    overflow-wrap: anywhere;
}

.passenger-wrap .status-badge {
    margin-left: 4px;
    white-space: nowrap;
}

.status-badge--expired {
    background: var(--kfo-red-100);
    color: var(--kfo-red-700);
}

.status-badge--expiring {
    background: var(--kfo-amber-100);
    color: var(--kfo-amber-700);
}

/* Landscaping grounds modules (lib/grounds-module.php), a property's
   features page: the type filter (a GET form: a "Filter" label and a
   dropdown that submits itself on change) on the left and the
   "Show/Hide removed features" switch on the right, above the table;
   they stack on narrow screens. */
.grounds-toolbar {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: 8px 16px;
    margin-bottom: 12px;
}

.grounds-filter {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 8px;
}

.grounds-filter .form-input {
    width: auto;
    min-width: 180px;
}

/* .form-label sits above its field elsewhere; here it's inline, beside
   the dropdown. */
.grounds-filter__label {
    margin-bottom: 0;
}

/* Admin → Properties' Edit screen (#0115): the type, shown but not
   editable. */
.admin-property-type {
    margin: 0;
    font-size: 0.95rem;
    color: #1f2937;
}

/* "Removed" badge after a feature's name; "Retired" note after a
   retired type's name. */
.grounds-removed-badge {
    margin-left: 8px;
}

.grounds-note {
    margin-left: 6px;
    font-size: 0.8rem;
    color: var(--kfo-gray-500);
}

/* "Latest assessment" column: the urgency badge, then the visit date
   (wraps below the badge when the cell is narrow). */
.grounds-latest-date {
    white-space: nowrap;
}

/* Landscaping feature page (grounds_route_feature()): the header (the
   breadcrumb and, on the same line, the summary: type pill, location ·
   quantity, Removed, Edit; a long name pushes the summary onto its own
   line under the breadcrumb, and "+ Add check-in" stays at the right of
   the first line), the "At a glance" strip of each check-in's main
   photo (newest first), and the check-in timeline with each check-in's
   photos. Thumbnails are cropped squares; everything wraps on narrow
   screens. */
.grounds-feature-header {
    align-items: flex-start;
    gap: 16px;
}

.grounds-feature-heading {
    display: flex;
    flex: 1;
    flex-wrap: wrap;
    align-items: center;
    gap: 6px 14px;
    min-width: 0;
}

.grounds-feature-header > .btn-primary {
    flex-shrink: 0;
    white-space: nowrap;
}

.grounds-feature-summary {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 4px 8px;
    margin: 0;
    font-size: 0.85rem;
    color: var(--kfo-gray-500);
}

.grounds-feature-summary .action-link {
    padding: 2px 4px;
}

.grounds-feature-summary__sep {
    margin: 0 -2px;
}

.grounds-feature-notes {
    max-width: 70ch;
    margin: 0 0 20px;
    white-space: normal;
    overflow-wrap: anywhere;
}

.grounds-section {
    margin-bottom: 24px;
}

.grounds-section__header {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    justify-content: space-between;
    gap: 8px;
}

.grounds-section__title {
    margin: 0 0 12px;
    font-size: 1.05rem;
    color: var(--kfo-purple-900);
}

.grounds-glance,
.grounds-checkins,
.grounds-photos {
    margin: 0;
    padding: 0;
    list-style: none;
}

.grounds-glance {
    display: flex;
    flex-wrap: wrap;
    gap: 12px;
}

.grounds-glance__item {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 4px;
    font-size: 0.8rem;
    color: var(--kfo-gray-500);
    text-decoration: none;
}

.grounds-glance__item img,
.grounds-photo img {
    display: block;
    width: 96px;
    height: 96px;
    object-fit: cover;
    border: 1px solid var(--kfo-gray-200);
    border-radius: 6px;
    background: var(--kfo-gray-100);
}

.grounds-glance__item:hover img,
.grounds-glance__item:focus-visible img,
.grounds-photo:hover img,
.grounds-photo:focus-visible img {
    border-color: var(--kfo-purple-600);
}

.grounds-checkins {
    display: flex;
    flex-direction: column;
    gap: 12px;
}

.grounds-checkin {
    padding: 16px;
    background: #fff;
    border: 1px solid var(--kfo-gray-200);
    border-radius: 10px;
    box-shadow: 0 1px 2px rgba(0, 0, 0, 0.04);
    scroll-margin-top: 16px;
}

.grounds-checkin:target {
    border-color: var(--kfo-purple-600);
}

.grounds-checkin__head {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: 4px 12px;
}

.grounds-checkin__date {
    font-weight: 600;
    color: var(--kfo-purple-900);
}

.grounds-checkin__meta {
    font-size: 0.85rem;
    color: var(--kfo-gray-500);
}

.grounds-photos {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
    margin-top: 12px;
}

.grounds-photo {
    display: block;
}

/* Landscaping check-in screens: the Edit link on a check-in card, the
   per-photo upload status lines (assets/js/grounds-checkin.js), and the
   edit screen's photos, each with its "Delete photo" button below. */
.grounds-checkin__edit {
    margin-left: auto;
}

.grounds-upload-status {
    margin: 0 0 16px;
    padding: 0;
    list-style: none;
    font-size: 0.85rem;
}

.grounds-upload-status li {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: 4px 12px;
    padding: 6px 0;
    border-bottom: 1px solid var(--kfo-gray-100);
}

.grounds-upload-status__name {
    min-width: 0;
    overflow-wrap: anywhere;
}

.grounds-upload-status__state {
    color: var(--kfo-gray-500);
}

.grounds-upload-status__state--done {
    color: var(--kfo-green-700);
    font-weight: 600;
}

.grounds-upload-status__state--failed {
    color: var(--kfo-red-700);
}

.grounds-photos--edit {
    gap: 16px;
    margin: 0 0 16px;
}

.grounds-photos--edit li {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 4px;
}

.grounds-photos--edit form {
    margin: 0;
}

.grounds-add-photos {
    margin-top: 8px;
}

/* Photo viewer (assets/js/photo-viewer.js): an in-page dialog over a
   92% near-black backdrop, so the page behind (the purple nav included)
   barely shows. The top bar and the caption have their own solid dark
   background, so their text never sits on page text. Top bar: "N of M",
   "Download" and close; the photo fitted to the screen between
   previous/next; caption below. On
   narrow screens the arrows sit over the photo's edges. */
.kfo-viewer {
    position: fixed;
    inset: 0;
    z-index: 1100;
    display: flex;
    flex-direction: column;
    padding: env(safe-area-inset-top, 0) env(safe-area-inset-right, 0) env(safe-area-inset-bottom, 0) env(safe-area-inset-left, 0);
    background: rgba(8, 8, 10, 0.92);
    color: #f5f3ff;
    overscroll-behavior: contain;
}

.kfo-viewer[hidden] {
    display: none;
}

/* Focus rests on the dialog itself after a click on the photo (#0120);
   its buttons keep their own focus rings. */
.kfo-viewer:focus {
    outline: none;
}

.kfo-viewer__bar {
    display: flex;
    align-items: center;
    gap: 16px;
    padding: 8px 12px 8px 16px;
    font-size: 0.85rem;
    background: #111114;
}

.kfo-viewer__counter {
    margin-right: auto;
    color: rgba(237, 233, 254, 0.85);
}

.kfo-viewer__action {
    color: #f5f3ff;
    font-weight: 600;
    text-decoration: underline;
    text-underline-offset: 3px;
}

.kfo-viewer__action:hover,
.kfo-viewer__action:focus-visible {
    color: #fff;
}

.kfo-viewer__close,
.kfo-viewer__nav {
    border: none;
    border-radius: 999px;
    background: rgba(255, 255, 255, 0.12);
    color: #fff;
    font: inherit;
    line-height: 1;
    cursor: pointer;
}

.kfo-viewer__close {
    width: 40px;
    height: 40px;
    font-size: 1.1rem;
}

.kfo-viewer__close:hover,
.kfo-viewer__nav:hover:not(:disabled) {
    background: rgba(255, 255, 255, 0.24);
}

.kfo-viewer__close:focus-visible,
.kfo-viewer__nav:focus-visible,
.kfo-viewer__action:focus-visible {
    outline: 2px solid var(--kfo-purple-200);
    outline-offset: 2px;
}

.kfo-viewer__stage {
    position: relative;
    flex: 1;
    min-height: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 12px;
    padding: 0 12px;
}

/* The area between previous/next that clips the photo (#0120). The
   script sizes the photo and moves it with a transform; drag, pinch and
   the wheel are handled there, so the browser's own touch gestures are
   off here. */
.kfo-viewer__view {
    position: relative;
    flex: 1;
    align-self: stretch;
    min-width: 0;
    overflow: hidden;
    display: flex;
    align-items: center;
    justify-content: center;
    touch-action: none;
}

.kfo-viewer__view.can-zoom .kfo-viewer__img {
    cursor: zoom-in;
}

.kfo-viewer__view.is-zoomed .kfo-viewer__img {
    cursor: grab;
}

.kfo-viewer__view.is-dragging .kfo-viewer__img {
    cursor: grabbing;
}

.kfo-viewer__img {
    position: absolute;
    top: 0;
    left: 0;
    display: block;
    max-width: none;
    border-radius: 4px;
    box-shadow: 0 4px 24px rgba(0, 0, 0, 0.4);
    transform-origin: 0 0;
    user-select: none;
    -webkit-user-select: none;
    -webkit-user-drag: none;
}

/* Zoom controls in the top bar: − 100% + Fit 100%, styled like the
   drawing viewer's, in the viewer's dark colours. */
.kfo-viewer__zoom {
    display: flex;
    align-items: center;
    gap: 6px;
}

.kfo-viewer__zoom-button {
    min-width: 36px;
    height: 34px;
    padding: 0 8px;
    border: 1px solid rgba(255, 255, 255, 0.18);
    border-radius: 8px;
    background: rgba(255, 255, 255, 0.08);
    color: #fff;
    font: inherit;
    font-size: 1.1rem;
    line-height: 1;
    cursor: pointer;
}

.kfo-viewer__zoom-button--text {
    font-size: 0.85rem;
}

.kfo-viewer__zoom-button:hover:not(:disabled) {
    background: rgba(255, 255, 255, 0.2);
}

.kfo-viewer__zoom-button:disabled {
    opacity: 0.35;
    cursor: default;
}

.kfo-viewer__zoom-button:focus-visible {
    outline: 2px solid var(--kfo-purple-200);
    outline-offset: 2px;
}

.kfo-viewer__zoom-level {
    min-width: 46px;
    text-align: center;
    font-variant-numeric: tabular-nums;
    color: rgba(237, 233, 254, 0.85);
}

.kfo-viewer__img[hidden] {
    display: none;
}

.kfo-viewer__status {
    margin: 0;
    color: rgba(237, 233, 254, 0.85);
}

.kfo-viewer__status:empty {
    display: none;
}

.kfo-viewer__nav {
    flex-shrink: 0;
    width: 48px;
    height: 48px;
    font-size: 2rem;
}

.kfo-viewer__nav:disabled {
    opacity: 0.3;
    cursor: default;
}

.kfo-viewer__nav[hidden] {
    display: none;
}

.kfo-viewer__caption {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    justify-content: center;
    gap: 2px 16px;
    padding: 10px 16px 14px;
    text-align: center;
    font-size: 0.9rem;
    background: #111114;
}

.kfo-viewer__caption p {
    margin: 0;
}

.kfo-viewer__caption-main {
    font-weight: 600;
}

.kfo-viewer__caption-sub {
    color: rgba(237, 233, 254, 0.85);
}

.kfo-viewer__caption [hidden] {
    display: none;
}

@media (max-width: 640px) {
    .kfo-viewer__stage {
        padding: 0;
    }

    /* The zoom controls take their own line under "N of M" and close. */
    .kfo-viewer__bar {
        flex-wrap: wrap;
        row-gap: 6px;
    }

    .kfo-viewer__zoom {
        order: 3;
        width: 100%;
        justify-content: center;
    }

    .kfo-viewer__nav {
        position: absolute;
        top: 50%;
        z-index: 1;
        transform: translateY(-50%);
        width: 40px;
        height: 40px;
        background: rgba(17, 12, 34, 0.55);
    }

    .kfo-viewer__nav--prev {
        left: 6px;
    }

    .kfo-viewer__nav--next {
        right: 6px;
    }

    .kfo-viewer__caption {
        flex-direction: column;
        align-items: center;
    }
}

/* Landscaping check-in cards: Notes under the photos (small label, text
   with its line breaks, up to a comfortable reading width, shortened to
   4 lines by assets/js/text-clamp.js); the edit form's opening hint. */
.grounds-checkin__text {
    margin-top: 12px;
}

.grounds-checkin__label {
    display: block;
    margin-bottom: 2px;
    font-size: 0.75rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.03em;
    color: var(--kfo-gray-500);
}

.grounds-checkin__text p {
    margin: 0;
    max-width: 90ch;
    overflow-wrap: anywhere;
}

/* Text shortened by assets/js/text-clamp.js: the first 4 lines with an
   ellipsis, until "Show more". Only applied when the script runs. */
.kfo-clamp--on {
    display: -webkit-box;
    -webkit-box-orient: vertical;
    -webkit-line-clamp: 4;
    line-clamp: 4;
    overflow: hidden;
}

/* A block cut to about 10 lines by assets/js/text-clamp.js (data-fold),
   fading into --kfo-fold-bg at the bottom, until "Show full …". Only
   applied when the script runs and the content really overflows. */
.kfo-fold--on {
    position: relative;
    max-height: 15em;
    overflow: hidden;
}

.kfo-fold--on::after {
    content: "";
    position: absolute;
    left: 0;
    right: 0;
    bottom: 0;
    height: 3em;
    background: linear-gradient(to bottom, rgba(255, 255, 255, 0), var(--kfo-fold-bg, #fff));
    pointer-events: none;
}

.kfo-clamp__toggle {
    margin: 2px 0 0 -8px;
}

.grounds-edit-hint {
    margin: 0 0 16px;
}

/* AI assessments on Landscaping check-in cards (grounds_assessment_area()
   in lib/grounds-module.php): the newest in a lightly shaded box with an
   urgency badge (word always shown; four colours from calm to red),
   older ones folded under "Earlier assessments", and the fixed caveat.
   The "Get AI assessment" button and status/error lines sit below. */
.grounds-assess {
    margin-top: 12px;
}

.grounds-assessment {
    /* The fold's fade ends in the box's own background. */
    --kfo-fold-bg: var(--kfo-purple-100);
    padding: 12px 14px;
    background: var(--kfo-purple-100);
    border: 1px solid var(--kfo-purple-200);
    border-radius: 8px;
}

.grounds-assessment__head {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: 4px 10px;
    margin-bottom: 4px;
}

.grounds-assessment__title {
    margin: 0;
    font-size: 0.95rem;
    color: var(--kfo-purple-900);
}

.grounds-assessment__meta {
    font-size: 0.8rem;
    color: var(--kfo-gray-500);
}

.grounds-urgency {
    display: inline-block;
    padding: 2px 9px;
    border-radius: 999px;
    font-size: 0.75rem;
    font-weight: 600;
    white-space: nowrap;
}

.grounds-urgency--low {
    background: var(--kfo-green-100);
    color: var(--kfo-green-700);
}

.grounds-urgency--moderate {
    background: #fef3c7;
    color: #854d0e;
}

.grounds-urgency--high {
    background: #ffedd5;
    color: #9a3412;
}

.grounds-urgency--urgent {
    background: var(--kfo-red-700);
    color: #fff;
}

.grounds-assessment__section {
    margin-top: 10px;
}

.grounds-assessment__section p {
    margin: 0;
    max-width: 90ch;
    overflow-wrap: anywhere;
}

.grounds-assessment__caveat {
    margin: 12px 0 0;
    font-size: 0.8rem;
    font-style: italic;
    color: var(--kfo-gray-500);
}

.grounds-assessment__changed {
    margin: 8px 0 0;
    font-size: 0.85rem;
    color: #854d0e;
}

.grounds-assessment__rerun {
    margin: 8px 0 0 -8px;
}

.grounds-assessment__earlier {
    margin-top: 10px;
    font-size: 0.85rem;
}

.grounds-assessment__earlier summary {
    cursor: pointer;
    color: var(--kfo-purple-700);
    font-weight: 600;
}

.grounds-assessment--earlier {
    --kfo-fold-bg: #fff;
    margin-top: 8px;
    padding: 10px 12px;
    background: #fff;
}

.grounds-assessment--earlier .grounds-assessment__title {
    font-size: 0.85rem;
}

.grounds-assess__status {
    margin: 8px 0 0;
    font-size: 0.85rem;
    color: var(--kfo-gray-500);
}

.grounds-assess__error {
    margin: 8px 0 0;
}

/* --- Landscape drawings (#0114) ---------------------------------------- */

/* The Admin's "Show removed drawings" link and "+ Add drawing" (and, on
   the viewer, Edit and "Upload new version"), beside a section title. */
.grounds-section__actions {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 8px;
}

/* "No drawings yet." inside the section's card. */
.grounds-drawings__empty {
    margin: 0;
    padding: 16px;
    color: var(--kfo-gray-500);
}

/* A form inside a table's Actions cell (Remove / Restore), on the same
   line as the links beside it. */
.inline-form {
    display: inline;
}

.drawing-upload-current {
    margin: 0 0 16px;
}

/* The upload form's size/type message, under the file field. */
.drawing-upload-error {
    margin: 8px 0 0;
}

/* Viewer page: type under the breadcrumb, then the toolbar (version
   picker, zoom buttons, "Open PDF"), notes, and the drawing itself. */
.drawing-meta {
    margin: -8px 0 12px;
    font-size: 0.9rem;
    color: var(--kfo-gray-500);
}

.drawing-toolbar {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 8px 16px;
    margin-bottom: 12px;
}

.drawing-zoom {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 6px;
}

.drawing-zoom__button {
    min-width: 36px;
    font-size: 1.1rem;
    line-height: 1;
}

.drawing-zoom__level {
    min-width: 48px;
    text-align: center;
    font-size: 0.9rem;
    font-variant-numeric: tabular-nums;
}

.drawing-note {
    margin: 0 0 12px;
    font-size: 0.85rem;
    color: var(--kfo-gray-500);
}

.drawing-note--old {
    padding: 8px 12px;
    border-radius: 6px;
    background: var(--kfo-amber-100);
    color: var(--kfo-amber-700);
}

.drawing-viewer {
    position: relative;
}

/* The scrolling box the sheet sits in: drag to pan, wheel or pinch to
   zoom (assets/js/drawing-viewer.js), so the browser's own touch
   scrolling and zooming are turned off here. The sheet's auto margins
   centre it while it's smaller than the box, and drop to 0 when it's
   bigger, so every edge can still be scrolled to. */
.drawing-viewer__stage {
    display: flex;
    height: calc(100vh - 300px);
    min-height: 420px;
    overflow: auto;
    border: 1px solid var(--kfo-gray-200);
    border-radius: 8px;
    background: var(--kfo-gray-100);
    cursor: grab;
    touch-action: none;
    user-select: none;
    -webkit-user-select: none;
}

.drawing-viewer__stage:focus-visible {
    outline: 3px solid var(--kfo-purple-600);
    outline-offset: 2px;
}

.drawing-viewer__stage.is-dragging {
    cursor: grabbing;
}

.drawing-viewer__sheet {
    position: relative;
    flex: none;
    margin: auto;
    background: #fff;
    box-shadow: 0 1px 4px rgba(0, 0, 0, 0.15);
}

.drawing-viewer__canvas {
    position: absolute;
    display: block;
}

.drawing-viewer__status {
    position: absolute;
    inset: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    margin: 0;
    padding: 16px;
    text-align: center;
    color: var(--kfo-gray-500);
}

/* Admin → Properties → Edit: the property's Drawings section (#0114),
   a wide card holding the list; its empty line needs no extra padding. */
.admin-drawings .grounds-drawings__empty {
    padding: 0;
}

/* --- Tabs (render_tabs(), #0116) ----------------------------------------- */

/* A row of links styled as tabs, under the page heading; the current one
   is underlined in purple. Each tab loads its own page address. */
.kfo-tabs {
    display: flex;
    flex-wrap: wrap;
    gap: 4px;
    margin: 0 0 16px;
    border-bottom: 1px solid var(--kfo-gray-200);
}

.kfo-tabs__tab {
    margin-bottom: -1px;
    padding: 8px 14px;
    border-bottom: 3px solid transparent;
    font-size: 0.95rem;
    font-weight: 600;
    color: var(--kfo-gray-500);
    text-decoration: none;
}

.kfo-tabs__tab:hover {
    color: var(--kfo-purple-900);
    border-bottom-color: var(--kfo-purple-200);
}

.kfo-tabs__tab.is-current {
    color: var(--kfo-purple-900);
    border-bottom-color: var(--kfo-purple-600);
}

.kfo-tabs__tab:focus-visible {
    outline: 3px solid var(--kfo-purple-600);
    outline-offset: 2px;
}

/* The Drawings tab's "Manage drawings in Admin → Properties" link (Admins),
   at the right above the list. */
.grounds-drawings .grounds-section__header {
    justify-content: flex-end;
    margin-bottom: 8px;
}

/* Viewer page navigation: ‹ Page [3] of 24 › */
.drawing-pager {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 6px;
}

.drawing-pager__label,
.drawing-pager__total {
    font-size: 0.9rem;
    white-space: nowrap;
}

.drawing-pager .drawing-pager__number {
    width: 4.5em;
    padding: 6px 8px;
    text-align: center;
}

/* --- Pins on landscape drawings (#0114, v0.9) ---------------------------- */

/* Dot colours: the urgency badges' colours (Low green, Moderate amber,
   High orange, Urgent red), as solid tones that read at dot size, and
   grey when not assessed. */
:root {
    --kfo-pin-low: #16a34a;
    --kfo-pin-moderate: #f59e0b;
    --kfo-pin-high: #ea580c;
    --kfo-pin-urgent: var(--kfo-red-700);
    --kfo-pin-none: #9ca3af;
}

/* The viewer and, while Edit pins is open, its side panel; the panel
   goes under the drawing on narrow screens. */
.drawing-workspace {
    display: flex;
    gap: 16px;
    align-items: stretch;
}

.drawing-workspace .drawing-viewer {
    flex: 1;
    min-width: 0;
}

/* "Show pins", "Edit pins" and the Saved / error line, in the toolbar. */
.drawing-pin-controls {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 6px 12px;
}

.drawing-pin-controls .btn-secondary.is-active {
    background: var(--kfo-purple-100);
    border-color: var(--kfo-purple-600);
    color: var(--kfo-purple-900);
}

.drawing-pin-status {
    font-size: 0.85rem;
    color: var(--kfo-green-700);
}

.drawing-pin-status.is-error {
    color: var(--kfo-red-700);
}

/* An on/off switch around a real checkbox (kept focusable). */
.drawing-switch {
    position: relative;
    display: inline-flex;
    align-items: center;
    gap: 8px;
    font-size: 0.9rem;
    cursor: pointer;
    user-select: none;
}

.drawing-switch__input {
    position: absolute;
    width: 1px;
    height: 1px;
    opacity: 0;
}

.drawing-switch__track {
    position: relative;
    flex: none;
    width: 34px;
    height: 20px;
    border-radius: 999px;
    background: #d1d5db;
    transition: background 0.15s;
}

.drawing-switch__track::after {
    content: "";
    position: absolute;
    top: 2px;
    left: 2px;
    width: 16px;
    height: 16px;
    border-radius: 50%;
    background: #fff;
    box-shadow: 0 1px 2px rgba(0, 0, 0, 0.3);
    transition: transform 0.15s;
}

.drawing-switch__input:checked + .drawing-switch__track {
    background: var(--kfo-purple-600);
}

.drawing-switch__input:checked + .drawing-switch__track::after {
    transform: translateX(14px);
}

.drawing-switch__input:focus-visible + .drawing-switch__track {
    outline: 3px solid var(--kfo-purple-600);
    outline-offset: 2px;
}

/* The pin layer covers the sheet exactly; pins sit at percentages of it,
   so they follow every zoom without being moved by the script. */
.drawing-pins {
    position: absolute;
    inset: 0;
    pointer-events: none;
}

/* A plain dot with a white ring and a thin dark outline, so it shows on
   busy linework; centred on its spot. The ::before widens the area that
   takes a press, for fingers. */
.drawing-pin {
    position: absolute;
    width: 14px;
    height: 14px;
    margin: -9px 0 0 -9px;
    border: 2px solid #fff;
    border-radius: 50%;
    background: var(--kfo-pin-none);
    box-shadow: 0 0 0 1px rgba(17, 24, 39, 0.85), 0 1px 3px rgba(0, 0, 0, 0.35);
    pointer-events: auto;
    cursor: pointer;
    touch-action: none;
    -webkit-user-drag: none;
    -webkit-tap-highlight-color: transparent;
}

.drawing-pin::before {
    content: "";
    position: absolute;
    inset: -8px;
    border-radius: 50%;
}

.drawing-pin--low,
.drawing-pin-dot--low { background: var(--kfo-pin-low); }
.drawing-pin--moderate,
.drawing-pin-dot--moderate { background: var(--kfo-pin-moderate); }
.drawing-pin--high,
.drawing-pin-dot--high { background: var(--kfo-pin-high); }
.drawing-pin--urgent,
.drawing-pin-dot--urgent { background: var(--kfo-pin-urgent); }
.drawing-pin--none,
.drawing-pin-dot--none { background: var(--kfo-pin-none); }

.drawing-pin:focus-visible {
    outline: 3px solid var(--kfo-purple-600);
    outline-offset: 3px;
}

.drawing-pins.is-editing .drawing-pin {
    cursor: move;
}

/* While a feature is picked, clicks go through pins to the sheet. */
.drawing-pins.is-picking .drawing-pin {
    pointer-events: none;
    opacity: 0.7;
}

.drawing-pin.is-dragging {
    cursor: grabbing;
    transform: scale(1.35);
}

/* A ?pin= link's pin, or a picked feature's: a few purple pulses. */
.drawing-pin.is-highlighted {
    z-index: 1;
    animation: drawing-pin-pulse 1s ease-out 4;
}

@keyframes drawing-pin-pulse {
    0% {
        box-shadow: 0 0 0 1px rgba(17, 24, 39, 0.85), 0 0 0 0 rgba(91, 33, 182, 0.75);
    }
    100% {
        box-shadow: 0 0 0 1px rgba(17, 24, 39, 0.85), 0 0 0 18px rgba(91, 33, 182, 0);
    }
}

.drawing-viewer__stage.is-placing {
    cursor: crosshair;
}

/* The urgency dot in the tooltip and the Edit pins list. */
.drawing-pin-dot {
    display: inline-block;
    flex: none;
    width: 10px;
    height: 10px;
    margin-right: 6px;
    border-radius: 50%;
    box-shadow: 0 0 0 1px rgba(17, 24, 39, 0.5);
    vertical-align: -1px;
}

.drawing-pin-tip {
    position: absolute;
    z-index: 5;
    max-width: 240px;
    padding: 6px 10px;
    border-radius: 6px;
    background: #111827;
    color: #fff;
    font-size: 0.8rem;
    line-height: 1.4;
    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.25);
    pointer-events: none;
}

.drawing-pin-tip__name {
    font-weight: 600;
}

.drawing-pin-tip__meta {
    color: #d1d5db;
}

.drawing-pin-tip .drawing-pin-dot {
    box-shadow: 0 0 0 1px #fff;
}

/* Edit pins: beside the drawing, as tall as it, the list scrolling. */
.drawing-pin-panel {
    display: flex;
    flex: none;
    flex-direction: column;
    width: 290px;
    height: calc(100vh - 300px);
    min-height: 420px;
    padding: 12px;
    box-sizing: border-box;
    border: 1px solid var(--kfo-gray-200);
    border-radius: 8px;
    background: #fff;
}

.drawing-pin-panel[hidden] {
    display: none;
}

.drawing-pin-panel__head {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
}

.drawing-pin-panel__title {
    margin: 0;
    font-size: 1rem;
    color: var(--kfo-purple-900);
}

.drawing-pin-panel__hint {
    margin: 6px 0 10px;
    font-size: 0.85rem;
    color: var(--kfo-gray-500);
}

.drawing-pin-panel__filter {
    margin-bottom: 8px;
}

.drawing-pin-panel__list {
    flex: 1;
    min-height: 0;
    margin: 0;
    padding: 0;
    overflow-y: auto;
    list-style: none;
}

.drawing-pin-panel__empty {
    padding: 8px 4px;
    font-size: 0.85rem;
    color: var(--kfo-gray-500);
}

.drawing-pin-row {
    display: flex;
    align-items: center;
    gap: 4px;
    border-bottom: 1px solid var(--kfo-gray-100);
}

.drawing-pin-row__pick {
    display: flex;
    flex: 1;
    flex-wrap: wrap;
    align-items: center;
    min-width: 0;
    padding: 7px 6px;
    border: none;
    border-radius: 6px;
    background: none;
    font: inherit;
    text-align: left;
    cursor: pointer;
}

.drawing-pin-row__pick:hover {
    background: var(--kfo-gray-100);
}

.drawing-pin-row.is-picked .drawing-pin-row__pick {
    background: var(--kfo-purple-100);
    box-shadow: inset 0 0 0 2px var(--kfo-purple-600);
}

.drawing-pin-row__pick:focus-visible {
    outline: 3px solid var(--kfo-purple-600);
    outline-offset: -1px;
}

.drawing-pin-row__name {
    flex: 1;
    min-width: 0;
    font-size: 0.9rem;
    font-weight: 600;
    overflow-wrap: anywhere;
}

.drawing-pin-row__meta {
    flex-basis: 100%;
    padding-left: 16px;
    font-size: 0.78rem;
    color: var(--kfo-gray-500);
}

.drawing-pin-row__remove {
    flex: none;
    font-size: 0.8rem;
    white-space: nowrap;
}

@media (max-width: 800px) {
    .drawing-workspace {
        flex-direction: column;
    }

    .drawing-pin-panel {
        width: auto;
        height: 360px;
        min-height: 0;
    }
}

/* The feature page's "On drawings" section. */
.grounds-pins__list {
    display: flex;
    flex-direction: column;
    gap: 4px;
    margin: 0;
    padding: 0;
    list-style: none;
}

.grounds-pins__subtitle {
    margin: 14px 0 6px;
    font-size: 0.9rem;
    color: var(--kfo-gray-500);
}

.grounds-pins__empty {
    margin: 0;
    color: var(--kfo-gray-500);
}

/* Pin filters (v0.10): "Filter [type] [urgency] Showing 6 of 18 pins". */
.drawing-pin-filters {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 6px 8px;
}

/* Compact, keeping select.form-input's right padding for its arrow. */
.drawing-pin-filters .form-input {
    width: auto;
    padding-top: 6px;
    padding-bottom: 6px;
    padding-left: 10px;
}

.drawing-pin-count {
    font-size: 0.85rem;
    color: var(--kfo-gray-500);
    white-space: nowrap;
}

/* --- Admin → Storage (v0.14) ----------------------------------------------- */

.storage-asof {
    margin: -8px 0 20px;
    font-size: 0.9rem;
    color: var(--kfo-gray-500);
}

/* Figures line up on the right. */
.data-table .storage-num {
    text-align: right;
    font-variant-numeric: tabular-nums;
}

.storage-total td {
    font-weight: 700;
    border-top: 2px solid var(--kfo-purple-200);
}

.storage-unavailable {
    margin: 0;
    padding: 16px;
    color: var(--kfo-gray-500);
}

/* Server disk: Total, Used and Available as three boxes side by side,
   a small grey label above a large figure. */
.storage-stats {
    display: grid;
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: 12px;
    margin: 0;
    padding: 16px 16px 0;
}

.storage-stat {
    padding: 12px 14px;
    border: 1px solid var(--kfo-gray-200);
    border-radius: 8px;
    background: var(--kfo-gray-100);
}

.storage-stat__label {
    font-size: 0.8rem;
    color: var(--kfo-gray-500);
}

.storage-stat__value {
    margin: 4px 0 0;
    font-size: 1.5rem;
    font-weight: 700;
    font-variant-numeric: tabular-nums;
    color: var(--kfo-purple-900);
    white-space: nowrap;
}

/* The figure's share of the disk ("34%"), after Used and Available,
   on the figure's baseline. */
.storage-stat__percent {
    font-size: 0.95rem;
    font-weight: 400;
    color: var(--kfo-gray-500);
    vertical-align: baseline;
}

/* Server disk: a bar of the share used, with "41% used" beside it. */
.storage-meter {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 12px 16px 16px;
}

.storage-bar {
    flex: 1;
    height: 12px;
    overflow: hidden;
    border-radius: 999px;
    background: var(--kfo-gray-200);
}

.storage-bar__fill {
    height: 100%;
    border-radius: 999px;
    background: var(--kfo-purple-600);
}

.storage-bar__label {
    font-size: 0.9rem;
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}

.storage-database-total {
    margin: -4px 0 12px;
}

.storage-note {
    margin: 8px 0 0;
    font-size: 0.8rem;
    color: var(--kfo-gray-500);
}
