/* SimpleWill — Modern Legist
 * Component atoms and shared utilities.
 * Tailwind v4 tokens live in app/assets/tailwind/application.css.
 * Material Symbols font is loaded via <link> in layouts/application.html.erb.
 */

.material-symbols-outlined {
  font-variation-settings: 'FILL' 0, 'wght' 400, 'GRAD' 0, 'opsz' 24;
  display: inline-block;
  line-height: 1;
  text-transform: none;
  letter-spacing: normal;
  word-wrap: normal;
  white-space: nowrap;
  direction: ltr;
}

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

@layer components {
  .btn {
    @apply inline-flex items-center justify-center gap-2
           font-sans text-sm font-medium
           rounded-md px-4 py-2.5
           transition-colors duration-150
           cursor-pointer disabled:opacity-50 disabled:cursor-not-allowed;
  }
  .btn-primary   { @apply bg-navy text-on-navy hover:bg-navy-soft; }
  .btn-secondary { @apply bg-teal-soft text-teal hover:bg-teal hover:text-on-teal; }
  .btn-ghost     { @apply bg-transparent text-navy hover:bg-surface-container-low; }
  .btn-danger    { @apply bg-danger text-white hover:opacity-90; }

  .field-label {
    @apply block text-sm font-medium text-navy mb-1.5;
  }
  .field-input {
    @apply w-full bg-surface-container-low text-ink
           border-0 border-b border-outline-variant
           rounded-none px-3 py-2.5
           placeholder:text-ink-placeholder
           focus:outline-none focus:border-navy
           transition-colors;
  }
  .field-help  { @apply mt-1.5 text-xs text-ink-muted; }
  .field-error { @apply mt-1.5 text-xs text-danger; }
  .field-input-error {
    @apply border-danger focus:border-danger;
  }

  .card          { @apply bg-surface-container-lowest rounded-lg p-8; }
  .card-elevated { @apply bg-surface-container-lowest rounded-lg p-8
                          shadow-[0px_20px_40px_rgba(26,35,61,0.06)]; }

  .badge {
    @apply inline-flex items-center px-2.5 py-1
           rounded-md text-xs font-medium;
  }
  .badge-draft   { @apply bg-surface-container-high text-ink-muted; }
  .badge-review  { @apply bg-navy/10 text-navy; }
  .badge-final   { @apply bg-teal-soft text-teal; }
  .badge-warning { @apply bg-plum/10 text-plum; }
  .badge-danger  { @apply bg-danger-soft text-danger; }

  .progress-rail      { @apply h-1 bg-surface-container-high rounded-full overflow-hidden; }
  .progress-rail-fill { @apply h-full bg-teal transition-all duration-300; }

  .btn-sm {
    @apply px-3 py-1 text-xs;
  }

  .pull-quote {
    @apply font-serif font-medium text-4xl md:text-5xl
           text-navy tracking-tight leading-tight
           max-w-[20ch] my-6;
  }

  .section-rule {
    @apply block border-0 border-t border-outline-variant
           my-12 md:my-16 w-full;
  }

  .step-ordinal {
    @apply font-serif font-medium text-6xl md:text-7xl
           text-navy/40 tracking-tight leading-none;
  }

  .eyebrow {
    @apply inline-block font-sans font-medium text-xs
           tracking-wider text-ink-muted mb-3;
  }

  .callout {
    @apply block bg-surface-container-lowest rounded-r-lg
           border-l-[3px] border-plum
           p-4 text-sm text-ink;
  }

  /* ------------------------------------------------------------------ */
  /* Interview-engine atoms (Slice 3b)                                  */
  /* ------------------------------------------------------------------ */

  /* Chip — replaces peer+sr-only radio/checkbox styled-span pattern.
     The hidden input uses .sr-only; the .chip span is styled via
     :has(input:checked) + utility modifiers. */
  .chip-group {
    @apply mt-2 flex flex-wrap gap-2;
  }
  .chip {
    @apply inline-flex items-center justify-center
           px-4 py-2 rounded-md
           bg-surface-container-lowest text-ink
           border border-outline-variant
           text-sm font-medium
           cursor-pointer transition-colors
           hover:bg-surface-container-low;
  }
  .chip-selected {
    @apply bg-teal text-on-teal border-teal;
  }
  .chip-disabled {
    @apply bg-surface-container-high text-ink-placeholder
           cursor-not-allowed opacity-60;
  }

  /* Save status — ONE element, three states. It lives in the step bar but
     OUTSIDE the streamed id, so a pages-changed re-stream cannot wipe an
     in-flight "Saving…".
     It costs the bar ZERO width at every breakpoint: it hangs off the bar's
     bottom-right corner, out of the flow, so the pill row keeps the whole
     line and nothing is reserved for a state that is absent most of the time.
     Out of the flow rather than in it because the bar is sticky: an in-flow
     pill appearing on every debounced keystroke would grow the bar and shove
     the whole page — including the field under the cursor — down ~34px and
     back. pointer-events-none because it is parked OVER page content for the
     two seconds "Saved" lingers, and it must not eat taps meant for what is
     underneath it.
     Idle renders NOTHING (:empty below). Only the ERROR state re-enters the
     flow — see .save-status-error. */
  .save-status {
    @apply absolute top-full right-3 mt-1.5 lg:right-6
           pointer-events-none
           inline-flex items-center gap-2 max-w-64
           overflow-hidden whitespace-nowrap
           bg-surface-container-low rounded-full px-3 py-1.5
           text-xs font-sans text-ink-muted transition-opacity;
  }
  .save-status:empty { display: none; }
  /* The dot renders ONLY in a named state. An idle indicator — no state
     class, empty text — renders literally nothing (the floating-bullet fix). */
  .save-status-saved::before,
  .save-status-saving::before,
  .save-status-error::before {
    content: "";
    @apply inline-block w-1.5 h-1.5 rounded-full;
  }
  .save-status-saved::before   { @apply bg-teal; }
  .save-status-saving::before  { @apply bg-navy animate-pulse; }
  /* The error string ("Couldn't save — check your connection") is ~250px at
     text-xs — past the corner pill's max-w-64 (256px) once the dot, the gap and
     px-3 are paid, so it would clip there — and it must never be clipped or
     printed over page content — so it takes a full-width
     row of the bar at EVERY breakpoint. `static` is what pulls it back into
     the flow — equal specificity to .save-status, later in source, so it wins
     at every width — and the row takes pointer events back, so the message
     stays selectable. */
  .save-status-error {
    @apply static pointer-events-auto w-full max-w-none justify-start
           mx-0 mt-0 mb-2 px-3 lg:px-6 py-2
           bg-danger-soft text-danger rounded-md;
  }
  .save-status-error::before   { @apply bg-danger; }

  /* ---------------------------------------------------------------- */
  /* Step bar — the interview's sticky navigation (2026-08-21 design) */
  /* ---------------------------------------------------------------- */

  /* Full-bleed: layouts/application wraps every page in `main px-6`, so a
     merely full-WIDTH bar would stop 24px short of both viewport edges and its
     bottom border would read as a floating card. -mx-6 cancels that padding;
     the rows inside carry their own. `top-16` is the layout's own chrome:
     shared/_top_nav is `fixed h-16 z-50`, so a bar pinned at top-0 would park
     UNDER it exactly while scrolled — which is the whole point of the bar.
     4rem == h-16. Sticky is a positioned box, which is what the save-status
     anchors to; flex-wrap plus a w-full rail is what gives the error state a
     full-width row of its own under the nav. */
  .step-bar {
    @apply sticky top-16 z-10 -mx-6
           flex flex-wrap items-center
           bg-paper border-b border-outline-variant;
    box-shadow: 0 1px 2px rgba(26, 28, 26, 0.04);
  }

  /* One step. Clickable (done, left-off) is a white bordered pill — a real
     button affordance; current is the navy pill; locked is a borderless span
     at FULL contrast (alpha-dimmed grey failed contrast). The LABEL truncates
     rather than the row overflowing: eight labeled pills plus the 249px of
     fixed chrome (actions 185 + px-6 48 + gap-4 16) want ~1201px measured, and
     ~1252px at the page-0 worst case, so they fit UNTRUNCATED at 1280 with
     28–79px of headroom in the 1031px pill column. Between lg (1024) and
     ~1200px the labels give up characters instead of colliding with the buttons
     or pushing steps off the row. */
  .step-pill {
    @apply inline-flex items-center gap-1.5 px-2.5 py-1.5 rounded-full
           bg-surface-container-lowest border border-outline-variant
           font-sans text-[13px] font-medium text-navy whitespace-nowrap
           cursor-pointer transition-colors hover:bg-surface-container-low;
  }
  .step-pill-current {
    @apply bg-navy border-navy text-on-navy font-semibold cursor-default hover:bg-navy;
  }
  .step-pill-locked {
    @apply bg-transparent border-transparent text-ink-muted font-normal cursor-default hover:bg-transparent;
  }
  .step-pill-circle {
    @apply inline-flex items-center justify-center w-6 h-6 rounded-full
           text-[13px] font-semibold shrink-0;
  }
  .step-pill-circle-done     { @apply bg-teal text-on-teal; }
  .step-pill-circle-left-off { @apply bg-surface-container-lowest text-teal border-2 border-teal; }
  .step-pill-circle-current  { @apply bg-surface-container-lowest text-navy font-bold; }
  .step-pill-circle-locked   { @apply bg-surface-container-low border border-outline-variant text-ink-muted font-medium; }

  /* The 2px line that runs THROUGH the circles: teal up to and including the
     frontier the client has reached, outline-variant after it. flex-1 so the
     row's slack lands in the connectors and every pill keeps its label. */
  .step-connector      { @apply flex-1 h-0.5 bg-outline-variant min-w-2; }
  .step-connector-done { @apply bg-teal; }

  /* Right cluster: Back (83px) + gap + Continue (92px) = 185px that COLLAPSES
     to its content. No fixed width and no reserved slot — every pixel not
     spent here is a pixel of un-truncated label, and the row needs them: the
     eight pills want ~952px against the 1031px that 1280 leaves once this
     cluster (185) + the row's px-6 (48) + the gap-4 (16) are paid. Submit
     swapping in for the wider Continue shifts the cluster ~10px and moves
     nothing else, because the pill column beside it is flex-1. */
  .step-bar-actions {
    @apply flex items-center gap-2.5 shrink-0;
  }
  .step-bar-back {
    @apply bg-surface-container-lowest text-navy border border-outline-variant
           px-3.5 py-2 gap-1.5;
  }

  /* Mobile bar — one row, every control at least 48px, and a 3px progress line
     under it in place of the dots. */
  .step-mobile-back {
    @apply inline-flex items-center justify-center w-12 min-h-12
           bg-surface-container-lowest text-navy border border-outline-variant rounded-lg
           disabled:opacity-50 disabled:bg-surface-container-low;
  }
  .step-mobile-center {
    @apply flex flex-col items-center justify-center gap-0.5 flex-1 min-w-0 min-h-12
           bg-surface-container-lowest border border-outline-variant rounded-lg
           font-sans cursor-pointer px-2;
  }
  .step-mobile-eyebrow { @apply text-[11px] font-medium tracking-wider uppercase text-ink-muted; }
  .step-mobile-title   { @apply inline-flex items-center gap-1.5 max-w-full text-[15px] font-semibold text-navy; }
  .step-mobile-continue {
    @apply inline-flex items-center justify-center gap-1.5 min-w-30 min-h-12
           bg-navy text-on-navy rounded-lg font-sans text-[15px] font-medium;
  }
  .step-progress-line      { @apply h-[3px] bg-paper-rule; }
  .step-progress-line-fill { @apply h-full bg-teal transition-all duration-300; }

  /* ---------------------------------------------------------------- */
  /* Step sheet — the mobile step list (2026-08-21 step-nav design)   */
  /* ---------------------------------------------------------------- */

  /* A native <dialog> styled into a bottom sheet. The element choice is what
     buys the focus trap, Esc, and the scrim; everything here only undoes the
     UA dialog defaults. That stylesheet centers the box with `margin: auto`
     inside `inset: 0`, so `margin: auto auto 0` keeps the horizontal centering
     and drops it onto the bottom edge; width/max-width/border/padding all
     override UA defaults (a dialog is fit-content with a 2px solid border and
     1em of padding). max-height keeps eight 52px rows reachable on a short
     viewport, and the bottom padding clears an iOS home indicator. */
  .step-sheet {
    @apply w-full max-w-none max-h-[85dvh] overflow-y-auto p-0 border-0
           bg-paper text-ink;
    margin: auto auto 0;
    border-radius: 16px 16px 0 0;
    padding-bottom: max(0.5rem, env(safe-area-inset-bottom));
    box-shadow: 0 -4px 24px rgba(26, 28, 26, 0.18);
  }
  .step-sheet::backdrop { background: rgba(26, 28, 26, 0.35); }

  /* Affordance only — the sheet does not drag. Esc, the backdrop, and any row
     close it. */
  .step-sheet-handle {
    @apply mx-auto mt-2.5 mb-1 w-9 h-1 rounded-full bg-surface-dim;
  }
  .step-sheet-eyebrow {
    @apply px-5 py-2 font-sans text-[11px] font-medium tracking-wider uppercase text-ink-muted;
  }

  /* One row per page, tappable at 52px. A locked row is separated by its TOKEN
     colour, never by opacity — same ruling as the locked pill: alpha-dimmed
     grey on paper failed contrast. */
  .step-sheet-row {
    @apply flex items-center gap-3 px-5 min-h-13 font-sans text-[15px] text-ink;
  }
  /* The 3px navy edge is paid for out of the row's own left padding, so the
     circles stay in one column with the rows above and below. */
  .step-sheet-row-current {
    @apply bg-surface-container;
    border-left: 3px solid var(--color-navy);
    padding-left: calc(1.25rem - 3px);
  }
  .step-sheet-row-locked    { @apply text-ink-muted; }
  .step-sheet-row-title     { @apply flex-1 min-w-0 truncate; }
  .step-sheet-row-note      { @apply shrink-0 text-[13px] font-medium text-ink-muted; }
  .step-sheet-row-note-done { @apply text-teal; }
  .step-sheet-row-current .step-sheet-row-note { @apply text-navy font-semibold; }
  .step-sheet-footer {
    @apply px-5 pt-3 pb-2 mt-1 border-t border-paper-rule
           font-sans text-[13px] text-ink-muted;
  }

  /* ------------------------------------------------------------------ */
  /* Representative ranks — ordered designation slots + a pool of people */
  /* ------------------------------------------------------------------ */

  .rep-ranks { @apply mt-2 space-y-4; }

  /* Rank list — one slot per choice, in order. Order is the answer, so the
     list is never allowed to show a gap; the server renders filled slots
     contiguously and then the empty trailing slot(s). */
  .rep-rank-list { @apply list-none pl-0 space-y-2; }
  .rep-rank-slot {
    @apply flex flex-wrap items-center gap-x-3 gap-y-2
           bg-surface-container-lowest
           border border-outline-variant
           rounded-md px-4 py-3
           transition-colors;
  }
  .rep-rank-slot-empty {
    @apply bg-transparent border-2 border-dashed;
  }
  .rep-rank-ordinal {
    @apply shrink-0 basis-24
           font-sans text-xs font-medium uppercase tracking-wide text-ink-muted;
  }
  .rep-rank-placeholder { @apply text-sm text-ink-placeholder; }
  /* The drop target for a choice. `grow` is load-bearing: it stretches the
     container across the rest of the slot row so a dragged person can be
     dropped anywhere on the row, not only onto the chips already there. */
  .rep-rank-chips       { @apply grow flex flex-wrap items-center gap-2; }

  /* Chip — one placed person inside a rank slot. Drag handle is the chip. */
  .rep-chip {
    @apply inline-flex items-center gap-2
           bg-teal-soft/40 border border-teal/40
           rounded-md px-3 py-1.5
           text-sm font-medium text-ink
           cursor-grab;
  }
  .rep-chip-kin { @apply text-xs font-normal text-ink-muted; }
  .rep-chip-remove {
    @apply inline-flex items-center justify-center
           w-5 h-5 rounded-full leading-none
           text-ink-muted cursor-pointer transition-colors
           hover:bg-surface-container-high hover:text-navy;
  }
  .rep-chip-together {
    @apply font-sans text-xs italic text-teal;
  }

  /* Per-group agreement — asked inside the slot it governs. `basis-full`
     drops it onto its own line within the slot's flex row, under the chips it
     belongs to; the ordinal's width is matched so the options line up with the
     chips above them. */
  .rep-slot-agreement {
    @apply basis-full md:pl-27
           flex flex-wrap items-center gap-x-5 gap-y-1;
  }
  .rep-slot-agreement label {
    @apply inline-flex items-center gap-2
           font-sans text-xs text-ink-muted cursor-pointer;
  }
  .rep-slot-agreement input[type="radio"] { @apply accent-teal; }

  /* Pool — the people not yet placed, plus the add-person affordance. */
  .rep-pool { @apply flex flex-wrap gap-2; }
  /* The add button and its form are full-width rows below the cards. */
  .rep-pool > .person-tile-add { @apply basis-full; }
  .rep-pool > .card-elevated   { @apply basis-full; }
  .rep-pool-card {
    @apply inline-flex items-center gap-2
           bg-surface-container-lowest
           border border-outline-variant
           rounded-md px-3 py-2
           text-sm font-medium text-ink
           cursor-grab transition-colors
           hover:bg-surface-container-low hover:border-navy;
  }
  .rep-pool-card-disabled {
    @apply cursor-not-allowed opacity-60
           hover:bg-surface-container-lowest hover:border-outline-variant;
  }
  .rep-pool-name  { @apply text-ink; }
  .rep-pool-kin   { @apply text-xs font-normal text-ink-muted; }
  .rep-pool-minor { @apply text-xs font-normal italic text-ink-placeholder; }

  /* Person tile — retained for the representative_ranks add-person button. */
  .person-tile {
    @apply block w-full text-left
           bg-surface-container-lowest
           border border-outline-variant
           rounded-md px-4 py-3
           cursor-pointer transition-colors
           hover:bg-surface-container-low;
  }
  .person-tile-selected {
    @apply border-l-4 border-l-teal bg-teal-soft/40;
  }
  .person-tile-disabled {
    @apply cursor-not-allowed opacity-60
           hover:bg-surface-container-lowest;
  }
  .person-tile-add {
    @apply border-2 border-dashed border-outline-variant
           text-ink-muted text-center font-medium
           hover:border-navy hover:text-navy hover:bg-surface-container-lowest;
  }

  /* Field group — shared label/control/error wrapper for interview fields. */
  .field-group {
    @apply block;
  }

  /* Divide stacked questions within a section so Yes/No groups don't blur together. */
  .field-group + .field-group {
    @apply mt-6 pt-6 border-t border-outline-variant;
  }

  /* Data-dense table chrome — first caller is attorney queue; admin
     firms/users tables are the next consumer (Slice 6). */
  .data-table              { @apply w-full text-sm text-ink; }
  .data-table thead tr     { @apply border-b border-outline-variant/40; }
  .data-table th           { @apply text-left px-4 py-3 text-xs font-normal text-ink-muted; }
  .data-table tbody tr     { @apply border-b border-outline-variant/20; }
  .data-table tbody tr:last-child { @apply border-b-0; }
  .data-table tbody tr:hover      { @apply bg-surface-container-low; }
  .data-table td           { @apply px-4 py-3.5; }

  /* Field-row grid: label/input on the left (1fr), saved-indicator on the
     right (fixed 160px). Prevents layout shift as indicator states change. */
  .field-row {
    @apply grid grid-cols-[1fr_160px] gap-6 items-center
           px-3 py-2.5 rounded
           hover:bg-surface-container-low transition-colors;
  }

  /* Saved-indicator state carriers. Colors come from existing design
     tokens; these classes add the icon+text+transition baseline. */
  .saved-indicator         { @apply inline-flex items-center gap-1 text-xs transition-opacity; }
  .saved-indicator-saving  { @apply text-ink-muted; }
  .saved-indicator-fresh   { @apply text-teal; }
  .saved-indicator-muted   { @apply text-ink-muted; }
  .saved-indicator-error   { @apply text-danger cursor-pointer hover:underline underline-offset-4; }

  /* Quiet Modern Legist link — already used inline in /login and the
     client portal; formalized here for consistent reuse. */
  .link-quiet {
    @apply text-navy underline underline-offset-4
           decoration-outline-variant
           hover:text-navy-soft hover:decoration-navy
           transition-colors;
  }

  /* ------------------------------------------------------------------ */
  /* Admin / superadmin atoms (Slice 6a)                                */
  /* ------------------------------------------------------------------ */

  /* Superadmin chrome pill — sits in _top_nav next to the brand. Two
     variants: static (on chambers) + roaming (on firm subs, clickable). */
  .chrome-pill {
    @apply inline-flex items-center gap-1.5
           px-3 py-1 rounded-full
           bg-surface-container-low text-navy
           border border-outline-variant/40
           text-xs font-medium tracking-wide;
  }
  .chrome-pill-roaming {
    @apply hover:border-navy/60 hover:bg-surface-container
           transition-colors cursor-pointer no-underline;
  }
  .chrome-pill-eyebrow { @apply font-serif text-navy/70; }
  .chrome-pill-dot     { @apply text-ink-muted; }
  .chrome-pill-firm    { @apply font-sans text-navy font-semibold; }

  /* Row-actions menu (kebab) — replaces inline action links in data-tables.
     Panel toggles visibility via the `.is-open` class set by
     row_actions_controller.js. Optional submenu body-swap for "Change role". */
  .row-actions-menu { @apply relative inline-block; }
  .row-actions-menu-trigger {
    @apply inline-flex items-center justify-center
           w-8 h-8 rounded-md
           text-ink-muted hover:text-navy
           hover:bg-surface-container-low
           bg-transparent border-0 cursor-pointer
           transition-colors;
  }
  .row-actions-menu-panel {
    @apply hidden absolute right-0 mt-1 z-20
           min-w-[200px] py-1
           bg-surface-container-lowest rounded-md
           border border-outline-variant/40
           shadow-[0px_8px_24px_rgba(26,35,61,0.12)];
  }
  .row-actions-menu-panel.is-open { @apply block; }
  .row-actions-menu-item {
    @apply block w-full text-left
           px-4 py-2 text-sm text-ink
           bg-transparent border-0 cursor-pointer no-underline
           hover:bg-surface-container-low hover:text-navy
           transition-colors;
  }
  .row-actions-menu-item-danger {
    @apply text-danger hover:text-danger hover:bg-danger/10;
  }
  .row-actions-menu-section-title {
    @apply block px-4 py-1.5 text-xs tracking-wide
           text-ink-muted uppercase;
  }

  /* Chip — removable variant (extends existing .chip). Used for licensed
     states on admin/firms/show. The base .chip atom is reused verbatim. */
  .chip-removable { @apply pr-2 gap-2; }
  .chip-remove {
    @apply inline-flex items-center justify-center
           w-5 h-5 rounded-full
           text-ink-muted hover:text-danger hover:bg-danger/10
           bg-transparent border-0 cursor-pointer
           transition-colors;
  }

  /* Page-level danger alert — promoted from inline flash styling. */
  .alert-danger {
    @apply block px-4 py-3 rounded-md
           bg-danger-soft text-danger
           border border-danger/30
           text-sm;
  }

  /* Settings sub-nav tab strip — ties /settings/firm, /settings/attorneys,
     /settings/assignment into one visual section. Active tab highlighted by
     .tab-strip-item-active. No JS — plain anchor links. */
  .tab-strip {
    @apply flex border-b border-outline-variant/40 -mb-px;
  }
  .tab-strip-item {
    @apply px-4 py-2.5 text-sm text-ink-muted
           hover:text-navy
           border-b-2 border-transparent -mb-px
           transition-colors no-underline;
  }
  .tab-strip-item-active {
    @apply text-navy border-navy font-medium;
  }

  /* Small inline preview block for the brand-color field. */
  .color-swatch {
    @apply inline-block w-6 h-6 rounded
           border border-outline-variant/60 align-middle ml-2;
  }

  /* Practice-areas chip editor container. Tag-input style — text input
     is transparent and blends with the editor shell. Focus-within border
     mirrors .field-input behavior. */
  .chip-editor {
    @apply flex flex-wrap items-center gap-2
           p-3 rounded-md
           bg-surface-container-low
           border border-outline-variant/60
           focus-within:border-navy;
  }
  .chip-input {
    @apply flex-1 min-w-[8rem]
           bg-transparent border-0
           text-sm text-ink
           placeholder:text-ink-placeholder
           focus:ring-0 focus:outline-none p-0;
  }

  /* File input with button-like affordance via the ::file-selector-button
     pseudo. Matches .btn-ghost visual weight. */
  .field-file {
    @apply block text-sm text-ink-muted
           file:mr-4 file:py-2 file:px-4 file:rounded-md
           file:border-0 file:text-sm file:font-medium
           file:bg-surface-container-low file:text-navy
           hover:file:bg-surface-container
           file:cursor-pointer cursor-pointer;
  }

  /* Neutral placeholder square shown when an attorney has no photo attached. */
  .photo-placeholder {
    @apply inline-flex items-center justify-center
           w-24 h-24 rounded-md
           bg-surface-container-low
           border border-outline-variant/60
           text-ink-muted;
  }
}
