* {
    margin: 0;
    padding: 0;
    box-sizing: border-box;
}

/* The chart is displayed inside the PMS, in an iframe that already sits in a card of
   the host's making. Drawing a second one here — grey page, inset, rounded white panel
   with a shadow — put a window inside a window and left the margins uneven, so this
   document brings no page of its own: it fills whatever box it is given, and the frame
   around it belongs to whoever embedded it. */
body {
    font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
    background: transparent;
    padding: 0;
    font-size: 11px;
}

.container {
    max-width: none;
    margin: 0;
    background: transparent;
    border-radius: 0;
    box-shadow: none;
    overflow: hidden;
}

/* ========== HEADER / TIMELINE ========== */
/* Title band: line legend + recession unit + static Print/Share/Add notes */
.header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 24px;
    padding: 18px 30px;
    background: linear-gradient(180deg, #eff4fb 0%, #f5f8fc 100%);
    border-bottom: 1px solid #e8ebed;
}

/* Colour-coded checkboxes that show/hide the measurement curves (also the colour
   legend/key). Lives inline in the title row, centred between the patient block
   and the action buttons — no longer a separate strip. */
.line-legend {
    display: flex;
    align-items: center;
    gap: 6px 14px;
    font-size: 12px;
}

.line-legend-title {
    font-weight: 600;
    color: #6b7280;
    margin-right: 2px;
}

.line-toggle {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    color: #374151;
    cursor: pointer;
    user-select: none;
}

.line-toggle input {
    cursor: pointer;
    margin: 0;
}

.line-swatch {
    width: 14px;
    height: 14px;
    border-radius: 3px;
    flex: none;
    box-shadow: inset 0 0 0 1px rgba(0,0,0,0.12);
}

/* Recession dictation INDICATOR — deliberately NOT styled like .line-toggle. The
   line toggles only hide/show curves; this reports how speech is being charted, so
   the non-default state is loud (amber), not quiet. Read-only: it reflects server
   truth (see app.js) and opens the settings modal; switching happens there. */
.recession-unit {
    display: inline-flex;
    align-items: center;
    gap: 7px;
    padding: 5px 12px;
    margin-left: 12px;
    border: 1px solid #cbd5e1;
    border-radius: 999px;
    background: #f8fafc;
    color: #334155;
    font-family: inherit;
    font-size: 12px;
    font-weight: 600;
    line-height: 1;
    cursor: pointer;
    white-space: nowrap;
    transition: background 0.15s, border-color 0.15s, color 0.15s;
}

.recession-unit:hover {
    background: #f1f5f9;
    border-color: #94a3b8;
}

.recession-unit:focus-visible {
    outline: 2px solid #2563eb;
    outline-offset: 2px;
}

.recession-unit-dot {
    width: 8px;
    height: 8px;
    border-radius: 50%;
    flex: none;
    background: #64748b;
}

/* per_tooth is the non-default interpretation — make it impossible to miss. */
.recession-unit[data-unit="per_tooth"] {
    background: #fffbeb;
    border-color: #f59e0b;
    color: #92400e;
}

.recession-unit[data-unit="per_tooth"]:hover {
    background: #fef3c7;
    border-color: #d97706;
}

.recession-unit[data-unit="per_tooth"] .recession-unit-dot {
    background: #f59e0b;
}

