Here's a quick and dirty way to do it. You can calculate the positions of the pots if you want, but it's probably faster to just build it, give it a test signal, and tweak it until it measures right.

simulate this circuit – Schematic created using CircuitLab
The extra arrows on the pots represent clockwise rotation of the knob, or upward movement of a slider. Offset
has a little bit of influence on Gain
, but the ratio of values keeps the interaction fairly low.
If you've had a basic course in opamp circuits, you might recognize this as a non-inverting amplifier, but with the gain network replaced by a pot to make it variable, and the ground (actually reference) terminal replaced by another pot to make it variable also.
This might also be a good way to show that an opamp itself has no concern whatsoever for "ground" as we call it. Its sole job is to move the output as needed to make the two inputs equal, within the constraints of its power supply of course, and in the direction specified by the input polarities. Ground does not appear anywhere in that job description.
Here's another way to do it, based on an inverting amplifier.

simulate this circuit
If you need a non-inverted output, then you'll need to add another inverting amp, exactly per the textbook, either up front to feed this or at the end to correct it.
And finally, one more way to do it, based this time on a summing amp, which is itself based on an inverting amp.

simulate this circuit
It works a little bit differently than the other two in that the offset is treated as its own legitimate input, whereas the other two change the reference that their single input is compared to.
Not all of the adjustments shown are necessary; Gain_offset
and one of the other two Gain
s could be fixed, leaving two real-time adjustments just like the other two circuits. This simply shows what's possible.
The loop back from the wiper to an end terminal is basically a functional safety net. It'll work exactly the same way without it...until the pot fails. At that point, you have a choice of open-circuit (infinite resistance) if you didn't use the loop back, or full-valued but finite resistance if you did. Either way, the wiper is required; it's the end terminal that's optional.
And here's one more interesting little bit. While keeping the operation linear, you can make the controls logarithmic (audio) in the last circuit by replacing the Gain_in
and maybe Gain_offset
pots with this.

simulate this circuit
This works because the summing node is held at 0V
by the opamp (provided it's not saturated of course), and so the 20k resistor is effectively to the same 0V
as the pot. Given a constant input, a loaded pot like this produces a response in between itself and the load that, with the ratio shown, pretty well approximates an audio volume control. Feed that through the standard model of a summing amp, using that intermediate voltage and the load resistor only, and you'll see that the idea works.
To look at signal linearity, as you asked for originally, you keep the pot constant and simplify the circuit, splitting the pot into two resistors in series, combining the lower one in parallel with the summing resistor, etc. So you should be able to see that the signal response is still linear; it's only the control response that isn't.
Note: There is no attempt to match the range of controllability between these circuits. The values shown are somewhat real-world, but still very much nominal.