* {
    margin: 0;
    padding: 0;
    box-sizing: border-box;
}

html, body { height: 100%; }

body {
    font-family: 'Arial', sans-serif;
    background: linear-gradient(135deg, #1a1a2e, #16213e);
    color: white;
    user-select: none;
    margin: 0;
    /* Fill the viewport as a column; no page scroll */
    height: 100vh;
    display: flex;
    flex-direction: column;
    overflow: hidden;
}

/* Brand on the left, title block centred in the remaining space. The logo is
   taken out of the centring by giving the text block flex:1 and reserving the
   same width on the right, so the title stays optically centred on the page. */
.game-header {
    display: flex;
    align-items: center;
    gap: 20px;
    padding: 8px 20px;
    background: rgba(0, 0, 0, 0.3);
    backdrop-filter: blur(10px);
    flex-shrink: 0;
}

.brand-link {
    flex-shrink: 0;
    width: 180px;
    display: block;
    line-height: 0;
}

.brand-logo {
    width: 100%;
    height: auto;
    display: block;
}

.header-text {
    flex: 1;
    text-align: center;
}

/* Balances the logo so the title block sits centred on the page, not on the
   space left over beside it. */
.game-header::after {
    content: '';
    flex-shrink: 0;
    width: 180px;
}

.game-title {
    font-size: 1.6rem;
    line-height: 1.1;
    background: linear-gradient(45deg, #ffd700, #ffeb3b);
    -webkit-background-clip: text;
    -webkit-text-fill-color: transparent;
    background-clip: text;
    margin-bottom: 4px;
}

.game-subtitle {
    font-size: 0.95rem;
    opacity: 0.9;
    margin-bottom: 6px;
}

.instructions {
    max-width: 900px;
    margin: 0 auto;
    font-size: 0.85rem;
    line-height: 1.35;
    opacity: 0.8;
}

.game-container {
    display: flex;
    justify-content: space-between;
    gap: 16px;
    max-width: 1600px;
    width: 100%;
    margin: 0 auto;
    padding: 12px 20px;
    /* Fill remaining viewport height under the header */
    flex: 1;
    min-height: 0;
    box-sizing: border-box;
}

.info-panel.left-panel {
    width: 300px;
    display: flex;
    flex-direction: column;
    gap: 15px;
    min-height: 0;
    overflow-y: auto;
}

/* Circuit Section — the workspace is its only child, so its height no longer
   depends on how long the current feedback message happens to be. */
.circuit-section {
    flex: 1;
    display: flex;
    flex-direction: column;
    min-width: 600px;
    min-height: 0;
}

/* Educational feedback, a card of its own in the left column — the colour is
   the heading, so it carries no title. Sized for a 300px column: left-aligned
   and free to wrap over several lines without disturbing anything else. */
.feedback-box {
    background: rgba(52, 152, 219, 0.95);
    color: white;
    padding: 14px 16px;
    border-radius: 8px;
    font-size: 0.78rem;
    line-height: 1.45;
    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);
    border: 1px solid rgba(255, 255, 255, 0.3);
    text-align: left;
    transition: background 0.3s ease, border-color 0.3s ease;
    font-family: 'Consolas', 'Monaco', 'Courier New', monospace;
    border-left: 4px solid rgba(255, 255, 255, 0.8);
}

.feedback-box.success {
    background: rgba(46, 204, 113, 0.95);
    border-left-color: #2ecc71;
}

.feedback-box.warning {
    background: rgba(230, 126, 34, 0.95);
    border-left-color: #f39c12;
}

.feedback-box.error {
    background: rgba(231, 76, 60, 0.95);
    border-left-color: #e74c3c;
}

.feedback-box.info {
    background: rgba(52, 152, 219, 0.95);
    border-left-color: #3498db;
}

/* Circuit Area */
.circuit-area {
    flex: 1;
    background: #2c3e50;
    border-radius: 15px;
    border: 3px solid #34495e;
    position: relative;
    min-height: 0; /* fill the section height instead of a fixed 550px */
    box-shadow: inset 0 0 20px rgba(0, 0, 0, 0.3);
    overflow: hidden;
}

.info-panel {
    width: 350px;
    display: flex;
    flex-direction: column;
    gap: 15px;
    min-height: 0;
    overflow-y: auto;
}

.panel {
    width: 100%;
    background: rgba(0, 0, 0, 0.4);
    border-radius: 12px;
    padding: 20px;
    backdrop-filter: blur(10px);
    border: 1px solid rgba(255, 255, 255, 0.1);
}

.panel h3 {
    color: #ffd700;
    margin-bottom: 15px;
    font-size: 1.1rem;
}

/* Link out to the build instructions. Card-shaped like its neighbours, but
   gold-on-dark so it reads as an action rather than another readout. */
.instructions-link {
    display: block;
    padding: 14px 16px;
    background: rgba(0, 0, 0, 0.4);
    border: 1px solid rgba(255, 215, 0, 0.35);
    border-radius: 12px;
    backdrop-filter: blur(10px);
    color: #ffd700;
    font-size: 0.95rem;
    font-weight: bold;
    text-align: center;
    text-decoration: none;
    transition: background 0.2s ease, border-color 0.2s ease;
}

.instructions-link:hover {
    background: rgba(255, 215, 0, 0.12);
    border-color: rgba(255, 215, 0, 0.75);
}

/* Buy This Kit card */
.kit-link { display: block; }
.kit-image {
    width: 100%;
    height: auto;
    display: block;
    border-radius: 8px;
    border: 1px solid rgba(255, 255, 255, 0.15);
}

.status-item {
    display: flex;
    align-items: center;
    margin: 8px 0;
    font-size: 0.9rem;
}

.status-dot {
    width: 12px;
    height: 12px;
    border-radius: 50%;
    margin-right: 10px;
    background: #e74c3c;
    transition: all 0.3s ease;
}

.status-dot.active {
    background: #2ecc71;
    box-shadow: 0 0 12px rgba(46, 204, 113, 0.6);
}

/* The track is the whole stage floor now, not a framed band: no background,
   border or radius of its own — the asphalt lives on .road, which is the layer
   that gets the 3D perspective. The track itself stays untransformed so the car
   inside it is not tilted along with the ground. */
.car-track {
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    width: 96%;
    height: 88%;
}

/* Horizon fade: dissolves the far edge of the road into the backdrop instead of
   cutting it off, which is what sells the vanishing point. */
.car-track::after {
    content: '';
    position: absolute;
    inset: 0 0 42% 0;
    background: linear-gradient(to bottom,
        #2c3e50 0%,
        rgba(44, 62, 80, 0.85) 45%,
        rgba(44, 62, 80, 0) 100%);
    pointer-events: none;
    z-index: 1;
}

/* Ground plane. Tipping the top edge away in Z gives the trapezoid (short side
   far) and makes the surface stripes converge toward the vanishing point — all
   for free, and without touching the JS: updateRoadScroll() still scrolls along
   the plane's local X axis.
   Pattern period = 40px, so it loops seamlessly under the JS modulo. */
.road {
    position: absolute;
    /* The plane runs well past the top of the track: the extra length is what
       recedes into the distance, and the horizon fade above swallows its end. */
    top: -70%;
    right: 0;
    bottom: 0;
    left: 0;
    transform: perspective(700px) rotateX(45deg);
    transform-origin: center bottom;
    backface-visibility: hidden;
    background-color: #2f4054;
    background-image: repeating-linear-gradient(
        to right,
        #3a4b5e 0px,
        #3a4b5e 20px,
        #33445673 20px,
        #334456 40px
    );
    background-size: 40px 100%;
}

/* Single knob for the whole car: every mounted part sizes itself as a
   percentage of its container, so changing this width rescales the lot. */
.car-chassis {
    position: absolute;
    width: 540px; /* +50% over the previous 360 */
    aspect-ratio: 360 / 230;
    /* Centred rather than offset from the bottom: the track is itself centred
       in the work area, so this centres the car in the area exactly, at any
       window size, without a percentage that would drift as the area resizes. */
    top: 50%;
    bottom: auto;
    left: 50%;
    transform: translate(-50%, -50%); /* car stays put; the road scrolls */
    z-index: 2;
}

/* The chassis is now the photographed part: plywood deck + vertical mast
   (parts/base.png, 903x544). aspect-ratio keeps it undistorted at any size. */
.car-body {
    position: absolute;
    width: 88.89%;
    aspect-ratio: 903 / 544;
    top: 4.35%;
    left: 50%;
    transform: translateX(-50%);
    background: url('parts/base.png') no-repeat center / 100% 100%;

    /* Mounting anchors, measured on base.png (see plan). Children position
       themselves against these instead of hardcoded pixels. */
    --mast-x: 21%;          /* mast centre line */
    --mast-top-y: 6.4%;     /* top face of the mast — motor + propeller */
    --deck-y: 76%;          /* usable strip of the deck surface */
    /* The battery is anchored by its centre and the switch by its base, so the
       same --deck-y would leave their terminals 5px apart. This value is
       derived from equalising the two (see plan): it puts the switch posts on
       the battery's positive line, which also makes the wire between them run
       dead level. A percentage, so it holds at any car scale. */
    --deck-switch-y: 74.35%;
    --deck-switch-x: 48%;
    --deck-battery-x: 80%;
    --motor-y: 12%;         /* motor straddles the top of the mast */
}

/* Car Wheels - sized off the chassis so they scale with it */
.car-wheel {
    position: absolute;
    width: 16.67%;
    aspect-ratio: 1;
    background: radial-gradient(circle, #2c3e50 30%, #34495e 70%, #2c3e50 100%);
    border: 3px solid #1a252f;
    border-radius: 50%;
    bottom: -4.35%;
    box-shadow:
        inset 0 2px 4px rgba(255, 255, 255, 0.2),
        inset 0 -2px 4px rgba(0, 0, 0, 0.6),
        0 2px 6px rgba(0, 0, 0, 0.4);
    transition: transform 0.1s linear;
}

.front-wheel {
    left: 2.78%;
}

.rear-wheel {
    right: 5.56%;
}

/* Visible spoke so wheel rotation reads while the car is stationary */
.car-wheel::before {
    content: '';
    position: absolute;
    top: 10%;
    left: 50%;
    width: 6.67%;
    height: 40%;
    background: #7f8c8d;
    border-radius: 2px;
    transform: translateX(-50%);
}
/* Hubcap center */
.car-wheel::after {
    content: '';
    position: absolute;
    top: 50%;
    left: 50%;
    width: 26.67%;
    aspect-ratio: 1;
    background: radial-gradient(circle, #95a5a6 30%, #34495e 100%);
    border: 2px solid #1a252f;
    border-radius: 50%;
    transform: translate(-50%, -50%);
}

/* DC Motor - the photographed part (parts/motor.png, 355x154 => ratio 2.305),
   mounted at the top of the mast with its shaft pointing forward, like the kit.
   Anchors below are measured on the image and drive both the propeller and the
   terminal positions in connections.js. */
.car-body .dc-motor {
    position: absolute;
    width: 31.25%;
    aspect-ratio: 355 / 154;
    left: var(--mast-x);
    top: var(--motor-y);
    /* -56.6% puts the motor *body* centre (not the image centre, which the
       shaft skews left) on the mast centre line. */
    transform: translate(-56.6%, -50%);
    background: url('parts/motor.png') no-repeat center / 100% 100%;
    cursor: default; /* Not draggable */

    /* Where the propeller hub sits on the output shaft. The shaft spans 0-24.8%
       of motor.png's width, so this rides its first third rather than floating
       at the very tip. The slipstream anchors here too, so it follows along. */
    --shaft-x: 8%;
    --shaft-y: 49.7%;
}

/* Body and shaft are painted into motor.png now. */
.motor-body,
.motor-shaft {
    display: none;
}

/* Slipstream off the propeller. Anchored to the shaft tip with the motor's own
   variables, so it follows and scales with the car without any JS positioning.
   parts/wind.gif is pure white on transparency, which reads straight over the
   dark asphalt. motor.js only toggles .blowing. */
.dc-motor .wind-stream {
    position: absolute;
    left: var(--shaft-x);
    top: var(--shaft-y);
    width: 220%; /* of the motor */
    aspect-ratio: 420 / 228;
    transform: translate(-100%, -50%); /* streams away to the left */
    background: url('parts/wind.gif') no-repeat center / 100% 100%;
    opacity: 0;
    transition: opacity 0.25s ease;
    pointer-events: none;
}

.dc-motor .wind-stream.blowing {
    opacity: 0.85;
}

/* Motor running backwards: the stream mirrors and swaps to the far side of the
   shaft, so the air is pushed over the car instead of ahead of it. The translate
   has to come first — put the scale first and it would mirror the offset too. */
.dc-motor .wind-stream.reversed {
    transform: translate(0, -50%) scaleX(-1);
}

/* Battery Pack - the photographed part (parts/batteryholder.png, 1052x615 =>
   ratio 1.711), a top-down view sitting flat on the deck right of the mast.
   No rotation needed: the image is already in its resting orientation. */
.car-body .battery-pack {
    position: absolute;
    width: 29.69%;
    aspect-ratio: 1052 / 615;
    left: var(--deck-battery-x);
    top: var(--deck-y);
    right: auto;
    cursor: default;
    transform: translate(-50%, -50%);
    background: url('parts/batteryholder.png') no-repeat center / 100% 100%;
}

/* Holder shell and cells are painted into batteryholder.png now. */
.car-body .battery-holder,
.car-body .battery-cell {
    display: none;
}

/* Short circuit: the pack runs red hot and shudders. Driven by
   batteryComponent.setOverheated(). The transform repeats the anchoring
   translate, or the shake would tear the pack off its mount. */
.car-body .battery-pack.overheating {
    animation: batteryOverheat 0.45s ease-in-out infinite;
    filter: saturate(1.5) hue-rotate(-14deg) brightness(1.15);
}

@keyframes batteryOverheat {
    0%, 100% {
        transform: translate(-50%, -50%);
        box-shadow: 0 0 18px 4px rgba(255, 76, 30, 0.55);
    }
    50% {
        transform: translate(calc(-50% + 1.5px), calc(-50% - 1px));
        box-shadow: 0 0 34px 10px rgba(255, 106, 30, 0.95);
    }
}

/* Sparks jumping at the contacts, on the pack's own free pseudo-elements. */
.car-body .battery-pack.overheating::before,
.car-body .battery-pack.overheating::after {
    content: '';
    position: absolute;
    left: 7.3%;
    width: 14%;
    aspect-ratio: 1;
    border-radius: 50%;
    background: radial-gradient(circle,
        #fffbe6 0%, #ffd84a 35%, rgba(255, 138, 30, 0.6) 65%, transparent 72%);
    pointer-events: none;
}

.car-body .battery-pack.overheating::before {
    top: 22.1%;
    transform: translate(-50%, -50%);
    animation: spark 0.28s steps(2, end) infinite;
}

.car-body .battery-pack.overheating::after {
    top: 55.9%;
    transform: translate(-50%, -50%);
    animation: spark 0.34s steps(2, end) infinite 0.12s;
}

@keyframes spark {
    0%, 100% { opacity: 0; }
    50%      { opacity: 1; }
}

/* Sits on the holder's bottom third — the only genuinely dark part of the
   photograph (luminance ~31, against 142-179 over the cells), so light type
   reads there without needing a chip of its own. */
.car-body .battery-label {
    position: absolute;
    bottom: auto;
    top: 84%;
    left: 50%;
    transform: translate(-50%, -50%);
    font-size: 0.6rem;
    font-weight: bold;
    color: #e3eaf1;
    text-shadow: 0 1px 2px rgba(0, 0, 0, 0.9);
    white-space: nowrap;
}

/* Switch on Car - two photographed states swapped by the .closed class that
   switch.js already toggles (parts/switch_open.png 301x290, switch_close.png
   285x166).

   The two shots are not the same size and their base plates are not the same
   scale, so they are aligned by the PLATE rather than by the box: each state
   gets a background-size that renders its plate at the same on-screen width,
   and `background-position: bottom` pins both plates to the element's bottom
   edge. Only the lever appears to move when toggling — the plate stays put. */
.car-body .switch {
    position: absolute;
    width: 15.625%;
    aspect-ratio: 50 / 46; /* the box, sized for the taller "open" shot */
    left: var(--deck-switch-x);
    top: var(--deck-switch-y);
    /* -100% rests the plate on the deck line; the lever rises above it. */
    transform: translate(-50%, -100%);
    cursor: pointer; /* Still clickable for toggling */
}

/* The photograph lives on a pseudo-element so it can be mirrored on its own.
   Flipping the switch itself would reverse the "Power Switch" caption it
   contains, and would overwrite the translate that anchors it. The plate is
   centred, so mirroring leaves it exactly where it was. */
.car-body .switch::before {
    content: '';
    position: absolute;
    inset: 0;
    transform: scaleX(-1);
    background: url('parts/switch_open.png') no-repeat bottom center;
    background-size: 92.6% auto; /* plate 299/301 of the image => 46px on screen */
}

.car-body .switch.closed::before {
    background-image: url('parts/switch_close.png');
    background-size: 94.3% auto; /* plate 278/285 => same 46px on screen */
}

/* Hover feedback via filter, not transform — the transform is doing the
   anchoring now, and switch.js used to overwrite it inline. */
.car-body .switch:hover {
    filter: brightness(1.25);
}

/* Lever and terminals are painted into the two switch images now. */
.car-body .switch-lever {
    display: none;
}

/* Crystal-dark chip (shared rule above) so it reads against the pale deck.
   Hung under the first section of the switch: its top edge is the base of the
   switch photograph (the box is bottom-anchored, so 100% is the image base),
   which puts the chip on the deck rather than over the part. Left-aligned with
   the plate, which is 92.6% wide and centred, so its edge falls at ~3.7%. */
.car-body .switch-label {
    position: absolute;
    bottom: auto;
    top: 100%;
    left: 3%;
    transform: none;
    padding: 2px 6px;
    font-size: 0.62rem;
    font-weight: bold;
    color: #e8eef5;
}

.motor-body {
    width: 70px; /* Made bigger */
    height: 40px; /* Made bigger */
    background: linear-gradient(to bottom, #95a5a6, #7f8c8d, #34495e);
    border: 2px solid #2c3e50;
    border-radius: 8px; /* Slightly more rounded */
    position: relative;
    box-shadow: 
        inset 0 2px 4px rgba(255, 255, 255, 0.3),
        inset 0 -2px 4px rgba(0, 0, 0, 0.5),
        0 2px 6px rgba(0, 0, 0, 0.4);
}

.motor-shaft {
    position: absolute;
    width: 25px; /* Made bigger */
    height: 6px; /* Made longer */
    background: linear-gradient(to bottom, #ecf0f1, #bdc3c7);
    border: 1px solid #95a5a6;
    border-radius: 3px;
    left: -15%;
    top: 15px; /* Adjusted for bigger size */
    transform: translateX(-50%);
    box-shadow: 0 1px 3px rgba(0, 0, 0, 0.4);
}

/* Printed straight onto the motor can, like a spec label on the real part.
   The body of motor.png is bright everywhere (luminance 160-248) and brightest
   in the 25-42% band, so dark type reads cleanly there. 56.6% is the body
   centre — the shaft skews the image centre to the left. */
.motor-label {
    position: absolute;
    top: 33%;
    left: 56.6%;
    transform: translate(-50%, -50%);
    font-size: 0.62rem;
    font-weight: bold;
    letter-spacing: 0.03em;
    color: #1c2b38;
    text-shadow: 0 1px 0 rgba(255, 255, 255, 0.45);
    white-space: nowrap;
}

/* Propeller - the photographed part (parts/propeller.png, 185x707 => ratio
   3.822 tall). Its hub sits at the centroid of the image, so centring the
   element on the shaft tip also puts the axis of rotation there. */
.propeller {
    position: absolute;
    width: 34.8%; /* of the motor, so it tracks the shaft length */
    aspect-ratio: 185 / 707;
    left: var(--shaft-x);
    top: var(--shaft-y);
    transform: translate(-50%, -50%);
    background: url('parts/propeller.png') no-repeat center / 100% 100%;
    animation-duration: 0.22s;
    animation-timing-function: linear;
    animation-iteration-count: infinite;
}

.propeller.spinning {
    animation-name: propellerSpin;
}

/* Spin is faked as a mirror flip about the horizontal axis, not a rotation:
   the part is a flat photo in 3/4 perspective, and rotating it in the page
   plane would read as the whole propeller tipping over. Interpolating scaleY
   from 1 to -1 passes through 0 — the blades swap top for bottom, which is
   what a real propeller does turning on its shaft.
   The centring translate has to be repeated here, otherwise the animation
   would drop it and the propeller would jump off the shaft. */
@keyframes propellerSpin {
    0%   { transform: translate(-50%, -50%) scaleY(1); }
    50%  { transform: translate(-50%, -50%) scaleY(-1); }
    100% { transform: translate(-50%, -50%) scaleY(1); }
}

/* Blades are painted into propeller.png now. */
.propeller-blade {
    display: none;
}




/* Dotted demo of the black wire's run, played on load to show the gesture.
   Built from the same route the real cable takes, so it teaches the actual
   path rather than a sketch of one. */
.circuit-wire-group.wire-demo .circuit-wire {
    background: none;
    border: 0;
    box-shadow: none;
    height: 4px;
    background-image: repeating-linear-gradient(
        to right,
        rgba(255, 255, 255, 0.85) 0 9px,
        transparent 9px 18px);
    animation: demoDashes 1.8s linear infinite;
}

.circuit-wire-group.wire-demo .circuit-wire::before,
.circuit-wire-group.wire-demo .circuit-wire::after,
.circuit-wire-group.wire-demo .circuit-wire-joint {
    display: none;
}

@keyframes demoDashes {
    to { background-position-x: 18px; }
}

/* The travelling dot — a finger tracing the path. */
.wire-demo-dot {
    position: absolute;
    width: 16px;
    height: 16px;
    margin: -8px 0 0 -8px;
    border-radius: 50%;
    background: radial-gradient(circle at 35% 35%, #fff 0%, #ffe9a8 45%, rgba(255, 208, 66, 0.2) 75%, transparent 78%);
    box-shadow: 0 0 12px 3px rgba(255, 208, 66, 0.8);
    pointer-events: none;
}

/* Caption for the demo. Same chip as the drag hint, so the two read as one
   voice, but parked under the route instead of chasing the pointer. */
.wire-demo-tip {
    position: absolute;
    z-index: 41;
    transform: translate(-50%, 14px);
    padding: 6px 10px;
    white-space: nowrap;
    border-radius: 8px;
    background: rgba(12, 18, 28, 0.92);
    border: 1px solid rgba(255, 215, 0, 0.45);
    backdrop-filter: blur(6px);
    color: #ffe9a8;
    font-size: 0.72rem;
    font-weight: bold;
    pointer-events: none;
    animation: demoTip 1.8s ease-in-out infinite;
}

@keyframes demoTip {
    0%, 100% { opacity: 0.75; }
    50%      { opacity: 1; }
}

/* Connection Points — small rings inside a gold bezel. The fill carries the
   polarity (red positive, black negative) so no glyph is needed; the switch
   posts sit on the positive line, and their terminal names end in -input /
   -output, so they fall through to the positive default.
   connections.js hands us the exact terminal coordinate; the centring is done
   here, which is why every other transform below has to repeat the translate
   instead of replacing it. */
.connection-point {
    position: absolute;
    width: 13px;
    height: 13px;
    box-sizing: border-box;
    background: #e03a2f;
    border: 2px solid #ffd042;
    border-radius: 50%;
    cursor: pointer;
    z-index: 20;
    transform: translate(-50%, -50%);
    transition: background 0.15s ease, border-color 0.15s ease,
                box-shadow 0.15s ease, transform 0.15s ease;
    box-shadow:
        0 0 0 1px rgba(0, 0, 0, 0.55),
        0 0 6px rgba(255, 208, 66, 0.5);
}

.connection-point[data-terminal$="negative"] {
    background: #14181e;
}

/* Without this a touch drag starting on a terminal scrolls the page instead of
   pulling the cable — the workspace is scrollable on small screens. */
.connection-point {
    touch-action: none;
}

/* The terminal the cable would land on if released now. */
.connection-point.drop-target {
    transform: translate(-50%, -50%) scale(1.5);
    border-color: #fff3c4;
    box-shadow:
        0 0 0 1px rgba(0, 0, 0, 0.55),
        0 0 14px 3px rgba(255, 215, 0, 1);
}

/* Dark glass chip for the switch caption, so the text carries its own surface
   and stays legible over the pale deck. */
.car-body .switch-label {
    background: rgba(12, 18, 28, 0.78);
    backdrop-filter: blur(6px);
    -webkit-backdrop-filter: blur(6px);
    border: 1px solid rgba(255, 255, 255, 0.22);
    border-radius: 4px;
    box-shadow: 0 2px 6px rgba(0, 0, 0, 0.5);
    line-height: 1;
    white-space: nowrap;
    pointer-events: none;
}

/* Polarity is carried by colour now, so the +/- glyphs in the markup are no
   longer drawn. Six captions over the parts read as clutter at this scale. */
.connection-point > strong {
    display: none;
}

/* Hover brightens the bezel, never the fill — the polarity colour has to stay
   readable at all times. */
.connection-point:hover {
    transform: translate(-50%, -50%) scale(1.35);
    border-color: #fff3c4;
    box-shadow:
        0 0 0 1px rgba(0, 0, 0, 0.55),
        0 0 10px rgba(255, 215, 0, 0.9);
}

/* Connected: the fill keeps its polarity colour and the gold bezel thickens,
   so a fully wired circuit still shows which terminal is which. */
.connection-point.connected {
    border-width: 3px;
    box-shadow:
        0 0 0 1px rgba(0, 0, 0, 0.5),
        0 0 12px 2px rgba(255, 208, 66, 0.95);
}

/* Applied by wires.js while a wire is selected, so the small rings announce
   themselves exactly when the user is looking for somewhere to plug in. */
.connection-point.ready-for-connection {
    animation: readyPulse 1.1s ease-in-out infinite;
}

/* Glow only, no scale — a ring that changes size while a wire is held reads as
   jitter next to the parts it sits on. */
@keyframes readyPulse {
    0%, 100% {
        border-color: #ffd042;
        box-shadow:
            0 0 0 1px rgba(0, 0, 0, 0.55),
            0 0 6px rgba(255, 208, 66, 0.6);
    }
    50% {
        border-color: #fff3c4;
        box-shadow:
            0 0 0 1px rgba(0, 0, 0, 0.55),
            0 0 16px 3px rgba(255, 208, 66, 0.95);
    }
}

/* The cable being pulled: same construction as a real one, just translucent so
   it reads as "not committed yet". */
.circuit-wire-group.wire-preview {
    opacity: 0.65;
    pointer-events: none;
}

/* Hint bubble that follows the cable end. A channel of its own, separate from
   the Guide card: that one reports the circuit, this one narrates the gesture. */
.drag-hint {
    position: absolute;
    z-index: 40;
    margin: -38px 0 0 14px;
    padding: 6px 10px;
    max-width: 190px;
    border-radius: 8px;
    background: rgba(12, 18, 28, 0.92);
    border: 1px solid rgba(255, 215, 0, 0.45);
    backdrop-filter: blur(6px);
    color: #ffe9a8;
    font-size: 0.72rem;
    font-weight: bold;
    line-height: 1.25;
    pointer-events: none;
    opacity: 0;
    transition: opacity 0.15s ease;
}

.drag-hint.visible {
    opacity: 1;
}

/* First beat: scissors nibbling at the insulation. */
.drag-hint.stripping::before {
    content: '✂️ ';
    display: inline-block;
    animation: snip 0.4s ease-in-out infinite;
}

@keyframes snip {
    0%, 100% { transform: rotate(-12deg); }
    50%      { transform: rotate(8deg); }
}

/* Circuit Wires — one group per connected wire, holding a rotated segment per
   leg of its route plus a round joint at each corner. */
.circuit-wire-group {
    position: absolute;
    inset: 0;
    pointer-events: none;
}

.circuit-wire {
    position: absolute;
    height: 8px;
    z-index: 10;
    border-radius: 4px;
    transition: all 0.3s ease;
    overflow: visible;
}

/* Corner cap: covers the notch two rotated rectangles leave where they meet,
   so an L or Z reads as one continuous cable. */
.circuit-wire-joint {
    position: absolute;
    width: 8px;
    height: 8px;
    margin: -4px 0 0 -4px;
    border-radius: 50%;
    z-index: 11;
    transition: all 0.3s ease;
}

.circuit-wire.red-circuit,
.circuit-wire-joint.red-circuit {
    background: linear-gradient(to bottom, #ff6b6b 0%, #e74c3c 50%, #c0392b 100%);
    border: 1px solid #a93226;
    box-shadow:
        inset 0 1px 2px rgba(255,255,255,0.4),
        inset 0 -1px 2px rgba(169,50,38,0.6),
        0 1px 4px rgba(0, 0, 0, 0.3);
}

.circuit-wire.white-circuit,
.circuit-wire-joint.white-circuit {
    background: linear-gradient(to bottom, #ff9f95 0%, #f4776a 50%, #e05a4c 100%);
    border: 1px solid #c9483c;
    box-shadow:
        inset 0 1px 2px rgba(255,255,255,0.5),
        inset 0 -1px 2px rgba(201,72,60,0.5),
        0 1px 4px rgba(0, 0, 0, 0.25);
}

.circuit-wire.black-circuit,
.circuit-wire-joint.black-circuit {
    background: linear-gradient(to bottom, #34495e 0%, #2c3e50 50%, #1b2631 100%);
    border: 1px solid #17202a;
    box-shadow: 
        inset 0 1px 2px rgba(255,255,255,0.2),
        inset 0 -1px 2px rgba(23,32,42,0.7),
        0 1px 4px rgba(0, 0, 0, 0.4);
}

/* Copper ferrules belong on the terminals only. A routed wire is several
   segments, so these are restricted to the first and last leg — otherwise every
   corner would sprout a pair of them. */
.circuit-wire.wire-start::before,
.circuit-wire.wire-end::after {
    content: '';
    position: absolute;
    width: 16px;
    height: 16px;
    background: linear-gradient(45deg, #cd7f32 0%, #ffd700 50%, #b8860b 100%);
    border: 1px solid #8b4513;
    border-radius: 50%;
    top: 50%;
    transform: translateY(-50%);
    box-shadow:
        inset 1px 1px 2px rgba(255, 215, 0, 0.8),
        inset -1px -1px 2px rgba(139, 69, 19, 0.6),
        0 0 6px rgba(255, 215, 0, 0.4);
}

.circuit-wire.wire-start::before {
    left: -8px;
}

.circuit-wire.wire-end::after {
    right: -8px;
}

/* Lit by game-logic.js marking the group, so every leg and corner of a routed
   wire glows together. */
.circuit-wire-group.active .circuit-wire,
.circuit-wire-group.active .circuit-wire-joint {
    filter: brightness(1.2);
    box-shadow:
        0 0 12px rgba(46, 204, 113, 0.8),
        0 0 6px rgba(46, 204, 113, 0.4);
}

/* The wire (or wires) forming a short circuit flash white hot. Listed after the
   .active rule so a shorted wire always reads as the alarm, never as running. */
.circuit-wire-group.shorted .circuit-wire,
.circuit-wire-group.shorted .circuit-wire-joint {
    animation: wireShort 0.32s ease-in-out infinite;
}

@keyframes wireShort {
    0%, 100% {
        filter: brightness(1.3);
        box-shadow: 0 0 10px rgba(255, 90, 30, 0.85);
    }
    50% {
        filter: brightness(2.6) saturate(0.4);
        box-shadow: 0 0 22px 6px rgba(255, 220, 120, 1);
    }
}

/* Electron Animation */
.electron {
    position: absolute;
    width: 6px;
    height: 6px;
    background: radial-gradient(circle, #ffffff 0%, #00ffff 40%, #0099ff 100%);
    border: 1px solid #00ccff;
    border-radius: 50%;
    z-index: 15;
    box-shadow: 
        0 0 8px rgba(0, 255, 255, 0.9),
        0 0 16px rgba(0, 153, 255, 0.6),
        inset 0 0 4px rgba(255, 255, 255, 0.8);
    transition: transform 0.1s ease-out;
    animation: electronGlow 1s ease-in-out infinite alternate;
}

@keyframes electronGlow {
    0% { 
        opacity: 0.8;
        box-shadow: 
            0 0 4px rgba(0, 255, 255, 0.7),
            0 0 8px rgba(0, 153, 255, 0.4),
            inset 0 0 2px rgba(255, 255, 255, 0.8);
    }
    100% { 
        opacity: 1;
        box-shadow: 
            0 0 8px rgba(0, 255, 255, 1),
            0 0 16px rgba(0, 153, 255, 0.8),
            inset 0 0 4px rgba(255, 255, 255, 1);
    }
}

/* Reset Button */
.reset-button {
    position: absolute;
    top: 15px;
    right: 15px;
    padding: 8px 16px;
    background: #e74c3c;
    color: white;
    border: none;
    border-radius: 5px;
    cursor: pointer;
    font-size: 0.9rem;
    z-index: 50;
    transition: background 0.3s ease;
}

.reset-button:hover {
    background: #c0392b;
}

/* Educational Info */
.concept-explanation {
    font-size: 0.85rem;
    line-height: 1.4;
    color: #ecf0f1;
}

.concept-explanation strong {
    color: #f39c12;
}

/* Mobile Responsive */
@media (max-width: 1200px) {
    /* Stacked layout can't fit one viewport — allow normal page scroll */
    body {
        height: auto;
        overflow: auto;
    }

    /* Header stacks: brand on top, then the title block, both full width. */
    .game-header {
        flex-direction: column;
        gap: 10px;
    }

    /* The spacer exists to keep the title optically centred beside the logo.
       Stacked there is nothing to balance, and it would just add dead height. */
    .game-header::after {
        display: none;
    }

    .brand-link {
        width: min(200px, 55%);
    }

    .game-container {
        flex-direction: column;
        flex: none;
        min-height: 0;
    }

    /* The left column dissolves so its three cards become items of the
       container itself — only then can `order` interleave them with the
       workspace, which sits between them in the reading order below. */
    .info-panel.left-panel {
        display: contents;
    }

    .circuit-section             { order: 1; }  /* the lab */
    .feedback-box                { order: 2; }
    .status-panel                { order: 3; }
    .instructions-link           { order: 4; }  /* stays right under Car Status */
    .info-panel:not(.left-panel) { order: 5; }  /* the reference panels */
    .kit-panel                   { order: 6; }  /* shop link, last */

    /* Everything full width and stacked; nothing scrolls sideways. */
    .info-panel {
        width: 100%;
        flex-direction: column;
        overflow: visible;
    }

    .panel,
    .feedback-box,
    .instructions-link {
        width: 100%;
        min-width: 0;
    }

    .feedback-box {
        font-size: 0.8rem;
    }

    .circuit-section {
        min-width: 0;
    }

    .circuit-area {
        height: 450px;
        min-height: 400px;
        min-width: 0;
        width: 100%;
        overflow-y: auto;
        padding: 20px;
    }

    /* One knob. Every part sizes and anchors itself in percentages, so the
       whole car — deck, motor, propeller, battery, switch, wheels — follows.
       Capped by the available width so it never spills on a narrow phone. */
    .car-chassis {
        width: min(380px, 95%);
    }

    .connection-point {
        width: 12px;
        height: 12px;
    }

    .reset-button {
        top: 10px;
        right: 10px;
        padding: 6px 12px;
        font-size: 0.8rem;
    }
}