.header-action {
    border: none;
    background: transparent;
    padding: 8px 10px;
    border-radius: 6px;
    font-size: 13px;
    font-weight: 500;
    color: #475569;
    cursor: default;
    display: inline-flex;
    align-items: center;
    gap: 6px;
}
.header-action:hover { background: rgba(37, 99, 235, 0.06); color: #2563eb; }
.header-action svg { width: 16px; height: 16px; }

/* Most .header-action buttons are decorative placeholders (tabindex="-1",
   cursor:default). This modifier marks the ones that actually do something. */
.header-action--live { cursor: pointer; }
.header-action--live:focus-visible {
    outline: 2px solid #2563eb;
    outline-offset: 2px;
}

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

.timeline {
    flex: 1;
    display: flex;
    align-items: center;
    position: relative;
    padding: 0 20px;
}

.timeline-line {
    position: absolute;
    top: 50%;
    left: 20px;
    right: 20px;
    height: 2px;
    background: #e8ebed;
    z-index: 1;
}

.timeline-progress {
    position: absolute;
    top: 50%;
    left: 20px;
    height: 2px;
    background: #3b82f6;
    z-index: 2;
    transition: width 0.3s ease;
}

.timeline-item {
    display: flex;
    flex-direction: column;
    align-items: center;
    z-index: 3;
    flex: 1;
}

.timeline-dot {
    width: 12px;
    height: 12px;
    border-radius: 50%;
    background: white;
    border: 2px solid #e8ebed;
    margin-bottom: 8px;
    transition: all 0.3s ease;
}

.timeline-dot.active {
    background: #3b82f6;
    border-color: #3b82f6;
}

.timeline-date {
    font-size: 10px;
    color: #6b7280;
    white-space: nowrap;
}

.timeline-date.active {
    color: #3b82f6;
    font-weight: 600;
}

.header-actions {
    display: flex;
    gap: 10px;
    align-items: center;
}

.btn {
    padding: 8px 16px;
    border-radius: 6px;
    border: 1px solid #e8ebed;
    background: white;
    cursor: pointer;
    font-size: 12px;
    font-weight: 500;
    transition: all 0.2s ease;
    display: flex;
    align-items: center;
    gap: 6px;
}

.btn:hover {
    background: #f9fafb;
    border-color: #d1d5db;
}

.btn-primary {
    background: #3b82f6;
    border-color: #3b82f6;
    color: white;
}

.btn-primary:hover {
    background: #2563eb;
    border-color: #2563eb;
}

.btn-success {
    background: #10b981;
    border-color: #10b981;
    color: white;
}

/* ========== VOICE CONTROLS ========== */
.voice-controls {
    background: #f9fafb;
    padding: 15px 30px;
    border-bottom: 1px solid #e8ebed;
    display: flex;
    justify-content: space-between;
    align-items: center;
}

.voice-status {
    display: flex;
    align-items: center;
    gap: 15px;
}

.connection-status {
    display: flex;
    align-items: center;
    gap: 8px;
    font-size: 12px;
    font-weight: 500;
}

.status-dot {
    width: 8px;
    height: 8px;
    border-radius: 50%;
    background: #ef4444;
}

.status-dot.connected {
    background: #10b981;
    animation: pulse-dot 2s infinite;
}

@keyframes pulse-dot {
    0%, 100% { opacity: 1; }
    50% { opacity: 0.5; }
}

.current-position {
    font-size: 12px;
    color: #6b7280;
}

.voice-state-badge {
    display: inline-flex;
    align-items: center;
    gap: 5px;
    padding: 3px 10px;
    border-radius: 12px;
    font-size: 11px;
    font-weight: 600;
    letter-spacing: 0.5px;
    text-transform: uppercase;
    margin-left: 12px;
}

.voice-state-badge.idle {
    background: #f3f4f6;
    color: #6b7280;
    border: 1px solid #d1d5db;
}

.voice-state-badge.active {
    background: #d1fae5;
    color: #065f46;
    border: 1px solid #6ee7b7;
    animation: pulse-active 2s infinite;
}

.voice-state-badge.paused {
    background: #fef3c7;
    color: #92400e;
    border: 1px solid #fcd34d;
}

@keyframes pulse-active {
    0%, 100% { box-shadow: 0 0 0 0 rgba(16, 185, 129, 0.3); }
    50% { box-shadow: 0 0 0 4px rgba(16, 185, 129, 0); }
}

.current-position strong {
    color: #111827;
}

/* ========== CHART CONTENT ========== */
.chart-content {
    padding: 30px;
}

/* A past exam is on screen and the chart takes no input. Sits above the table rather than
   inside it: the state belongs to the whole chart, not to one arch. */
.snapshot-notice {
    margin: 0 30px;
    padding: 8px 12px;
    border: 1px solid #fcd34d;
    border-radius: 6px;
    background: #fffbeb;
    color: #92400e;
    font-size: 12px;
    font-weight: 600;
}

/* The locked look. Read-only fields keep their text selectable, so they are only muted,
   never hidden; the indicator boxes have no handler to switch off, so their pointer events
   go instead — which also holds if one ever gains a handler. */
.chart-readonly .triplet-input[readonly],
.chart-readonly .single-input:disabled {
    background: #f8fafc;
    color: #64748b;
    cursor: default;
}

.chart-readonly .indicator-box {
    pointer-events: none;
}

.chart-section {
    margin-bottom: 40px;
    /* On narrow screens the fixed grid hits its min-width; scroll the whole
       section (table + teeth share the same min-width) so columns stay aligned
       instead of squeezing the triplet inputs (strategy A+B). */
    overflow-x: auto;
}

/* Buccal sections (1st/3rd = upper/lower buccal) carry the tooth-number row and
   are immediately followed by their lingual pair. Tighten the gap between buccal
   and lingual of the same arch (the lingual sections keep the full 40px so the
   two arches stay clearly separated). Spacing between blocks only -- nothing
   inside the strips (teeth/CEJ/scale) changes. */
.chart-section:nth-child(odd) {
    margin-bottom: 8px;
}

/* Section label now lives INSIDE .teeth-visualization, stacked to the LEFT of the
   row (Figma reference screen_from_video.png): [label] -> [scale digits] -> [teeth].
   It sits in the left part of the same 140px gutter that holds the scale numbers
   (numbers stay right-aligned at the gutter's right edge, x~120-134), so the teeth
   grid is NOT shifted and stays column-aligned with the data tables above/below.
   Vertically centered over the full row height; bold, two lines via <br>. */
.section-title {
    position: absolute;
    left: 0;
    top: 0;
    bottom: 0;
    width: 116px;
    display: flex;
    flex-direction: column;
    justify-content: center;
    padding-left: 8px;
    padding-right: 4px;
    font-size: 15px;
    font-weight: 800;
    line-height: 1.15;
    color: #111827;
    z-index: 4;
    pointer-events: none;
}

/* ========== DATA TABLE ========== */
.data-table {
    width: 100%;
    /* Fixed layout = deterministic equal columns: label gutter 140px + 16 equal
       tooth columns. The teeth grid below mirrors this exactly so tooth #N sits
       under column #N. min-width floors the tooth column at ~72px so the triplet
       (~70px after shrink) fits; below it the section scrolls (see .chart-section). */
    table-layout: fixed;
    min-width: 1300px;
    border-collapse: collapse;
    margin-bottom: 20px;
    font-size: 10px;
}

.data-table th {
    background: #f9fafb;
    color: #6b7280;
    font-weight: 500;
    text-align: left;
    padding: 8px 4px;
    border: 1px solid #e8ebed;
    font-size: 10px;
}

.data-table td {
    padding: 4px 4px;
    border: 1px solid #e8ebed;
    text-align: center;
}

.data-table .label-cell {
    text-align: left;
    font-weight: 500;
    color: #374151;
    white-space: nowrap;
    background: #fafbfc;
}

/* Triplet inputs */
.triplet-group {
    display: flex;
    gap: 1px;
    justify-content: center;
}

.triplet-input {
    width: 20px;
    height: 22px;
    border: 1px solid #e8ebed;
    border-radius: 3px;
    text-align: center;
    font-size: 11px;
    background: white;
}

.triplet-input:focus {
    outline: none;
    border-color: #3b82f6;
    background: #eff6ff;
}

.triplet-input.filled {
    background: #f0f9ff;
    border-color: #3b82f6;
    color: #1e40af;
    font-weight: 600;
}

/* Single value inputs */
.single-input {
    width: 32px;
    height: 22px;
    border: 1px solid #e8ebed;
    border-radius: 3px;
    text-align: center;
    font-size: 11px;
}

/* Mobility dropdown (shares .single-input so updateMobilityCell still finds it). */
.mobility-select {
    width: auto;
    min-width: 62px;
    height: 24px;
    padding: 0 4px;
    cursor: pointer;
    background: #fff;
}

/* Indicator boxes (bleeding, plaque, calculus, etc) */
.indicators-group {
    display: flex;
    gap: 3px;
    justify-content: center;
}

.indicator-box {
    width: 16px;
    height: 16px;
    border: 1px solid #e8ebed;
    border-radius: 4px;
    cursor: pointer;
    transition: all 0.2s ease;
    display: flex;
    align-items: center;
    justify-content: center;
}

.indicator-box:hover {
    border-color: #9ca3af;
}

/* White checkmark on the active state for all indicators except plaque. The
   check is purely presentational (CSS ::after); fill is still toggled by the
   .active class in updateIndicators — no JS change. */
.indicator-box.active::after {
    content: '✓';
    color: #fff;
    font-size: 11px;
    font-weight: 700;
    line-height: 1;
}

.indicator-box.plaque.active::after {
    content: none;
}

/* Bleeding — light pink → crimson */
.indicator-box.bleeding {
    background: #fde0e6;
    border-color: #f9a8c0;
}

.indicator-box.bleeding.active {
    background: #e11d48;
    border-color: #be123c;
}

/* Plaque — light blue, no checkmark */
.indicator-box.plaque {
    background: #e6f0ff;
    border-color: #bfdbfe;
}

.indicator-box.plaque.active {
    background: #93c5fd;
    border-color: #60a5fa;
}

/* Calculus — light green → green */
.indicator-box.calculus {
    background: #d1fae5;
    border-color: #a7f3d0;
}

.indicator-box.calculus.active {
    background: #16a34a;
    border-color: #15803d;
}

/* Suppuration — light blue → deep blue (darker than plaque so they don't read
   the same; suppuration also keeps a checkmark, plaque does not) */
.indicator-box.suppuration {
    background: #dbeafe;
    border-color: #bfdbfe;
}

.indicator-box.suppuration.active {
    background: #2563eb;
    border-color: #1d4ed8;
}

/* Stain — cream → brown */
.indicator-box.stain {
    background: #f5f1de;
    border-color: #e6d9a8;
}

.indicator-box.stain.active {
    background: #a16207;
    border-color: #854d0e;
}

/* ========== TEETH VISUALIZATION ========== */
.teeth-visualization {
    position: relative;
    width: 100%;
    /* Same min-width as .data-table so the table and the teeth scroll together
       and stay column-aligned when .chart-section overflows. */
    min-width: 1300px;
    height: 180px;   /* apical slack: room for the 0-18 scale + roots on the apex
                        side of the gum line (upper -> up, lower -> down). */
    /* Gum line level, shared by .gingival-line and the depth scale so the "0"
       tick and the red line are locked together. 50% keeps the upper and lower
       half-scales equal height => same px-per-mm on both arches (foundation for
       recession/bone curves drawn from the gum line). --apex-pad keeps the
       outermost "12" number from being clipped by overflow:hidden. */
    --gum: 50%;
    --apex-pad: 6px;
    margin: 12px 0;
    background: #ffffff;
    border: 1px solid #e8ebed;
    border-radius: 6px;
    overflow: hidden;
}

.teeth-row {
    /* 16 equal columns mirror the fixed table columns; the 140px left gutter
       matches the table label column so tooth #N lands under column #N.
       Full-height cells (no vertical padding) so each tooth's absolute anchor is
       measured from the visualization top, where anchorAllTeeth() positions it. */
    display: grid;
    grid-template-columns: repeat(16, 1fr);
    height: 100%;
    padding: 0 0 0 140px;
    position: relative;
}

.gingival-line {
    position: absolute;
    left: 140px;   /* start at tooth #1, aligned with the gridlines (.depth-tick) and .teeth-row gutter */
    right: 0;
    top: var(--gum);
    transform: translateY(-1px);  /* center the 2px line on the gum level (= the "0" tick) */
    height: 0;
    border-top: 2px solid #7ba3d0;   /* pale blue "0" / gum line. border (not background) so it prints without "Background graphics" */
    z-index: 1;
}

/* ========== Depth scale (axis: 0/3/6/9/12) ==========
   Static per-section axis inside .teeth-visualization. Left gutter holds the
   numbers; each tick draws a faint full-width gridline behind the teeth.

   The scale occupies only the APICAL half of the box so the "0" tick lands
   exactly on the gum line and the crown hangs into the other half:
     --upper : region [apex-pad .. gum], 12 at top  -> 0 on the gum line
     --lower : region [gum .. -apex-pad], 0 on the gum line -> 12 at bottom
   With zero-height ticks + justify-content:space-between, the first/last ticks
   sit exactly on the region's edges (no padding), so "0" is exactly on --gum.

   Fully LINEAR: even tick spacing = even 3mm steps, so mm->px is constant across
   0-12mm (bandScalePxPerMm reads it from the 0<->3 ticks). z-index 0 keeps it
   behind the gingival line (1) and teeth (2). */
.depth-scale {
    position: absolute;
    left: 0;
    right: 0;
    z-index: 0;
    display: flex;
    flex-direction: column;
    justify-content: space-between;
    pointer-events: none;
}

.depth-scale--upper {
    top: var(--apex-pad);
    bottom: calc(100% - var(--gum));   /* region bottom edge = gum line */
}

.depth-scale--lower {
    top: var(--gum);                   /* region top edge = gum line */
    bottom: var(--apex-pad);
}

.depth-tick {
    position: relative;
    height: 0;
    display: flex;
    align-items: center;
}

.depth-tick::after {
    content: '';
    position: absolute;
    left: 140px;            /* gridlines start where the teeth begin (gutter = 140) */
    right: 0;
    height: 0;
    border-top: 1px solid #eef3fb;   /* pale blue gridline. border (not background) so it prints without "Background graphics" */
}

.depth-num {
    flex: none;
    width: 140px;           /* gutter width; number right-aligned next to teeth */
    text-align: right;
    padding-right: 8px;
    font-size: 9px;
    line-height: 1;
    color: #9ca3af;
}

.tooth {
    position: relative;
    height: 100%;
    z-index: 2;
}

/* Per-tooth box: fixed display height, aspect-ratio width and vertical CEJ
   offset are set inline by anchorAllTeeth(). No per-arch flip — both arches are
   stored already in the target orientation (see chart-renderer.js anchorY note). */
.tooth-anchor {
    position: absolute;
    top: 0;
    left: 50%;
    height: 130px;
    width: auto;
    transform: translate(-50%, 20px);   /* JS overrides width + Y offset per tooth */
    transform-origin: center center;
}

.tooth-image {
    width: 100%;
    height: 100%;
    background: linear-gradient(180deg, #f5f5f5 0%, #e8e8e8 100%);
    border-radius: 40% 40% 50% 50% / 60% 60% 40% 40%;
    box-shadow: 0 2px 4px rgba(0,0,0,0.1);
    position: relative;
}

/* Probe-depth curve layer: one SVG per band over the whole arch (viewBox in band
   px). Drawn above teeth (z 2) and the CEJ overlay so the blue curve reads clearly. */
.pd-curve-layer {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    pointer-events: none;
    overflow: visible;
    z-index: 3;
}

/* Derived/output row (Clinical Attachment Loss = recession + PD, computed by
   recomputeCAL). Class sits on the <td>; the muted look is applied to its inputs
   via descendant selector so the field reads as calculated, not editable. */
.derived-cell .triplet-input {
    background: #f1f3f6;
    color: #6b7280;
    cursor: default;
}

/* Per-measurement line visibility: each curve <g> carries .measure-line--KEY; a
   body class (set by the header checkboxes) hides that key across all 4 bands. */
.lines-hide-probing    .measure-line--probing,
.lines-hide-recession  .measure-line--recession,
.lines-hide-attachment .measure-line--attachment,
.lines-hide-boneLoss   .measure-line--boneLoss {
    display: none;
}

/* CEJ verification overlay (green 3-point cervical curve), drawn over the box;
   inside .tooth-anchor so it shares the tooth's transform (position/size). */
.cej-overlay {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    overflow: visible;
    pointer-events: none;
    z-index: 1;
}

/* Old in-strip tooth numbers (generated per .tooth, pinned to the strip bottom)
   are replaced by a single dedicated .teeth-number-row per arch (below), so hide
   them everywhere. .tooth / data-tooth used by the tables + probe curve untouched. */
.tooth-number {
    display: none;
}

/* Dedicated tooth-number row: ONE per arch, placed in the DOM between the buccal
   teeth-visualization and the lingual section, so the numbers sit cleanly in the
   buccal/lingual gap -- NOT inside the strip, so the lower-arch roots that drop to
   the strip's bottom no longer crowd them. Mirrors .teeth-row geometry exactly
   (same 1300 min-width, 140px left gutter, 16 equal columns) so each number is
   centered under its tooth and scrolls in sync with the teeth/table. */
.teeth-number-row {
    min-width: 1300px;
    display: grid;
    grid-template-columns: repeat(16, 1fr);
    padding: 0 0 0 140px;
}

/* Vertically center the row on the REAL teeth in the gap (not the strip edges):
   inside each strip there is empty space above/below the teeth, and the two arches
   are mirror-asymmetric (upper buccal crowns end high ~141/180 with lots of white
   below; lower buccal roots reach ~176/180 with almost none; lingual is the
   opposite), so each arch gets its own top/bottom margins. Values keep the total
   buccal<->lingual gap at 52px and equalize the white space to the nearest tooth
   tips on both sides (~38px upper, ~39px lower). Spacing around the row only. */
.teeth-number-row--upper {
    margin: -12px 0 18px;
}

.teeth-number-row--lower {
    margin: 36px 0 -18px;
}

.teeth-number-row span {
    text-align: center;
    font-size: 10px;
    font-weight: 600;
    line-height: 14px;
    color: #374151;
}

.tooth.missing .tooth-image {
    background: transparent;
    border: 1px dashed #d1d5db;
    box-shadow: none;
}

.tooth.current .tooth-image {
    background: linear-gradient(180deg, #dbeafe 0%, #bfdbfe 100%);
    border: 2px solid #3b82f6;
}

/* ========== Stage B: image teeth (all four sections) ==========
   Scoped by the four teeth-row ids so the id specificity overrides the global
   .tooth / .tooth-image rules above. The per-tooth PNG is set via an inline
   background-image in JS; inline always wins over a stylesheet background, so
   the .missing/.current states use opacity/filter/box-shadow instead of
   swapping the background (which an inline value would override). */
#upperBuccalTeeth .tooth-image,
#upperLingualTeeth .tooth-image,
#lowerBuccalTeeth .tooth-image,
#lowerLingualTeeth .tooth-image {
    background-color: transparent;
    background-repeat: no-repeat;
    background-position: center;         /* box is sized to the tooth aspect (JS) */
    background-size: contain;            /* preserve each tooth's aspect, no stretch */
    border-radius: 0;
    box-shadow: none;
}

#upperBuccalTeeth .tooth.missing .tooth-image,
#upperLingualTeeth .tooth.missing .tooth-image,
#lowerBuccalTeeth .tooth.missing .tooth-image,
#lowerLingualTeeth .tooth.missing .tooth-image {
    opacity: 0.18;
    filter: grayscale(1);
    border: none;
}

#upperBuccalTeeth .tooth.current .tooth-image,
#upperLingualTeeth .tooth.current .tooth-image,
#lowerBuccalTeeth .tooth.current .tooth-image,
#lowerLingualTeeth .tooth.current .tooth-image {
    border: none;
    box-shadow: 0 0 0 2px #3b82f6;       /* blue ring around the image */
    border-radius: 4px;
}

/* Per-arch vertical orientation: NONE needed. The sprites are stored in opposite
   native orientations that both already match the clinical target (upper roots
   up, lower roots down), so no scaleY flip is applied on either arch. See the
   anchorY note in chart-renderer.js. */

/* ========== LOGS ========== */
.logs-panel {
    background: #1f2937;
    color: #e5e7eb;
    padding: 15px;
    /* No fixed height + inner vertical scroll: the panel stretches under its
       content so the whole page scrolls with the browser instead of an inner
       scrollbar. overflow-y:visible (not hidden) keeps everything visible.
       app.js still sets logsPanel.scrollTop (= no-op now), which is harmless. */
    height: auto;
    overflow-y: visible;
    font-family: 'Courier New', monospace;
    font-size: 11px;
    line-height: 1.6;
}

.log-entry {
    margin-bottom: 5px;
}

.log-entry.transcription {
    color: #60a5fa;
}

.log-entry.success {
    color: #34d399;
}

.log-entry.batch {
    color: #fbbf24;
    font-weight: bold;
}

.log-entry.error {
    color: #f87171;
}

.log-entry.info {
    color: #a78bfa;
}

/* ========== BUTTON VARIANTS ========== */
.voice-actions {
    display: flex;
    gap: 8px;
    align-items: center;
}

.btn-finish {
    background: #f59e0b;
    border-color: #f59e0b;
    color: white;
}

.btn-finish:hover {
    background: #d97706;
    border-color: #d97706;
}

.btn-danger {
    background: #ef4444;
    border-color: #ef4444;
    color: white;
}

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

.btn-cancel {
    background: #f3f4f6;
    border-color: #d1d5db;
    color: #374151;
}

.btn-cancel:hover {
    background: #e5e7eb;
    border-color: #9ca3af;
}

/* ========== CONFIRMATION MODAL ========== */
.modal-overlay {
    display: none;
    position: fixed;
    top: 0;
    left: 0;
    right: 0;
    bottom: 0;
    background: rgba(0, 0, 0, 0.5);
    z-index: 1000;
    align-items: center;
    justify-content: center;
}

.modal-overlay.visible {
    display: flex;
}

.modal-dialog {
    background: white;
    border-radius: 12px;
    padding: 24px;
    max-width: 400px;
    width: 90%;
    box-shadow: 0 8px 30px rgba(0, 0, 0, 0.2);
}

.modal-title {
    font-size: 18px;
    font-weight: 600;
    color: #111827;
    margin-bottom: 8px;
}

.modal-message {
    font-size: 13px;
    color: #6b7280;
    margin-bottom: 20px;
    line-height: 1.5;
}

.modal-actions {
    display: flex;
    gap: 8px;
    justify-content: flex-end;
}

/* ========== SETTINGS MODAL ==========
   Reuses .modal-overlay/.modal-dialog. The dialog is a CONTAINER OF SECTIONS —
   adding a setting means appending another .settings-section, which inherits the
   spacing and divider below. */
.settings-dialog {
    max-width: 520px;
    padding: 20px 24px 24px;
}

.settings-header {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: 12px;
    margin-bottom: 4px;
}

.settings-close {
    border: none;
    background: transparent;
    color: #9ca3af;
    font-size: 24px;
    line-height: 1;
    padding: 0 4px;
    cursor: pointer;
    border-radius: 4px;
}
.settings-close:hover { color: #374151; }
.settings-close:focus-visible {
    outline: 2px solid #2563eb;
    outline-offset: 2px;
}

.settings-section + .settings-section {
    margin-top: 20px;
    padding-top: 20px;
    border-top: 1px solid #e5e7eb;
}

.settings-section-title {
    font-size: 14px;
    font-weight: 600;
    color: #111827;
    margin-bottom: 4px;
}

.settings-section-hint {
    font-size: 12px;
    color: #6b7280;
    line-height: 1.5;
    margin-bottom: 12px;
}

.settings-options {
    display: flex;
    flex-direction: column;
    gap: 8px;
}

.settings-option {
    position: relative;   /* anchors the ✓ / … state marker */
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: 2px;
    width: 100%;
    padding: 10px 12px;
    border: 1px solid #d1d5db;
    border-radius: 8px;
    background: #ffffff;
    color: #374151;
    font-family: inherit;
    text-align: left;
    cursor: pointer;
    transition: border-color 0.15s, background 0.15s;
}

.settings-option:hover:not(:disabled) { border-color: #9ca3af; background: #f9fafb; }
.settings-option:focus-visible {
    outline: 2px solid #2563eb;
    outline-offset: 2px;
}
.settings-option:disabled { opacity: 0.5; cursor: not-allowed; }

.settings-option-label { font-size: 13px; font-weight: 600; }
.settings-option-desc  { font-size: 11px; color: #6b7280; }

/* CONFIRMED by the server. */
.settings-option.selected {
    border-color: #2563eb;
    background: #eff6ff;
    color: #1e40af;
}
.settings-option.selected::after {
    content: '✓';
    position: absolute;
    right: 12px;
    font-weight: 700;
}

/* The amber accent marks per_tooth as the non-default interpretation, matching
   the header indicator. */
.settings-option.selected[data-unit="per_tooth"] {
    border-color: #f59e0b;
    background: #fffbeb;
    color: #92400e;
}

/* IN FLIGHT — asked for, not yet confirmed. Must NOT look like .selected: showing
   it as chosen before the server agrees is the optimistic rendering this UI avoids.
   Dashed + ellipsis, no check mark. */
.settings-option.pending {
    border-style: dashed;
    border-color: #9ca3af;
    background: #f9fafb;
}
.settings-option.pending::after {
    content: '…';
    position: absolute;
    right: 12px;
    color: #6b7280;
    font-weight: 700;
}

.settings-offline {
    margin-top: 10px;
    padding: 8px 10px;
    border-radius: 6px;
    background: #fef2f2;
    color: #b91c1c;
    font-size: 12px;
}

/* ========== RESPONSIVE ========== */
@media (max-width: 1400px) {
    .container {
        max-width: 100%;
    }

    .data-table {
        font-size: 9px;
    }

    .triplet-input {
        width: 20px;
        height: 20px;
        font-size: 10px;
    }
}

/* =====================================================================
   PRINT — render ONLY the periodontal chart (patient header + colour
   legend key + measurement table + teeth visualization with all 4 bands,
   curves and numbers). Everything operational (top nav, voice controls,
   action buttons, logs) is hidden. Screen view is untouched. */
@media print {
    /* Reset chrome so the chart owns the page */
    body {
        background: #ffffff;
    }

    .container {
        max-width: none;
        overflow: visible;          /* don't clip the wide chart */
        box-shadow: none;
        border-radius: 0;
        margin: 0;
        padding: 0;
    }

    /* Hide everything that isn't the chart itself */
    .voice-controls,
    .header-actions,
    .logs-panel,
    .modal-overlay {
        display: none !important;
    }

    /* Keep the patient header band, drop its interactive frame */
    .header {
        border: none;
        box-shadow: none;
        padding: 0 0 6px;
        margin: 0 0 6px;
    }

    /* Legend becomes a static colour key: hide the checkbox, keep swatch+label */
    .line-legend {
        border: none;
        box-shadow: none;
        padding: 4px 0;
        margin: 0 0 4px;
    }
    .line-toggle input[type="checkbox"] {
        display: none !important;
    }
    .line-toggle {
        cursor: default;
    }

    /* Keep the recession unit on the printout — it documents how the chart was
       dictated — but drop the control affordance. */
    .recession-unit {
        cursor: default;
        padding: 2px 8px;
    }

    /* The chart is ~1300px wide; shrink to fit a landscape page.
       zoom reflows (Chrome) so column alignment is preserved. Fine-tune
       the rest with the print dialog's Scale control if needed. */
    .chart-content {
        zoom: 0.72;
    }

    /* Print the FULL width of every scroller, not just the visible slice.
       (.teeth-visualization keeps overflow:hidden so teeth stay clipped.) */
    .chart-section,
    .teeth-row {
        overflow: visible !important;
    }

    /* Don't split a section/band across pages mid-block */
    .chart-section {
        page-break-inside: avoid;
        break-inside: avoid;
    }

    /* Force colours (line curves, filled cells, tooth images, SVG) to print */
    .chart-content,
    .chart-content *,
    .line-legend,
    .line-swatch,
    .recession-unit,
    .recession-unit-dot,
    .teeth-row img,
    .pd-curve-layer,
    .pd-curve-layer * {
        -webkit-print-color-adjust: exact !important;
        print-color-adjust: exact !important;
    }

    @page {
        size: landscape;
        margin: 8mm;
    }
}

/* ========== DISABLED ACTIONS ========== */
/* A disabled .btn (Start, while a past exam is on screen) otherwise looks identical to
   a live one — the rules above set their own background and cursor. */
.btn:disabled {
    opacity: 0.5;
    cursor: not-allowed;
}
